##// END OF EJS Templates
blackbox: simplify ui states...
Jun Wu -
r34275:a37e18b5 default
parent child Browse files
Show More
@@ -1,245 +1,226
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 47 registrar,
48 48 ui as uimod,
49 49 util,
50 50 )
51 51
52 52 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
53 53 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
54 54 # be specifying the version(s) of Mercurial they are tested with, or
55 55 # leave the attribute unspecified.
56 56 testedwith = 'ships-with-hg-core'
57 57
58 58 cmdtable = {}
59 59 command = registrar.command(cmdtable)
60 60
61 61 configtable = {}
62 62 configitem = registrar.configitem(configtable)
63 63
64 64 configitem('blackbox', 'dirty',
65 65 default=False,
66 66 )
67 67 configitem('blackbox', 'maxsize',
68 68 default='1 MB',
69 69 )
70 70 configitem('blackbox', 'logsource',
71 71 default=False,
72 72 )
73 73
74 74 lastui = None
75 75
76 76 def wrapui(ui):
77 77 class blackboxui(ui.__class__):
78 def __init__(self, src=None):
79 super(blackboxui, self).__init__(src)
80 if src is None:
81 self._partialinit()
82 else:
83 self._bbinlog = False
84 self._bbrepo = getattr(src, '_bbrepo', None)
85
86 def _partialinit(self):
87 if self._bbvfs:
88 return
89 self._bbinlog = False
90 self._bbrepo = None
91
92 def copy(self):
93 self._partialinit()
94 return self.__class__(self)
95
96 78 @property
97 79 def _bbvfs(self):
98 80 repo = getattr(self, '_bbrepo', None)
99 81 if repo:
100 82 return repo.vfs
101 83
102 84 @util.propertycache
103 85 def track(self):
104 86 return self.configlist('blackbox', 'track', ['*'])
105 87
106 88 def _openlogfile(self):
107 89 def rotate(oldpath, newpath):
108 90 try:
109 91 self._bbvfs.unlink(newpath)
110 92 except OSError as err:
111 93 if err.errno != errno.ENOENT:
112 94 self.debug("warning: cannot remove '%s': %s\n" %
113 95 (newpath, err.strerror))
114 96 try:
115 97 if newpath:
116 98 self._bbvfs.rename(oldpath, newpath)
117 99 except OSError as err:
118 100 if err.errno != errno.ENOENT:
119 101 self.debug("warning: cannot rename '%s' to '%s': %s\n" %
120 102 (newpath, oldpath, err.strerror))
121 103
122 104 maxsize = self.configbytes('blackbox', 'maxsize')
123 105 name = 'blackbox.log'
124 106 if maxsize > 0:
125 107 try:
126 108 st = self._bbvfs.stat(name)
127 109 except OSError:
128 110 pass
129 111 else:
130 112 if st.st_size >= maxsize:
131 113 path = self._bbvfs.join(name)
132 114 maxfiles = self.configint('blackbox', 'maxfiles', 7)
133 115 for i in xrange(maxfiles - 1, 1, -1):
134 116 rotate(oldpath='%s.%d' % (path, i - 1),
135 117 newpath='%s.%d' % (path, i))
136 118 rotate(oldpath=path,
137 119 newpath=maxfiles > 0 and path + '.1')
138 120 return self._bbvfs(name, 'a')
139 121
140 122 def log(self, event, *msg, **opts):
141 123 global lastui
142 124 super(blackboxui, self).log(event, *msg, **opts)
143 self._partialinit()
144 125
145 126 if not '*' in self.track and not event in self.track:
146 127 return
147 128
148 129 if self._bbvfs:
149 130 ui = self
150 131 else:
151 132 # certain ui instances exist outside the context of
152 133 # a repo, so just default to the last blackbox that
153 134 # was seen.
154 135 ui = lastui
155 136
156 137 if not ui:
157 138 return
158 if not lastui or ui._bbrepo:
139 repo = getattr(ui, '_bbrepo', None)
140 if not lastui or repo:
159 141 lastui = ui
160 if ui._bbinlog:
142 if getattr(ui, '_bbinlog', False):
161 143 # recursion and failure guard
162 144 return
163 145 try:
164 146 ui._bbinlog = True
165 147 default = self.configdate('devel', 'default-date')
166 148 date = util.datestr(default, '%Y/%m/%d %H:%M:%S')
167 149 user = util.getuser()
168 150 pid = '%d' % util.getpid()
169 151 formattedmsg = msg[0] % msg[1:]
170 152 rev = '(unknown)'
171 153 changed = ''
172 if ui._bbrepo:
173 ctx = ui._bbrepo[None]
154 if repo:
155 ctx = repo[None]
174 156 parents = ctx.parents()
175 157 rev = ('+'.join([hex(p.node()) for p in parents]))
176 158 if (ui.configbool('blackbox', 'dirty') and
177 159 ctx.dirty(missing=True, merge=False, branch=False)):
178 160 changed = '+'
179 161 if ui.configbool('blackbox', 'logsource'):
180 162 src = ' [%s]' % event
181 163 else:
182 164 src = ''
183 165 try:
184 166 fmt = '%s %s @%s%s (%s)%s> %s'
185 167 args = (date, user, rev, changed, pid, src, formattedmsg)
186 168 with ui._openlogfile() as fp:
187 169 fp.write(fmt % args)
188 170 except (IOError, OSError) as err:
189 171 self.debug('warning: cannot write to blackbox.log: %s\n' %
190 172 err.strerror)
191 173 # do not restore _bbinlog intentionally to avoid failed
192 174 # logging again
193 175 else:
194 176 ui._bbinlog = False
195 177 finally:
196 178 pass
197 179
198 180 def setrepo(self, repo):
199 self._bbinlog = False
200 181 self._bbrepo = repo
201 182
202 183 ui.__class__ = blackboxui
203 184 uimod.ui = blackboxui
204 185
205 186 def uisetup(ui):
206 187 wrapui(ui)
207 188
208 189 def reposetup(ui, repo):
209 190 # During 'hg pull' a httppeer repo is created to represent the remote repo.
210 191 # It doesn't have a .hg directory to put a blackbox in, so we don't do
211 192 # the blackbox setup for it.
212 193 if not repo.local():
213 194 return
214 195
215 196 if util.safehasattr(ui, 'setrepo'):
216 197 ui.setrepo(repo)
217 198 repo._wlockfreeprefix.add('blackbox.log')
218 199
219 200 @command('^blackbox',
220 201 [('l', 'limit', 10, _('the number of events to show')),
221 202 ],
222 203 _('hg blackbox [OPTION]...'))
223 204 def blackbox(ui, repo, *revs, **opts):
224 205 '''view the recent repository events
225 206 '''
226 207
227 208 if not repo.vfs.exists('blackbox.log'):
228 209 return
229 210
230 211 limit = opts.get('limit')
231 212 fp = repo.vfs('blackbox.log', 'r')
232 213 lines = fp.read().split('\n')
233 214
234 215 count = 0
235 216 output = []
236 217 for line in reversed(lines):
237 218 if count >= limit:
238 219 break
239 220
240 221 # count the commands by matching lines like: 2013/01/23 19:13:36 root>
241 222 if re.match('^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} .*> .*', line):
242 223 count += 1
243 224 output.append(line)
244 225
245 226 ui.status('\n'.join(reversed(output)))
General Comments 0
You need to be logged in to leave comments. Login now