##// END OF EJS Templates
Minor fix to diagnostics, forgot to list raw input.
fperez -
Show More
@@ -1,257 +1,258 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 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 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 print 'Raw input log :',self.log_raw_input
172 173 print 'Timestamping :',self.timestamp
173 174 print 'State :',state
174 175
175 176 def log(self,line_ori,line_mod,continuation=None):
176 177 """Write the line to a log and create input cache variables _i*.
177 178
178 179 Inputs:
179 180
180 181 - line_ori: unmodified input line from the user. This is not
181 182 necessarily valid Python.
182 183
183 184 - line_mod: possibly modified input, such as the transformations made
184 185 by input prefilters or input handlers of various kinds. This should
185 186 always be valid Python.
186 187
187 188 - continuation: if True, indicates this is part of multi-line input."""
188 189
189 190 # update the auto _i tables
190 191 #print '***logging line',line_mod # dbg
191 192 #print '***cache_count', self.shell.outputcache.prompt_count # dbg
192 193 try:
193 194 input_hist = self.shell.user_ns['_ih']
194 195 except:
195 196 print 'userns:',self.shell.user_ns.keys()
196 197 return
197 198
198 199 if not continuation and line_mod:
199 200 self._iii = self._ii
200 201 self._ii = self._i
201 202 self._i = self._i00
202 203 # put back the final \n of every input line
203 204 self._i00 = line_mod+'\n'
204 205 #print 'Logging input:<%s>' % line_mod # dbg
205 206 input_hist.append(self._i00)
206 207 #print '---[%s]' % (len(input_hist)-1,) # dbg
207 208
208 209 # hackish access to top-level namespace to create _i1,_i2... dynamically
209 210 to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
210 211 if self.shell.outputcache.do_full_cache:
211 212 in_num = self.shell.outputcache.prompt_count
212 213 # add blank lines if the input cache fell out of sync. This can
213 214 # happen for embedded instances which get killed via C-D and then
214 215 # get resumed.
215 216 while in_num >= len(input_hist):
216 217 input_hist.append('\n')
217 218 # but if the opposite is true (a macro can produce multiple inputs
218 219 # with no output display called), then bring the output counter in
219 220 # sync:
220 221 last_num = len(input_hist)-1
221 222 if in_num != last_num:
222 223 in_num = self.shell.outputcache.prompt_count = last_num
223 224 new_i = '_i%s' % in_num
224 225 if continuation:
225 226 self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod)
226 227 input_hist[in_num] = self._i00
227 228 to_main[new_i] = self._i00
228 229 self.shell.user_ns.update(to_main)
229 230
230 231 # Write the log line, but decide which one according to the
231 232 # log_raw_input flag, set when the log is started.
232 233 if self.log_raw_input:
233 234 self.log_write(line_ori)
234 235 else:
235 236 self.log_write(line_mod)
236 237
237 238 def log_write(self,data,kind='input'):
238 239 """Write data to the log file, if active"""
239 240
240 241 #print 'data: %r' % data # dbg
241 242 if self.log_active and data:
242 243 write = self.logfile.write
243 244 if kind=='input':
244 245 if self.timestamp:
245 246 write(time.strftime('# %a, %d %b %Y %H:%M:%S\n',
246 247 time.localtime()))
247 248 write('%s\n' % data)
248 249 elif kind=='output' and self.log_output:
249 250 odata = '\n'.join(['#[Out]# %s' % s
250 251 for s in data.split('\n')])
251 252 write('%s\n' % odata)
252 253 self.logfile.flush()
253 254
254 255 def close_log(self):
255 256 self.logfile.close()
256 257 self.logfile = None
257 258 self.logfname = ''
General Comments 0
You need to be logged in to leave comments. Login now