Closed Menyadar closed 1 year ago
from eliot import start_action, log_call
class LoggingWrapper:
def __init__(self, protocol):
self.ctx = start_action("protocol_connection")
self.protocol = protocol
def __getattr__(self, attr):
f = getattr(self.protocol, attr)
if not callable(f): return f
f = log_call(f)
return lambda *args, **kwargs: ctx.run(f, *args, **kwargs)
class EchoFactory(protocol.Factory):
def buildProtocol(self, addr):
return LoggingWrapper(Echo())
This is untested, mind you.
Made a bunch of edits, so make sure you use latest version. Probably also want to finish the action on connectionLost, this is just a sketch.
And even better would be Protocol-y class that just knew to wrap the Protocol-specific methods.
(I will try to add working version tomorrow.)
OK, here's someting that I tested and actually works:
class Echo(protocol.Protocol):
def connectionMade(self):
self.action = start_action(action_type="protocol:connection")
def dataReceived(self, data):
with self.action.context():
with start_action(action_type="protocol:datareceived",
bytes=len(data)):
self.transport.write(data)
def connectionLost(self, reason):
if isinstance(reason.value, ConnectionDone):
self.action.finish()
else:
self.action.finish(reason.value)
Thanks a lot. During the day I've come to mostly the same solution based on your first hint. Right now I'm hijacking transport.write/writeSequence to inject logging into outgoing messages.
I assume there is no (easy) way to mix this approach with standard @log_call
decorator without rewriting it to somehow pickup self.action
as it's context, right?
Honestly for dataReceived
I wouldn't typically want to use log_call
, logging every single byte will get very slow very fast for a high-traffic server.
You could write a custom decorator, something like:
from funcutils import wraps
def log_call_with_self_action(f):
f = log_call(f)
@wraps(f)
def wrap(self, *args, **kwargs):
with self.action.context():
return f(self, *args, **kwargs)
return wrap
Untested...
What're you working on, by the way?
@log_call
on dataReceived
was just an example.
I'm working on IMAP server (using twisted.mail.imap4
) with quite complex dependencies (pulling messages from multiple sources / dbs, custom caching, etc.) and debugging it with existing clients in heavy load was quite difficult without context-aware logging - each client holds multiple connections open and it gets messy really fast.
I'm trying to use Eliot mainly to help me follow stream of requests from Twisted protocols, but can't figure a way to do it. Just to make minimal sample, i've modified basic Echo server from twistedmatrix homepage to include Eliot logging:
When i run it with
twistd -noy sample.py
and then connect to it, i get each received message logged as single event, even when they are sent from same connection. This is after opening two client connections, first connection received 2 messages, second got one message:What is the correct way to get all messages from single connection grouped under one "parent" task_uuid? I plan to use mostly existing Twisted factories / protocols.