Passed
Push — master ( a6ab5f...fdd83f )
by Dean
02:55
created

LogHandler.emit()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 2
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
crap 2
1
from plugin.core.constants import PLUGIN_IDENTIFIER
2
from plugin.core.environment import Environment
3
from plugin.core.logger.filters import FrameworkFilter, AuthorizationFilter, RequestsLogFilter
4
5
from logging.handlers import RotatingFileHandler
6
import logging
7
import warnings
8
9
LOG_FORMAT = '%(asctime)-15s - %(name)-32s (%(thread)x) :  %(levelname)s (%(name)s:%(lineno)d) - %(message)s'
10
LOG_OPTIONS = {
11
    'plex':                         'libraries',
12
    'plex_activity':                'libraries',
13
    'plex_metadata':                'libraries',
14
    'requests':                     'libraries',
15
    'trakt':                        'libraries',
16
17
    'peewee':                       'peewee',
18
    'peewee_migrate':               'peewee',
19
20
    'com.plexapp.plugins.trakttv':  'plugin',
21
    'plugin':                       'plugin',
22
23
    'pyemitter':                    'pyemitter'
24
}
25
26
TRACE = 5
27
28
log = logging.getLogger(__name__)
29
30
31
class LogHandler(logging.Handler):
32
    def __init__(self, handler):
33
        super(LogHandler, self).__init__()
34
35
        self.handler = handler
36
37
        # Update formatter for log file
38
        self.handler.formatter._fmt = LOG_FORMAT
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _fmt was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
39
40
    @property
41
    def baseFilename(self):
42
        return self.handler.baseFilename
43
44
    def emit(self, record):
45
        return self.handler.emit(record)
46
47
48
class LoggerManager(object):
49
    @staticmethod
50
    def get_handler():
51
        logger = logging.getLogger(PLUGIN_IDENTIFIER)
52
53
        for h in logger.handlers:
54
            if type(h) is RotatingFileHandler:
55
                logger.handlers.remove(h)
56
                return LogHandler(h)
57
58
        return None
59
60
    @classmethod
61
    def setup(cls, report=True, storage=True):
62
        cls.setup_logging(report, storage)
63
        cls.setup_warnings()
64
65
        log.debug('Initialized logging (report: %r, storage: %r)', report, storage)
66
67
    @staticmethod
68
    def setup_logging(report=True, storage=True):
0 ignored issues
show
Unused Code introduced by
The argument report seems to be unused.
Loading history...
69
        # Initialize "root" logger
70
        rootLogger = logging.getLogger()
71
        rootLogger.setLevel(logging.DEBUG)
72
73
        rootLogger.filters = [
74
            FrameworkFilter()
75
        ]
76
77
        rootLogger.handlers = [
78
            LOG_HANDLER
79
        ]
80
81
        # Initialize "com.plexapp.plugins.trakttv" logger
82
        pluginLogger = logging.getLogger('com.plexapp.plugins.trakttv')
83
        pluginLogger.setLevel(logging.DEBUG)
84
85
        pluginLogger.filters = [
86
            FrameworkFilter()
87
        ]
88
89
        # Setup local error storage (if enabled)
90
        if storage:
91
            from plugin.core.logger.handlers.error_storage import ERROR_STORAGE_HANDLER
92
93
            rootLogger.handlers.append(ERROR_STORAGE_HANDLER)
94
95
    @classmethod
96
    def setup_warnings(cls):
97
        logger = logging.getLogger('warnings')
98
99
        def callback(message, category, filename, lineno, file=None, line=None):
0 ignored issues
show
Unused Code introduced by
The argument line seems to be unused.
Loading history...
Unused Code introduced by
The argument file seems to be unused.
Loading history...
Unused Code introduced by
The argument filename seems to be unused.
Loading history...
Unused Code introduced by
The argument lineno seems to be unused.
Loading history...
100
            if not category:
101
                logger.warn(message)
102
                return
103
104
            logger.warn('[%s] %s' % (category.__name__, message))
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
105
106
        warnings.showwarning = callback
107
108
    @classmethod
109
    def refresh(cls):
110
        for name, option in LOG_OPTIONS.items():
111
            logger = logging.getLogger(name)
112
113
            # Retrieve logger level, check if it has changed
114
            level = cls.get_level(option)
115
116
            if level == logger.level:
117
                continue
118
119
            # Update logger level
120
            log.debug('Changed %r logger level to %s', name, logging.getLevelName(level))
121
122
            logger.setLevel(level)
123
124
    @staticmethod
125
    def get_level(option):
126
        # Try retrieve preference
127
        try:
128
            value = Environment.prefs['level_%s' % option]
129
        except KeyError:
130
            # Default to "plugin" preference
131
            value = Environment.prefs['level_plugin']
132
133
        # Parse labels into level attributes
134
        if value == 'ERROR':
135
            return logging.ERROR
136
137
        if value == 'WARN' or value == 'WARNING':
138
            return logging.WARNING
139
140
        if value == 'INFO':
141
            return logging.INFO
142
143
        if value == 'DEBUG':
144
            return logging.DEBUG
145
146
        if value == "TRACE":
147
            return TRACE
148
149
        log.warn('Unknown logging level "%s"', value)
150
        return logging.DEBUG
151
152
# Get the logging file handler
153
LOG_HANDLER = LoggerManager.get_handler()
154
155
if LOG_HANDLER:
156
    LOG_HANDLER.addFilter(AuthorizationFilter())
157
    LOG_HANDLER.addFilter(RequestsLogFilter())
158