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