]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
pybind/mgr_module: infrastructure to allow logging to cluster log
authorSage Weil <sage@redhat.com>
Tue, 25 Feb 2020 19:23:06 +0000 (13:23 -0600)
committerSage Weil <sage@redhat.com>
Thu, 27 Feb 2020 17:57:03 +0000 (11:57 -0600)
This is incomplete--a module that wants to opt-in needs to call
_enable_cluster_log() and manage the level themselves.

Signed-off-by: Sage Weil <sage@redhat.com>
src/pybind/mgr/mgr_module.py

index 05fb74df6a811aa2163ce16c1bd57cf84a35222e..6235b67fc7a9c244d57df3487d703a18bbf1b098 100644 (file)
@@ -420,6 +420,25 @@ class CPlusPlusHandler(logging.Handler):
         if record.levelno >= self.level:
             self._module._ceph_log(self.format(record))
 
+class ClusterLogHandler(logging.Handler):
+    def __init__(self, module_inst):
+        super().__init__()
+        self._module = module_inst
+        self.setFormatter(logging.Formatter("%(message)s"))
+
+    def emit(self, record):
+        levelmap = {
+            'DEBUG': MgrModule.CLUSTER_LOG_PRIO_DEBUG,
+            'INFO': MgrModule.CLUSTER_LOG_PRIO_INFO,
+            'WARNING': MgrModule.CLUSTER_LOG_PRIO_WARN,
+            'ERROR': MgrModule.CLUSTER_LOG_PRIO_ERROR,
+            'CRITICAL': MgrModule.CLUSTER_LOG_PRIO_ERROR,
+        }
+        level = levelmap[record.levelname]
+        if record.levelno >= self.level:
+            self._module.cluster_log(self._module.module_name,
+                                     level,
+                                     self.format(record))
 
 class FileHandler(logging.FileHandler):
     def __init__(self, module_inst):
@@ -434,7 +453,8 @@ class FileHandler(logging.FileHandler):
 
 
 class MgrModuleLoggingMixin(object):
-    def _configure_logging(self, mgr_level, module_level, log_to_file):
+    def _configure_logging(self, mgr_level, module_level, cluster_level,
+                           log_to_file, log_to_cluster):
         self._mgr_level = None
         self._module_level = None
         self._root_logger = logging.getLogger()
@@ -442,25 +462,35 @@ class MgrModuleLoggingMixin(object):
         self._unconfigure_logging()
 
         # the ceph log handler is initialized only once
-        self._ceph_log_handler = CPlusPlusHandler(self)
+        self._mgr_log_handler = CPlusPlusHandler(self)
+        self._cluster_log_handler = ClusterLogHandler(self)
         self._file_log_handler = FileHandler(self)
 
         self.log_to_file = log_to_file
+        self.log_to_cluster = log_to_cluster
+
+        self._root_logger.addHandler(self._mgr_log_handler)
         if log_to_file:
             self._root_logger.addHandler(self._file_log_handler)
+        if log_to_cluster:
+            self._root_logger.addHandler(self._cluster_log_handler)
 
         self._root_logger.setLevel(logging.NOTSET)
-        self._set_log_level(mgr_level, module_level)
+        self._set_log_level(mgr_level, module_level, cluster_level)
+
 
     def _unconfigure_logging(self):
         # remove existing handlers:
         rm_handlers = [
-            h for h in self._root_logger.handlers if isinstance(h, CPlusPlusHandler) or isinstance(h, FileHandler)]
+            h for h in self._root_logger.handlers if isinstance(h, CPlusPlusHandler) or isinstance(h, FileHandler) or isinstance(h, ClusterLogHandler)]
         for h in rm_handlers:
             self._root_logger.removeHandler(h)
         self.log_to_file = False
+        self.log_to_cluster = False
+
+    def _set_log_level(self, mgr_level, module_level, cluster_level):
+        self._cluster_log_handler.setLevel(cluster_level.upper())
 
-    def _set_log_level(self, mgr_level, module_level):
         module_level = module_level.upper() if module_level else ''
         if not self._module_level:
             # using debug_mgr level
@@ -474,19 +504,19 @@ class MgrModuleLoggingMixin(object):
 
         if not self._module_level and not module_level:
             level = self._ceph_log_level_to_python(mgr_level)
-            self.getLogger().warning("setting log level based on debug_mgr: %s (%s)", level, mgr_level)
+            self.getLogger().debug("setting log level based on debug_mgr: %s (%s)", level, mgr_level)
         elif self._module_level and not module_level:
             level = self._ceph_log_level_to_python(mgr_level)
             self.getLogger().warning("unsetting module log level, falling back to "
                                      "debug_mgr level: %s (%s)", level, mgr_level)
         elif module_level:
             level = module_level
-            self.getLogger().warning("setting log level: %s", level)
+            self.getLogger().debug("setting log level: %s", level)
 
         self._module_level = module_level
         self._mgr_level = mgr_level
 
-        self._ceph_log_handler.setLevel(level)
+        self._mgr_log_handler.setLevel(level)
         self._file_log_handler.setLevel(level)
 
     def _enable_file_log(self):
@@ -501,6 +531,18 @@ class MgrModuleLoggingMixin(object):
         self.log_to_file = False
         self._root_logger.removeHandler(self._file_log_handler)
 
+    def _enable_cluster_log(self):
+        # enable cluster log
+        self.getLogger().warning("enabling logging to cluster")
+        self.log_to_cluster = True
+        self._root_logger.addHandler(self._cluster_log_handler)
+
+    def _disable_cluster_log(self):
+        # disable cluster log
+        self.getLogger().warning("disabling logging to cluster")
+        self.log_to_cluster = False
+        self._root_logger.removeHandler(self._cluster_log_handler)
+
     def _ceph_log_level_to_python(self, ceph_log_level):
         if ceph_log_level:
             try:
