add parser-specific docs & rewrite sudo parser for journald
[logparse.git] / logparse / load_parsers.py
index f752fb47cb9dd56d2e3e7e40e81f25fa44d48f75..c0ed52450437a2b5739d400a572732aa31b78053 100644 (file)
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
 # -*- coding: utf-8 -*-
+
 """
 A basic "plugin loader" implementation which searches for default packaged and
 user-supplied parser modules and verifies them so they can be executed by
@@ -37,24 +37,27 @@ class Parser():
     logparse.formatting.Section object.
     """
 
-    def __init__(self, name=None, path=None, info=None, deprecated=False):
+    def __init__(self, name=None, path=None, info=None, deprecated=False,
+            successor=""):
         """
         The following variables can be set to display information about the
-        parser. The object `self.logger` can be used as for outputting messages
-        to whatever sink is set up in logparse.interface (no setup required in
-        the parser module itself).
+        parser. The object `self.logger` can be used for outputting messages to
+        to whatever sink is set up in __init__.py (no setup required in the
+        parser module itself).
         """
         self.name = str(name) if name else None
         self.path = Path(path) if path else None
         self.info = dict(info) if info else None
-        self.logger = logging.getLogger(__name__)
+        self.logger = logging.getLogger(self.__module__)
         self.deprecated = deprecated
+        self.successor = successor
 
     def load(self):
         """
         A generic loading method to import a parser, only used for debugging
         """
-        logger.debug("Loading parser {0} from {1}".format(self.name, str(self.path) if self.path != None else "defaults"))
+        logger.debug("Loading parser {0} from {1}".format(
+            self.name, str(self.path) if self.path != None else "defaults"))
         return importlib.import_module(self.name)
 
     def parse_log(self, **args) -> None:
@@ -101,7 +104,8 @@ class ParserLoader:
                 self.parsers.append(user_parser)
                 return user_parser
             else:
-                logger.warning("Couldn't find a matching parser module for search term {0}".format(pattern))
+                logger.warning("Couldn't find a matching parser module "
+                    "for search term {0}".format(pattern))
                 return None
 
     def _search_user(self, pattern):
@@ -111,19 +115,20 @@ class ParserLoader:
 
         logger.debug("Searching for {0} in {1}".format(pattern, self.path))
         try:
-            spec = importlib.machinery.PathFinder.find_spec(pattern, path=[self.path])
+            spec = importlib.machinery.PathFinder.find_spec(
+                    pattern, path=[self.path])
             parser_module = spec.loader.load_module(spec.name)
             return self._validate_module(parser_module)
         except Exception as e:
-            logger.debug("Couldn't find parser {0} in {1}: {2}".format(pattern, self.path, str(e)))
             return None
 
     def _search_default(self, pattern):
         """
         Search for a parser name `pattern` in the default parser package
+        TODO use importlib.resources.is_resources() once there is a backport
+        to Python 3.6 or below
         """
         
-        # TODO use importlib.resources.is_resources() once there is a backport to Python 3.6 or below
         logger.debug("Searching for {0} in default parsers".format(pattern))
         try:
             parser_module = importlib.import_module(self.pkg + "." + pattern)
@@ -141,7 +146,8 @@ class ParserLoader:
             6. Must not match an already-loaded class
         """
 
-        logger.debug("Checking validity of module {0} at {1}".format(parser_module.__name__, parser_module.__file__))
+        logger.debug("Checking validity of module {0} at {1}".format(
+            parser_module.__name__, parser_module.__file__))
         available_parsers = []
         clsmembers = inspect.getmembers(parser_module, inspect.isclass)
 
@@ -150,25 +156,41 @@ class ParserLoader:
             if not (issubclass(c, Parser) & (c is not Parser)):
                 continue
             if c in self.parsers:
-                logger.warning("Parser class {0} has already been loaded from another source, ignoring it".format(c.__class__.__name__, c.__file__))
+                logger.warning("Parser class {0} has already been loaded "
+                    "from another source, ignoring it".format(
+                        c.__class__.__name__, c.__file__))
             if not inspect.isroutine(c.parse_log):
-                logger.warning("Parser class {0} in {1} does not contain a parse_log() method".format(c.__class__.__name__, c.__file__))
+                logger.warning("Parser class {0} in {1} does not contain a "
+                    "parse_log() method".format(
+                        c.__class__.__name__, c.__file__))
                 continue
             if None in get_type_hints(c):
-                logger.warning("Parser class {0} in {1} contains a null-returning parse_log() method".format(c.__class__.__name__, c.__file__))
+                logger.warning("Parser class {0} in {1} contains a "
+                    "null-returning parse_log() method".format(
+                        c.__class__.__name__, c.__file__))
                 continue
-            logger.debug("Found parser {0}.{1}".format(c.__module__, c.__class__.__name__))
-            available_parsers.append(c())
+            parser_obj = c()
+            if parser_obj.deprecated:
+                logger.warning("Parser {0} is deprecated - "
+                    "use {1} instead".format(
+                        parser_obj.name, parser_obj.successor))
+            logger.debug("Found parser {0}.{1}".format(
+                c.__module__, c.__class__.__name__))
+            available_parsers.append(parser_obj)
 
         # Check module structure
         if len(available_parsers) == 1:
-            logger.debug("Parser module {0} at {1} passed validity checks".format(parser_module.__name__, parser_module.__file__))
+            logger.debug("Parser module {0} at {1} passed validity checks"
+                    .format(parser_module.__name__, parser_module.__file__))
             return available_parsers[0]
         elif len(available_parsers) == 0:
-            logger.warning("No valid classes in {0} at {1}".format(parser_module.__name__, parser_module.__file__))
+            logger.warning("No valid classes in {0} at {1}".
+                    format(parser_module.__name__, parser_module.__file__))
             return None
         elif len(available_parsers) > 1:
-            logger.warning("Found multiple valid parser classes in {0} at {1} - ignoring this module".format(parser_module.__name__, parser_module.__file__))
+            logger.warning("Found multiple valid parser classes in {0} at {1} "
+                "- ignoring this module"
+                .format(parser_module.__name__, parser_module.__file__))
             return None
 
     def load_pkg(self):
@@ -177,15 +199,17 @@ class ParserLoader:
         non-deprecated parser classes from self.pkg using importlib.
         """
 
-        available_parsers = [name for _, name, _ in iter_modules([dirname(importlib.import_module(self.pkg).__file__)])]
+        available_parsers = [name for _, name, _ in iter_modules(
+            [dirname(importlib.import_module(self.pkg).__file__)])]
         for parser_name in available_parsers:
-            parser_module = importlib.import_module("logparse.parsers." + parser_name)
+            parser_module = importlib.import_module(
+                    "logparse.parsers." + parser_name)
             parser_class = self._validate_module(parser_module)
             if parser_class == None:
                 continue
-            parser_obj = parser_class
-            if parser_obj.deprecated:
-                logger.debug("Ignoring parser {0} because it is deprecated".format(parser_class.__class__.__name__))
+            if parser_class.deprecated:
+                logger.debug("Ignoring parser {0} because it is deprecated"
+                        .format(parser_class.__class__.__name__))
                 continue
             self.parsers.append(parser_class)
         return self.parsers