Patchwork [5,of,5,V2] ui: manage logger instances and event filtering by core ui

login
register
mail settings
Submitter Yuya Nishihara
Date Nov. 21, 2018, 2:03 p.m.
Message ID <a580ee2c7ef735dfc0ef.1542808990@mimosa>
Download mbox | patch
Permalink /patch/36692/
State Accepted
Headers show

Comments

Yuya Nishihara - Nov. 21, 2018, 2:03 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1541927313 -32400
#      Sun Nov 11 18:08:33 2018 +0900
# Node ID a580ee2c7ef735dfc0efb69a30ec1482576ea1df
# Parent  9e8a18bf900b65f91f1cb9b9e65bb9e654fa3dd8
ui: manage logger instances and event filtering by core ui

The setup code in blackbox needs more tweaks since it has lots of black
magics. I'll fix them by follow-up patches.

To be clear, the goal of this series is to provide a proper way for command
server to install its own logger. I need it to debug in-memory repository
cache.

Patch

diff --git a/hgext/blackbox.py b/hgext/blackbox.py
--- a/hgext/blackbox.py
+++ b/hgext/blackbox.py
@@ -53,7 +53,6 @@  from mercurial import (
     pycompat,
     registrar,
     ui as uimod,
-    util,
 )
 from mercurial.utils import (
     dateutil,
@@ -147,9 +146,6 @@  class blackboxlogger(object):
 
     def log(self, ui, event, msg, opts):
         global _lastlogger
-        if not self.tracked(event):
-            return
-
         if self._bbvfs:
             _lastlogger = self
         elif _lastlogger and _lastlogger._bbvfs:
@@ -201,33 +197,20 @@  class blackboxlogger(object):
 
 def wrapui(ui):
     class blackboxui(ui.__class__):
-        def __init__(self, src=None):
-            super(blackboxui, self).__init__(src)
-            if src and r'_bblogger' in src.__dict__:
-                self._bblogger = src._bblogger
-
-        # trick to initialize logger after configuration is loaded, which
-        # can be replaced later with blackboxlogger(ui) in uisetup(), where
-        # both user and repo configurations should be available.
-        @util.propertycache
-        def _bblogger(self):
-            return blackboxlogger(self)
-
         def debug(self, *msg, **opts):
             super(blackboxui, self).debug(*msg, **opts)
             if self.debugflag:
                 self.log('debug', '%s', ''.join(msg))
 
-        def log(self, event, *msg, **opts):
-            super(blackboxui, self).log(event, *msg, **opts)
-            self._bblogger.log(self, event, msg, opts)
-
     ui.__class__ = blackboxui
     uimod.ui = blackboxui
 
 def uisetup(ui):
     wrapui(ui)
 
+def uipopulate(ui):
+    ui.setlogger(b'blackbox', blackboxlogger(ui))
+
 def reposetup(ui, repo):
     # During 'hg pull' a httppeer repo is created to represent the remote repo.
     # It doesn't have a .hg directory to put a blackbox in, so we don't do
@@ -235,7 +218,10 @@  def reposetup(ui, repo):
     if not repo.local():
         return
 
-    logger = getattr(ui, '_bblogger', None)
+    # Since blackbox.log is stored in the repo directory, the logger should be
+    # instantiated per repository.
+    logger = blackboxlogger(ui)
+    ui.setlogger(b'blackbox', logger)
     if logger:
         logger.setrepo(repo)
 
diff --git a/hgext/logtoprocess.py b/hgext/logtoprocess.py
--- a/hgext/logtoprocess.py
+++ b/hgext/logtoprocess.py
@@ -38,7 +38,6 @@  import os
 
 from mercurial import (
     pycompat,
-    util,
 )
 from mercurial.utils import (
     procutil,
@@ -63,9 +62,7 @@  class processlogger(object):
         return bool(self._scripts.get(event))
 
     def log(self, ui, event, msg, opts):
-        script = self._scripts.get(event)
-        if not script:
-            return
+        script = self._scripts[event]
         env = {
             b'EVENT': event,
             b'HGPID': os.getpid(),
@@ -77,24 +74,5 @@  class processlogger(object):
         fullenv = procutil.shellenviron(env)
         procutil.runbgcommand(script, fullenv, shell=True)
 
-def uisetup(ui):
-
-    class logtoprocessui(ui.__class__):
-        def __init__(self, src=None):
-            super(logtoprocessui, self).__init__(src)
-            if src and r'_ltplogger' in src.__dict__:
-                self._ltplogger = src._ltplogger
-
-        # trick to initialize logger after configuration is loaded, which
-        # can be replaced later with processlogger(ui) in uisetup(), where
-        # both user and repo configurations should be available.
-        @util.propertycache
-        def _ltplogger(self):
-            return processlogger(self)
-
-        def log(self, event, *msg, **opts):
-            self._ltplogger.log(self, event, msg, opts)
-            return super(logtoprocessui, self).log(event, *msg, **opts)
-
-    # Replace the class for this instance and all clones created from it:
-    ui.__class__ = logtoprocessui
+def uipopulate(ui):
+    ui.setlogger(b'logtoprocess', processlogger(ui))
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -235,6 +235,7 @@  class ui(object):
             self._fmsgout = src._fmsgout
             self._fmsgerr = src._fmsgerr
             self._finoutredirected = src._finoutredirected
+            self._loggers = src._loggers.copy()
             self.pageractive = src.pageractive
             self._disablepager = src._disablepager
             self._tweaked = src._tweaked
@@ -263,6 +264,7 @@  class ui(object):
             self._fmsgout = self.fout  # configurable
             self._fmsgerr = self.ferr  # configurable
             self._finoutredirected = False
+            self._loggers = {}
             self.pageractive = False
             self._disablepager = False
             self._tweaked = False
@@ -1709,6 +1711,18 @@  class ui(object):
         '''exists only so low-level modules won't need to import scmutil'''
         return scmutil.progress(self, topic, unit, total)
 
+    def getlogger(self, name):
+        """Returns a logger of the given name; or None if not registered"""
+        return self._loggers.get(name)
+
+    def setlogger(self, name, logger):
+        """Install logger which can be identified later by the given name
+
+        More than one loggers can be registered. Use extension or module
+        name to uniquely identify the logger instance.
+        """
+        self._loggers[name] = logger
+
     def log(self, event, *msg, **opts):
         '''hook for logging facility extensions
 
@@ -1720,6 +1734,14 @@  class ui(object):
 
         **opts currently has no defined meanings.
         '''
+        if not self._loggers:
+            return
+        activeloggers = [l for l in self._loggers.itervalues()
+                         if l.tracked(event)]
+        if not activeloggers:
+            return
+        for logger in activeloggers:
+            logger.log(self, event, msg, opts)
 
     def label(self, msg, label):
         '''style msg based on supplied label