diff --git a/docs/changelog.md b/docs/changelog.md index c51d7ff..ccec8cb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -10,6 +10,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support `DictConfigurator` prefixes for `rename_fields` and `static_fields`. [#45](https://github.com/nhairs/python-json-logger/pull/45) - Allows using values like `ext://sys.stderr` in `fileConfig`/`dictConfig` value fields. +### Changed +- Rename `pythonjsonlogger.core.LogRecord` and `log_record` arguments to avoid confusion / overlapping with `logging.LogRecord`. [#38](https://github.com/nhairs/python-json-logger/issues/38) + - Affects arguments to `pythonjsonlogger.core.BaseJsonFormatter` (and any child classes). + - `serialize_log_record` + - `add_fields` + - `jsonify_log_record` + - `process_log_record` + - Note: functions referring to `log_record` have **not** had their function name changed. + ### Removed - Remove support for providing strings instead of objects when instantiating formatters. Instead use the `DictConfigurator` `ext://` prefix format when using `fileConfig`/`dictConfig`. [#47](https://github.com/nhairs/python-json-logger/issues/47) - Affects `pythonjsonlogger.json.JsonFormatter`: `json_default`, `json_encoder`, `json_serializer`. diff --git a/docs/cookbook.md b/docs/cookbook.md index 94ebd17..91a60ac 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -32,8 +32,8 @@ You can modify the `dict` of data that will be logged by overriding the `process ```python class SillyFormatter(JsonFormatter): - def process_log_record(log_record): - new_record = {k[::-1]: v for k, v in log_record.items()} + def process_log_record(log_data): + new_record = {k[::-1]: v for k, v in log_data.items()} return new_record ``` @@ -119,9 +119,9 @@ Another method would be to create a custom formatter class and override the `pro ## ----------------------------------------------------------------------------- # Reuse REQUEST_ID stuff from solution 2 class MyFormatter(JsonFormatter): - def process_log_record(self, log_record): - log_record["request_id"] = get_request_id() - return log_record + def process_log_record(self, log_data): + log_data["request_id"] = get_request_id() + return log_data handler.setFormatter(MyFormatter()) diff --git a/src/pythonjsonlogger/core.py b/src/pythonjsonlogger/core.py index b18f154..e3d5ab8 100644 --- a/src/pythonjsonlogger/core.py +++ b/src/pythonjsonlogger/core.py @@ -71,8 +71,11 @@ ## Type Aliases ## ----------------------------------------------------------------------------- -LogRecord: TypeAlias = Dict[str, Any] -"""Type alias""" +LogData: TypeAlias = Dict[str, Any] +"""Type alias + +*Changed in 4.0*: renamed from `LogRecord` to `LogData` +""" ### FUNCTIONS @@ -115,7 +118,7 @@ class BaseJsonFormatter(logging.Formatter): *Changed in 3.2*: `defaults` argument is no longer ignored. - *Added in UNRELEASED*: `exc_info_as_array` and `stack_info_as_array` options are added. + *Added in 4.0*: `exc_info_as_array` and `stack_info_as_array` options are added. """ _style: Union[logging.PercentStyle, str] # type: ignore[assignment] @@ -249,11 +252,11 @@ def format(self, record: logging.LogRecord) -> str: if record.stack_info and not message_dict.get("stack_info"): message_dict["stack_info"] = self.formatStack(record.stack_info) - log_record: LogRecord = {} - self.add_fields(log_record, record, message_dict) - log_record = self.process_log_record(log_record) + log_data: LogData = {} + self.add_fields(log_data, record, message_dict) + log_data = self.process_log_record(log_data) - return self.serialize_log_record(log_record) + return self.serialize_log_record(log_data) ## JSON Formatter Specific Methods ## ------------------------------------------------------------------------- @@ -287,17 +290,19 @@ def parse(self) -> List[str]: return [] - def serialize_log_record(self, log_record: LogRecord) -> str: - """Returns the final representation of the log record. + def serialize_log_record(self, log_data: LogData) -> str: + """Returns the final representation of the data to be logged Args: - log_record: the log record + log_data: the data + + *Changed in 4.0*: `log_record` renamed to `log_data` """ - return self.prefix + self.jsonify_log_record(log_record) + return self.prefix + self.jsonify_log_record(log_data) def add_fields( self, - log_record: Dict[str, Any], + log_data: Dict[str, Any], record: logging.LogRecord, message_dict: Dict[str, Any], ) -> None: @@ -306,38 +311,40 @@ def add_fields( This method can be overridden to implement custom logic for adding fields. Args: - log_record: data that will be logged + log_data: data that will be logged record: the record to extract data from message_dict: dictionary that was logged instead of a message. e.g `logger.info({"is_this_message_dict": True})` + + *Changed in 4.0*: `log_record` renamed to `log_data` """ for field in self.defaults: - log_record[self._get_rename(field)] = self.defaults[field] + log_data[self._get_rename(field)] = self.defaults[field] for field in self._required_fields: - log_record[self._get_rename(field)] = record.__dict__.get(field) + log_data[self._get_rename(field)] = record.__dict__.get(field) for data_dict in [self.static_fields, message_dict]: for key, value in data_dict.items(): - log_record[self._get_rename(key)] = value + log_data[self._get_rename(key)] = value merge_record_extra( record, - log_record, + log_data, reserved=self._skip_fields, rename_fields=self.rename_fields, ) if self.timestamp: key = self.timestamp if isinstance(self.timestamp, str) else "timestamp" - log_record[self._get_rename(key)] = datetime.fromtimestamp( + log_data[self._get_rename(key)] = datetime.fromtimestamp( record.created, tz=timezone.utc ) if self.rename_fields_keep_missing: for field in self.rename_fields.values(): - if field not in log_record: - log_record[field] = None + if field not in log_data: + log_data[field] = None return def _get_rename(self, key: str) -> str: @@ -345,26 +352,30 @@ def _get_rename(self, key: str) -> str: # Child Methods # .......................................................................... - def jsonify_log_record(self, log_record: LogRecord) -> str: - """Convert this log record into a JSON string. + def jsonify_log_record(self, log_data: LogData) -> str: + """Convert the log data into a JSON string. Child classes MUST override this method. Args: - log_record: the data to serialize + log_data: the data to serialize + + *Changed in 4.0*: `log_record` renamed to `log_data` """ raise NotImplementedError() - def process_log_record(self, log_record: LogRecord) -> LogRecord: - """Custom processing of the log record. + def process_log_record(self, log_data: LogData) -> LogData: + """Custom processing of the data to be logged. Child classes can override this method to alter the log record before it is serialized. Args: - log_record: incoming data + log_data: incoming data + + *Changed in 4.0*: `log_record` renamed to `log_data` """ - return log_record + return log_data def formatException(self, ei) -> Union[str, list[str]]: # type: ignore """Format and return the specified exception information. diff --git a/src/pythonjsonlogger/json.py b/src/pythonjsonlogger/json.py index 27b5efc..cce67c6 100644 --- a/src/pythonjsonlogger/json.py +++ b/src/pythonjsonlogger/json.py @@ -96,10 +96,10 @@ def __init__( self.json_encoder = JsonEncoder return - def jsonify_log_record(self, log_record: core.LogRecord) -> str: - """Returns a json string of the log record.""" + def jsonify_log_record(self, log_data: core.LogData) -> str: + """Returns a json string of the log data.""" return self.json_serializer( - log_record, + log_data, default=self.json_default, cls=self.json_encoder, indent=self.json_indent, diff --git a/src/pythonjsonlogger/msgspec.py b/src/pythonjsonlogger/msgspec.py index 208254c..4f17391 100644 --- a/src/pythonjsonlogger/msgspec.py +++ b/src/pythonjsonlogger/msgspec.py @@ -58,6 +58,6 @@ def __init__( self._encoder = msgspec.json.Encoder(enc_hook=self.json_default) return - def jsonify_log_record(self, log_record: core.LogRecord) -> str: - """Returns a json string of the log record.""" - return self._encoder.encode(log_record).decode("utf8") + def jsonify_log_record(self, log_data: core.LogData) -> str: + """Returns a json string of the log data.""" + return self._encoder.encode(log_data).decode("utf8") diff --git a/src/pythonjsonlogger/orjson.py b/src/pythonjsonlogger/orjson.py index ebbcd48..af726b3 100644 --- a/src/pythonjsonlogger/orjson.py +++ b/src/pythonjsonlogger/orjson.py @@ -62,10 +62,10 @@ def __init__( self.json_indent = json_indent return - def jsonify_log_record(self, log_record: core.LogRecord) -> str: - """Returns a json string of the log record.""" + def jsonify_log_record(self, log_data: core.LogData) -> str: + """Returns a json string of the log data.""" opt = orjson.OPT_NON_STR_KEYS if self.json_indent: opt |= orjson.OPT_INDENT_2 - return orjson.dumps(log_record, default=self.json_default, option=opt).decode("utf8") + return orjson.dumps(log_data, default=self.json_default, option=opt).decode("utf8") diff --git a/tests/test_formatters.py b/tests/test_formatters.py index 050fc5e..35ebe5e 100644 --- a/tests/test_formatters.py +++ b/tests/test_formatters.py @@ -380,9 +380,9 @@ def test_log_extra(env: LoggingEnvironment, class_: type[BaseJsonFormatter]): def test_custom_logic_adds_field(env: LoggingEnvironment, class_: type[BaseJsonFormatter]): class CustomJsonFormatter(class_): # type: ignore[valid-type,misc] - def process_log_record(self, log_record): - log_record["custom"] = "value" - return super().process_log_record(log_record) + def process_log_record(self, log_data): + log_data["custom"] = "value" + return super().process_log_record(log_data) env.set_formatter(CustomJsonFormatter()) env.logger.info("message")