Skip to main content

Severity

The logger uses OpenTelemetry severity numbering — an integer in the range 1..24 — for severity_number, and exactly one of six canonical strings for severity_text. The two fields together let backends filter on either numeric range or exact string equality.

Why two fields

Numeric severity_number carries the granularity (e.g., INFO2 = 10, INFO3 = 11); the canonical severity_text carries the bucket ("INFO"). Backends like Grafana, Honeycomb, and Datadog filter by exact match on severity_text, so the bucket value must come from a fixed set; otherwise a query like severity_text = "INFO" would silently miss "INFO2" records. The numeric field stays available for severity_number > 9 AND severity_number < 13 style queries.

The bucket table

The 24 numeric values map to six canonical strings:

severity_number rangeseverity_textMeaning
1-4"TRACE"Fine-grained diagnostic
5-8"DEBUG"Debug-level diagnostic
9-12"INFO"Informational (default INFO = 9)
13-16"WARN"Warning conditions
17-20"ERROR"Error requiring attention
21-24"FATAL"Critical — application-level failure

The primary names are trace, debug, info, warn, error, fatal with severity_number values 1, 5, 9, 13, 17, 21 respectively. Intermediate numbers go through the generic log(severity_number, ...) method; their bucket follows the table above.

Calling the severity methods

from dagstack.logger import Logger

logger = Logger.get("order_service.checkout")

logger.trace("entering function", attributes={"args.order_id": 1234})
logger.debug("cache miss", attributes={"cache.key": "user:42"})
logger.info("order placed", attributes={"order.id": 1234})
logger.warn("retry triggered", attributes={"retry.attempt": 2})
logger.error("payment declined", attributes={"order.id": 1234})
logger.fatal("config invariant violated", attributes={"reason": "missing service.name"})

For an intermediate value (INFO2, DEBUG3) use the generic log method with an explicit severity_number:

logger.log(11, "intermediate level", attributes={"phase": "warmup"})
# severity_number=11 → severity_text="INFO" (still in 9-12 bucket).

The constants

Each binding exposes the bucket boundaries as language-native constants:

from dagstack.logger import Severity

assert int(Severity.TRACE) == 1
assert int(Severity.DEBUG) == 5
assert int(Severity.INFO) == 9
assert int(Severity.WARN) == 13
assert int(Severity.ERROR) == 17
assert int(Severity.FATAL) == 21

Each binding emits the same six constants — values stay aligned with OTel numbering and never drift between languages. A planned _meta/severity.yaml (v1.1) will become the source of truth bindings vendor; v1.0 bindings ship the constants inline.

Mapping to other systems

SourceTRACEDEBUGINFOWARNERRORFATAL
OTel severity_number1-45-89-1213-1617-2021-24
Python logging— (no built-in)1020304050
Go slog-4048
Syslog (RFC 5424)776432/1/0

The mapping table will live in _meta/severity.yaml in the spec repository (v1.1; v1.0 bindings ship the table inline). See the full table on the Severity reference page.

See also