Custom processor
A custom payload processor is an implementation of the payload processor protocol.
It can be assigned to LogContext when the default LogPayloadProcessor is not the right normalization engine for a particular application or layer.
The processor has one responsibility: convert raw payload data into a log-ready representation before LogContext creates the final LogEvent.
It must not decide whether an event should be logged and must not deliver events.
Protocol
A payload processor must provide three public methods.
from collections.abc import Mapping
from typing import Any
class CustomPayloadProcessor:
def normalize_payload(
self,
payload: Mapping[str, Any],
*,
unbounded: bool = False,
) -> dict[str, Any]:
...
def normalize_value_for_log(
self,
value: Any,
*,
unbounded: bool = False,
) -> str | int | float | bool | bytes | dict[str, Any] | list[Any] | None:
...
def get_plain_verbosity_level(self) -> str | None:
...
A custom processor implements the public methods required by LogPayloadProcessorProto. The protocol is used for type checking and describes the expected interface; the processor class provides the actual implementation.
For type annotations, use LogPayloadProcessorProto.
from mvx.common.logger import LogPayloadProcessorProto
processor: LogPayloadProcessorProto = CustomPayloadProcessor()
Public facade
LogContext calls the processor through this protocol.
For a normal logging call, the context delegates payload normalization to:
processor.normalize_payload(payload)
For explicit value normalization, the context delegates to:
processor.normalize_value_for_log(value)
For verbosity-aware helpers, the context uses:
processor.get_plain_verbosity_level()
This means a custom processor must be usable through these methods only. LogContext must not depend on implementation-specific attributes.
What the processor must do
normalize_payload() must return the final payload dictionary for LogEvent.payload.
normalize_value_for_log() must return a value that is safe to store inside that payload.
Allowed normalized value types are:
str | int | float | bool | bytes | dict[str, Any] | list[Any] | None
get_plain_verbosity_level() must return a string representation of the current verbosity mode, or None if the processor does not support verbosity.
A custom processor may implement any normalization rules it needs, but the result must be log-ready.
Minimal example
This example keeps primitive values unchanged, recursively normalizes mappings and lists, and converts unsupported objects to repr().
from collections.abc import Mapping
from typing import Any
class SimplePayloadProcessor:
def normalize_payload(
self,
payload: Mapping[str, Any],
*,
unbounded: bool = False,
) -> dict[str, Any]:
return {
str(key): self.normalize_value_for_log(value, unbounded=unbounded)
for key, value in payload.items()
}
def normalize_value_for_log(
self,
value: Any,
*,
unbounded: bool = False,
) -> str | int | float | bool | bytes | dict[str, Any] | list[Any] | None:
if value is None or isinstance(value, str | int | float | bool | bytes):
return value
if isinstance(value, Mapping):
return {
str(key): self.normalize_value_for_log(item, unbounded=unbounded)
for key, item in value.items()
}
if isinstance(value, list | tuple):
return [
self.normalize_value_for_log(item, unbounded=unbounded)
for item in value
]
return repr(value)
def get_plain_verbosity_level(self) -> str | None:
return None
Assign it to a context:
from mvx.common.logger import configure_log_context
processor = SimplePayloadProcessor()
ctx = configure_log_context(
"my_app.worker",
payload_processor=processor,
)
or assign it later:
ctx.set_payload_processor(processor)
Boundaries
A custom processor should only normalize payload data.
It should not:
decide whether an event is enabled;
create
LogEvent;send events to a sink;
write logs itself;
depend on a concrete delivery format.
Event selection belongs to event policy.
LogEvent creation belongs to LogContext.
Delivery belongs to sinks.
What to remember
A custom processor is any object compatible with
LogPayloadProcessorProto.It must implement
normalize_payload(),normalize_value_for_log(), andget_plain_verbosity_level().It is used through the public processor facade exposed by
LogContext.Its only responsibility is to turn raw payload data into log-ready payload data.