@@ -540,13 +582,6 @@ class MgrStandbyModule(ceph_module.BaseMgrStandbyModule, MgrModuleLoggingMixin):
         super(MgrStandbyModule, self).__init__(capsule)
         self.module_name = module_name
 
-        mgr_level = self.get_ceph_option("debug_mgr")
-        log_level = self.get_module_option("log_level")
-        self._configure_logging(mgr_level, log_level, False)
-
-        # for backwards compatibility
-        self._logger = self.getLogger()
-
         # see also MgrModule.__init__()
         for o in self.MODULE_OPTIONS:
             if 'default' in o:
@@ -555,6 +590,15 @@ class MgrStandbyModule(ceph_module.BaseMgrStandbyModule, MgrModuleLoggingMixin):
                 else:
                     self.MODULE_OPTION_DEFAULTS[o['name']] = str(o['default'])
 
+        mgr_level = self.get_ceph_option("debug_mgr")
+        log_level = self.get_module_option("log_level")
+        cluster_level = self.get_module_option('log_to_cluster_level')
+        self._configure_logging(mgr_level, log_level, cluster_level,
+                                False, False)
+
+        # for backwards compatibility
+        self._logger = self.getLogger()
+
     def __del__(self):
         self._unconfigure_logging()
 
@@ -646,10 +690,25 @@ class MgrModule(ceph_module.BaseMgrModule, MgrModuleLoggingMixin):
         self.module_name = module_name
         super(MgrModule, self).__init__(py_modules_ptr, this_ptr)
 
+        for o in self.MODULE_OPTIONS:
+            if 'default' in o:
+                if 'type' in o:
+                    # we'll assume the declared type matches the
+                    # supplied default value's type.
+                    self.MODULE_OPTION_DEFAULTS[o['name']] = o['default']
+                else:
+                    # module not declaring it's type, so normalize the
+                    # default value to be a string for consistent behavior
+                    # with default and user-supplied option values.
+                    self.MODULE_OPTION_DEFAULTS[o['name']] = str(o['default'])
+
         mgr_level = self.get_ceph_option("debug_mgr")
         log_level = self.get_module_option("log_level")
-        self._configure_logging(mgr_level, log_level,
-                                self.get_module_option("log_to_file", False))
+        cluster_level = self.get_module_option('log_to_cluster_level')
+        log_to_file = self.get_module_option("log_to_file")
+        log_to_cluster = self.get_module_option("log_to_cluster")
+        self._configure_logging(mgr_level, log_level, cluster_level,
+                                log_to_file, log_to_cluster)
 
         # for backwards compatibility
         self._logger = self.getLogger()
@@ -661,26 +720,27 @@ class MgrModule(ceph_module.BaseMgrModule, MgrModuleLoggingMixin):
         # Keep a librados instance for those that need it.
         self._rados = None
 
-        for o in self.MODULE_OPTIONS:
-            if 'default' in o:
-                if 'type' in o:
-                    # we'll assume the declared type matches the
-                    # supplied default value's type.
-                    self.MODULE_OPTION_DEFAULTS[o['name']] = o['default']
-                else:
-                    # module not declaring it's type, so normalize the
-                    # default value to be a string for consistent behavior
-                    # with default and user-supplied option values.
-                    self.MODULE_OPTION_DEFAULTS[o['name']] = str(o['default'])
 
     def __del__(self):
         self._unconfigure_logging()
 
     @classmethod
     def _register_commands(cls, module_name):
-        cls.MODULE_OPTIONS.append(Option(name='log_level', type='str', default="", runtime=True,
-                                         enum_allowed=['info', 'debug', 'critical', 'error', 'warning', '']))
-        cls.MODULE_OPTIONS.append(Option(name='log_to_file', type='bool', default=False, runtime=True))
+        cls.MODULE_OPTIONS.append(
+            Option(name='log_level', type='str', default="", runtime=True,
+                   enum_allowed=['info', 'debug', 'critical', 'error',
+                                 'warning', '']))
+        cls.MODULE_OPTIONS.append(
+            Option(name='log_to_file', type='bool', default=False, runtime=True))
+        if not [x for x in cls.MODULE_OPTIONS if x['name'] == 'log_to_cluster']:
+            cls.MODULE_OPTIONS.append(
+                Option(name='log_to_cluster', type='bool', default=False,
+                       runtime=True))
+        cls.MODULE_OPTIONS.append(
+            Option(name='log_to_cluster_level', type='str', default='info',
+                   runtime=True,
+                   enum_allowed=['info', 'debug', 'critical', 'error',
+                                 'warning', '']))
 
         cls.COMMANDS.extend(CLICommand.dump_cmd_list())
 
@@ -740,15 +800,22 @@ class MgrModule(ceph_module.BaseMgrModule, MgrModuleLoggingMixin):
         # check logging options for changes
         mgr_level = self.get_ceph_option("debug_mgr")
         module_level = self.get_module_option("log_level")
+        cluster_level = self.get_module_option("log_to_cluster_level")
         log_to_file = self.get_module_option("log_to_file", False)
+        log_to_cluster = self.get_module_option("log_to_cluster", False)
 
-        self._set_log_level(mgr_level, module_level)
+        self._set_log_level(mgr_level, module_level, cluster_level)
 
         if log_to_file != self.log_to_file:
             if log_to_file:
                 self._enable_file_log()
             else:
                 self._disable_file_log()
+        if log_to_cluster != self.log_to_cluster:
+            if log_to_cluster:
+                self._enable_cluster_log()
+            else:
+                self._disable_cluster_log()
 
         # call module subclass implementations
         self.config_notify()