commandserver: install logger to record server events through canonical API
authorYuya Nishihara <yuya@tcha.org>
Sat, 10 Nov 2018 19:00:17 +0900
changeset 53589 82210d88d814172b0317e5e5a1737b695b125a0e
parent 53588 368ecbf734af109b936f309e7feec788d5597290
child 53590 eaabcb689747c7fc99628ec8847a77d217d124f7
push id1079
push usergszorc@mozilla.com
push dateMon, 10 Dec 2018 19:44:59 +0000
commandserver: install logger to record server events through canonical API The global commandserver.log() will be replaced with this.
mercurial/chgserver.py
mercurial/commandserver.py
mercurial/server.py
--- a/mercurial/chgserver.py
+++ b/mercurial/chgserver.py
@@ -214,17 +214,17 @@ def _newchgui(srcui, csystem, attachio):
 
         def _runpager(self, cmd, env=None):
             self._csystem(cmd, procutil.shellenviron(env), type='pager',
                           cmdtable={'attachio': attachio})
             return True
 
     return chgui(srcui)
 
-def _loadnewui(srcui, args):
+def _loadnewui(srcui, args, cdebug):
     from . import dispatch  # avoid cycle
 
     newui = srcui.__class__.load()
     for a in ['fin', 'fout', 'ferr', 'environ']:
         setattr(newui, a, getattr(srcui, a))
     if util.safehasattr(srcui, '_csystem'):
         newui._csystem = srcui._csystem
 
@@ -242,18 +242,20 @@ def _loadnewui(srcui, args):
 
     # load wd and repo config, copied from dispatch.py
     cwd = options['cwd']
     cwd = cwd and os.path.realpath(cwd) or None
     rpath = options['repository']
     path, newlui = dispatch._getlocal(newui, rpath, wd=cwd)
 
     extensions.populateui(newui)
+    commandserver.setuplogging(newui, fp=cdebug)
     if newui is not newlui:
         extensions.populateui(newlui)
+        commandserver.setuplogging(newlui, fp=cdebug)
 
     return (newui, newlui)
 
 class channeledsystem(object):
     """Propagate ui.system() request in the following format:
 
     payload length (unsigned int),
     type, '\0',
@@ -418,17 +420,17 @@ class chgcmdserver(commandserver.server)
         If neither "reconnect" nor "redirect" is included in the instruction
         list, the client can continue with this server after completing all
         the instructions.
         """
         from . import dispatch  # avoid cycle
 
         args = self._readlist()
         try:
-            self.ui, lui = _loadnewui(self.ui, args)
+            self.ui, lui = _loadnewui(self.ui, args, self.cdebug)
         except error.ParseError as inst:
             dispatch._formatparse(self.ui.warn, inst)
             self.ui.flush()
             self.cresult.write('exit 255')
             return
         except error.Abort as inst:
             self.ui.error(_("abort: %s\n") % inst)
             if inst.hint:
--- a/mercurial/commandserver.py
+++ b/mercurial/commandserver.py
@@ -21,18 +21,20 @@ try:
     selectors.BaseSelector
 except ImportError:
     from .thirdparty import selectors2 as selectors
 
 from .i18n import _
 from . import (
     encoding,
     error,
+    loggingutil,
     pycompat,
     util,
+    vfs as vfsmod,
 )
 from .utils import (
     cborutil,
     procutil,
 )
 
 logfile = None
 
@@ -218,21 +220,28 @@ class server(object):
             # end up with its local configuration
             self.ui = repo.baseui
             self.repo = repo
             self.repoui = repo.ui
         else:
             self.ui = ui
             self.repo = self.repoui = None
 
+        self.cdebug = logfile
         self.cerr = channeledoutput(fout, 'e')
         self.cout = channeledoutput(fout, 'o')
         self.cin = channeledinput(fin, fout, 'I')
         self.cresult = channeledoutput(fout, 'r')
 
+        if self.ui.config(b'cmdserver', b'log') == b'-':
+            # switch log stream of server's ui to the 'd' (debug) channel
+            # (don't touch repo.ui as its lifetime is longer than the server)
+            self.ui = self.ui.copy()
+            setuplogging(self.ui, repo=None, fp=self.cdebug)
+
         # TODO: add this to help/config.txt when stabilized
         # ``channel``
         #   Use separate channel for structured output. (Command-server only)
         self.cmsg = None
         if ui.config(b'ui', b'message-output') == b'channel':
             encname, encfn = _selectmessageencoder(ui)
             self.cmsg = channeledmessage(fout, b'm', encname, encfn)
 
@@ -351,34 +360,51 @@ class server(object):
                 pass
         except EOFError:
             # we'll get here if the client disconnected while we were reading
             # its request
             return 1
 
         return 0
 
-def setuplogging(ui):
+def setuplogging(ui, repo=None, fp=None):
     """Set up server logging facility
 
-    If cmdserver.log is '-', log messages will be sent to the 'd' channel
-    while a client is connected. Otherwise, messages will be written to
-    the stderr of the server process.
+    If cmdserver.log is '-', log messages will be sent to the given fp.
+    It should be the 'd' channel while a client is connected, and otherwise
+    is the stderr of the server process.
     """
     # developer config: cmdserver.log
     logpath = ui.config(b'cmdserver', b'log')
     if not logpath:
         return
+    tracked = {b'cmdserver'}
 
     global logfile
     if logpath == b'-':
         logfile = ui.ferr
     else:
         logfile = open(logpath, 'ab')
 
+    if logpath == b'-' and fp:
+        logger = loggingutil.fileobjectlogger(fp, tracked)
+    elif logpath == b'-':
+        logger = loggingutil.fileobjectlogger(ui.ferr, tracked)
+    else:
+        logpath = os.path.abspath(logpath)
+        vfs = vfsmod.vfs(os.path.dirname(logpath))
+        logger = loggingutil.filelogger(vfs, os.path.basename(logpath), tracked)
+
+    targetuis = {ui}
+    if repo:
+        targetuis.add(repo.baseui)
+        targetuis.add(repo.ui)
+    for u in targetuis:
+        u.setlogger(b'cmdserver', logger)
+
 class pipeservice(object):
     def __init__(self, ui, repo, opts):
         self.ui = ui
         self.repo = repo
 
     def init(self):
         pass
 
--- a/mercurial/server.py
+++ b/mercurial/server.py
@@ -153,17 +153,17 @@ def runservice(opts, parentfn=None, init
 }
 
 def _createcmdservice(ui, repo, opts):
     mode = opts['cmdserver']
     try:
         servicefn = _cmdservicemap[mode]
     except KeyError:
         raise error.Abort(_('unknown mode %s') % mode)
-    commandserver.setuplogging(ui)
+    commandserver.setuplogging(ui, repo)
     return servicefn(ui, repo, opts)
 
 def _createhgwebservice(ui, repo, opts):
     # this way we can check if something was given in the command-line
     if opts.get('port'):
         opts['port'] = util.getport(opts.get('port'))
 
     alluis = {ui}