Observability#
User Info Service produces three kinds of signal an operator can use to monitor it and diagnose problems: structured logs, distributed traces, and metrics. Logging is always on; tracing and metrics are off by default and are turned on by pointing the service at an OpenTelemetry collector. This page describes each signal, what it contains, how trace context flows through a request, and how to configure the three signals in the Helm chart.
Both roles emit all three signals. Readers report on the requests they serve; the writer reports on directory synchronization and its calls to the identity provider. For the readiness and health probes referenced below, see User Info Service Configuration; for how the two roles relate, see Directory Synchronization and Data Freshness.
Logging#
The service writes logs to standard output, so a Kubernetes log collector picks them up without extra configuration. Two aspects are configurable: the log level and the output format.
Level. The log level accepts a single level such as
info, or per-target directives such asmicrosoft_graph_userinfo_service=debug,hyper=warnto raise verbosity for one component while keeping the rest quiet. The default isinfo.Format. By default logs are human-readable text, which is convenient when reading them directly. Enabling structured logging emits one JSON object per line instead, suitable for ingestion by a log aggregator such as Loki or Elasticsearch.
Log records emitted while a request or synchronization cycle is being handled carry the identifiers of the active trace, so a log line can be correlated with the trace it belongs to when tracing is enabled.
Distributed Tracing#
When tracing is enabled, the service exports spans over OTLP/gRPC to the collector endpoint an operator configures. Each incoming REST or gRPC request becomes a server span, and the outgoing calls made while serving it — to Microsoft Graph and, when authorization is enabled, to the Permission Service — become child spans, so a single trace shows the whole path of a request.
Trace Context Propagation#
The service participates in a caller’s existing trace using the W3C Trace Context standard. It
reads the incoming trace context from the traceparent request header (REST) or the equivalent
request metadata (gRPC), continues that trace rather than starting a new one, and injects the
current trace context into the requests it makes to Microsoft Graph. Graph calls additionally
carry a client request identifier derived from the active trace, so a single request can be
followed from the caller, through the service, to the identity provider.
Metrics#
When metrics are enabled, the service exports them over OTLP/gRPC to the configured collector endpoint on a periodic interval. Metrics are pushed to the collector; the service does not expose a scrape endpoint of its own. Each metric carries a set of attributes (labels) that partition its values, and each falls into one of three OpenTelemetry instrument types:
Counter — a monotonically increasing total, reset only when the process restarts. Use rates derived from a counter (per second, per minute) rather than its raw value.
Histogram — a distribution of recorded values, used here for durations in seconds. Query it for percentiles (p50, p95, p99), averages, and counts.
Gauge — a current value that can rise and fall. The snapshot sizes are reported when they change; the snapshot age is an observable gauge, sampled by the exporter at collection time.
The subsections below list every metric, grouped by the part of the service it measures. Which role emits a given metric depends on what that role does: readers emit the request, authorization, and snapshot metrics while serving; the writer emits the synchronization and commit metrics; and the Microsoft Graph metrics come from whichever role calls Graph — the writer during synchronization, or a reader in live mode.
Request Metrics#
Request metrics follow the RED pattern (rate, errors, duration) and are the basis for service-level dashboards and alerts. REST and gRPC have parallel instruments.
Metric |
Type |
Unit |
Attributes |
Description |
|---|---|---|---|---|
|
Histogram |
s |
|
Duration of REST request handling. |
|
Counter |
— |
|
Total REST requests received. |
|
Counter |
— |
|
REST requests that resulted in a 4xx or 5xx status. |
|
Histogram |
s |
|
Duration of gRPC request handling. |
|
Counter |
— |
|
Total gRPC requests received. |
|
Counter |
— |
|
gRPC requests that resulted in a non-OK status code. |
Microsoft Graph Client Metrics#
These measure the outgoing calls the service makes to Microsoft Graph, including retries against the identity provider’s rate limits.
Metric |
Type |
Unit |
Attributes |
Description |
|---|---|---|---|---|
|
Histogram |
s |
|
Duration of outgoing Microsoft Graph API calls. |
|
Counter |
— |
|
Total Graph API calls, including retries. |
|
Counter |
— |
|
Graph call retries, labeled with the reason for the retry. |
Directory Synchronization Metrics#
These describe the writer’s synchronization cycles and the freshness of the directory each instance holds. The snapshot age is the primary freshness signal — see Data Freshness and Eventual Consistency.
Metric |
Type |
Unit |
Attributes |
Description |
|---|---|---|---|---|
|
Histogram |
s |
|
Duration of each synchronization cycle. |
|
Counter |
— |
|
Total synchronization cycles completed. |
|
Gauge |
— |
— |
Number of users in the current in-memory snapshot. |
|
Gauge |
— |
— |
Number of groups in the current in-memory snapshot. |
|
Gauge |
— |
— |
Number of membership edges in the current snapshot. |
|
Observable gauge |
s |
— |
Seconds since the last successful synchronization cycle completed. |
PostgreSQL Persistence Metrics#
These measure the shared store operations: loading the snapshot into memory at startup, and (for the writer) committing it after a synchronization cycle.
Metric |
Type |
Unit |
Attributes |
Description |
|---|---|---|---|---|
|
Histogram |
s |
— |
Duration of loading the snapshot from PostgreSQL at startup. |
|
Histogram |
s |
— |
Duration of committing the snapshot after a synchronization cycle. |
Readiness and Health Signals#
Alongside the exported telemetry, the service exposes two HTTP probes on the REST port that are
worth monitoring directly. A liveness probe at /healthz reports that the process is running; a
readiness probe at /readyz returns a 2xx status only when the instance can serve directory data
— for a reader, once it has loaded a snapshot; for the writer, once its first full synchronization
has completed. Both roles serve these probes. Watching readiness across the fleet surfaces
readers that have dropped out of rotation and a writer that has not yet finished its initial
synchronization; see
Memory and CPU Sizing for sizing the writer’s readiness
budget to a large directory.
Configuration#
All three signals are configured through the Helm chart’s observability section. The defaults
enable text logging at info and leave tracing and metrics off.
Setting |
Helm value ( |
Default |
Description |
|---|---|---|---|
Log level |
|
|
Log level or per-target directives (e.g. |
Structured logs |
|
|
Emit logs as one JSON object per line instead of human-readable text. |
Enable tracing |
|
|
Export traces over OTLP/gRPC. |
Trace endpoint |
|
|
OTLP/gRPC collector address (e.g. |
Enable metrics |
|
|
Export metrics over OTLP/gRPC. |
Metrics endpoint |
|
|
OTLP/gRPC collector address (e.g. |
Service name |
|
|
Service name reported on exported traces and metrics. |
The endpoint values are collector addresses in host:port form and are used verbatim, with no
URL path appended, because OTLP/gRPC routes by service method rather than by path. The default
network port for OTLP/gRPC is 4317. Set the corresponding endpoint whenever you enable tracing
or metrics; leaving it empty while the signal is enabled leaves the exporter without a
destination.
These settings render as environment variables in the reader and writer pod specifications, so changing any of them and upgrading the release rolls the affected pods automatically. Each value takes effect when the pod starts.
Configuring Observability in the Helm Chart#
Add the observability section to userinfo-values.yaml. The example below emits structured logs
and exports both traces and metrics to an in-cluster collector:
observability:
logging:
level: "info"
structured:
enabled: true
tracing:
enabled: true
endpoint:
grpc: "http://otel-collector.observability.svc.cluster.local:4317"
metrics:
enabled: true
endpoint:
grpc: "http://otel-collector.observability.svc.cluster.local:4317"
serviceName: "microsoft-graph-userinfo-service"
Install or upgrade the release with the same commands used for the synchronized directory deployment in User Info Service Configuration.
References#
OpenTelemetry Protocol (OTLP) specification — the export protocol used for traces and metrics; its OTLP/gRPC default network port is
4317.W3C Trace Context — the standard for the
traceparentheader the service reads and propagates.