Exporting Guardrails Logs to OpenTelemetry
The NVIDIA NeMo Guardrails library emits operational logs through Python’s standard logging module. When you have OpenTelemetry tracing configured, you can forward those log records into the same backend as your traces with a few lines of application code. Records emitted inside an active guardrails span automatically carry the span’s trace_id and span_id, so every log line correlates to the request that produced it.
This page covers the setup. For plain Python logging (verbose mode, explain, generation options), refer to Logging.
API and SDK Responsibilities
The library follows the OpenTelemetry library-instrumentation pattern.
- The library depends on the OpenTelemetry API only. It creates spans, emits log records, and participates in whatever OTEL pipeline the host application provides.
- The host application owns the SDK. Configuring a
TracerProvider, aLoggerProvider, exporters, and attaching handlers to Python’sloggingtree are all the application’s responsibility.
This split is deliberate. It lets the library stay decoupled from SDK version churn, avoids injecting the library into a host’s observability stack without opt-in, and gives applications full control over where their telemetry is exported.
The three-line recipe below is therefore a user-side setup, not something the library does for you.
Prerequisites
Before enabling log export, install the OpenTelemetry SDK as described in OpenTelemetry: Installation. The OpenTelemetry log components live in the same opentelemetry-sdk package that powers trace export. No additional installation is required for in-process log forwarding.
For exporting logs to an external backend over OTLP, install the OTLP exporter.
Attach the Logging Handler
Configure a LoggerProvider first, then attach the handler. The surrounding SDK setup appears in the full example below. The core of the bridge is three lines.
Configure the LoggerProvider through set_logger_provider(...) before you call addHandler(LoggingHandler()). The handler resolves its LoggerProvider on first emit and caches the result. If no provider is set by then, the SDK hands back a no-op logger and every forwarded record is silently discarded. The SDK raises no error, and calling set_logger_provider(...) later does not recover the handler.
Each line does the following:
logging.getLogger("nemoguardrails")selects the logger namespace that catches most records emitted by the library. Submodules that uselogging.getLogger(__name__)inherit this handler. Verbose mode (nemoguardrails.logging.verbose) is the known exception. It writes to the root logger, so attach the handler to the root logger as well if you need verbose output forwarded.LoggingHandler()is an OpenTelemetry-providedlogging.Handlersubclass that converts each PythonLogRecordinto an OTEL log record. On first emit it resolves the activeLoggerProviderthroughget_logger_provider(), caches the resulting logger, and attaches trace context automatically..addHandler(...)attaches the handler. From this point forward, every record the NeMo Guardrails library emits flows to both the host’s existing handlers (console, files, and so on) and the OpenTelemetry pipeline, provided aLoggerProviderwas configured before this call.
This is additive. Your existing Python logging configuration continues to work unchanged. OpenTelemetry export happens alongside, not instead.
Full Example with Traces and Logs
This program configures a TracerProvider and a LoggerProvider, both exporting to the console, then runs a guardrails request so you can see correlated spans and log records.
Running this script prints both the span tree and the log records to your console. Records emitted while the guardrails request is in flight carry trace_id and span_id fields that match the enclosing span.
Exporting to a Backend
The log-record processor in the example above can target any OpenTelemetry log exporter. The following example uses an OTLP collector.
The OpenTelemetry Collector then forwards the records to any compatible backend, such as Loki, Datadog, New Relic, or Elastic. Refer to the OpenTelemetry Registry for the list.
What the Exported Records Contain
Each forwarded LogRecord becomes an OTEL log record with the following fields populated automatically.
Log records emitted outside any guardrails request, such as startup, engine registration, or teardown, still flow through. Their trace_id and span_id are zero because there is no active span.
Considerations
- Experimental SDK surface
: Both the
opentelemetry.sdk._logsmodule and the OTLP log exporter atopentelemetry.exporter.otlp.proto.grpc._log_exporterare still under active development in the OpenTelemetry Python ecosystem. The underscore prefix on both paths denotes a non-stable API. Pin youropentelemetry-sdkandopentelemetry-exporter-otlpversions in production and review release notes before upgrading. - Privacy : Guardrails log messages include user inputs and rail decisions. Before exporting to a third-party backend, review whether the records may contain PII and whether your retention and redaction policies cover them.
- Performance
: At high log volumes or DEBUG level, log export can add measurable overhead. Use
BatchLogRecordProcessor(as shown) rather than the synchronousSimpleLogRecordProcessorin production, and consider filtering at the logger level (logging.getLogger("nemoguardrails").setLevel(logging.INFO)) to limit what crosses the bridge. - Interaction with
propagate=False: If your application callsnemoguardrails.guardrails.configure_logging()on a freshly initialized logger, that helper setspropagate=Falseon thenemoguardrails.guardrailslogger to prevent duplicate console output. The flag is only set on the first call, when no handlers exist yet. Records from submodules undernemoguardrails.guardrails.*will then not reach the handler attached tonemoguardrails. To capture them, attach the handler tonemoguardrails.guardrailsinstead of (or in addition to)nemoguardrails.
Related Resources
- OpenTelemetry covers SDK installation and trace export setup.
- Quick Start provides a minimal tracing setup with the OpenTelemetry SDK.
- Logging covers Python logging, verbose mode, and the
loggeneration option for in-process debugging.