Passed
Pull Request — master (#377)
by macartur
02:04
created

LogManager.load_logging_file()   A

Complexity

Conditions 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1.216

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 2
cts 5
cp 0.4
rs 9.4285
cc 1
crap 1.216
1
"""Handle logs displayed by Kytos SDN Platform."""
2 1
import inspect
3 1
import re
4 1
from configparser import RawConfigParser
5 1
from logging import Formatter, StreamHandler, config, getLogger
6
7 1
__all__ = ('LogManager', 'NAppLog')
8
9 1
10 1
class LogManager:
11
    """Manage handlers for all loggers."""
12
13 1
    configuration = None
14
15
    @classmethod
16 1
    def load_logging_file(cls, logging_file):
17
        """Loadding logs configuration from a file.
18
19
        Args:
20
           logging_file(str): Address of logging configuration file.
21
        """
22
        cls.configuration = RawConfigParser()
23 1
        cls.configuration.read(logging_file)
24
        config.fileConfig(logging_file)
25
26
    @classmethod
27
    def add_stream_handler(cls, stream):
28
        """Output all logs to the given stream.
29
30 1
        Args:
31 1
            stream: Object that supports ``write()`` and ``flush()``.
32 1
        """
33
        handler = StreamHandler(stream)
34 1
        cls._add_handler(handler)
35
        return handler
36 1
37 1
    @classmethod
38
    def _add_handler(cls, handler):
39
        """Method used to add a new handler to loggers.
40 1
41
        Args:
42
            handler(Handler): Handle to be added.
43
        """
44
        options = {}
45
        if cls.configuration:
46
            options = dict(cls.configuration.items('formatter_console'))
47
48
        fmt = Formatter(options.get('format', None),
49
                        options.get('datefmt', None))
50
        handler.setFormatter(fmt)
51
        getLogger().addHandler(handler)
52
53
54
class NAppLog:
55
    """High-level logger for NApp devs.
56 1
57
    From NApp dev's point of view:
58 1
    - No need for instantiation
59 1
    - Logger name is automatically assigned
60 1
61
    Redirect all calls to a logger with the correct name (NApp ID).
62
63
    The appropriate logger is a logging.Logger with NApp ID as its name. If no
64 1
    NApp is found, use the root logger.
65
66
    As any NApp can use this logger, its name is detected in every call by
67 1
    inspecting the caller's stack. If no NApp is found, use the root logger.
68
    """
69
70
    def __getattribute__(self, name):
71
        """Detect NApp ID and use its logger."""
72
        napp_id = detect_napp_id()
73
        logger = getLogger(napp_id)
74
        return logger.__getattribute__(name)
75
76
77 1
#: Detect NApp ID from filename
78 1
NAPP_ID_RE = re.compile(r'.*napps/(.*?)/(.*?)/')
79 1
80 1
81 1
def detect_napp_id():
82
    """Get the last called NApp in caller's stack.
83
84
    We use the last innermost NApp because a NApp *A* may call a NApp *B* and,
85
    when *B* uses the logger, the logger's name should be *B*.
86
87
    Returns:
88
        str: NApp ID.
89
        None: If no NApp is found in the caller's stack.
90
    """
91
    for frame in inspect.stack():
92
        if not frame.filename == __file__:
93
            match = NAPP_ID_RE.match(frame.filename)
94
            if match:
95
                return '/'.join(match.groups())
96
    return None
97