File: logger.py

package info (click to toggle)
python-fixtures 4.2.5-5
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 408 kB
  • sloc: python: 2,547; makefile: 29; sh: 9
file content (137 lines) | stat: -rw-r--r-- 4,699 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#  fixtures: Fixtures with cleanups for testing and convenience.
#
# Copyright (c) 2011, Robert Collins <robertc@robertcollins.net>
#
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
# license at the users choice. A copy of both licenses are available in the
# project source as Apache-2.0 and BSD. You may not use this file except in
# compliance with one of these two licences.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
# license you chose for the specific language governing permissions and
# limitations under that license.

from logging import StreamHandler, getLogger, INFO, Formatter
import sys

from fixtures import Fixture
from fixtures._fixtures.streams import StringStream

__all__ = [
    'FakeLogger',
    'LoggerFixture',
    'LogHandler',
]


class LogHandler(Fixture):
    """Replace a logger's handlers."""

    def __init__(self, handler, name="", level=None, nuke_handlers=True):
        """Create a LogHandler fixture.

        :param handler: The handler to replace other handlers with.
            If nuke_handlers is False, then added as an extra handler.
        :param name: The name of the logger to replace. Defaults to "".
        :param level: The log level to set, defaults to not changing the level.
        :param nuke_handlers: If True remove all existing handles (prevents
            existing messages going to e.g. stdout). Defaults to True.
        """
        super(LogHandler, self).__init__()
        self.handler = handler
        self._name = name
        self._level = level
        self._nuke_handlers = nuke_handlers

    def _setUp(self):
        logger = getLogger(self._name)
        if self._level:
            self.addCleanup(logger.setLevel, logger.level)
            logger.setLevel(self._level)
        if self._nuke_handlers:
            for handler in reversed(logger.handlers):
                self.addCleanup(logger.addHandler, handler)
                logger.removeHandler(handler)
        try:
            logger.addHandler(self.handler)
        finally:
            self.addCleanup(logger.removeHandler, self.handler)


class StreamHandlerRaiseException(StreamHandler):
    """Handler class that will raise an exception on formatting errors."""

    def handleError(self, record):
        _, value, tb = sys.exc_info()
        raise value.with_traceback(tb)


class FakeLogger(Fixture):
    """Replace a logger and capture its output."""

    def __init__(
        self,
        name="",
        level=INFO,
        format=None,
        datefmt=None,
        nuke_handlers=True,
        formatter=None,
    ):
        """Create a FakeLogger fixture.

        :param name: The name of the logger to replace. Defaults to "".
        :param level: The log level to set, defaults to INFO.
        :param format: Logging format to use. Defaults to capturing supplied
            messages verbatim.
        :param datefmt: Logging date format to use.
            Mirrors the datefmt used in python loggging.
        :param nuke_handlers: If True remove all existing handles (prevents
            existing messages going to e.g. stdout). Defaults to True.
        :param formatter: a custom log formatter class. Use this if you want
            to use a log Formatter other than the default one in python.

        Example:

          def test_log(self)
              fixture = self.useFixture(LoggerFixture())
              logging.info('message')
              self.assertEqual('message', fixture.output)
        """
        super(FakeLogger, self).__init__()
        self._name = name
        self._level = level
        self._format = format
        self._datefmt = datefmt
        self._nuke_handlers = nuke_handlers
        self._formatter = formatter

    def _setUp(self):
        name = "pythonlogging:'%s'" % self._name
        output = self.useFixture(StringStream(name)).stream
        self._output = output
        handler = StreamHandlerRaiseException(output)
        if self._format:
            formatter = self._formatter or Formatter
            handler.setFormatter(formatter(self._format, self._datefmt))
        self.useFixture(
            LogHandler(
                handler,
                name=self._name,
                level=self._level,
                nuke_handlers=self._nuke_handlers,
            )
        )

    @property
    def output(self):
        self._output.seek(0)
        return self._output.read()

    def reset_output(self):
        self._output.truncate(0)


LoggerFixture = FakeLogger