Observability¶
Structured logging, event constants, correlation tracking, and log sinks.
Logger¶
observability
¶
Observability module for structured logging and correlation tracking.
Provides:
- Structured logging via structlog with stdlib bridge
- Log configuration with console and file sinks
- Sensitive field sanitization
- Correlation ID tracking via context variables
.. note::
Call :func:`configure_logging` once at application startup to
initialise the logging pipeline. Use :func:`get_logger` in all
modules to obtain a bound structured logger.
get_logger
¶
Get a structured logger bound to the given name.
Thin wrapper over :func:structlog.get_logger that ensures
consistent logger creation across the codebase.
Usage::
from synthorg.observability import get_logger
logger = get_logger(__name__)
logger.info("something happened", key="value")
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Logger name, typically |
required |
**initial_bindings
|
Any
|
Key-value pairs bound to every log entry. |
{}
|
Returns:
| Type | Description |
|---|---|
BoundLogger
|
A bound structlog logger. |
Source code in src/synthorg/observability/_logger.py
Config¶
config
¶
Observability configuration models.
Frozen Pydantic models for log sinks, rotation, and top-level logging configuration. All models are immutable and validated on construction.
.. note::
``DEFAULT_SINKS`` provides the standard eleven-sink layout described
in the design spec (console + ten file sinks).
RotationConfig
pydantic-model
¶
Bases: BaseModel
Log file rotation configuration.
Attributes:
| Name | Type | Description |
|---|---|---|
strategy |
RotationStrategy
|
Rotation mechanism to use. |
max_bytes |
int
|
Maximum file size in bytes before rotation.
Only used when |
backup_count |
int
|
Number of rotated backup files to keep. |
compress_rotated |
bool
|
Whether to gzip-compress rotated backup files. Only supported with builtin rotation. |
Config:
frozen:Trueallow_inf_nan:Falseextra:forbid
Fields:
-
strategy(RotationStrategy) -
max_bytes(int) -
backup_count(int) -
compress_rotated(bool)
Validators:
-
_reject_compress_with_external
SinkConfig
pydantic-model
¶
Bases: BaseModel
Configuration for a single log output destination.
Attributes:
| Name | Type | Description |
|---|---|---|
sink_type |
SinkType
|
Where to send log output. |
level |
LogLevel
|
Minimum log level for this sink. |
file_path |
str | None
|
Relative path for FILE sinks (within |
rotation |
RotationConfig | None
|
Rotation settings for FILE sinks. |
json_format |
bool
|
Whether to format output as JSON. |
syslog_host |
str | None
|
Hostname for SYSLOG sinks. |
syslog_port |
int
|
Port for SYSLOG sinks. |
syslog_facility |
SyslogFacility
|
Syslog facility code. |
syslog_protocol |
SyslogProtocol
|
Transport protocol (TCP or UDP). |
http_url |
str | None
|
Endpoint URL for HTTP sinks. |
http_headers |
tuple[tuple[str, str], ...]
|
Extra HTTP headers as |
http_batch_size |
int
|
Records per HTTP POST batch. |
http_flush_interval_seconds |
float
|
Seconds between automatic flushes. |
http_timeout_seconds |
float
|
HTTP request timeout. |
http_max_retries |
int
|
Retry count on HTTP failure. |
Config:
frozen:Trueallow_inf_nan:Falseextra:forbid
Fields:
-
sink_type(SinkType) -
level(LogLevel) -
file_path(str | None) -
rotation(RotationConfig | None) -
json_format(bool) -
syslog_host(str | None) -
syslog_port(int) -
syslog_facility(SyslogFacility) -
syslog_protocol(SyslogProtocol) -
http_url(str | None) -
http_headers(tuple[tuple[str, str], ...]) -
http_batch_size(int) -
http_flush_interval_seconds(float) -
http_timeout_seconds(float) -
http_max_retries(int) -
otlp_endpoint(str | None) -
otlp_protocol(OtlpProtocol) -
otlp_headers(tuple[tuple[str, str], ...]) -
otlp_export_interval_seconds(float) -
otlp_batch_size(int) -
otlp_timeout_seconds(float)
Validators:
-
_validate_sink_type_fields
http_flush_interval_seconds
pydantic-field
¶
Seconds between automatic flushes
otlp_export_interval_seconds
pydantic-field
¶
Seconds between OTLP export batches
otlp_batch_size
pydantic-field
¶
Records per OTLP export batch
otlp_timeout_seconds
pydantic-field
¶
HTTP request timeout in seconds for OTLP export
ContainerLogShippingConfig
pydantic-model
¶
Bases: BaseModel
Configuration for shipping container logs to the observability stack.
Controls whether sandbox and sidecar container logs are collected and shipped through the structlog pipeline after execution.
Attributes:
| Name | Type | Description |
|---|---|---|
enabled |
bool
|
Whether container log shipping is active. |
ship_raw_logs |
bool
|
Whether to include raw stdout/stderr/sidecar payloads in shipped events (security-sensitive). |
collection_timeout_seconds |
float
|
Timeout for collecting container logs. |
max_log_bytes |
int
|
Total byte budget across all shipped fields per execution (stdout + stderr + sidecar logs combined). |
Config:
frozen:Trueallow_inf_nan:Falseextra:forbid
Fields:
-
enabled(bool) -
ship_raw_logs(bool) -
collection_timeout_seconds(float) -
max_log_bytes(int)
ship_raw_logs
pydantic-field
¶
Include raw stdout/stderr/sidecar payloads in shipped events. When False, only metadata (sizes, counts, timing) is shipped. Enable only in trusted environments -- raw output may contain secrets that bypass key-name-based redaction.
collection_timeout_seconds
pydantic-field
¶
Timeout for log collection from containers
max_log_bytes
pydantic-field
¶
Total byte budget per execution across all shipped fields
LogConfig
pydantic-model
¶
Bases: BaseModel
Top-level logging configuration.
Attributes:
| Name | Type | Description |
|---|---|---|
root_level |
LogLevel
|
Root logger level (handlers filter individually). |
logger_levels |
tuple[tuple[NotBlankStr, LogLevel], ...]
|
Per-logger level overrides as |
sinks |
tuple[SinkConfig, ...]
|
Tuple of sink configurations. |
enable_correlation |
bool
|
Whether to enable correlation ID tracking. |
log_dir |
NotBlankStr
|
Directory for log files. |
console_level |
str
|
Optional override for the console sink's log
level, distinct from
|
container_log_shipping |
ContainerLogShippingConfig
|
Container log shipping configuration. |
Config:
frozen:Trueallow_inf_nan:Falseextra:forbid
Fields:
-
root_level(LogLevel) -
logger_levels(tuple[tuple[NotBlankStr, LogLevel], ...]) -
sinks(tuple[SinkConfig, ...]) -
enable_correlation(bool) -
log_dir(NotBlankStr) -
console_level(str) -
container_log_shipping(ContainerLogShippingConfig)
Validators:
-
_validate_at_least_one_sink -
_validate_no_duplicate_logger_names -
_validate_no_duplicate_file_paths -
_validate_no_duplicate_syslog_endpoints -
_validate_no_duplicate_http_urls -
_validate_log_dir_safe
root_level
pydantic-field
¶
Root logger level. Defaults to INFO so HTTP log sinks do not leak verbose payloads or burn bandwidth on sampled streams; set to DEBUG explicitly (settings: observability.root_level) when operators need the full event stream. Per-logger overrides still force DEBUG on synthorg.engine / synthorg.memory so agent traces stay detailed.
enable_correlation
pydantic-field
¶
Whether to enable correlation ID tracking
console_level
pydantic-field
¶
Optional console-sink level override (mutable); empty string means use the per-sink / root level. The applier resolves DB > env (SYNTHORG_LOG_LEVEL) > YAML (this field) > unset through the observability.log_level_console registry entry.
Correlation¶
correlation
¶
Correlation ID management for structured logging.
Uses structlog's contextvars integration for async-safe context propagation across agent actions, tasks, and API requests.
.. note::
All binding functions are safe to call from both sync and async
code because Python's :mod:`contextvars` is natively async-aware.
generate_correlation_id
¶
Generate a new correlation ID.
Returns:
| Type | Description |
|---|---|
str
|
A UUID4 string suitable for use as a correlation identifier. |
bind_correlation_id
¶
Bind correlation IDs to the current context.
Only non-None values are bound. Existing bindings for
unspecified keys are left unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_id
|
str | None
|
Request correlation identifier. |
None
|
task_id
|
str | None
|
Task correlation identifier. |
None
|
agent_id
|
str | None
|
Agent correlation identifier. |
None
|
Source code in src/synthorg/observability/correlation.py
unbind_correlation_id
¶
Remove specific correlation IDs from the current context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_id
|
bool
|
Whether to unbind the |
False
|
task_id
|
bool
|
Whether to unbind the |
False
|
agent_id
|
bool
|
Whether to unbind the |
False
|
Source code in src/synthorg/observability/correlation.py
clear_correlation_ids
¶
Remove all correlation IDs from the current context.
Unbinds request_id, task_id, and agent_id. Other
context variables are preserved.
Source code in src/synthorg/observability/correlation.py
correlation_scope
¶
Scoped correlation binding that restores prior values on exit.
Uses structlog's bound_contextvars to save and restore any
pre-existing correlation IDs, making this safe for nested
execution contexts (e.g. hierarchical agent delegation).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_id
|
str | None
|
Request correlation identifier to bind. |
None
|
task_id
|
str | None
|
Task correlation identifier to bind. |
None
|
agent_id
|
str | None
|
Agent correlation identifier to bind. |
None
|
Source code in src/synthorg/observability/correlation.py
with_correlation
¶
Decorator that binds correlation IDs for a function's duration.
Correlation IDs are bound before the function executes and unbound
after it returns or raises. Only non-None IDs are managed.
Note
This decorator is for synchronous functions only. Applying
it to an async def function raises :exc:TypeError. For
async functions, use :func:with_correlation_async instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_id
|
str | None
|
Request correlation identifier to bind. |
None
|
task_id
|
str | None
|
Task correlation identifier to bind. |
None
|
agent_id
|
str | None
|
Agent correlation identifier to bind. |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[Callable[_P, _T]], Callable[_P, _T]]
|
A decorator that manages correlation ID lifecycle. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If the decorated function is a coroutine function. |
Source code in src/synthorg/observability/correlation.py
with_correlation_async
¶
Decorator that binds correlation IDs for an async function's duration.
Correlation IDs are bound before the coroutine executes and unbound
after it returns or raises. Only non-None IDs are managed.
Note
This decorator is for async functions only. Applying it to
a synchronous function raises :exc:TypeError. For sync
functions use :func:with_correlation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request_id
|
str | None
|
Request correlation identifier to bind. |
None
|
task_id
|
str | None
|
Task correlation identifier to bind. |
None
|
agent_id
|
str | None
|
Agent correlation identifier to bind. |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[Callable[_P, Coroutine[object, object, _T]]], Callable[_P, Coroutine[object, object, _T]]]
|
A decorator that manages correlation ID lifecycle for async |
Callable[[Callable[_P, Coroutine[object, object, _T]]], Callable[_P, Coroutine[object, object, _T]]]
|
functions. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If the decorated function is not a coroutine function. |
Source code in src/synthorg/observability/correlation.py
Setup¶
setup
¶
Logging system setup and configuration.
Provides the idempotent :func:configure_logging entry point that
wires structlog processors, stdlib handlers, and per-logger levels.
configure_logging
¶
Configure the structured logging system.
Sets up structlog processor chains, stdlib handlers, and per-logger levels. This function is idempotent -- calling it multiple times replaces the previous configuration without duplicating handlers.
Respects the SYNTHORG_LOG_LEVEL env var to override the console
sink level (useful for Docker deployments).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
LogConfig | None
|
Logging configuration. When |
None
|
routing_overrides
|
Mapping[str, tuple[str, ...]] | None
|
Optional extra logger-name routing entries
(e.g. from custom sinks) merged with the default
|
None
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If a critical sink fails to initialise. |
Source code in src/synthorg/observability/setup.py
Processors¶
processors
¶
Custom structlog processors for the observability pipeline.
sanitize_sensitive_fields
¶
Redact values of keys matching sensitive patterns.
Returns a new dict rather than mutating the original event dict, following the project's immutability convention. Redaction is applied recursively to nested dicts, lists, and tuples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logger
|
Any
|
The wrapped logger object (unused, required by structlog). |
required |
method_name
|
str
|
The name of the log method called (unused). |
required |
event_dict
|
MutableMapping[str, Any]
|
The event dictionary to process. |
required |
Returns:
| Type | Description |
|---|---|
Mapping[str, Any]
|
A new event dict with sensitive values replaced by |
Mapping[str, Any]
|
|
Source code in src/synthorg/observability/processors.py
scrub_event_fields
¶
Deep-scrub credential patterns out of every string value.
Belt-and-braces defence against the error=str(exc) leak
vector: even when a caller embeds a stringified exception (or
response body) that carries client_secret=...,
"access_token":"...", Authorization: Bearer ..., or raw
Fernet ciphertext, this processor rewrites the string so those
substrings are masked before the renderer sees them.
Runs after sanitize_sensitive_fields so keys that the
field-name scrubber already replaced with **REDACTED** stay
redacted.
Robustness contract: this processor runs on every log record.
If _scrub_value raises (e.g. a corrupted object whose repr
blows up, or a pathological recursive structure), we return the
original event dict unchanged rather than letting the exception
propagate and abort the caller's log call. Losing scrubbing on one
event is preferable to silencing the entire logging pipeline at the
moment of crisis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logger
|
Any
|
The wrapped logger object (unused, required by structlog). |
required |
method_name
|
str
|
The name of the log method called (unused). |
required |
event_dict
|
MutableMapping[str, Any]
|
The event dictionary to process. |
required |
Returns:
| Type | Description |
|---|---|
Mapping[str, Any]
|
A new event dict with every string value scrubbed via |
Mapping[str, Any]
|
func: |
Mapping[str, Any]
|
or the original dict if the scrub itself fails. |
Source code in src/synthorg/observability/processors.py
Sinks¶
sinks
¶
Log handler factory for building stdlib handlers from sink config.
Translates :class:~synthorg.observability.config.SinkConfig instances
into fully configured :class:logging.Handler objects with the
appropriate structlog :class:~structlog.stdlib.ProcessorFormatter.
build_handler
¶
Build a stdlib logging handler from a sink configuration.
For CONSOLE sinks a :class:logging.StreamHandler writing to
stderr is created. For FILE sinks see
:func:_build_file_handler. For SYSLOG and HTTP sinks,
dedicated handler builders are used.
Note: SYSLOG and HTTP sinks are built and returned by dedicated handler modules; they do not participate in logger-name routing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sink
|
SinkConfig
|
The sink configuration describing the handler to build. |
required |
log_dir
|
Path
|
Base directory for log files. |
required |
foreign_pre_chain
|
list[Any]
|
Processor chain for stdlib-originated logs. |
required |
routing
|
Mapping[str, tuple[str, ...]] | None
|
Optional routing table to use instead of the
module-level |
None
|
Returns:
| Type | Description |
|---|---|
Handler
|
A configured :class: |
Source code in src/synthorg/observability/sinks.py
Sink Config Builder¶
sink_config_builder
¶
Build a LogConfig from DEFAULT_SINKS + runtime overrides + custom sinks.
Pure-function module that merges static defaults with runtime settings
to produce a validated :class:LogConfig suitable for
:func:configure_logging.
The two JSON inputs come from SettingsService settings:
sink_overrides: JSON object keyed by sink identifier (__console__for the console sink, file path for file sinks). Each value is an object with optional fields:enabled,level,json_format,rotation.custom_sinks: JSON array of objects, each describing a new sink (file, syslog, or http). File sinks requirefile_path; syslog sinks requiresyslog_host; HTTP sinks requirehttp_url. All types accept optionallevel.
SinkBuildResult
dataclass
¶
Result of building a LogConfig from settings.
Attributes:
| Name | Type | Description |
|---|---|---|
config |
LogConfig
|
The fully validated logging configuration. |
routing_overrides |
MappingProxyType[str, tuple[str, ...]]
|
Custom sink routing entries keyed by file_path, mapping to logger name prefix tuples. |
build_log_config_from_settings
¶
build_log_config_from_settings(
*,
root_level,
enable_correlation,
sink_overrides_json,
custom_sinks_json,
log_dir="logs",
)
Merge DEFAULT_SINKS with runtime overrides and custom sinks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root_level
|
LogLevel
|
Root logger level. |
required |
enable_correlation
|
bool
|
Whether to enable correlation ID tracking. |
required |
sink_overrides_json
|
str
|
JSON object of per-sink overrides. |
required |
custom_sinks_json
|
str
|
JSON array of custom sink definitions. |
required |
log_dir
|
str
|
Directory for log files. |
'logs'
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
SinkBuildResult
|
class: |
SinkBuildResult
|
class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
On invalid JSON, validation failures, or attempts to disable the console sink. |
Source code in src/synthorg/observability/sink_config_builder.py
Enums¶
enums
¶
Observability-specific enumerations.
LogLevel
¶
Bases: StrEnum
Standard log severity levels.
Values match Python's stdlib logging level names for seamless
integration between structlog and the logging module.
RotationStrategy
¶
Bases: StrEnum
Log file rotation strategies.
Attributes:
| Name | Type | Description |
|---|---|---|
BUILTIN |
Size-based rotation via |
|
EXTERNAL |
Watched rotation via |
SinkType
¶
Bases: StrEnum
Log output destination types.
Attributes:
| Name | Type | Description |
|---|---|---|
CONSOLE |
Write to stderr via |
|
FILE |
Write to a log file with optional rotation. |
|
SYSLOG |
Ship structured JSON to a syslog endpoint. |
|
HTTP |
POST JSON log batches to an HTTP endpoint. |
|
PROMETHEUS |
Prometheus metrics scrape endpoint (pull-based). |
|
OTLP |
OpenTelemetry Protocol log/trace exporter (push-based). |
OtlpProtocol
¶
Bases: StrEnum
OpenTelemetry Protocol transport.
Attributes:
| Name | Type | Description |
|---|---|---|
HTTP_JSON |
HTTP with JSON encoding (the only implemented transport). |
|
GRPC |
gRPC transport (not implemented; rejected at handler init). |
SyslogFacility
¶
Bases: StrEnum
Syslog facility codes.
Maps to logging.handlers.SysLogHandler.LOG_* constants.
SyslogProtocol
¶
Bases: StrEnum
Syslog transport protocol.
Attributes:
| Name | Type | Description |
|---|---|---|
TCP |
Reliable delivery via |
|
UDP |
Lightweight delivery via |
Events¶
events
¶
Per-domain event name constants for observability.
All event names follow a dotted domain.subject[.qualifier] convention and are
used as the first positional argument to structured log calls::
from synthorg.observability.events.config import CONFIG_LOADED
logger.info(CONFIG_LOADED, config_path=path)
Import constants from their domain module directly (e.g.
events.provider, events.budget, events.tool).