123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- """
- Test cases for L{twisted.logger._legacy}.
- """
- from time import time
- import logging as py_logging
- from zope.interface.verify import verifyObject, BrokenMethodImplementation
- from twisted.trial import unittest
- from twisted.python import context
- from twisted.python import log as legacyLog
- from twisted.python.failure import Failure
- from .._levels import LogLevel
- from .._observer import ILogObserver
- from .._format import formatEvent
- from .._legacy import LegacyLogObserverWrapper
- from .._legacy import publishToNewObserver
- class LegacyLogObserverWrapperTests(unittest.TestCase):
- """
- Tests for L{LegacyLogObserverWrapper}.
- """
- def test_interface(self):
- """
- L{LegacyLogObserverWrapper} is an L{ILogObserver}.
- """
- legacyObserver = lambda e: None
- observer = LegacyLogObserverWrapper(legacyObserver)
- try:
- verifyObject(ILogObserver, observer)
- except BrokenMethodImplementation as e:
- self.fail(e)
- def test_repr(self):
- """
- L{LegacyLogObserverWrapper} returns the expected string.
- """
- class LegacyObserver(object):
- def __repr__(self):
- return "<Legacy Observer>"
- def __call__(self):
- return
- observer = LegacyLogObserverWrapper(LegacyObserver())
- self.assertEqual(
- repr(observer),
- "LegacyLogObserverWrapper(<Legacy Observer>)"
- )
- def observe(self, event):
- """
- Send an event to a wrapped legacy observer and capture the event as
- seen by that observer.
- @param event: an event
- @type event: L{dict}
- @return: the event as observed by the legacy wrapper
- """
- events = []
- legacyObserver = lambda e: events.append(e)
- observer = LegacyLogObserverWrapper(legacyObserver)
- observer(event)
- self.assertEqual(len(events), 1)
- return events[0]
- def forwardAndVerify(self, event):
- """
- Send an event to a wrapped legacy observer and verify that its data is
- preserved.
- @param event: an event
- @type event: L{dict}
- @return: the event as observed by the legacy wrapper
- """
- # Make sure keys that are expected by the logging system are present
- event.setdefault("log_time", time())
- event.setdefault("log_system", "-")
- event.setdefault("log_level", LogLevel.info)
- # Send a copy: don't mutate me, bro
- observed = self.observe(dict(event))
- # Don't expect modifications
- for key, value in event.items():
- self.assertIn(key, observed)
- return observed
- def test_forward(self):
- """
- Basic forwarding: event keys as observed by a legacy observer are the
- same.
- """
- self.forwardAndVerify(dict(foo=1, bar=2))
- def test_time(self):
- """
- The new-style C{"log_time"} key is copied to the old-style C{"time"}
- key.
- """
- stamp = time()
- event = self.forwardAndVerify(dict(log_time=stamp))
- self.assertEqual(event["time"], stamp)
- def test_timeAlreadySet(self):
- """
- The new-style C{"log_time"} key does not step on a pre-existing
- old-style C{"time"} key.
- """
- stamp = time()
- event = self.forwardAndVerify(dict(log_time=stamp + 1, time=stamp))
- self.assertEqual(event["time"], stamp)
- def test_system(self):
- """
- The new-style C{"log_system"} key is copied to the old-style
- C{"system"} key.
- """
- event = self.forwardAndVerify(dict(log_system="foo"))
- self.assertEqual(event["system"], "foo")
- def test_systemAlreadySet(self):
- """
- The new-style C{"log_system"} key does not step on a pre-existing
- old-style C{"system"} key.
- """
- event = self.forwardAndVerify(dict(log_system="foo", system="bar"))
- self.assertEqual(event["system"], "bar")
- def test_noSystem(self):
- """
- If the new-style C{"log_system"} key is absent, the old-style
- C{"system"} key is set to C{"-"}.
- """
- # Don't use forwardAndVerify(), since that sets log_system.
- event = dict(log_time=time(), log_level=LogLevel.info)
- observed = self.observe(dict(event))
- self.assertEqual(observed["system"], "-")
- def test_levelNotChange(self):
- """
- If explicitly set, the C{isError} key will be preserved when forwarding
- from a new-style logging emitter to a legacy logging observer,
- regardless of log level.
- """
- self.forwardAndVerify(dict(log_level=LogLevel.info, isError=1))
- self.forwardAndVerify(dict(log_level=LogLevel.warn, isError=1))
- self.forwardAndVerify(dict(log_level=LogLevel.error, isError=0))
- self.forwardAndVerify(dict(log_level=LogLevel.critical, isError=0))
- def test_pythonLogLevelNotSet(self):
- """
- The new-style C{"log_level"} key is not translated to the old-style
- C{"logLevel"} key.
- Events are forwarded from the old module from to new module and are
- then seen by old-style observers.
- We don't want to add unexpected keys to old-style events.
- """
- event = self.forwardAndVerify(dict(log_level=LogLevel.info))
- self.assertNotIn("logLevel", event)
- def test_stringPythonLogLevel(self):
- """
- If a stdlib log level was provided as a string (eg. C{"WARNING"}) in
- the legacy "logLevel" key, it does not get converted to a number.
- The documentation suggested that numerical values should be used but
- this was not a requirement.
- """
- event = self.forwardAndVerify(dict(
- logLevel="WARNING", # py_logging.WARNING is 30
- ))
- self.assertEqual(event["logLevel"], "WARNING")
- def test_message(self):
- """
- The old-style C{"message"} key is added, even if no new-style
- C{"log_format"} is given, as it is required, but may be empty.
- """
- event = self.forwardAndVerify(dict())
- self.assertEqual(event["message"], ()) # "message" is a tuple
- def test_messageAlreadySet(self):
- """
- The old-style C{"message"} key is not modified if it already exists.
- """
- event = self.forwardAndVerify(dict(message=("foo", "bar")))
- self.assertEqual(event["message"], ("foo", "bar"))
- def test_format(self):
- """
- Formatting is translated such that text is rendered correctly, even
- though old-style logging doesn't use PEP 3101 formatting.
- """
- event = self.forwardAndVerify(
- dict(log_format="Hello, {who}!", who="world")
- )
- self.assertEqual(
- legacyLog.textFromEventDict(event),
- "Hello, world!"
- )
- def test_formatMessage(self):
- """
- Using the message key, which is special in old-style, works for
- new-style formatting.
- """
- event = self.forwardAndVerify(
- dict(log_format="Hello, {message}!", message="world")
- )
- self.assertEqual(
- legacyLog.textFromEventDict(event),
- "Hello, world!"
- )
- def test_formatAlreadySet(self):
- """
- Formatting is not altered if the old-style C{"format"} key already
- exists.
- """
- event = self.forwardAndVerify(
- dict(log_format="Hello!", format="Howdy!")
- )
- self.assertEqual(legacyLog.textFromEventDict(event), "Howdy!")
- def eventWithFailure(self, **values):
- """
- Create a new-style event with a captured failure.
- @param values: Additional values to include in the event.
- @type values: L{dict}
- @return: the new event
- @rtype: L{dict}
- """
- failure = Failure(RuntimeError("nyargh!"))
- return self.forwardAndVerify(dict(
- log_failure=failure,
- log_format="oopsie...",
- **values
- ))
- def test_failure(self):
- """
- Captured failures in the new style set the old-style C{"failure"},
- C{"isError"}, and C{"why"} keys.
- """
- event = self.eventWithFailure()
- self.assertIs(event["failure"], event["log_failure"])
- self.assertTrue(event["isError"])
- self.assertEqual(event["why"], "oopsie...")
- def test_failureAlreadySet(self):
- """
- Captured failures in the new style do not step on a pre-existing
- old-style C{"failure"} key.
- """
- failure = Failure(RuntimeError("Weak salsa!"))
- event = self.eventWithFailure(failure=failure)
- self.assertIs(event["failure"], failure)
- def test_isErrorAlreadySet(self):
- """
- Captured failures in the new style do not step on a pre-existing
- old-style C{"isError"} key.
- """
- event = self.eventWithFailure(isError=0)
- self.assertEqual(event["isError"], 0)
- def test_whyAlreadySet(self):
- """
- Captured failures in the new style do not step on a pre-existing
- old-style C{"failure"} key.
- """
- event = self.eventWithFailure(why="blah")
- self.assertEqual(event["why"], "blah")
- class PublishToNewObserverTests(unittest.TestCase):
- """
- Tests for L{publishToNewObserver}.
- """
- def setUp(self):
- self.events = []
- self.observer = self.events.append
- def legacyEvent(self, *message, **values):
- """
- Return a basic old-style event as would be created by L{legacyLog.msg}.
- @param message: a message event value in the legacy event format
- @type message: L{tuple} of L{bytes}
- @param values: additional event values in the legacy event format
- @type event: L{dict}
- @return: a legacy event
- """
- event = (context.get(legacyLog.ILogContext) or {}).copy()
- event.update(values)
- event["message"] = message
- event["time"] = time()
- if "isError" not in event:
- event["isError"] = 0
- return event
- def test_observed(self):
- """
- The observer is called exactly once.
- """
- publishToNewObserver(
- self.observer, self.legacyEvent(), legacyLog.textFromEventDict
- )
- self.assertEqual(len(self.events), 1)
- def test_time(self):
- """
- The old-style C{"time"} key is copied to the new-style C{"log_time"}
- key.
- """
- publishToNewObserver(
- self.observer, self.legacyEvent(), legacyLog.textFromEventDict
- )
- self.assertEqual(
- self.events[0]["log_time"], self.events[0]["time"]
- )
- def test_message(self):
- """
- A published old-style event should format as text in the same way as
- the given C{textFromEventDict} callable would format it.
- """
- def textFromEventDict(event):
- return "".join(reversed(" ".join(event["message"])))
- event = self.legacyEvent("Hello,", "world!")
- text = textFromEventDict(event)
- publishToNewObserver(self.observer, event, textFromEventDict)
- self.assertEqual(formatEvent(self.events[0]), text)
- def test_defaultLogLevel(self):
- """
- Published event should have log level of L{LogLevel.info}.
- """
- publishToNewObserver(
- self.observer, self.legacyEvent(), legacyLog.textFromEventDict
- )
- self.assertEqual(self.events[0]["log_level"], LogLevel.info)
- def test_isError(self):
- """
- If C{"isError"} is set to C{1} (true) on the legacy event, the
- C{"log_level"} key should get set to L{LogLevel.critical}.
- """
- publishToNewObserver(
- self.observer,
- self.legacyEvent(isError=1),
- legacyLog.textFromEventDict
- )
- self.assertEqual(self.events[0]["log_level"], LogLevel.critical)
- def test_stdlibLogLevel(self):
- """
- If the old-style C{"logLevel"} key is set to a standard library logging
- level, using a predefined (L{int}) constant, the new-style
- C{"log_level"} key should get set to the corresponding log level.
- """
- publishToNewObserver(
- self.observer,
- self.legacyEvent(logLevel=py_logging.WARNING),
- legacyLog.textFromEventDict
- )
- self.assertEqual(self.events[0]["log_level"], LogLevel.warn)
- def test_stdlibLogLevelWithString(self):
- """
- If the old-style C{"logLevel"} key is set to a standard library logging
- level, using a string value, the new-style C{"log_level"} key should
- get set to the corresponding log level.
- """
- publishToNewObserver(
- self.observer,
- self.legacyEvent(logLevel="WARNING"),
- legacyLog.textFromEventDict
- )
- self.assertEqual(self.events[0]["log_level"], LogLevel.warn)
- def test_stdlibLogLevelWithGarbage(self):
- """
- If the old-style C{"logLevel"} key is set to a standard library logging
- level, using an unknown value, the new-style C{"log_level"} key should
- not get set.
- """
- publishToNewObserver(
- self.observer,
- self.legacyEvent(logLevel="Foo!!!!!"),
- legacyLog.textFromEventDict
- )
- self.assertNotIn("log_level", self.events[0])
- def test_defaultNamespace(self):
- """
- Published event should have a namespace of C{"log_legacy"} to indicate
- that it was forwarded from legacy logging.
- """
- publishToNewObserver(
- self.observer, self.legacyEvent(), legacyLog.textFromEventDict
- )
- self.assertEqual(self.events[0]["log_namespace"], "log_legacy")
- def test_system(self):
- """
- The old-style C{"system"} key is copied to the new-style
- C{"log_system"} key.
- """
- publishToNewObserver(
- self.observer, self.legacyEvent(), legacyLog.textFromEventDict
- )
- self.assertEqual(
- self.events[0]["log_system"], self.events[0]["system"]
- )
|