generated from CDCgov/template
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Description Setting up the project to emit structured (JSON) logs in production so its easier to index and query in Splunk. Also adding a request correlation id to each request, so we can group together different logging messages. ## Related Issues closes #84 ## Additional Notes - adding new default logging configurations for development and production (the latter includes correlation ids and outputs logs as JSON) - adding the LOG_CONFIG env variable for selecting the logging configuration to use - adding middleware to append a correlation id to the request (this is so we can group logs together based on the same request) and another for writing a custom access log - removing the `recordlinker.linkage` module, as its no longer being used
- Loading branch information
1 parent
c85f555
commit 0000ec5
Showing
17 changed files
with
390 additions
and
144 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,70 @@ | ||
{ | ||
"version": 1, | ||
"disable_existing_loggers": false, | ||
"filters": { | ||
"correlation_id": { | ||
"()": "asgi_correlation_id.CorrelationIdFilter", | ||
"default_value": "-" | ||
}, | ||
"dict_values": { | ||
"()": "recordlinker.log.DictArgFilter" | ||
} | ||
}, | ||
"formatters": { | ||
"default": { | ||
"()": "recordlinker.log.JSONFormatter", | ||
"format": "%(levelname)s %(name)s %(message)s %(correlation_id)s", | ||
"timestamp": true | ||
}, | ||
"access": { | ||
"()": "recordlinker.log.JSONFormatter", | ||
"fmt": "%(message)s", | ||
"static_fields": {"message": "ACCESS"} | ||
} | ||
}, | ||
"handlers": { | ||
"console": { | ||
"formatter": "default", | ||
"class": "logging.StreamHandler", | ||
"filters": ["correlation_id"], | ||
"stream": "ext://sys.stderr" | ||
}, | ||
"access": { | ||
"formatter": "access", | ||
"class": "logging.StreamHandler", | ||
"filters": ["dict_values"], | ||
"stream": "ext://sys.stdout" | ||
} | ||
}, | ||
"loggers": { | ||
"": { | ||
"handlers": ["console"], | ||
"level": "WARNING" | ||
}, | ||
"uvicorn": { | ||
"handlers": ["console"], | ||
"level": "INFO", | ||
"propagate": false | ||
}, | ||
"uvicorn.error": { | ||
"handlers": ["console"], | ||
"level": "INFO", | ||
"propagate": false | ||
}, | ||
"uvicorn.access": { | ||
"handlers": ["console"], | ||
"level": "CRITICAL", | ||
"propagate": false | ||
}, | ||
"recordlinker": { | ||
"handlers": ["console"], | ||
"level": "INFO", | ||
"propagate": false | ||
}, | ||
"recordlinker.access": { | ||
"handlers": ["access"], | ||
"level": "INFO", | ||
"propagate": false | ||
} | ||
} | ||
} |
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
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
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,2 @@ | ||
# initialize the configuration early | ||
import recordlinker.config # noqa: F401 |
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
Empty file.
This file was deleted.
Oops, something went wrong.
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,44 @@ | ||
import logging | ||
import typing | ||
|
||
import pythonjsonlogger.jsonlogger | ||
|
||
RESERVED_ATTRS = pythonjsonlogger.jsonlogger.RESERVED_ATTRS + ("taskName",) | ||
|
||
|
||
# Custom filter to transform log arguments into JSON fields | ||
class DictArgFilter(logging.Filter): | ||
def filter(self, record): | ||
""" | ||
Filter the log record to extract the dictionary arguments as fields. | ||
""" | ||
# if the args are a dictionary, set the key-value pairs as attributes | ||
if isinstance(record.args, dict): | ||
for key, value in record.args.items(): | ||
setattr(record, key, value) | ||
return True | ||
|
||
|
||
class KeyValueFilter(logging.Filter): | ||
def filter(self, record): | ||
""" | ||
Filter the log record to extract the key-value pairs from the log message. | ||
""" | ||
for key, value in record.__dict__.items(): | ||
if key not in RESERVED_ATTRS: | ||
record.msg = f"{record.msg} {key}={value}" | ||
return True | ||
|
||
|
||
class JSONFormatter(pythonjsonlogger.jsonlogger.JsonFormatter): | ||
""" | ||
A custom JSON formatter that excldues the taskName field by default. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
*args: typing.Any, | ||
reserved_attrs: tuple[str, ...] = RESERVED_ATTRS, | ||
**kwargs: typing.Any, | ||
): | ||
super().__init__(*args, reserved_attrs=reserved_attrs, **kwargs) |
Oops, something went wrong.