srlogger.py 6.91 KB
Newer Older
1
# Author: echel0n <[email protected]>
echel0n's avatar
echel0n committed
2
# URL: https://sickrage.ca
3
#
echel0n's avatar
echel0n committed
4
# This file is part of SickRage.
5
#
echel0n's avatar
echel0n committed
6
# SickRage is free software: you can redistribute it and/or modify
7
8
9
10
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
echel0n's avatar
echel0n committed
11
# SickRage is distributed in the hope that it will be useful,
12
13
14
15
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
echel0n's avatar
echel0n committed
17
# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
18

19
from __future__ import unicode_literals
20

21
import logging
22
import os
echel0n's avatar
echel0n committed
23
import pkgutil
24
import re
echel0n's avatar
v8.8.1    
echel0n committed
25
from logging import FileHandler, CRITICAL, DEBUG, ERROR, INFO, WARNING
26
from logging.handlers import RotatingFileHandler
27

28
29
30
import sickrage
from sickrage.core import makeDir

echel0n's avatar
echel0n committed
31

32
class srLogger(logging.getLoggerClass()):
33
    logging.captureWarnings(True)
34
    logging.getLogger().addHandler(logging.NullHandler())
35

36
37
38
    def __init__(self, name="sickrage"):
        super(srLogger, self).__init__(name)
        self.propagate = False
39

40
41
42
        self.consoleLogging = True
        self.fileLogging = False
        self.debugLogging = False
43

44
45
46
        self.logFile = None
        self.logSize = 1048576
        self.logNr = 5
47
48
49
50
51
52
53
54

        self.CRITICAL = CRITICAL
        self.DEBUG = DEBUG
        self.ERROR = ERROR
        self.WARNING = WARNING
        self.INFO = INFO
        self.DB = 5

55
56
        self.CENSORED_ITEMS = {}

57
        self.logLevels = {
58
59
60
61
62
            'CRITICAL': self.CRITICAL,
            'ERROR': self.ERROR,
            'WARNING': self.WARNING,
            'INFO': self.INFO,
            'DEBUG': self.DEBUG,
63
64
65
            'DB': 5
        }

66
        # list of allowed loggers
echel0n's avatar
echel0n committed
67
68
69
70
71
        self.loggers = {'sickrage': self,
                        'tornado.general': logging.getLogger('tornado.general'),
                        'tornado.application': logging.getLogger('tornado.application'),
                        'apscheduler.jobstores': logging.getLogger('apscheduler.jobstores'),
                        'apscheduler.scheduler': logging.getLogger('apscheduler.scheduler')}
72

73
        # set custom level for database logging
74
        logging.addLevelName(self.logLevels['DB'], 'DB')
echel0n's avatar
echel0n committed
75
        self.setLevel(self.logLevels['DB'])
76

77
78
79
80
        # start logger
        self.start()

    def start(self):
81
82
83
        # remove all handlers
        self.handlers = []

84
85
86
        # console log handler
        if self.consoleLogging:
            console = logging.StreamHandler()
echel0n's avatar
echel0n committed
87
88
89
            formatter = logging.Formatter('%(asctime)s %(levelname)s::%(threadName)s::%(message)s', '%H:%M:%S')

            console.setFormatter(formatter)
90
            console.setLevel(self.logLevels['INFO'] if not self.debugLogging else self.logLevels['DEBUG'])
91
            self.addHandler(console)
92

echel0n's avatar
echel0n committed
93
        # file log handlers
echel0n's avatar
echel0n committed
94
        if self.logFile and makeDir(os.path.dirname(self.logFile)):
echel0n's avatar
echel0n committed
95
            if sickrage.srCore.srConfig.DEVELOPER:
echel0n's avatar
echel0n committed
96
97
98
99
100
101
102
103
104
                rfh = FileHandler(
                    filename=self.logFile,
                )
            else:
                rfh = RotatingFileHandler(
                    filename=self.logFile,
                    maxBytes=self.logSize,
                    backupCount=self.logNr
                )
105

