##// END OF EJS Templates
logtoprocess: extract logger class from ui wrapper...
Yuya Nishihara -
r40713:2b859742 default
parent child Browse files
Show More
@@ -1,76 +1,100 b''
1 # logtoprocess.py - send ui.log() data to a subprocess
1 # logtoprocess.py - send ui.log() data to a subprocess
2 #
2 #
3 # Copyright 2016 Facebook, Inc.
3 # Copyright 2016 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """send ui.log() data to a subprocess (EXPERIMENTAL)
7 """send ui.log() data to a subprocess (EXPERIMENTAL)
8
8
9 This extension lets you specify a shell command per ui.log() event,
9 This extension lets you specify a shell command per ui.log() event,
10 sending all remaining arguments to as environment variables to that command.
10 sending all remaining arguments to as environment variables to that command.
11
11
12 Positional arguments construct a log message, which is passed in the `MSG1`
12 Positional arguments construct a log message, which is passed in the `MSG1`
13 environment variables. Each keyword argument is set as a `OPT_UPPERCASE_KEY`
13 environment variables. Each keyword argument is set as a `OPT_UPPERCASE_KEY`
14 variable (so the key is uppercased, and prefixed with `OPT_`). The original
14 variable (so the key is uppercased, and prefixed with `OPT_`). The original
15 event name is passed in the `EVENT` environment variable, and the process ID
15 event name is passed in the `EVENT` environment variable, and the process ID
16 of mercurial is given in `HGPID`.
16 of mercurial is given in `HGPID`.
17
17
18 So given a call `ui.log('foo', 'bar %s\n', 'baz', spam='eggs'), a script
18 So given a call `ui.log('foo', 'bar %s\n', 'baz', spam='eggs'), a script
19 configured for the `foo` event can expect an environment with `MSG1=bar baz`,
19 configured for the `foo` event can expect an environment with `MSG1=bar baz`,
20 and `OPT_SPAM=eggs`.
20 and `OPT_SPAM=eggs`.
21
21
22 Scripts are configured in the `[logtoprocess]` section, each key an event name.
22 Scripts are configured in the `[logtoprocess]` section, each key an event name.
23 For example::
23 For example::
24
24
25 [logtoprocess]
25 [logtoprocess]
26 commandexception = echo "$MSG1" > /var/log/mercurial_exceptions.log
26 commandexception = echo "$MSG1" > /var/log/mercurial_exceptions.log
27
27
28 would log the warning message and traceback of any failed command dispatch.
28 would log the warning message and traceback of any failed command dispatch.
29
29
30 Scripts are run asynchronously as detached daemon processes; mercurial will
30 Scripts are run asynchronously as detached daemon processes; mercurial will
31 not ensure that they exit cleanly.
31 not ensure that they exit cleanly.
32
32
33 """
33 """
34
34
35 from __future__ import absolute_import
35 from __future__ import absolute_import
36
36
37 import os
37 import os
38
38
39 from mercurial import (
39 from mercurial import (
40 pycompat,
40 pycompat,
41 util,
41 )
42 )
42 from mercurial.utils import (
43 from mercurial.utils import (
43 procutil,
44 procutil,
44 )
45 )
45
46
46 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
47 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
47 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
48 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
48 # be specifying the version(s) of Mercurial they are tested with, or
49 # be specifying the version(s) of Mercurial they are tested with, or
49 # leave the attribute unspecified.
50 # leave the attribute unspecified.
50 testedwith = 'ships-with-hg-core'
51 testedwith = 'ships-with-hg-core'
51
52
53 class processlogger(object):
54 """Map log events to external commands
55
56 Arguments are passed on as environment variables.
57 """
58
59 def __init__(self, ui):
60 self._scripts = dict(ui.configitems(b'logtoprocess'))
61
62 def tracked(self, event):
63 return bool(self._scripts.get(event))
64
65 def log(self, ui, event, msg, opts):
66 script = self._scripts.get(event)
67 if not script:
68 return
69 env = {
70 b'EVENT': event,
71 b'HGPID': os.getpid(),
72 b'MSG1': msg[0] % msg[1:],
73 }
74 # keyword arguments get prefixed with OPT_ and uppercased
75 env.update((b'OPT_%s' % key.upper(), value)
76 for key, value in pycompat.byteskwargs(opts).items())
77 fullenv = procutil.shellenviron(env)
78 procutil.runbgcommand(script, fullenv, shell=True)
79
52 def uisetup(ui):
80 def uisetup(ui):
53
81
54 class logtoprocessui(ui.__class__):
82 class logtoprocessui(ui.__class__):
55 def log(self, event, *msg, **opts):
83 def __init__(self, src=None):
56 """Map log events to external commands
84 super(logtoprocessui, self).__init__(src)
57
85 if src and r'_ltplogger' in src.__dict__:
58 Arguments are passed on as environment variables.
86 self._ltplogger = src._ltplogger
59
87
60 """
88 # trick to initialize logger after configuration is loaded, which
61 script = self.config('logtoprocess', event)
89 # can be replaced later with processlogger(ui) in uisetup(), where
62 if script:
90 # both user and repo configurations should be available.
63 env = {
91 @util.propertycache
64 b'EVENT': event,
92 def _ltplogger(self):
65 b'HGPID': os.getpid(),
93 return processlogger(self)
66 b'MSG1': msg[0] % msg[1:],
94
67 }
95 def log(self, event, *msg, **opts):
68 # keyword arguments get prefixed with OPT_ and uppercased
96 self._ltplogger.log(self, event, msg, opts)
69 env.update((b'OPT_%s' % key.upper(), value)
70 for key, value in pycompat.byteskwargs(opts).items())
71 fullenv = procutil.shellenviron(env)
72 procutil.runbgcommand(script, fullenv, shell=True)
73 return super(logtoprocessui, self).log(event, *msg, **opts)
97 return super(logtoprocessui, self).log(event, *msg, **opts)
74
98
75 # Replace the class for this instance and all clones created from it:
99 # Replace the class for this instance and all clones created from it:
76 ui.__class__ = logtoprocessui
100 ui.__class__ = logtoprocessui
General Comments 0
You need to be logged in to leave comments. Login now