Skip to content

Commit

Permalink
Merge branch 'master' into 4078.race-condition
Browse files Browse the repository at this point in the history
  • Loading branch information
meejah committed Dec 23, 2023
2 parents 56c8614 + 5c6d65b commit bec5ee8
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 49 deletions.
1 change: 1 addition & 0 deletions newsfragments/4804.feature
@@ -0,0 +1 @@
Logs are now written in a thread, which should make the application more responsive under load.
53 changes: 16 additions & 37 deletions src/allmydata/test/test_eliotutil.py
@@ -1,20 +1,7 @@
"""
Tests for ``allmydata.util.eliotutil``.
Ported to Python 3.
"""

from __future__ import (
unicode_literals,
print_function,
absolute_import,
division,
)

from future.utils import PY2
if PY2:
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401

from sys import stdout
import logging

Expand Down Expand Up @@ -67,6 +54,7 @@
_parse_destination_description,
_EliotLogging,
)
from ..util.deferredutil import async_to_deferred

from .common import (
SyncTestCase,
Expand Down Expand Up @@ -214,53 +202,45 @@ def test_regular_file(self):
)


# Opt out of the great features of common.SyncTestCase because we're
# interacting with Eliot in a very obscure, particular, fragile way. :/
class EliotLoggingTests(TestCase):
# We need AsyncTestCase because logging happens in a thread tied to the
# reactor.
class EliotLoggingTests(AsyncTestCase):
"""
Tests for ``_EliotLogging``.
"""
def test_stdlib_event_relayed(self):
@async_to_deferred
async def test_stdlib_event_relayed(self):
"""
An event logged using the stdlib logging module is delivered to the Eliot
destination.
"""
collected = []
service = _EliotLogging([collected.append])
service.startService()
self.addCleanup(service.stopService)

# The first destination added to the global log destinations gets any
# buffered messages delivered to it. We don't care about those.
# Throw them on the floor. Sorry.
del collected[:]

logging.critical("oh no")
self.assertThat(
collected,
AfterPreprocessing(
len,
Equals(1),
),
await service.stopService()

self.assertTrue(
"oh no" in str(collected[-1]), collected
)

def test_twisted_event_relayed(self):
@async_to_deferred
async def test_twisted_event_relayed(self):
"""
An event logged with a ``twisted.logger.Logger`` is delivered to the Eliot
destination.
"""
collected = []
service = _EliotLogging([collected.append])
service.startService()
self.addCleanup(service.stopService)

from twisted.logger import Logger
Logger().critical("oh no")
self.assertThat(
collected,
AfterPreprocessing(
len, Equals(1),
),
await service.stopService()

self.assertTrue(
"oh no" in str(collected[-1]), collected
)

def test_validation_failure(self):
Expand Down Expand Up @@ -318,7 +298,6 @@ def test_skipped(self):
)



class LogCallDeferredTests(TestCase):
"""
Tests for ``log_call_deferred``.
Expand Down
22 changes: 10 additions & 12 deletions src/allmydata/util/eliotutil.py
Expand Up @@ -36,13 +36,11 @@
optional,
provides,
)

from twisted.internet import reactor
from eliot import (
ILogger,
Message,
FileDestination,
add_destinations,
remove_destination,
write_traceback,
start_action,
)
Expand All @@ -58,6 +56,7 @@
DeferredContext,
inline_callbacks,
)
from eliot.logwriter import ThreadedWriter
from twisted.python.usage import (
UsageError,
)
Expand All @@ -75,7 +74,7 @@
from twisted.internet.defer import (
maybeDeferred,
)
from twisted.application.service import Service
from twisted.application.service import MultiService

from .jsonbytes import AnyBytesJSONEncoder

Expand Down Expand Up @@ -144,7 +143,7 @@ def opt_help_eliot_destinations(self):
raise SystemExit(0)


class _EliotLogging(Service):
class _EliotLogging(MultiService):
"""
A service which adds stdout as an Eliot destination while it is running.
"""
Expand All @@ -153,23 +152,22 @@ def __init__(self, destinations):
:param list destinations: The Eliot destinations which will is added by this
service.
"""
self.destinations = destinations

MultiService.__init__(self)
for destination in destinations:
service = ThreadedWriter(destination, reactor)
service.setServiceParent(self)

def startService(self):
self.stdlib_cleanup = _stdlib_logging_to_eliot_configuration(getLogger())
self.twisted_observer = _TwistedLoggerToEliotObserver()
globalLogPublisher.addObserver(self.twisted_observer)
add_destinations(*self.destinations)
return Service.startService(self)
return MultiService.startService(self)


def stopService(self):
for dest in self.destinations:
remove_destination(dest)
globalLogPublisher.removeObserver(self.twisted_observer)
self.stdlib_cleanup()
return Service.stopService(self)
return MultiService.stopService(self)


@implementer(ILogObserver)
Expand Down

0 comments on commit bec5ee8

Please sign in to comment.