Logging
- Start here: Logging guide
- Common recipes:
configure(...)for json, logfmt, text, pretty output.AccessLog()for one structured record per HTTP request.dict_config()to hand the configuration to the application server. Filters:DuplicateFilter,RateLimitFilter.
grelmicro.log
Logging.
LogBackend
LogBackend = Literal['loguru', 'structlog', 'stdlib']
Logging backend selection, matched in any case.
LogFormat
LogFormat = Literal[
"auto",
"json",
"logfmt",
"text",
"pretty",
"AUTO",
"JSON",
"LOGFMT",
"TEXT",
"PRETTY",
]
Built-in log format, matched in any case.
Type checkers accept the uppercase spellings until 0.45.0.
LogSerializer
LogSerializer = Literal['auto', 'stdlib', 'orjson']
JSON serializer selection, matched in any case.
AccessLog
AccessLog(
*,
include: tuple[str, ...] | None = None,
exclude: tuple[str, ...] | None = None,
quiet: tuple[str, ...] | None = None,
query: bool | None = None,
user_agent: bool | None = None,
enduser: bool | None = None,
name: str = "default",
env_prefix: str | None = None,
env_load: bool | None = None,
)
Bases: Reconfigurable[AccessLogConfig]
Write one structured record per HTTP request.
Register it and micro.install(app) adds the middleware:
from grelmicro import Grelmicro
from grelmicro.log import AccessLog, Log
micro = Grelmicro(uses=[Log(), AccessLog()])
micro.install(app)
While it is registered, uvicorn's own access log is silenced, because two access logs on one stream is worse than either alone. Nothing else about uvicorn's logging changes.
The level follows the answer: 5xx is an error, 4xx a warning, and
anything else information. The probe paths and /metrics are logged at
debug while they answer, so they stay out of the way without going
missing when they fail.
Every field is live: a mounted ConfigMap that adds a path to
GREL_ACCESS_LOG_EXCLUDE stops the records for it on the next
request, without a restart. Read more in Live
reconfiguration.
Read more in the Access Log docs.
Initialize the component with what to log and what to leave out.
| PARAMETER | DESCRIPTION |
|---|---|
include
|
Paths to log. Empty (the default) means every path. A pattern ending in
TYPE:
|
exclude
|
Paths to leave alone, whatever
TYPE:
|
quiet
|
Paths logged at debug while they answer, and at the level their status earns when they do not. Defaults to the probe paths and
TYPE:
|
query
|
Whether the record carries
TYPE:
|
user_agent
|
Whether the record carries
TYPE:
|
enduser
|
Whether the record carries
TYPE:
|
name
|
Registration name. Only one may be registered.
TYPE:
|
env_prefix
|
Override the derived prefix,
TYPE:
|
env_load
|
Whether to read environment variables.
TYPE:
|
kind
class-attribute
kind: str = 'access_log'
singleton
class-attribute
singleton: bool = True
asgi_observes
class-attribute
asgi_observes: bool = True
singleton_reason
class-attribute
singleton_reason: str = "One access log answers for the whole app, so two would write every request twice"
name
property
name: str
Return the registration name.
from_config
classmethod
from_config(
config: AccessLogConfig, *, name: str = "default"
) -> AccessLog
Build the component from a configuration that is already whole.
The one declarative door. What you pass is what runs: no environment variable is read, and the instance is not registered for live reload.
| PARAMETER | DESCRIPTION |
|---|---|
config
|
The pre-built access log configuration.
TYPE:
|
name
|
Registration name. Only one may be registered.
TYPE:
|
asgi_middleware
asgi_middleware() -> tuple[type[Any], dict[str, Any]]
Return the middleware class and the arguments to build it with.
Being asked is what says this app serves HTTP, which is what makes
uvicorn's access log worth silencing. A FastStream app serves
none, so install never asks, and its uvicorn access log is left
exactly where it was.
The middleware is handed the cell rather than the values, so a live reconfigure reaches it without the stack being rebuilt, which a framework will not do once it is serving.
AccessLogMiddleware
AccessLogMiddleware(
app: ASGIApp,
*,
include: tuple[str, ...] = (),
exclude: tuple[str, ...] = (),
quiet: tuple[str, ...] = DEFAULT_QUIET,
query: bool = True,
user_agent: bool = True,
enduser: bool = False,
live: Live[_State] | None = None,
)
Write one structured record per request.
Pure ASGI, so it runs on FastAPI, Starlette, Litestar, and anything else
that speaks ASGI. micro.install(app) adds it for a registered
AccessLog, and an app on a framework install does not know wraps
itself with it.
The record carries OpenTelemetry semantic conventions field names, the same ones the request span carries, so a backend reads one vocabulary across the log and the trace.
A request already seen by an access log further out, such as the one
of an installed app mounting this one, is passed through, so each
request writes one record. The outermost access log decides, its
include and exclude included: a path it leaves out is not
recorded by a nested one either.
Initialize the middleware with what to log and what to leave out.
| PARAMETER | DESCRIPTION |
|---|---|
app
|
The next ASGI application in the middleware chain.
TYPE:
|
include
|
Paths to log. Empty means every path. A pattern ending in
TYPE:
|
exclude
|
Paths to leave alone, whatever
TYPE:
|
quiet
|
Paths logged at debug while they answer, and at the level their status earns when they do not.
TYPE:
|
query
|
Whether the record carries
TYPE:
|
user_agent
|
Whether the record carries
TYPE:
|
enduser
|
Whether the record carries
TYPE:
|
live
|
The cell a registered
TYPE:
|
app
instance-attribute
app = app
DuplicateFilter
DuplicateFilter(
*,
allowed_repetitions: PositiveInt | None = None,
cache_size: PositiveInt | None = None,
key_mode: KeyMode | None = None,
ttl: int | timedelta | None = None,
key: Callable[[LogRecord], Hashable] | None = None,
env_name: str = "default",
env_prefix: str | None = None,
env_load: bool | None = None,
)
Bases: Filter
Drop log records that repeat beyond allowed_repetitions.
Keys are tracked in an LRU cache with at most cache_size
entries. Use key_mode to pick the default key, or pass
key to override it.
Set ttl to emit a new burst of
allowed_repetitions records every ttl during
a long flood. This gives operators regular reminders that
the issue is still active.
key_mode="template" is about 3 times faster than
"rendered" because it skips message formatting.
Thread-safe.
State is kept in the current process only. There is no reset method. Create a new filter to clear all counters.
Initialize the filter.
| PARAMETER | DESCRIPTION |
|---|---|
allowed_repetitions
|
Maximum number of records per key that pass the filter before subsequent records are dropped. Default: 5. When unset and env reads are enabled (see
TYPE:
|
cache_size
|
Maximum number of distinct keys tracked. When exceeded, the least-recently-seen key is evicted. Default: 100.
TYPE:
|
key_mode
|
Default key strategy:
TYPE:
|
ttl
|
Silence window for automatic counter reset, in whole
seconds or as a
TYPE:
|
key
|
Override the default key function. Receives the record and returns any hashable value.
TYPE:
|
env_name
|
Environment variable namespace segment. Default:
TYPE:
|
env_prefix
|
Override the auto-derived environment variable prefix. Default:
TYPE:
|
env_load
|
Whether to read environment variables. When None (the default), follow the process-wide
Pass False when the values here are the whole truth. Env reads fill every field not passed, so a config half taken from somewhere else silently gets the rest from the environment.
TYPE:
|
from_config
classmethod
from_config(
config: DuplicateFilterConfig,
*,
key: Callable[[LogRecord], Hashable] | None = None,
) -> Self
Construct a DuplicateFilter from a pre-built DuplicateFilterConfig.
| PARAMETER | DESCRIPTION |
|---|---|
config
|
The pre-built duplicate filter configuration. Use this path when the configuration is assembled at startup from a settings tree. The environment path is bypassed and the config is used as-is.
TYPE:
|
key
|
Override the default key function. Receives the record and returns any hashable value.
TYPE:
|
filter
filter(record: LogRecord) -> bool
Return True if the record should pass, False to drop.
DuplicateFilterConfig
Bases: BaseModel
Duplicate Filter Config.
allowed_repetitions
class-attribute
instance-attribute
allowed_repetitions: PositiveInt = 5
Maximum number of records per key that pass the filter before subsequent records are dropped.
cache_size
class-attribute
instance-attribute
cache_size: PositiveInt = 100
Maximum number of distinct keys tracked. When exceeded, the least-recently-seen key is evicted.
key_mode
class-attribute
instance-attribute
key_mode: KeyMode = 'template'
Default key strategy. "logger" keys per logger name. "level" keys per log level. "global" shares one counter for every record. "template" (default) keys per (logger, level, str(record.msg)): collapses across arg values of the same template and is about 3x faster. "rendered" keys per (logger, level, record.getMessage()): distinguishes fully-rendered messages.
ttl
class-attribute
instance-attribute
ttl: Duration | None = None
Silence window for automatic counter reset, in whole seconds or as a timedelta. A float is refused. If a key has not been seen for ttl, its counter resets the next time it appears. None (default) disables time-based expiry, so only LRU eviction can reset a counter. From text, such as an environment variable, it reads whole seconds ("60"), an ISO 8601 duration ("PT0.5S"), or "none" for no time limit.
ErrorDict
Bases: TypedDict
Structured error representation.
type
instance-attribute
type: str
message
instance-attribute
message: str
stack
instance-attribute
stack: NotRequired[str]
JSONRecordDict
Bases: TypedDict
Structured JSON log record after serialization.
Describes the shape of each log line after it is serialized
to JSON. Internally, formatters use datetime objects
for time. The JSON serializer converts them to ISO 8601.
The core fields follow common structured-logging conventions. Extra context fields are added flat at the top level.
Example::
{"time": "2026-03-30T14:00:00+00:00", "level": "INFO",
"msg": "request handled", "logger": "myapp.api",
"caller": "handle:45",
"method": "GET", "status": 200}
time
instance-attribute
time: str
level
instance-attribute
level: str
msg
instance-attribute
msg: str
logger
instance-attribute
logger: str
caller
instance-attribute
caller: NotRequired[str]
trace_id
instance-attribute
trace_id: NotRequired[str]
span_id
instance-attribute
span_id: NotRequired[str]
Log
Log(
*,
name: str = "default",
backend: LogBackend | None = None,
level: LogLevel | None = None,
format: LogFormat | str | None = None,
timezone: TimeZoneName | None = None,
json_serializer: LogSerializer | None = None,
caller_enabled: bool | None = None,
otel_enabled: bool | None = None,
uvicorn_enabled: bool | None = None,
queue_enabled: bool | None = None,
queue_size: int | None = None,
env_load: bool | None = None,
)
Log component: installs logging on enter, restores stdlib root state on exit.
Registered as micro.log after Grelmicro.use(Log(...)). Mirrors the
knobs on grelmicro.log.configure(...). Construction stays cheap, the
backend is configured when the surrounding Grelmicro opens.
Example
from grelmicro import Grelmicro
from grelmicro.log import Log
micro = Grelmicro(uses=[Log()])
async with micro:
...
On exit, the previous stdlib root handlers and level are restored so
sequential Grelmicro(...) blocks do not pile handlers up. Every
backend installs a root handler, so every backend gives the root
logger back. What each one installs for its own records, loguru's sink
and structlog's processors, stays configured: those are process-wide
settings of libraries the app chose, not state this component took.
The stdlib root logger is a single global. Log.__aenter__ and
Log.__aexit__ serialize on a class-level threading.Lock so the
snapshot/restore sequence cannot interleave across concurrent
Grelmicro lifecycles in the same process. Run one Log at a
time per process.
Read more in the Logging docs.
Initialize the component (defer configuration until __aenter__).
| PARAMETER | DESCRIPTION |
|---|---|
name
|
Registration name.
TYPE:
|
backend
|
Logging backend (
TYPE:
|
level
|
Log level threshold.
TYPE:
|
format
|
Log format.
TYPE:
|
timezone
|
IANA timezone for timestamps.
TYPE:
|
json_serializer
|
JSON serializer.
TYPE:
|
caller_enabled
|
Include caller (function and line) in log records.
TYPE:
|
otel_enabled
|
Extract OpenTelemetry trace context.
TYPE:
|
uvicorn_enabled
|
Take over uvicorn's own loggers so they match this format and write to the same stream. Turn it off only when something other than grelmicro owns uvicorn's logging.
TYPE:
|
queue_enabled
|
Hand log writes to a background thread.
TYPE:
|
queue_size
|
How many rendered records the queue holds before dropping.
TYPE:
|
env_load
|
Whether to read
TYPE:
|
kind
class-attribute
kind: str = 'log'
singleton
class-attribute
singleton: bool = True
name
property
name: str
Return the registration name.
config
property
config: LogConfig
Return the resolved LogConfig.
| RAISES | DESCRIPTION |
|---|---|
RuntimeError
|
If accessed before the component has been entered. |
from_config
classmethod
from_config(
config: LogConfig, *, name: str = "default"
) -> Self
Construct a Log from a pre-built LogConfig.
| PARAMETER | DESCRIPTION |
|---|---|
config
|
The pre-built logging configuration. Use this path when the configuration is assembled at
startup from a settings tree (for example YAML, Vault,
or a
TYPE:
|
name
|
Registration name. Defaults to
TYPE:
|
LogConfig
Bases: BaseModel
Log Config.
backend
class-attribute
instance-attribute
backend: LogBackend = 'stdlib'
Logging backend implementation.
format
class-attribute
instance-attribute
format: LogFormat | str = _DEFAULT_FORMAT
Log format. Built-in option or a custom template string.
timezone
class-attribute
instance-attribute
timezone: TimeZoneName = TimeZoneName(UTC_NAME)
IANA timezone for timestamps.
json_serializer
class-attribute
instance-attribute
json_serializer: LogSerializer = 'auto'
JSON serializer used for structured output. auto uses orjson when it is installed and the standard library otherwise. orjson requires it and raises when it is missing.
caller_enabled
class-attribute
instance-attribute
caller_enabled: bool = False
Include caller (function and line) in log records.
otel_enabled
class-attribute
instance-attribute
otel_enabled: bool = opentelemetry is not None
Extract OpenTelemetry trace context into log records.
uvicorn_enabled
class-attribute
instance-attribute
uvicorn_enabled: bool = True
Take over uvicorn's own loggers so they match this format and write to the same stream. Uvicorn installs its own handlers with propagation off, so without this its lines keep their own format and the process emits two. Turn it off only when something other than grelmicro owns uvicorn's logging.
queue_enabled
class-attribute
instance-attribute
queue_enabled: bool = False
Hand log writes to a background thread. The record is rendered on the calling thread and queued, so a slow sink no longer blocks the event loop. A full queue drops the arriving record and reports the count.
queue_size
class-attribute
instance-attribute
queue_size: int = 10000
How many rendered records the queue holds before dropping.
LogError
Bases: GrelmicroError
Base log error.
RateLimitFilter
RateLimitFilter(
*,
capacity: PositiveInt | None = None,
refill_rate: PositiveFloat | None = None,
key_mode: KeyMode | None = None,
cost: PositiveFloat | None = None,
key: Callable[[LogRecord], str] | None = None,
env_name: str = "default",
env_prefix: str | None = None,
env_load: bool | None = None,
)
Bases: Filter
Rate-limit log records with a token bucket.
Drops records once the per-key bucket is empty. Each key refills
at refill_rate tokens per second, capped at capacity.
Pick the default key strategy via key_mode (per-logger,
per-level, global, template, rendered) or supply a custom key
callable. Tune capacity and refill_rate to match your
acceptable burst and sustained log rate.
Thread-safe. State lives in a
MemoryTokenBucket.
State is kept in the current process only. It is not shared
between processes. Create a new filter to clear all counters,
or call
reset for a
single key.
Example:
import logging
from grelmicro.log import RateLimitFilter
logger = logging.getLogger("grelmicro.ingest")
logger.addFilter(RateLimitFilter(capacity=10, refill_rate=1))
Read more in the Logging docs.
Initialize the filter.
| PARAMETER | DESCRIPTION |
|---|---|
capacity
|
Maximum burst size. Default: 5. When unset and env reads are enabled (see
TYPE:
|
refill_rate
|
Tokens replenished per second. Default: 1.
TYPE:
|
key_mode
|
Default key strategy:
TYPE:
|
cost
|
Tokens consumed per record. Default: 1.0.
TYPE:
|
key
|
Override the default key function. It receives the record and returns a string key. Returned keys share the same namespace as the default key function. Add a unique prefix if you need to avoid collisions.
TYPE:
|
env_name
|
Environment variable namespace segment. Default:
TYPE:
|
env_prefix
|
Override the auto-derived environment variable prefix. Default:
TYPE:
|
env_load
|
Whether to read environment variables. When None (the default), follow the process-wide
Pass False when the values here are the whole truth. Env reads fill every field not passed, so a config half taken from somewhere else silently gets the rest from the environment.
TYPE:
|
from_config
classmethod
from_config(
config: RateLimitFilterConfig,
*,
key: Callable[[LogRecord], str] | None = None,
) -> Self
Construct a RateLimitFilter from a pre-built RateLimitFilterConfig.
| PARAMETER | DESCRIPTION |
|---|---|
config
|
The pre-built rate limit filter configuration. Use this path when the configuration is assembled at startup from a settings tree. The environment path is bypassed and the config is used as-is.
TYPE:
|
key
|
Override the default key function. It receives the record and returns a string key.
TYPE:
|
filter
filter(record: LogRecord) -> bool
Return True to keep the record, False to drop it.
reset
reset(key: str = '') -> None
Restore key's bucket to full capacity.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
Identifier to reset. Use the same value the key function produces for the records you want to clear. Pass
TYPE:
|
RateLimitFilterConfig
Bases: BaseModel
Rate Limit Filter Config.
capacity
class-attribute
instance-attribute
capacity: PositiveInt = 5
Maximum burst size: the bucket holds at most this many tokens. A burst of up to capacity records is allowed before any are dropped.
refill_rate
class-attribute
instance-attribute
refill_rate: PositiveFloat = 1
Tokens (records) replenished per second. Sets the sustained rate after the initial burst is exhausted.
key_mode
class-attribute
instance-attribute
key_mode: KeyMode = 'logger'
Default key strategy. "logger" (default) buckets per logger name (each logger gets its own burst budget). "level" buckets per log level. "global" uses a single shared bucket for all records. "template" buckets per (logger, level, str(record.msg)): collapses across arg values of the same template. "rendered" buckets per (logger, level, record.getMessage()): distinguishes fully-rendered messages. Ignored when key is set.
cost
class-attribute
instance-attribute
cost: PositiveFloat = 1.0
Tokens used per record. Increase this to make some filters drain their bucket faster than others. For example, a filter on a verbose-level handler.
configure
configure(
*,
backend: LogBackend | None = None,
level: LogLevel | None = None,
format: LogFormat | str | None = None,
timezone: TimeZoneName | None = None,
json_serializer: LogSerializer | None = None,
caller_enabled: bool | None = None,
otel_enabled: bool | None = None,
uvicorn_enabled: bool | None = None,
queue_enabled: bool | None = None,
queue_size: int | None = None,
env_load: bool | None = None,
) -> LogConfig
Configure logging with the selected backend.
Two paths:
- Programmatic: pass any of the per-field kwargs. Unset fields
resolve from
GREL_LOG_*env vars (unlessenv_load=FalseorGREL_ENV_LOAD=0), then fromLogConfigdefaults. - Environmental: omit all kwargs.
GREL_LOG_*env vars populate every field.
For the declarative path, use
configure_with.
| PARAMETER | DESCRIPTION |
|---|---|
backend
|
Logging backend (
TYPE:
|
level
|
Log level threshold. Default:
TYPE:
|
format
|
Log format. Default:
TYPE:
|
timezone
|
IANA timezone for timestamps. Default:
TYPE:
|
json_serializer
|
JSON serializer. Default:
TYPE:
|
caller_enabled
|
Include caller (function and line) in log records. Default: False.
TYPE:
|
otel_enabled
|
Extract OpenTelemetry trace context. Default: True if OpenTelemetry is installed, else False.
TYPE:
|
uvicorn_enabled
|
Take over uvicorn's own loggers so they match this format. Default: True. Pass False when something other than grelmicro
owns uvicorn's logging. Leave it on when the server was given
TYPE:
|
queue_enabled
|
Hand log writes to a background thread. Default: False. Turn it on when the sink is slow enough that a write shows up as request latency.
TYPE:
|
queue_size
|
How many rendered records the queue holds before it starts dropping. Default: 10000.
TYPE:
|
env_load
|
Whether to read
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
LogConfig
|
The applied |
| RAISES | DESCRIPTION |
|---|---|
DependencyNotFoundError
|
If the selected backend module is not installed. |
SettingsValidationError
|
If configuration is invalid. |
configure_with
Configure logging from a pre-built LogConfig.
| PARAMETER | DESCRIPTION |
|---|---|
config
|
Pre-built logging configuration. Use this path when the configuration is assembled at startup from a settings tree. The environment path is bypassed and the config is used as-is.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
LogConfig
|
The same |
dict_config
dict_config(
*, env_load: bool | None = None
) -> dict[str, Any]
Return a logging configuration that renders records in this format.
Hand it to the application server and the process reads in one format from its first record, including the records written before the application module is imported:
uvicorn.run(app, log_config=dict_config())
The same document is what Gunicorn takes as logconfig_dict,
Hypercorn as logconfig_dict, and Granian as log_dictconfig. Every
logger those servers write to is given to the root logger, so a server
line and an application line render the same way.
It is a plain logging.config.dictConfig document, so it is also what
goes in the file uvicorn --log-config reads:
Path("logging.json").write_text(json.dumps(dict_config()))
Fields resolve from GREL_LOG_* when the document is built, and the
document carries them, so build it where the process starts rather
than where an image does. Two answers are still worked out where the
document is applied: the auto format is carried unresolved and reads
the format from the process that renders, and whether to colorize follows the
stream that process writes to. The environment is read unless
GREL_ENV_LOAD=0, the same as everywhere else, and env_load= overrides
that for one call. To render against
settings that never reach the environment, use
dict_config_with().
A document applied on its own is behind the queue queue_enabled asks
for, because the handler starts the writer when none is running.
A GREL_LOG_* variable set with the environment path off is reported
as a warning on this path rather than as a log record, because nothing
has configured the logger the record would be written to.
The document is applied by a process with the same packages installed.
otel_enabled defaults to whether OpenTelemetry is importable, and a
document carrying it as true refuses to apply where it is not, the
same as configure(otel_enabled=True) does.
An application that also writes through loguru or structlog calls
configure() as well, which adds the
backend. Each pass replaces the root handler rather than adding one,
so the last one applied is what the process reads in, and which one
that is depends on how the server was started. uvicorn app:app
imports the application after it builds Config, so configure()
runs last. uvicorn.run(app, ...) has already imported it, so the
document runs last.
Build the document from what configure() returns and the order stops
mattering, which is what to do whenever both run:
config = configure(format="pretty")
uvicorn.run(app, log_config=dict_config_with(config))
| PARAMETER | DESCRIPTION |
|---|---|
env_load
|
Whether to read
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Any]
|
A |
dict[str, Any]
|
a file, and a fresh copy on every call, so a server that writes |
dict[str, Any]
|
into it changes nothing else. |
| RAISES | DESCRIPTION |
|---|---|
DependencyNotFoundError
|
If orjson or OpenTelemetry is enabled but not installed. |
SettingsValidationError
|
If configuration is invalid. |
dict_config_with
dict_config_with(config: LogConfig) -> dict[str, Any]
Return a logging configuration built from a pre-built LogConfig.
The same document dict_config() returns,
built from settings given here rather than read from the environment.
| PARAMETER | DESCRIPTION |
|---|---|
config
|
Pre-built logging configuration. Use this path when the configuration is assembled at startup from a settings tree. The environment path is bypassed and the config is used as-is.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Any]
|
A |
dict[str, Any]
|
|
| RAISES | DESCRIPTION |
|---|---|
DependencyNotFoundError
|
If orjson or OpenTelemetry is enabled but not installed. |
formatter
formatter(
config: LogConfig | Mapping[str, Any] | None = None,
*,
use_colors: bool | None = None,
env_load: bool | None = None,
) -> Formatter
Return the formatter that renders a record in grelmicro's format.
Built to be named from a logging.config.dictConfig, which is how an
application server renders its own records the way the application
does:
{"()": "grelmicro.log.formatter"}
dict_config() assembles the whole
document, and this is the piece it names.
| PARAMETER | DESCRIPTION |
|---|---|
config
|
A resolved
TYPE:
|
use_colors
|
Whether to colorize, overriding
TYPE:
|
env_load
|
Whether to read
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
DependencyNotFoundError
|
If orjson or OpenTelemetry is enabled but not installed. |
SettingsValidationError
|
If configuration is invalid. |
handler
handler(
config: LogConfig | Mapping[str, Any] | None = None,
*,
env_load: bool | None = None,
) -> Handler
Return the handler grelmicro writes every record through.
Built to be named from a logging.config.dictConfig:
{"()": "grelmicro.log.handler", "formatter": "default"}
It writes to the stream the rest of the process writes to, so a record a server renders through this document goes through the same queue as the application's own. When the settings ask for a queue and no writer is running, one is started here, which is what puts a document applied on its own behind a queue.
A running writer is never taken out, whatever the settings say. It belongs to whoever started it, and stopping it here would strand the records it is holding. To turn a queue off, reconfigure the owner.
| PARAMETER | DESCRIPTION |
|---|---|
config
|
A resolved
TYPE:
|
env_load
|
Whether to read
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
DependencyNotFoundError
|
If orjson or OpenTelemetry is enabled but not installed. |
SettingsValidationError
|
If configuration is invalid. |
grelmicro.log.uvicorn
Uvicorn-friendly formatters for dictConfig usage.
UvicornFormatter
UvicornFormatter(
config: LogConfig | Mapping[str, Any] | None = None,
*,
use_colors: bool | None = None,
env_load: bool | None = None,
)
Bases: _BaseFormatter
Format-aware uvicorn formatter compatible with logging.config.dictConfig.
Reads GREL_LOG_FORMAT and produces the matching output (auto, json,
logfmt, text, pretty). The environment is read unless
GREL_ENV_LOAD=0, and env_load= overrides that for one formatter.
Pass config to format against an already-resolved LogConfig
instead of re-reading the environment. configure() uses that path so
uvicorn matches settings passed as keyword arguments, which never reach
the environment.
Pass use_colors to override the terminal check. Uvicorn writes it
into the document when it is started with --use-colors or
--no-use-colors, so a formatter named from a dictConfig has to
take it.
Initialize from a resolved config, or from the environment.
format
format(record: LogRecord) -> str
Format the log record.
UvicornAccessFormatter
UvicornAccessFormatter(
config: LogConfig | Mapping[str, Any] | None = None,
*,
use_colors: bool | None = None,
env_load: bool | None = None,
)
Bases: UvicornFormatter
Format-aware uvicorn access log formatter.
Parses uvicorn's access log tuple arguments into structured fields
(client_addr, method, full_path, http_version,
status_code).
format
format(record: LogRecord) -> str
Format access records with split request fields.
A record is split when it carries the request arguments and either uvicorn's access message or uvicorn's access logger. A record that is neither is formatted whole, so an application record reaching this formatter through a shared handler keeps its message instead of being read as a request.
The split runs on a copy. A record is formatted once per handler and
stays readable afterwards, so rewriting msg and args in place
would hand every later reader the rewritten record: a second handler
on the same logger, a queue listener, or a test reading caplog.