forked from twisted/twisted
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
370dabf
commit 991156e
Showing
3 changed files
with
352 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| # -*- test-case-name: twisted.python.logger.test.test_file -*- | ||
| # Copyright (c) Twisted Matrix Laboratories. | ||
| # See LICENSE for details. | ||
| # | ||
| # Author: Roberto Polli <roberto.polli@babel.it> | ||
|
|
||
| """ | ||
| File log observer. | ||
| """ | ||
|
|
||
| from zope.interface import implementer | ||
| import syslog as stdsyslog | ||
|
|
||
| from ._observer import ILogObserver | ||
| from ._format import formatTime | ||
| from ._format import timeFormatRFC3339 | ||
| from ._format import formatEventAsClassicLogText | ||
| from ._levels import LogLevel | ||
|
|
||
| # These defaults come from the Python syslog docs. | ||
| DEFAULT_OPTIONS = 0 | ||
| DEFAULT_FACILITY = stdsyslog.LOG_USER | ||
|
|
||
| # Mappings to Python's syslog module | ||
| toSyslogLevelMapping = { | ||
| LogLevel.debug: stdsyslog.LOG_DEBUG, | ||
| LogLevel.info: stdsyslog.LOG_INFO, | ||
| LogLevel.warn: stdsyslog.LOG_WARNING, | ||
| LogLevel.error: stdsyslog.LOG_ERR, | ||
| LogLevel.critical: stdsyslog.LOG_ALERT, | ||
| } | ||
| fromSyslogLevelMapping = dict([ | ||
| (value, key) for (key, value) | ||
| in toSyslogLevelMapping.items() | ||
| ]) | ||
|
|
||
| @implementer(ILogObserver) | ||
| class SyslogObserver(object): | ||
| """ | ||
| A log observer for logging to syslog. | ||
| See L{twisted.python.log} for context. | ||
| This logObserver will automatically use LOG_ALERT priority for logged | ||
| failures (such as from C{log.err()}), but you can use any priority and | ||
| facility by setting the 'C{syslogPriority}' and 'C{syslogFacility}' keys in | ||
| the event dict. | ||
| """ | ||
| openlog = stdsyslog.openlog | ||
|
|
||
| def __init__(self, formatEvent, prefix, options=DEFAULT_OPTIONS, | ||
| facility=DEFAULT_FACILITY, syslog=stdsyslog.syslog): | ||
| """ | ||
| @type prefix: C{str} | ||
| @param prefix: The syslog prefix to use. | ||
| @type options: C{int} | ||
| @param options: A bitvector represented as an integer of the syslog | ||
| options to use. | ||
| @type facility: C{int} | ||
| @param facility: An indication to the syslog daemon of what sort of | ||
| program this is (essentially, an additional arbitrary metadata | ||
| classification for messages sent to syslog by this observer). | ||
| @param formatEvent: A callable that formats an event. | ||
| @type formatEvent: L{callable} that takes an C{event} argument and | ||
| returns a formatted event as L{unicode}. | ||
| """ | ||
| self.openlog(prefix, options, facility) | ||
|
|
||
| if False: | ||
| self._encoding = "utf-8" | ||
| else: | ||
| self._encoding = None | ||
|
|
||
| self.formatEvent = formatEvent | ||
| self.syslog = stdsyslog.syslog | ||
|
|
||
| def __call__(self, event): | ||
| """ | ||
| Write event to file. | ||
| @param event: An event. | ||
| @type event: L{dict} | ||
| """ | ||
| text = self.formatEvent(event) | ||
|
|
||
| if text is None: | ||
| text = u"" | ||
|
|
||
| # Set priority by loglevel and eventually | ||
| # override it if log_failure or syslogPriority is defined | ||
| try: | ||
| priority = toSyslogLevelMapping[event['log_level']] | ||
| except KeyError: | ||
| priority = stdsyslog.LOG_INFO | ||
|
|
||
| if "log_failure" in event: | ||
| text = u"\n".join((text, event["log_failure"].getTraceback())) | ||
| priority = stdsyslog.LOG_ALERT | ||
| elif 'syslogPriority' in event: | ||
| priority = int(event['syslogPriority']) | ||
|
|
||
|
|
||
| if self._encoding is not None: | ||
| text = text.encode(self._encoding) | ||
|
|
||
| if text: | ||
| facility = 0 | ||
| if 'syslogFacility' in event: | ||
| facility = int(event['syslogFacility']) | ||
| # write multi-line text | ||
| lines = text.split('\n') | ||
| while lines[-1:] == ['']: | ||
| lines.pop() | ||
|
|
||
| firstLine = True | ||
| for line in lines: | ||
| if firstLine: | ||
| firstLine = False | ||
| else: | ||
| # indent further lines | ||
| line = '\t' + line | ||
|
|
||
| self.syslog(priority | facility, text) | ||
|
|
||
|
|
||
|
|
||
| def textSyslogObserver(prefix="Twisted", options=DEFAULT_OPTIONS, | ||
| facility=DEFAULT_FACILITY, timeFormat=timeFormatRFC3339): | ||
| """ | ||
| Create a L{SyslogObserver} that emits text to a specified syslog object. | ||
| @type prefix: C{str} | ||
| @param prefix: The syslog prefix to use. | ||
| @type options: C{int} | ||
| @param options: A bitvector represented as an integer of the syslog | ||
| options to use. | ||
| @type facility: C{int} | ||
| @param facility: An indication to the syslog daemon of what sort of | ||
| program this is (essentially, an additional arbitrary metadata | ||
| classification for messages sent to syslog by this observer). | ||
| @param timeFormat: The format to use when adding timestamp prefixes to | ||
| logged events. If C{None}, or for events with no C{"log_timestamp"} | ||
| key, the default timestamp prefix of C{u"-"} is used. | ||
| @type timeFormat: L{unicode} or C{None} | ||
| @return: A file log observer. | ||
| @rtype: L{SyslogObserver} | ||
| """ | ||
| def formatEvent(event): | ||
| return formatEventAsClassicLogText( | ||
| event, formatTime=lambda e: formatTime(e, timeFormat) | ||
| ) | ||
|
|
||
| return SyslogObserver(formatEvent, prefix, options=DEFAULT_OPTIONS, | ||
| facility=DEFAULT_FACILITY) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,183 @@ | ||
| # Copyright (c) Twisted Matrix Laboratories. | ||
| # See LICENSE for details. | ||
| # | ||
| # Author: Roberto Polli <roberto.polli@babel.it> | ||
|
|
||
| """ | ||
| Test cases for L{twisted.python.logger._file}. | ||
| """ | ||
|
|
||
| from zope.interface.verify import verifyObject, BrokenMethodImplementation | ||
|
|
||
| from twisted.trial.unittest import TestCase | ||
|
|
||
| from twisted.python.failure import Failure | ||
| from twisted.python.compat import unicode | ||
| from .._observer import ILogObserver | ||
| from .._syslog import SyslogObserver | ||
| from .._syslog import textSyslogObserver | ||
|
|
||
| def mock_syslog(list_buffer): | ||
| """ | ||
| A mock function to collect syslog() invocations into | ||
| a given list_buffer. | ||
| """ | ||
| def tmp(*args, **kwds): | ||
| list_buffer.append((args, kwds)) | ||
| return tmp | ||
|
|
||
| class SyslogObserverTests(TestCase): | ||
| """ | ||
| Tests for L{SyslogObserver}. | ||
| """ | ||
|
|
||
| def test_interface(self): | ||
| """ | ||
| L{SyslogObserver} is an L{ILogObserver}. | ||
| """ | ||
| observer = SyslogObserver(lambda e: unicode(e), prefix="test_syslog") | ||
| try: | ||
| verifyObject(ILogObserver, observer) | ||
| except BrokenMethodImplementation as e: | ||
| self.fail(e) | ||
|
|
||
|
|
||
|
|
||
| def test_observeWrites(self): | ||
| """ | ||
| L{SyslogObserver} writes to the given file when it observes events. | ||
| """ | ||
| result = [] | ||
| observer = SyslogObserver(lambda e: unicode(e), prefix="test_syslog") | ||
| observer.syslog = mock_syslog(result) | ||
| event = dict(x=1) | ||
| observer(event) | ||
| #unpack the syslog event | ||
| (syslog_flags, syslog_text), _ = result[0] | ||
| self.assertEquals(syslog_text, unicode(event)) | ||
|
|
||
|
|
||
|
|
||
| def _test_observeWrites(self, what, count): | ||
| """ | ||
| Verify that observer performs an expected number of writes when the | ||
| formatter returns a given value. | ||
| @param what: the value for the formatter to return. | ||
| @type what: L{unicode} | ||
| @param count: the expected number of writes. | ||
| @type count: L{int} | ||
| """ | ||
| try: | ||
| fileHandle = DummyFile() | ||
| observer = SyslogObserver(lambda e: what, prefix="test_syslog") | ||
| event = dict(x=1) | ||
| observer(event) | ||
| self.assertEquals(fileHandle.writes, count) | ||
|
|
||
| finally: | ||
| fileHandle.close() | ||
|
|
||
|
|
||
| def test_observeWritesNone(self): | ||
| """ | ||
| L{SyslogObserver} does not write to the given file when it observes | ||
| events and C{formatEvent} returns C{None}. | ||
| """ | ||
| self._test_observeWrites(None, 0) | ||
|
|
||
|
|
||
| def test_observeWritesEmpty(self): | ||
| """ | ||
| L{SyslogObserver} does not write to the given file when it observes | ||
| events and C{formatEvent} returns C{u""}. | ||
| """ | ||
| self._test_observeWrites(u"", 0) | ||
|
|
||
|
|
||
| def test_observeFailure(self): | ||
| """ | ||
| If the C{"log_failure"} key exists in an event, the observer should | ||
| append the failure's traceback to the output. | ||
| """ | ||
|
|
||
| result = [] | ||
| observer = SyslogObserver(lambda e: unicode(e), prefix="test_syslog") | ||
| observer.syslog = mock_syslog(result) | ||
| try: | ||
| 1 / 0 | ||
| except ZeroDivisionError: | ||
| failure = Failure() | ||
|
|
||
| event = dict(log_failure=failure) | ||
| observer(event) | ||
| (syslog_flags, output), _ = result[0] | ||
| self.assertTrue( | ||
| output.startswith("{0}\nTraceback ".format(unicode(event))), | ||
| "Incorrect output:\n{0}".format(output) | ||
| ) | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| class TextSyslogObserverTests(TestCase): | ||
| """ | ||
| Tests for L{textSyslogObserver}. | ||
| """ | ||
|
|
||
| def test_returnsSyslogObserver(self): | ||
| """ | ||
| L{textSyslogObserver} returns a L{SyslogObserver}. | ||
| """ | ||
| observer = textSyslogObserver(prefix="Twisted") | ||
| self.assertIsInstance(observer, SyslogObserver) | ||
|
|
||
|
|
||
|
|
||
| def test_timeFormat(self): | ||
| """ | ||
| Returned L{SyslogObserver} has the correct text message. | ||
| """ | ||
| result = [] | ||
| observer = textSyslogObserver(prefix="test_syslog", timeFormat=u"%f") | ||
| observer.syslog = mock_syslog(result) | ||
| observer(dict(log_format=u"XYZZY", log_time=1.23456)) | ||
| (syslog_flags, syslog_text), _ = result[0] | ||
| self.assertEquals(syslog_text, u"234560 [-#-] XYZZY\n") | ||
|
|
||
|
|
||
|
|
||
| class DummyFile(object): | ||
| """ | ||
| File that counts writes and flushes. | ||
| """ | ||
|
|
||
| def __init__(self): | ||
| self.writes = 0 | ||
| self.flushes = 0 | ||
|
|
||
|
|
||
| def write(self, data): | ||
| """ | ||
| Write data. | ||
| @param data: data | ||
| @type data: L{unicode} or L{bytes} | ||
| """ | ||
| self.writes += 1 | ||
|
|
||
|
|
||
| def flush(self): | ||
| """ | ||
| Flush buffers. | ||
| """ | ||
| self.flushes += 1 | ||
|
|
||
|
|
||
| def close(self): | ||
| """ | ||
| Close. | ||
| """ | ||
| pass |