##// END OF EJS Templates
py3: cast error message to localstr in blackbox.py...
Gregory Szorc -
r35685:de598e84 default
parent child Browse files
Show More
@@ -1,244 +1,245 b''
1 1 # blackbox.py - log repository events to a file for post-mortem debugging
2 2 #
3 3 # Copyright 2010 Nicolas Dumazet
4 4 # Copyright 2013 Facebook, Inc.
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 """log repository events to a blackbox for debugging
10 10
11 11 Logs event information to .hg/blackbox.log to help debug and diagnose problems.
12 12 The events that get logged can be configured via the blackbox.track config key.
13 13
14 14 Examples::
15 15
16 16 [blackbox]
17 17 track = *
18 18 # dirty is *EXPENSIVE* (slow);
19 19 # each log entry indicates `+` if the repository is dirty, like :hg:`id`.
20 20 dirty = True
21 21 # record the source of log messages
22 22 logsource = True
23 23
24 24 [blackbox]
25 25 track = command, commandfinish, commandexception, exthook, pythonhook
26 26
27 27 [blackbox]
28 28 track = incoming
29 29
30 30 [blackbox]
31 31 # limit the size of a log file
32 32 maxsize = 1.5 MB
33 33 # rotate up to N log files when the current one gets too big
34 34 maxfiles = 3
35 35
36 36 """
37 37
38 38 from __future__ import absolute_import
39 39
40 40 import errno
41 41 import re
42 42
43 43 from mercurial.i18n import _
44 44 from mercurial.node import hex
45 45
46 46 from mercurial import (
47 encoding,
47 48 registrar,
48 49 ui as uimod,
49 50 util,
50 51 )
51 52
52 53 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
53 54 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
54 55 # be specifying the version(s) of Mercurial they are tested with, or
55 56 # leave the attribute unspecified.
56 57 testedwith = 'ships-with-hg-core'
57 58
58 59 cmdtable = {}
59 60 command = registrar.command(cmdtable)
60 61
61 62 configtable = {}
62 63 configitem = registrar.configitem(configtable)
63 64
64 65 configitem('blackbox', 'dirty',
65 66 default=False,
66 67 )
67 68 configitem('blackbox', 'maxsize',
68 69 default='1 MB',
69 70 )
70 71 configitem('blackbox', 'logsource',
71 72 default=False,
72 73 )
73 74 configitem('blackbox', 'maxfiles',
74 75 default=7,
75 76 )
76 77 configitem('blackbox', 'track',
77 78 default=lambda: ['*'],
78 79 )
79 80
80 81 lastui = None
81 82
82 83 def _openlogfile(ui, vfs):
83 84 def rotate(oldpath, newpath):
84 85 try:
85 86 vfs.unlink(newpath)
86 87 except OSError as err:
87 88 if err.errno != errno.ENOENT:
88 89 ui.debug("warning: cannot remove '%s': %s\n" %
89 90 (newpath, err.strerror))
90 91 try:
91 92 if newpath:
92 93 vfs.rename(oldpath, newpath)
93 94 except OSError as err:
94 95 if err.errno != errno.ENOENT:
95 96 ui.debug("warning: cannot rename '%s' to '%s': %s\n" %
96 97 (newpath, oldpath, err.strerror))
97 98
98 99 maxsize = ui.configbytes('blackbox', 'maxsize')
99 100 name = 'blackbox.log'
100 101 if maxsize > 0:
101 102 try:
102 103 st = vfs.stat(name)
103 104 except OSError:
104 105 pass
105 106 else:
106 107 if st.st_size >= maxsize:
107 108 path = vfs.join(name)
108 109 maxfiles = ui.configint('blackbox', 'maxfiles')
109 110 for i in xrange(maxfiles - 1, 1, -1):
110 111 rotate(oldpath='%s.%d' % (path, i - 1),
111 112 newpath='%s.%d' % (path, i))
112 113 rotate(oldpath=path,
113 114 newpath=maxfiles > 0 and path + '.1')
114 115 return vfs(name, 'a')
115 116
116 117 def wrapui(ui):
117 118 class blackboxui(ui.__class__):
118 119 @property
119 120 def _bbvfs(self):
120 121 vfs = None
121 122 repo = getattr(self, '_bbrepo', None)
122 123 if repo:
123 124 vfs = repo.vfs
124 125 if not vfs.isdir('.'):
125 126 vfs = None
126 127 return vfs
127 128
128 129 @util.propertycache
129 130 def track(self):
130 131 return self.configlist('blackbox', 'track')
131 132
132 133 def log(self, event, *msg, **opts):
133 134 global lastui
134 135 super(blackboxui, self).log(event, *msg, **opts)
135 136
136 137 if not '*' in self.track and not event in self.track:
137 138 return
138 139
139 140 if self._bbvfs:
140 141 ui = self
141 142 else:
142 143 # certain ui instances exist outside the context of
143 144 # a repo, so just default to the last blackbox that
144 145 # was seen.
145 146 ui = lastui
146 147
147 148 if not ui:
148 149 return
149 150 vfs = ui._bbvfs
150 151 if not vfs:
151 152 return
152 153
153 154 repo = getattr(ui, '_bbrepo', None)
154 155 if not lastui or repo:
155 156 lastui = ui
156 157 if getattr(ui, '_bbinlog', False):
157 158 # recursion and failure guard
158 159 return
159 160 ui._bbinlog = True
160 161 default = self.configdate('devel', 'default-date')
161 162 date = util.datestr(default, '%Y/%m/%d %H:%M:%S')
162 163 user = util.getuser()
163 164 pid = '%d' % util.getpid()
164 165 formattedmsg = msg[0] % msg[1:]
165 166 rev = '(unknown)'
166 167 changed = ''
167 168 if repo:
168 169 ctx = repo[None]
169 170 parents = ctx.parents()
170 171 rev = ('+'.join([hex(p.node()) for p in parents]))
171 172 if (ui.configbool('blackbox', 'dirty') and
172 173 ctx.dirty(missing=True, merge=False, branch=False)):
173 174 changed = '+'
174 175 if ui.configbool('blackbox', 'logsource'):
175 176 src = ' [%s]' % event
176 177 else:
177 178 src = ''
178 179 try:
179 180 fmt = '%s %s @%s%s (%s)%s> %s'
180 181 args = (date, user, rev, changed, pid, src, formattedmsg)
181 182 with _openlogfile(ui, vfs) as fp:
182 183 fp.write(fmt % args)
183 184 except (IOError, OSError) as err:
184 185 self.debug('warning: cannot write to blackbox.log: %s\n' %
185 err.strerror)
186 encoding.strtolocal(err.strerror))
186 187 # do not restore _bbinlog intentionally to avoid failed
187 188 # logging again
188 189 else:
189 190 ui._bbinlog = False
190 191
191 192 def setrepo(self, repo):
192 193 self._bbrepo = repo
193 194
194 195 ui.__class__ = blackboxui
195 196 uimod.ui = blackboxui
196 197
197 198 def uisetup(ui):
198 199 wrapui(ui)
199 200
200 201 def reposetup(ui, repo):
201 202 # During 'hg pull' a httppeer repo is created to represent the remote repo.
202 203 # It doesn't have a .hg directory to put a blackbox in, so we don't do
203 204 # the blackbox setup for it.
204 205 if not repo.local():
205 206 return
206 207
207 208 if util.safehasattr(ui, 'setrepo'):
208 209 ui.setrepo(repo)
209 210
210 211 # Set lastui even if ui.log is not called. This gives blackbox a
211 212 # fallback place to log.
212 213 global lastui
213 214 if lastui is None:
214 215 lastui = ui
215 216
216 217 repo._wlockfreeprefix.add('blackbox.log')
217 218
218 219 @command('^blackbox',
219 220 [('l', 'limit', 10, _('the number of events to show')),
220 221 ],
221 222 _('hg blackbox [OPTION]...'))
222 223 def blackbox(ui, repo, *revs, **opts):
223 224 '''view the recent repository events
224 225 '''
225 226
226 227 if not repo.vfs.exists('blackbox.log'):
227 228 return
228 229
229 230 limit = opts.get(r'limit')
230 231 fp = repo.vfs('blackbox.log', 'r')
231 232 lines = fp.read().split('\n')
232 233
233 234 count = 0
234 235 output = []
235 236 for line in reversed(lines):
236 237 if count >= limit:
237 238 break
238 239
239 240 # count the commands by matching lines like: 2013/01/23 19:13:36 root>
240 241 if re.match('^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} .*> .*', line):
241 242 count += 1
242 243 output.append(line)
243 244
244 245 ui.status('\n'.join(reversed(output)))
General Comments 0
You need to be logged in to leave comments. Login now