106
107
108
109
110
111
            rfh_errors = RotatingFileHandler(
                filename=self.logFile.replace('.log', '.error.log'),
                maxBytes=self.logSize,
                backupCount=self.logNr
            )

echel0n's avatar
echel0n committed
112
113
114
            formatter = logging.Formatter('%(asctime)s %(levelname)s::%(threadName)s::%(message)s', '%Y-%m-%d %H:%M:%S')

            rfh.setFormatter(formatter)
115
            rfh.setLevel(self.logLevels['INFO'] if not self.debugLogging else self.logLevels['DEBUG'])
116
            self.addHandler(rfh)
117

echel0n's avatar
echel0n committed
118
            rfh_errors.setFormatter(formatter)
119
120
121
            rfh_errors.setLevel(self.logLevels['ERROR'])
            self.addHandler(rfh_errors)

122
    def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
echel0n's avatar
echel0n committed
123
        if (False, True)[name in self.loggers]:
124
            record = super(srLogger, self).makeRecord(name, level, fn, lno, msg, args, exc_info, func, extra)
125

126
            try:
127
                record.msg = re.sub(
128
                    r"(.*)\b({})\b(.*)".format(
129
                        '|'.join([x for x in self.CENSORED_ITEMS.values() if len(x)])), r"\1\3",
130
                    record.msg)
131

132
133
134
135
136
137
138
                # needed because Newznab apikey isn't stored as key=value in a section.
                record.msg = re.sub(r"([&?]r|[&?]apikey|[&?]api_key)=[^&]*([&\w]?)", r"\1=**********\2", record.msg)
            except:
                pass

            # sending record to UI
            if record.levelno in [WARNING, ERROR]:
139
140
                from sickrage.core.classes import WarningViewer
                from sickrage.core.classes import ErrorViewer
141
142
143
144
                (WarningViewer(), ErrorViewer())[record.levelno == ERROR].add(record.msg, True)

            return record

echel0n's avatar
echel0n committed
145
146
147
    def set_level(self):
        self.debugLogging = sickrage.srCore.srConfig.DEBUG
        level = DEBUG if self.debugLogging else INFO
echel0n's avatar
echel0n committed
148
        for __, logger in self.loggers.items():
echel0n's avatar
echel0n committed
149
150
151
152
            logger.setLevel(level)
            for handler in logger.handlers:
                handler.setLevel(level)

echel0n's avatar
echel0n committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
    def list_modules(self, package):
        """Return all sub-modules for the specified package.

        :param package:
        :type package: module
        :return:
        :rtype: list of str
        """
        return [modname for importer, modname, ispkg in pkgutil.walk_packages(
            path=package.__path__, prefix=package.__name__ + '.', onerror=lambda x: None)]

    def get_loggers(self, package):
        """Return all loggers for package and sub-packages.

        :param package:
        :type package: module
        :return:
        :rtype: list of logging.Logger
        """
        return [logging.getLogger(modname) for modname in self.list_modules(package)]

174
175
176
    def log(self, level, msg, *args, **kwargs):
        super(srLogger, self).log(level, msg, *args, **kwargs)

177
    def db(self, msg, *args, **kwargs):
178
        super(srLogger, self).log(self.logLevels['DB'], msg, *args, **kwargs)
179

180
181
182
183
184
185
186
187
188
189
190
    def info(self, msg, *args, **kwargs):
        super(srLogger, self).info(msg, *args, **kwargs)

    def debug(self, msg, *args, **kwargs):
        super(srLogger, self).debug(msg, *args, **kwargs)

    def critical(self, msg, *args, **kwargs):
        super(srLogger, self).critical(msg, *args, **kwargs)

    def exception(self, msg, *args, **kwargs):
        super(srLogger, self).exception(msg, *args, **kwargs)
191

192
    def error(self, msg, *args, **kwargs):
193
        super(srLogger, self).error(msg, exc_info=1, *args, **kwargs)
194

195
    def warning(self, msg, *args, **kwargs):
196
        super(srLogger, self).warning(msg, *args, **kwargs)
197

echel0n's avatar
v8.8.1    
echel0n committed
198
    def close(self, *args, **kwargs):
199
        logging.shutdown()