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