##// END OF EJS Templates
Minor fix to diagnostics, forgot to list raw input.
fperez -
Show More
@@ -1,257 +1,258 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Logger class for IPython's logging facilities.
3 Logger class for IPython's logging facilities.
4
4
5 $Id: Logger.py 1335 2006-05-30 06:02:44Z fperez $
5 $Id: Logger.py 1336 2006-05-30 06:09:15Z fperez $
6 """
6 """
7
7
8 #*****************************************************************************
8 #*****************************************************************************
9 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
9 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #*****************************************************************************
14 #*****************************************************************************
15
15
16 #****************************************************************************
16 #****************************************************************************
17 # Modules and globals
17 # Modules and globals
18
18
19 from IPython import Release
19 from IPython import Release
20 __author__ = '%s <%s>\n%s <%s>' % \
20 __author__ = '%s <%s>\n%s <%s>' % \
21 ( Release.authors['Janko'] + Release.authors['Fernando'] )
21 ( Release.authors['Janko'] + Release.authors['Fernando'] )
22 __license__ = Release.license
22 __license__ = Release.license
23
23
24 # Python standard modules
24 # Python standard modules
25 import glob
25 import glob
26 import os
26 import os
27 import time
27 import time
28
28
29 #****************************************************************************
29 #****************************************************************************
30 # FIXME: This class isn't a mixin anymore, but it still needs attributes from
30 # FIXME: This class isn't a mixin anymore, but it still needs attributes from
31 # ipython and does input cache management. Finish cleanup later...
31 # ipython and does input cache management. Finish cleanup later...
32
32
33 class Logger(object):
33 class Logger(object):
34 """A Logfile class with different policies for file creation"""
34 """A Logfile class with different policies for file creation"""
35
35
36 def __init__(self,shell,logfname='Logger.log',loghead='',logmode='over'):
36 def __init__(self,shell,logfname='Logger.log',loghead='',logmode='over'):
37
37
38 self._i00,self._i,self._ii,self._iii = '','','',''
38 self._i00,self._i,self._ii,self._iii = '','','',''
39
39
40 # this is the full ipython instance, we need some attributes from it
40 # this is the full ipython instance, we need some attributes from it
41 # which won't exist until later. What a mess, clean up later...
41 # which won't exist until later. What a mess, clean up later...
42 self.shell = shell
42 self.shell = shell
43
43
44 self.logfname = logfname
44 self.logfname = logfname
45 self.loghead = loghead
45 self.loghead = loghead
46 self.logmode = logmode
46 self.logmode = logmode
47 self.logfile = None
47 self.logfile = None
48
48
49 # Whether to log raw or processed input
49 # Whether to log raw or processed input
50 self.log_raw_input = False
50 self.log_raw_input = False
51
51
52 # whether to also log output
52 # whether to also log output
53 self.log_output = False
53 self.log_output = False
54
54
55 # whether to put timestamps before each log entry
55 # whether to put timestamps before each log entry
56 self.timestamp = False
56 self.timestamp = False
57
57
58 # activity control flags
58 # activity control flags
59 self.log_active = False
59 self.log_active = False
60
60
61 # logmode is a validated property
61 # logmode is a validated property
62 def _set_mode(self,mode):
62 def _set_mode(self,mode):
63 if mode not in ['append','backup','global','over','rotate']:
63 if mode not in ['append','backup','global','over','rotate']:
64 raise ValueError,'invalid log mode %s given' % mode
64 raise ValueError,'invalid log mode %s given' % mode
65 self._logmode = mode
65 self._logmode = mode
66
66
67 def _get_mode(self):
67 def _get_mode(self):
68 return self._logmode
68 return self._logmode
69
69
70 logmode = property(_get_mode,_set_mode)
70 logmode = property(_get_mode,_set_mode)
71
71
72 def logstart(self,logfname=None,loghead=None,logmode=None,
72 def logstart(self,logfname=None,loghead=None,logmode=None,
73 log_output=False,timestamp=False,log_raw_input=False):
73 log_output=False,timestamp=False,log_raw_input=False):
74 """Generate a new log-file with a default header.
74 """Generate a new log-file with a default header.
75
75
76 Raises RuntimeError if the log has already been started"""
76 Raises RuntimeError if the log has already been started"""
77
77
78 if self.logfile is not None:
78 if self.logfile is not None:
79 raise RuntimeError('Log file is already active: %s' %
79 raise RuntimeError('Log file is already active: %s' %
80 self.logfname)
80 self.logfname)
81
81
82 self.log_active = True
82 self.log_active = True
83
83
84 # The parameters can override constructor defaults
84 # The parameters can override constructor defaults
85 if logfname is not None: self.logfname = logfname
85 if logfname is not None: self.logfname = logfname
86 if loghead is not None: self.loghead = loghead
86 if loghead is not None: self.loghead = loghead
87 if logmode is not None: self.logmode = logmode
87 if logmode is not None: self.logmode = logmode
88
88
89 # Parameters not part of the constructor
89 # Parameters not part of the constructor
90 self.timestamp = timestamp
90 self.timestamp = timestamp
91 self.log_output = log_output
91 self.log_output = log_output
92 self.log_raw_input = log_raw_input
92 self.log_raw_input = log_raw_input
93
93
94 # init depending on the log mode requested
94 # init depending on the log mode requested
95 isfile = os.path.isfile
95 isfile = os.path.isfile
96 logmode = self.logmode
96 logmode = self.logmode
97
97
98 if logmode == 'append':
98 if logmode == 'append':
99 self.logfile = open(self.logfname,'a')
99 self.logfile = open(self.logfname,'a')
100
100
101 elif logmode == 'backup':
101 elif logmode == 'backup':
102 if isfile(self.logfname):
102 if isfile(self.logfname):
103 backup_logname = self.logfname+'~'
103 backup_logname = self.logfname+'~'
104 # Manually remove any old backup, since os.rename may fail
104 # Manually remove any old backup, since os.rename may fail
105 # under Windows.
105 # under Windows.
106 if isfile(backup_logname):
106 if isfile(backup_logname):
107 os.remove(backup_logname)
107 os.remove(backup_logname)
108 os.rename(self.logfname,backup_logname)
108 os.rename(self.logfname,backup_logname)
109 self.logfile = open(self.logfname,'w')
109 self.logfile = open(self.logfname,'w')
110
110
111 elif logmode == 'global':
111 elif logmode == 'global':
112 self.logfname = os.path.join(self.shell.home_dir,self.logfname)
112 self.logfname = os.path.join(self.shell.home_dir,self.logfname)
113 self.logfile = open(self.logfname, 'a')
113 self.logfile = open(self.logfname, 'a')
114
114
115 elif logmode == 'over':
115 elif logmode == 'over':
116 if isfile(self.logfname):
116 if isfile(self.logfname):
117 os.remove(self.logfname)
117 os.remove(self.logfname)
118 self.logfile = open(self.logfname,'w')
118 self.logfile = open(self.logfname,'w')
119
119
120 elif logmode == 'rotate':
120 elif logmode == 'rotate':
121 if isfile(self.logfname):
121 if isfile(self.logfname):
122 if isfile(self.logfname+'.001~'):
122 if isfile(self.logfname+'.001~'):
123 old = glob.glob(self.logfname+'.*~')
123 old = glob.glob(self.logfname+'.*~')
124 old.sort()
124 old.sort()
125 old.reverse()
125 old.reverse()
126 for f in old:
126 for f in old:
127 root, ext = os.path.splitext(f)
127 root, ext = os.path.splitext(f)
128 num = int(ext[1:-1])+1
128 num = int(ext[1:-1])+1
129 os.rename(f, root+'.'+`num`.zfill(3)+'~')
129 os.rename(f, root+'.'+`num`.zfill(3)+'~')
130 os.rename(self.logfname, self.logfname+'.001~')
130 os.rename(self.logfname, self.logfname+'.001~')
131 self.logfile = open(self.logfname,'w')
131 self.logfile = open(self.logfname,'w')
132
132
133 if logmode != 'append':
133 if logmode != 'append':
134 self.logfile.write(self.loghead)
134 self.logfile.write(self.loghead)
135
135
136 self.logfile.flush()
136 self.logfile.flush()
137
137
138 def switch_log(self,val):
138 def switch_log(self,val):
139 """Switch logging on/off. val should be ONLY a boolean."""
139 """Switch logging on/off. val should be ONLY a boolean."""
140
140
141 if val not in [False,True,0,1]:
141 if val not in [False,True,0,1]:
142 raise ValueError, \
142 raise ValueError, \
143 'Call switch_log ONLY with a boolean argument, not with:',val
143 'Call switch_log ONLY with a boolean argument, not with:',val
144
144
145 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
145 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
146
146
147 if self.logfile is None:
147 if self.logfile is None:
148 print """
148 print """
149 Logging hasn't been started yet (use logstart for that).
149 Logging hasn't been started yet (use logstart for that).
150
150
151 %logon/%logoff are for temporarily starting and stopping logging for a logfile
151 %logon/%logoff are for temporarily starting and stopping logging for a logfile
152 which already exists. But you must first start the logging process with
152 which already exists. But you must first start the logging process with
153 %logstart (optionally giving a logfile name)."""
153 %logstart (optionally giving a logfile name)."""
154
154
155 else:
155 else:
156 if self.log_active == val:
156 if self.log_active == val:
157 print 'Logging is already',label[val]
157 print 'Logging is already',label[val]
158 else:
158 else:
159 print 'Switching logging',label[val]
159 print 'Switching logging',label[val]
160 self.log_active = not self.log_active
160 self.log_active = not self.log_active
161 self.log_active_out = self.log_active
161 self.log_active_out = self.log_active
162
162
163 def logstate(self):
163 def logstate(self):
164 """Print a status message about the logger."""
164 """Print a status message about the logger."""
165 if self.logfile is None:
165 if self.logfile is None:
166 print 'Logging has not been activated.'
166 print 'Logging has not been activated.'
167 else:
167 else:
168 state = self.log_active and 'active' or 'temporarily suspended'
168 state = self.log_active and 'active' or 'temporarily suspended'
169 print 'Filename :',self.logfname
169 print 'Filename :',self.logfname
170 print 'Mode :',self.logmode
170 print 'Mode :',self.logmode
171 print 'Output logging :',self.log_output
171 print 'Output logging :',self.log_output
172 print 'Raw input log :',self.log_raw_input
172 print 'Timestamping :',self.timestamp
173 print 'Timestamping :',self.timestamp
173 print 'State :',state
174 print 'State :',state
174
175
175 def log(self,line_ori,line_mod,continuation=None):
176 def log(self,line_ori,line_mod,continuation=None):
176 """Write the line to a log and create input cache variables _i*.
177 """Write the line to a log and create input cache variables _i*.
177
178
178 Inputs:
179 Inputs:
179
180
180 - line_ori: unmodified input line from the user. This is not
181 - line_ori: unmodified input line from the user. This is not
181 necessarily valid Python.
182 necessarily valid Python.
182
183
183 - line_mod: possibly modified input, such as the transformations made
184 - line_mod: possibly modified input, such as the transformations made
184 by input prefilters or input handlers of various kinds. This should
185 by input prefilters or input handlers of various kinds. This should
185 always be valid Python.
186 always be valid Python.
186
187
187 - continuation: if True, indicates this is part of multi-line input."""
188 - continuation: if True, indicates this is part of multi-line input."""
188
189
189 # update the auto _i tables
190 # update the auto _i tables
190 #print '***logging line',line_mod # dbg
191 #print '***logging line',line_mod # dbg
191 #print '***cache_count', self.shell.outputcache.prompt_count # dbg
192 #print '***cache_count', self.shell.outputcache.prompt_count # dbg
192 try:
193 try:
193 input_hist = self.shell.user_ns['_ih']
194 input_hist = self.shell.user_ns['_ih']
194 except:
195 except:
195 print 'userns:',self.shell.user_ns.keys()
196 print 'userns:',self.shell.user_ns.keys()
196 return
197 return
197
198
198 if not continuation and line_mod:
199 if not continuation and line_mod:
199 self._iii = self._ii
200 self._iii = self._ii
200 self._ii = self._i
201 self._ii = self._i
201 self._i = self._i00
202 self._i = self._i00
202 # put back the final \n of every input line
203 # put back the final \n of every input line
203 self._i00 = line_mod+'\n'
204 self._i00 = line_mod+'\n'
204 #print 'Logging input:<%s>' % line_mod # dbg
205 #print 'Logging input:<%s>' % line_mod # dbg
205 input_hist.append(self._i00)
206 input_hist.append(self._i00)
206 #print '---[%s]' % (len(input_hist)-1,) # dbg
207 #print '---[%s]' % (len(input_hist)-1,) # dbg
207
208
208 # hackish access to top-level namespace to create _i1,_i2... dynamically
209 # hackish access to top-level namespace to create _i1,_i2... dynamically
209 to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
210 to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
210 if self.shell.outputcache.do_full_cache:
211 if self.shell.outputcache.do_full_cache:
211 in_num = self.shell.outputcache.prompt_count
212 in_num = self.shell.outputcache.prompt_count
212 # add blank lines if the input cache fell out of sync. This can
213 # add blank lines if the input cache fell out of sync. This can
213 # happen for embedded instances which get killed via C-D and then
214 # happen for embedded instances which get killed via C-D and then
214 # get resumed.
215 # get resumed.
215 while in_num >= len(input_hist):
216 while in_num >= len(input_hist):
216 input_hist.append('\n')
217 input_hist.append('\n')
217 # but if the opposite is true (a macro can produce multiple inputs
218 # but if the opposite is true (a macro can produce multiple inputs
218 # with no output display called), then bring the output counter in
219 # with no output display called), then bring the output counter in
219 # sync:
220 # sync:
220 last_num = len(input_hist)-1
221 last_num = len(input_hist)-1
221 if in_num != last_num:
222 if in_num != last_num:
222 in_num = self.shell.outputcache.prompt_count = last_num
223 in_num = self.shell.outputcache.prompt_count = last_num
223 new_i = '_i%s' % in_num
224 new_i = '_i%s' % in_num
224 if continuation:
225 if continuation:
225 self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod)
226 self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod)
226 input_hist[in_num] = self._i00
227 input_hist[in_num] = self._i00
227 to_main[new_i] = self._i00
228 to_main[new_i] = self._i00
228 self.shell.user_ns.update(to_main)
229 self.shell.user_ns.update(to_main)
229
230
230 # Write the log line, but decide which one according to the
231 # Write the log line, but decide which one according to the
231 # log_raw_input flag, set when the log is started.
232 # log_raw_input flag, set when the log is started.
232 if self.log_raw_input:
233 if self.log_raw_input:
233 self.log_write(line_ori)
234 self.log_write(line_ori)
234 else:
235 else:
235 self.log_write(line_mod)
236 self.log_write(line_mod)
236
237
237 def log_write(self,data,kind='input'):
238 def log_write(self,data,kind='input'):
238 """Write data to the log file, if active"""
239 """Write data to the log file, if active"""
239
240
240 #print 'data: %r' % data # dbg
241 #print 'data: %r' % data # dbg
241 if self.log_active and data:
242 if self.log_active and data:
242 write = self.logfile.write
243 write = self.logfile.write
243 if kind=='input':
244 if kind=='input':
244 if self.timestamp:
245 if self.timestamp:
245 write(time.strftime('# %a, %d %b %Y %H:%M:%S\n',
246 write(time.strftime('# %a, %d %b %Y %H:%M:%S\n',
246 time.localtime()))
247 time.localtime()))
247 write('%s\n' % data)
248 write('%s\n' % data)
248 elif kind=='output' and self.log_output:
249 elif kind=='output' and self.log_output:
249 odata = '\n'.join(['#[Out]# %s' % s
250 odata = '\n'.join(['#[Out]# %s' % s
250 for s in data.split('\n')])
251 for s in data.split('\n')])
251 write('%s\n' % odata)
252 write('%s\n' % odata)
252 self.logfile.flush()
253 self.logfile.flush()
253
254
254 def close_log(self):
255 def close_log(self):
255 self.logfile.close()
256 self.logfile.close()
256 self.logfile = None
257 self.logfile = None
257 self.logfname = ''
258 self.logfname = ''
General Comments 0
You need to be logged in to leave comments. Login now