# Logs and telemetry

Canonical page: https://docs.ovok.com/native-sdk/guide/how-to/observability

Connect SDK diagnostics to the systems your team already uses. Ovok provides a
logger plus OpenTelemetry helpers for traces, metrics, and log records. Your app
owns provider registration, exporters, destinations, retention, access controls,
and alerting.

The OpenTelemetry API peers are optional. Without providers, telemetry calls are
no-ops and SDK features continue to run; the default SDK logger still writes to the
console. For the complete API surface, see [Logging](/native-sdk/guide/components/helpers/logging)
and [Telemetry](/native-sdk/guide/components/helpers/telemetry).

## Configure telemetry during startup [#configure-telemetry-during-startup]

If your app registers OpenTelemetry providers globally, the SDK uses those
providers. Otherwise, pass the providers you want the SDK to use:

~~~ts
import { configureOvokTelemetry } from "@ovok/native/helpers";

configureOvokTelemetry({
  tracerProvider: appTracerProvider,
  meterProvider: appMeterProvider,
  loggerProvider: appLoggerProvider,
});
~~~

Each provider is optional. `tracerProvider` supplies spans, `meterProvider`
supplies counters and histograms, and `loggerProvider` supplies OpenTelemetry log
records. Call this before mounting feature providers or starting work that should
be observed. The SDK does not register or shut down your providers.

Install `@opentelemetry/api` for traces and metrics and `@opentelemetry/api-logs`
for OTel log records. Both are optional peer dependencies. You do not need to
install them when you only want the default console logger or a custom
`configureOvokLogger` callback.

## Route logs to the app logger [#route-logs-to-the-app-logger]

Use `configureOvokLogger` to replace the SDK's default console destination:

~~~ts
import {
  configureOvokLogger,
  sanitizeOvokTelemetryAttributes,
} from "@ovok/native/helpers";

configureOvokLogger((level, message, extra = {}) => {
  appLogger.write({
    level,
    message,
    attributes: sanitizeOvokTelemetryAttributes(extra),
  });
});
~~~

The callback receives the message and `extra` values as supplied. The SDK also
emits an OpenTelemetry log when a logger provider is available, even when you have
configured this callback. Keep the callback fast and non-throwing: the SDK calls
it synchronously and does not catch exceptions from app logger code.

Use `ovokLogger(level, message, extra?)` for app-owned diagnostics through the
same path. `slog(error, scope, additionalData?, level?)` is the established
compatibility helper used by SDK features. Calling `configureOvokLogger()` without
an argument restores the default console logger.

## Add spans around app-owned work [#add-spans-around-app-owned-work]

`withOvokSpan` runs an operation in a span when a tracer is available and returns
the operation's result:

~~~ts
import { withOvokSpan } from "@ovok/native/helpers";

const result = await withOvokSpan(
  "app.measurement.persist",
  { provider: "bluetooth", measurementTypeKey: "body-weight" },
  () => measurementRepository.save(measurement),
);
~~~

It records success or failure on an available span, ends the span, and rethrows
the original operation error. If telemetry is unavailable, the operation still
runs normally. Use `recordOvokMetric` for counters and `recordOvokDuration` for
durations in milliseconds; their attributes use the SDK's operational allowlist.

## Know what the SDK already records [#know-what-the-sdk-already-records]

The SDK currently emits Bluetooth connection and discovery signals, device reading
and status events, background queue delivery metrics, and health-import spans and
batch metrics. The exact span names, event names, metric names, and meanings are in
the [built-in signal catalog](/native-sdk/guide/components/helpers/telemetry#built-in-sdk-signals).
These signals appear only when the relevant feature runs and an OpenTelemetry
provider is available.

The `ovok.queue.depth` instrument is a counter: the SDK adds the current queue
length when it writes the queue. It is not a gauge. Use the reference catalog to
interpret it correctly in dashboards.

## Keep diagnostic data safe [#keep-diagnostic-data-safe]

The SDK filters attributes passed to its span, metric, duration, and span-event
helpers through an allowlist of operational keys. The filter does not apply to
`emitOvokLog`, `slog`, `ovokLogger`, or a custom logger callback. Log messages and
`extra` values are forwarded as supplied.

Keep credentials, patient and account identifiers, clinical values, and raw
device payloads out of log messages and extras. The allowlist is not a guarantee
that every allowed identifier is non-sensitive; review those values under your
app's data policy. Redact app-owned log fields before forwarding them to a vendor.

## Verify the integration [#verify-the-integration]

1. Register or pass the providers before starting the feature you want to observe.
2. Trigger a representative Bluetooth, background-delivery, or health-import flow.
3. Confirm the expected records reach the app's exporter and inspect their
   attributes for data your policy excludes.
4. Confirm the feature still works with OpenTelemetry peers omitted if your app
   treats telemetry as optional.

For SDK logger behavior, see [Logging](/native-sdk/guide/components/helpers/logging). For every
telemetry helper, type, attribute rule, and built-in signal, see
[Telemetry](/native-sdk/guide/components/helpers/telemetry). Related feature guides cover
[Bluetooth](/native-sdk/guide/modules/bt-management), [Background sync](/native-sdk/guide/modules/background-sync), and
[HealthKit and Health Connect](/native-sdk/guide/modules/data-sync).
