Skip to content

Commit

Permalink
Update logging (#95)
Browse files Browse the repository at this point in the history
This commit cleans up the logger setup by adding a class to
handle the object creation.

Besides the cleanup, this change fixes the duplicate logging to stdout by
setting the logger propagation to false.
  • Loading branch information
PFigs authored Jul 22, 2019
1 parent c9730f9 commit 4138272
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 28 deletions.
6 changes: 4 additions & 2 deletions python_transport/wirepas_gateway/dbus_print_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from datetime import datetime

from wirepas_gateway.dbus.dbus_client import BusClient
from wirepas_gateway.utils import setup_log
from wirepas_gateway.utils import LoggerHelper


class PrintClient(BusClient):
Expand Down Expand Up @@ -59,7 +59,9 @@ def main(log_name="print_client"):
except KeyError:
debug_level = "info"

logger = setup_log(log_name, level=debug_level)
log = LoggerHelper(module_name=__name__, level=debug_level)
logger = log.setup()

obj = PrintClient()
obj.logger = logger
obj.run()
Expand Down
8 changes: 5 additions & 3 deletions python_transport/wirepas_gateway/transport_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@
from wirepas_gateway.protocol.topic_helper import TopicGenerator, TopicParser
from wirepas_gateway.protocol.mqtt_wrapper import MQTTWrapper
from wirepas_gateway.utils import ParserHelper
from wirepas_gateway.utils import setup_log
from wirepas_gateway.utils import LoggerHelper
from wirepas_messaging.gateway.api import (
GatewayResultCode,
GatewayState,
GatewayAPIParsingException,
)

from wirepas_gateway import __version__ as transport_version
from wirepas_gateway import __pkg_name__

# This constant is the actual API level implemented by this transport module (cf WP-RM-128)
IMPLEMENTED_API_VERSION = 1
Expand Down Expand Up @@ -644,14 +645,15 @@ def main():
except KeyError:
pass

logger = setup_log("transport_service", level=debug_level)
log = LoggerHelper(module_name=__pkg_name__, level=debug_level)
logger = log.setup()

_update_parameters(settings, logger)
# after this stage, mqtt deprecated argument cannot be used

_check_parameters(settings, logger)

TransportService(settings, logger).run()
TransportService(settings=settings, logger=logger).run()


if __name__ == "__main__":
Expand Down
141 changes: 118 additions & 23 deletions python_transport/wirepas_gateway/utils/log_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,134 @@
Copyright 2019 Wirepas Ltd licensed under Apache License, Version 2.0
See file LICENSE for full license details.
"""

import sys
import ast
import logging


def setup_log(
module,
level="debug",
log_format="%(asctime)s | [%(levelname)s] %(name)s@%(filename)s:%(lineno)d:%(message)s",
):
class LoggerHelper(object):
"""
Prepares logging.
Setups Python's logging and by default send up to LEVEL
logs to stdout.
LoggerHelper
Args:
level - logging level to enable.
The loggerhelper is a class to abstract the creation of logger
instances.
"""

logger = logging.getLogger(module)
level = "{0}".format(level.upper())
def __init__(self, module_name, level: str = "debug", **kwargs):
super(LoggerHelper, self).__init__()

self._logger = logging.getLogger(module_name)
self._name = module_name
self._level = "{0}".format(level.upper())
self._handlers = dict()

self._log_format = dict()
self._log_format["stdout"] = logging.Formatter(
"%(asctime)s | [%(levelname)s] %(name)s@%(filename)s:%(lineno)d:%(message)s"
)

self._log_format["stderr"] = logging.Formatter(
"%(asctime)s | [%(levelname)s] %(name)s@%(filename)s:%(lineno)d:%(message)s"
)

try:
self._logger.setLevel(ast.literal_eval("logging.{0}".format(self._level)))
except Exception:
self._logger.setLevel(logging.DEBUG)

@property
def level(self):
""" Return the logging level """
return self._level

@level.setter
def level(self, value):
""" Sets the log level """
self._level = "{0}".format(value.upper())

try:
self._logger.setLevel(ast.literal_eval("logging.{0}".format(self._level)))
except Exception:
self._logger.setLevel(logging.DEBUG)

def format(self, name):
""" Return the format for a known stream """
return self._log_format[name]

def add_stdout(self):
""" Adds a handler for stdout """
try:
if self._handlers["stdout"]:
self._handlers["stdout"].close()
except KeyError:
self._handlers["stdout"] = None

self._handlers["stdout"] = logging.StreamHandler(stream=sys.stdout)
self._handlers["stdout"].setFormatter(self.format("stdout"))
self._logger.addHandler(self._handlers["stdout"])

def add_stderr(self, value="error"):
""" Adds a handler for stderr """
try:
if self._handlers["stderr"]:
self._handlers["stderr"].close()
except KeyError:
self._handlers["stderr"] = None

self._handlers["stderr"] = logging.StreamHandler(stream=sys.stderr)
self._handlers["stderr"].setFormatter(self.format("stderr"))
# By default stderr handler limits logging level to error.
# In case logger itself has higher value, e.g. critical,
# it will limit the input of this handler.
try:
level = "{0}".format(value.upper())
self._handlers["stderr"].setLevel(
ast.literal_eval("logging.{0}".format(level))
)
except Exception:
self._handlers["stderr"].setLevel(logging.ERROR)
self._logger.addHandler(self._handlers["stderr"])

def setup(self, level: str = None, propagate=False):
"""
Constructs the logger with the system arguments provided upon
the object creation.
"""

if level is not None:
self.level = level

self.add_stdout()
self._logger.propagate = propagate

return self._logger

def add_custom_level(self, debug_level_name, debug_level_number):
""" Add a custom debug level for log filtering purposes.
To set a logging level called sensitive please call
self.add_custom_level(debug_level_name='sensitive',
debug_level_number=100)
afterwards the method will be available to the logger as
try:
logger.setLevel(eval("logging.{0}".format(level)))
except:
logger.setLevel(logging.DEBUG)
logger.sensitive('my logging message')
"""

formatter = logging.Formatter(log_format)
logging.addLevelName(debug_level_number, debug_level_name.upper())

# configures stdout
h = logging.StreamHandler(stream=sys.stdout)
h.setFormatter(formatter)
def cb(self, message, *pargs, **kws):
# Yes, logger takes its '*pargs' as 'args'.
if self.isEnabledFor(debug_level_number):
self._log(debug_level_number, message, pargs, **kws)

logger.addHandler(h)
setattr(logging.Logger, debug_level_name, cb)

return logger
def close(self):
""" Attempts to close log handlers """
for _, handler in self._handlers.items():
try:
handler.close()
except Exception as err:
self._logger.exception("Could not close logger {}".format(err))

0 comments on commit 4138272

Please sign in to comment.