##// END OF EJS Templates
blackbox: prevent failed I/O from causing hg to abort...
Bryan O'Sullivan -
r18786:ed39a8f9 default
parent child Browse files
Show More
@@ -1,106 +1,115 b''
1 # blackbox.py - log repository events to a file for post-mortem debugging
1 # blackbox.py - log repository events to a file for post-mortem debugging
2 #
2 #
3 # Copyright 2010 Nicolas Dumazet
3 # Copyright 2010 Nicolas Dumazet
4 # Copyright 2013 Facebook, Inc.
4 # Copyright 2013 Facebook, Inc.
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 """log repository events to a blackbox for debugging
9 """log repository events to a blackbox for debugging
10
10
11 Logs event information to .hg/blackbox.log to help debug and diagnose problems.
11 Logs event information to .hg/blackbox.log to help debug and diagnose problems.
12 The events that get logged can be configured via the blackbox.track config key.
12 The events that get logged can be configured via the blackbox.track config key.
13 Examples:
13 Examples:
14
14
15 [blackbox]
15 [blackbox]
16 track = *
16 track = *
17
17
18 [blackbox]
18 [blackbox]
19 track = command, commandfinish, commandexception, exthook, pythonhook
19 track = command, commandfinish, commandexception, exthook, pythonhook
20
20
21 [blackbox]
21 [blackbox]
22 track = incoming
22 track = incoming
23
23
24 """
24 """
25
25
26 from mercurial import util, cmdutil
26 from mercurial import util, cmdutil
27 from mercurial.i18n import _
27 from mercurial.i18n import _
28 import os, getpass, re
28 import os, getpass, re
29
29
30 cmdtable = {}
30 cmdtable = {}
31 command = cmdutil.command(cmdtable)
31 command = cmdutil.command(cmdtable)
32 testedwith = 'internal'
32 testedwith = 'internal'
33 lastblackbox = None
33 lastblackbox = None
34
34
35 def wrapui(ui):
35 def wrapui(ui):
36 class blackboxui(ui.__class__):
36 class blackboxui(ui.__class__):
37 @util.propertycache
37 @util.propertycache
38 def track(self):
38 def track(self):
39 return ui.configlist('blackbox', 'track', ['*'])
39 return ui.configlist('blackbox', 'track', ['*'])
40
40
41 def log(self, event, *msg, **opts):
41 def log(self, event, *msg, **opts):
42 global lastblackbox
42 global lastblackbox
43 super(blackboxui, self).log(event, *msg, **opts)
43 super(blackboxui, self).log(event, *msg, **opts)
44
44
45 if not '*' in self.track and not event in self.track:
45 if not '*' in self.track and not event in self.track:
46 return
46 return
47
47
48 if util.safehasattr(self, '_blackbox'):
48 if util.safehasattr(self, '_blackbox'):
49 blackbox = self._blackbox
49 blackbox = self._blackbox
50 else:
50 else:
51 # certain ui instances exist outside the context of
51 # certain ui instances exist outside the context of
52 # a repo, so just default to the last blackbox that
52 # a repo, so just default to the last blackbox that
53 # was seen.
53 # was seen.
54 blackbox = lastblackbox
54 blackbox = lastblackbox
55
55
56 if blackbox:
56 if blackbox:
57 date = util.datestr(None, '%Y/%m/%d %H:%M:%S')
57 date = util.datestr(None, '%Y/%m/%d %H:%M:%S')
58 user = getpass.getuser()
58 user = getpass.getuser()
59 formattedmsg = msg[0] % msg[1:]
59 formattedmsg = msg[0] % msg[1:]
60 blackbox.write('%s %s> %s' % (date, user, formattedmsg))
60 try:
61 blackbox.write('%s %s> %s' % (date, user, formattedmsg))
62 except IOError, err:
63 self.debug('warning: cannot write to blackbox.log: %s\n' %
64 err.strerror)
61 lastblackbox = blackbox
65 lastblackbox = blackbox
62
66
63 def setrepo(self, repo):
67 def setrepo(self, repo):
64 self._blackbox = repo.opener('blackbox.log', 'a')
68 try:
69 self._blackbox = repo.opener('blackbox.log', 'a')
70 except IOError, err:
71 self.debug('warning: cannot write to blackbox.log: %s\n' %
72 err.strerror)
73 self._blackbox = None
65
74
66 ui.__class__ = blackboxui
75 ui.__class__ = blackboxui
67
76
68 def uisetup(ui):
77 def uisetup(ui):
69 wrapui(ui)
78 wrapui(ui)
70
79
71 def reposetup(ui, repo):
80 def reposetup(ui, repo):
72 # During 'hg pull' a httppeer repo is created to represent the remote repo.
81 # During 'hg pull' a httppeer repo is created to represent the remote repo.
73 # It doesn't have a .hg directory to put a blackbox in, so we don't do
82 # It doesn't have a .hg directory to put a blackbox in, so we don't do
74 # the blackbox setup for it.
83 # the blackbox setup for it.
75 if not repo.local():
84 if not repo.local():
76 return
85 return
77
86
78 ui.setrepo(repo)
87 ui.setrepo(repo)
79
88
80 @command('^blackbox',
89 @command('^blackbox',
81 [('l', 'limit', 10, _('the number of events to show')),
90 [('l', 'limit', 10, _('the number of events to show')),
82 ],
91 ],
83 _('hg blackbox [OPTION]...'))
92 _('hg blackbox [OPTION]...'))
84 def blackbox(ui, repo, *revs, **opts):
93 def blackbox(ui, repo, *revs, **opts):
85 '''view the recent repository events
94 '''view the recent repository events
86 '''
95 '''
87
96
88 if not os.path.exists(repo.join('blackbox.log')):
97 if not os.path.exists(repo.join('blackbox.log')):
89 return
98 return
90
99
91 limit = opts.get('limit')
100 limit = opts.get('limit')
92 blackbox = repo.opener('blackbox.log', 'r')
101 blackbox = repo.opener('blackbox.log', 'r')
93 lines = blackbox.read().split('\n')
102 lines = blackbox.read().split('\n')
94
103
95 count = 0
104 count = 0
96 output = []
105 output = []
97 for line in reversed(lines):
106 for line in reversed(lines):
98 if count >= limit:
107 if count >= limit:
99 break
108 break
100
109
101 # count the commands by matching lines like: 2013/01/23 19:13:36 root>
110 # count the commands by matching lines like: 2013/01/23 19:13:36 root>
102 if re.match('^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} .*> .*', line):
111 if re.match('^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} .*> .*', line):
103 count += 1
112 count += 1
104 output.append(line)
113 output.append(line)
105
114
106 ui.status('\n'.join(reversed(output)))
115 ui.status('\n'.join(reversed(output)))
@@ -1,94 +1,136 b''
1 setup
1 setup
2 $ cat > mock.py <<EOF
2 $ cat > mock.py <<EOF
3 > from mercurial import util
3 > from mercurial import util
4 > import getpass
4 > import getpass
5 >
5 >
6 > def makedate():
6 > def makedate():
7 > return 0, 0
7 > return 0, 0
8 > def getuser():
8 > def getuser():
9 > return 'bob'
9 > return 'bob'
10 > # mock the date and user apis so the output is always the same
10 > # mock the date and user apis so the output is always the same
11 > def uisetup(ui):
11 > def uisetup(ui):
12 > util.makedate = makedate
12 > util.makedate = makedate
13 > getpass.getuser = getuser
13 > getpass.getuser = getuser
14 > EOF
14 > EOF
15 $ cat >> $HGRCPATH <<EOF
15 $ cat >> $HGRCPATH <<EOF
16 > [extensions]
16 > [extensions]
17 > blackbox=
17 > blackbox=
18 > mock=`pwd`/mock.py
18 > mock=`pwd`/mock.py
19 > mq=
19 > mq=
20 > EOF
20 > EOF
21 $ hg init blackboxtest
21 $ hg init blackboxtest
22 $ cd blackboxtest
22 $ cd blackboxtest
23
23
24 command, exit codes, and duration
24 command, exit codes, and duration
25
25
26 $ echo a > a
26 $ echo a > a
27 $ hg add a
27 $ hg add a
28 $ hg blackbox
28 $ hg blackbox
29 1970/01/01 00:00:00 bob> add a
29 1970/01/01 00:00:00 bob> add a
30 1970/01/01 00:00:00 bob> add exited 0 after * seconds (glob)
30 1970/01/01 00:00:00 bob> add exited 0 after * seconds (glob)
31
31
32 incoming change tracking
32 incoming change tracking
33
33
34 create two heads to verify that we only see one change in the log later
34 create two heads to verify that we only see one change in the log later
35 $ hg commit -ma
35 $ hg commit -ma
36 $ hg up null
36 $ hg up null
37 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
37 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
38 $ echo b > b
38 $ echo b > b
39 $ hg commit -Amb
39 $ hg commit -Amb
40 adding b
40 adding b
41 created new head
41 created new head
42
42
43 clone, commit, pull
43 clone, commit, pull
44 $ hg clone . ../blackboxtest2
44 $ hg clone . ../blackboxtest2
45 updating to branch default
45 updating to branch default
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ echo c > c
47 $ echo c > c
48 $ hg commit -Amc
48 $ hg commit -Amc
49 adding c
49 adding c
50 $ cd ../blackboxtest2
50 $ cd ../blackboxtest2
51 $ hg pull
51 $ hg pull
52 pulling from $TESTTMP/blackboxtest (glob)
52 pulling from $TESTTMP/blackboxtest (glob)
53 searching for changes
53 searching for changes
54 adding changesets
54 adding changesets
55 adding manifests
55 adding manifests
56 adding file changes
56 adding file changes
57 added 1 changesets with 1 changes to 1 files
57 added 1 changesets with 1 changes to 1 files
58 (run 'hg update' to get a working copy)
58 (run 'hg update' to get a working copy)
59 $ hg blackbox -l 3
59 $ hg blackbox -l 3
60 1970/01/01 00:00:00 bob> pull
60 1970/01/01 00:00:00 bob> pull
61 1970/01/01 00:00:00 bob> 1 incoming changes - new heads: d02f48003e62
61 1970/01/01 00:00:00 bob> 1 incoming changes - new heads: d02f48003e62
62 1970/01/01 00:00:00 bob> pull exited None after * seconds (glob)
62 1970/01/01 00:00:00 bob> pull exited None after * seconds (glob)
63
63
64 we must not cause a failure if we cannot write to the log
65
66 $ hg rollback
67 repository tip rolled back to revision 1 (undo pull)
68 $ chmod 000 .hg/blackbox.log
69 $ hg --debug incoming
70 warning: cannot write to blackbox.log: Permission denied
71 comparing with $TESTTMP/blackboxtest
72 query 1; heads
73 searching for changes
74 all local heads known remotely
75 changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51
76 tag: tip
77 phase: draft
78 parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06
79 parent: -1:0000000000000000000000000000000000000000
80 manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892
81 user: test
82 date: Thu Jan 01 00:00:00 1970 +0000
83 files+: c
84 extra: branch=default
85 description:
86 c
87
88
89 $ hg pull
90 pulling from $TESTTMP/blackboxtest
91 searching for changes
92 adding changesets
93 adding manifests
94 adding file changes
95 added 1 changesets with 1 changes to 1 files
96 (run 'hg update' to get a working copy)
97
98 a failure reading from the log is fine
99
100 $ hg blackbox -l 3
101 abort: Permission denied: $TESTTMP/blackboxtest2/.hg/blackbox.log
102 [255]
103
104 $ chmod 600 .hg/blackbox.log
105
64 backup bundles get logged
106 backup bundles get logged
65
107
66 $ touch d
108 $ touch d
67 $ hg commit -Amd
109 $ hg commit -Amd
68 adding d
110 adding d
69 created new head
111 created new head
70 $ hg strip tip
112 $ hg strip tip
71 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
72 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
114 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
73 $ hg blackbox -l 3
115 $ hg blackbox -l 3
74 1970/01/01 00:00:00 bob> strip tip
116 1970/01/01 00:00:00 bob> strip tip
75 1970/01/01 00:00:00 bob> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
117 1970/01/01 00:00:00 bob> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
76 1970/01/01 00:00:00 bob> strip exited 0 after * seconds (glob)
118 1970/01/01 00:00:00 bob> strip exited 0 after * seconds (glob)
77
119
78 extension and python hooks - use the eol extension for a pythonhook
120 extension and python hooks - use the eol extension for a pythonhook
79
121
80 $ echo '[extensions]' >> .hg/hgrc
122 $ echo '[extensions]' >> .hg/hgrc
81 $ echo 'eol=' >> .hg/hgrc
123 $ echo 'eol=' >> .hg/hgrc
82 $ echo '[hooks]' >> .hg/hgrc
124 $ echo '[hooks]' >> .hg/hgrc
83 $ echo 'update = echo hooked' >> .hg/hgrc
125 $ echo 'update = echo hooked' >> .hg/hgrc
84 $ hg update
126 $ hg update
85 hooked
127 hooked
86 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 $ hg blackbox -l 4
129 $ hg blackbox -l 4
88 1970/01/01 00:00:00 bob> update
130 1970/01/01 00:00:00 bob> update
89 1970/01/01 00:00:00 bob> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
131 1970/01/01 00:00:00 bob> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
90 1970/01/01 00:00:00 bob> exthook-update: echo hooked finished in * seconds (glob)
132 1970/01/01 00:00:00 bob> exthook-update: echo hooked finished in * seconds (glob)
91 1970/01/01 00:00:00 bob> update exited False after * seconds (glob)
133 1970/01/01 00:00:00 bob> update exited False after * seconds (glob)
92
134
93 cleanup
135 cleanup
94 $ cd ..
136 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now