Last active
December 16, 2020 19:55
-
-
Save danielrichman/1acbbd66166ab857c404 to your computer and use it in GitHub Desktop.
logger adapters
This file contains hidden or 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
class FlaskLoggerAdapter(logging.LoggerAdapter): | |
""" | |
A :class:`logging.LoggerAdapter` that adds request context | |
Adds the following attributes to log records (which can then be used in a | |
:class:`logging.Formatter` string). | |
* base_url: ``flask.request.base_url`` | |
* flask_endpoint: ``flask.request.endpoint`` | |
* remote_addr: ``flask.request.remote_addr`` | |
* session_id: ``flask.session["session_id"]`` | |
* user_id: ``flask.session["user_id"]`` | |
Relevant properties will be None, if there is no request context, | |
or the session is bad. | |
""" | |
def __init__(self, logger): | |
super(LoggerAdapter, self).__init__(logger, None) | |
def process(self, msg, kwargs): | |
try: | |
add = {} | |
if flask.has_request_context(): | |
add["request_path"] = flask.request.path | |
add["flask_endpoint"] = flask.request.endpoint | |
add["remote_addr"] = flask.request.remote_addr | |
# Example: | |
# if getattr(flask.session, "ok", False): | |
# add["session_id"] = flask.session["session_id"] | |
# add["user_id"] = flask.session["user_id"] | |
extra = kwargs.setdefault("extra", {}) | |
for key in add: extra.setdefault(key, add[key]) | |
except Exception as e: | |
traceback.print_exc() | |
return msg, kwargs |
This file contains hidden or 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
class OptionalKeysFormatter(logging.Formatter): | |
""" | |
Adapt LogRecords that haven't come from :class:`LoggerAdapter` | |
This is a :class:`logging.Formatter` that fills in the keys that | |
:class:`LoggerAdapter` adds with ``None`` if they are not already set, | |
so that the same formatter may be used for records from | |
:class:`LoggerAdapter` and regular loggers. | |
""" | |
fill_keys = ("request_path", "flask_endpoint", "remote_addr") | |
# Example: "session_id", "user_id") | |
def format(self, record): | |
for key in self.fill_keys: | |
if not hasattr(record, key): | |
setattr(record, key, None) | |
return super(OptionalKeysFormatter, self).format(record) | |
def getLogger(name): | |
""":func:`logging.getLogger`, but returns :class:`LoggerAdapter` objects""" | |
return LoggerAdapter(logging.getLogger(name)) | |
# Example: | |
_format_string = "%(name)s %(levelname)s " \ | |
"(%(remote_addr)s %(flask_endpoint)s " \ | |
"%(session_id)s %(user_id)s) %(message)s" | |
_format_email = \ | |
"""%(levelname)s from logger %(name)s | |
Time: %(asctime)s | |
Location: %(pathname)s:%(lineno)d | |
Module: %(module)s | |
Function: %(funcName)s | |
Request: %(request_path)s | |
Endpoint: %(flask_endpoint)s | |
Client: %(remote_addr)s | |
Session ID: %(session_id)s | |
User ID: %(user_id)s | |
%(message)s""" |
This file contains hidden or 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
class LoggingContext: | |
""" | |
A :class:`LoggingContext` provides a method of setting attributes inside | |
a `with` block (setting the context), like so:: | |
with context.set(job=5): | |
assert context.job == 5 | |
with context.set(crsid="djr61"): | |
assert (context.job, context.crsid) == (5, "djr61") | |
assert context.crsid is None | |
Create a single context, and pass it to your (potentially many) | |
:class:`LoggerAdapter`s to add the attributes to records. | |
""" | |
def __init__(self): | |
self.crsid = None | |
self.job = None | |
self.vm = None | |
class _Keep: pass | |
Keep = _Keep() | |
@contextlib.contextmanager | |
def set(self, crsid=Keep, job=Keep, vm=Keep): | |
old = (self.crsid, self.job, self.vm) | |
if crsid is not self.Keep: self.crsid = crsid | |
if job is not self.Keep: self.job = job | |
if vm is not self.Keep: self.vm = vm | |
yield | |
self.crsid, self.job, self.vm = old | |
class LoggerAdapter(logging.LoggerAdapter): | |
""" | |
A :class:`logging.LoggerAdapter` that adds CRSID, Job and VM attributes | |
to log records. | |
""" | |
def __init__(self, context, logger): | |
super(LoggerAdapter, self).__init__(logger, None) | |
self.context = context | |
def process(self, msg, kwargs): | |
try: | |
extra = kwargs.setdefault("extra", {}) | |
extra["crsid"] = self.context.crsid | |
extra["job"] = self.context.job | |
extra["vm"] = self.context.vm | |
except Exception as e: | |
traceback.print_exc() | |
return msg, kwargs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment