##// END OF EJS Templates
Disable debug message
Fernando Perez -
Show More
@@ -1,270 +1,270 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 2875 2007-11-26 08:37:39Z fperez $
5 $Id: Logger.py 2875 2007-11-26 08:37:39Z 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 'Raw input log :',self.log_raw_input
173 print 'Timestamping :',self.timestamp
173 print 'Timestamping :',self.timestamp
174 print 'State :',state
174 print 'State :',state
175
175
176 def log(self,line_ori,line_mod,continuation=None):
176 def log(self,line_ori,line_mod,continuation=None):
177 """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*.
178
178
179 Inputs:
179 Inputs:
180
180
181 - line_ori: unmodified input line from the user. This is not
181 - line_ori: unmodified input line from the user. This is not
182 necessarily valid Python.
182 necessarily valid Python.
183
183
184 - line_mod: possibly modified input, such as the transformations made
184 - line_mod: possibly modified input, such as the transformations made
185 by input prefilters or input handlers of various kinds. This should
185 by input prefilters or input handlers of various kinds. This should
186 always be valid Python.
186 always be valid Python.
187
187
188 - continuation: if True, indicates this is part of multi-line input."""
188 - continuation: if True, indicates this is part of multi-line input."""
189
189
190 # update the auto _i tables
190 # update the auto _i tables
191 #print '***logging line',line_mod # dbg
191 #print '***logging line',line_mod # dbg
192 #print '***cache_count', self.shell.outputcache.prompt_count # dbg
192 #print '***cache_count', self.shell.outputcache.prompt_count # dbg
193 try:
193 try:
194 input_hist = self.shell.user_ns['_ih']
194 input_hist = self.shell.user_ns['_ih']
195 except:
195 except:
196 print 'userns:',self.shell.user_ns.keys()
196 #print 'userns:',self.shell.user_ns.keys() # dbg
197 return
197 return
198
198
199 out_cache = self.shell.outputcache
199 out_cache = self.shell.outputcache
200
200
201 # add blank lines if the input cache fell out of sync.
201 # add blank lines if the input cache fell out of sync.
202 if out_cache.do_full_cache and \
202 if out_cache.do_full_cache and \
203 out_cache.prompt_count +1 > len(input_hist):
203 out_cache.prompt_count +1 > len(input_hist):
204 input_hist.extend(['\n'] * (out_cache.prompt_count - len(input_hist)))
204 input_hist.extend(['\n'] * (out_cache.prompt_count - len(input_hist)))
205
205
206 if not continuation and line_mod:
206 if not continuation and line_mod:
207 self._iii = self._ii
207 self._iii = self._ii
208 self._ii = self._i
208 self._ii = self._i
209 self._i = self._i00
209 self._i = self._i00
210 # put back the final \n of every input line
210 # put back the final \n of every input line
211 self._i00 = line_mod+'\n'
211 self._i00 = line_mod+'\n'
212 #print 'Logging input:<%s>' % line_mod # dbg
212 #print 'Logging input:<%s>' % line_mod # dbg
213 input_hist.append(self._i00)
213 input_hist.append(self._i00)
214 #print '---[%s]' % (len(input_hist)-1,) # dbg
214 #print '---[%s]' % (len(input_hist)-1,) # dbg
215
215
216 # hackish access to top-level namespace to create _i1,_i2... dynamically
216 # hackish access to top-level namespace to create _i1,_i2... dynamically
217 to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
217 to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
218 if self.shell.outputcache.do_full_cache:
218 if self.shell.outputcache.do_full_cache:
219 in_num = self.shell.outputcache.prompt_count
219 in_num = self.shell.outputcache.prompt_count
220
220
221 # but if the opposite is true (a macro can produce multiple inputs
221 # but if the opposite is true (a macro can produce multiple inputs
222 # with no output display called), then bring the output counter in
222 # with no output display called), then bring the output counter in
223 # sync:
223 # sync:
224 last_num = len(input_hist)-1
224 last_num = len(input_hist)-1
225 if in_num != last_num:
225 if in_num != last_num:
226 in_num = self.shell.outputcache.prompt_count = last_num
226 in_num = self.shell.outputcache.prompt_count = last_num
227 new_i = '_i%s' % in_num
227 new_i = '_i%s' % in_num
228 if continuation:
228 if continuation:
229 self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod)
229 self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod)
230 input_hist[in_num] = self._i00
230 input_hist[in_num] = self._i00
231 to_main[new_i] = self._i00
231 to_main[new_i] = self._i00
232 self.shell.user_ns.update(to_main)
232 self.shell.user_ns.update(to_main)
233
233
234 # Write the log line, but decide which one according to the
234 # Write the log line, but decide which one according to the
235 # log_raw_input flag, set when the log is started.
235 # log_raw_input flag, set when the log is started.
236 if self.log_raw_input:
236 if self.log_raw_input:
237 self.log_write(line_ori)
237 self.log_write(line_ori)
238 else:
238 else:
239 self.log_write(line_mod)
239 self.log_write(line_mod)
240
240
241 def log_write(self,data,kind='input'):
241 def log_write(self,data,kind='input'):
242 """Write data to the log file, if active"""
242 """Write data to the log file, if active"""
243
243
244 #print 'data: %r' % data # dbg
244 #print 'data: %r' % data # dbg
245 if self.log_active and data:
245 if self.log_active and data:
246 write = self.logfile.write
246 write = self.logfile.write
247 if kind=='input':
247 if kind=='input':
248 if self.timestamp:
248 if self.timestamp:
249 write(time.strftime('# %a, %d %b %Y %H:%M:%S\n',
249 write(time.strftime('# %a, %d %b %Y %H:%M:%S\n',
250 time.localtime()))
250 time.localtime()))
251 write('%s\n' % data)
251 write('%s\n' % data)
252 elif kind=='output' and self.log_output:
252 elif kind=='output' and self.log_output:
253 odata = '\n'.join(['#[Out]# %s' % s
253 odata = '\n'.join(['#[Out]# %s' % s
254 for s in data.split('\n')])
254 for s in data.split('\n')])
255 write('%s\n' % odata)
255 write('%s\n' % odata)
256 self.logfile.flush()
256 self.logfile.flush()
257
257
258 def logstop(self):
258 def logstop(self):
259 """Fully stop logging and close log file.
259 """Fully stop logging and close log file.
260
260
261 In order to start logging again, a new logstart() call needs to be
261 In order to start logging again, a new logstart() call needs to be
262 made, possibly (though not necessarily) with a new filename, mode and
262 made, possibly (though not necessarily) with a new filename, mode and
263 other options."""
263 other options."""
264
264
265 self.logfile.close()
265 self.logfile.close()
266 self.logfile = None
266 self.logfile = None
267 self.log_active = False
267 self.log_active = False
268
268
269 # For backwards compatibility, in case anyone was using this.
269 # For backwards compatibility, in case anyone was using this.
270 close_log = logstop
270 close_log = logstop
General Comments 0
You need to be logged in to leave comments. Login now