{
  "topic": "telemetry",
  "path": [
    "telemetry"
  ],
  "title": "telemetry — OpenTelemetry emission",
  "synopsis": "cyoda-go integrates the OpenTelemetry Go SDK (`go.opentelemetry.io/otel`). When `CYODA_OTEL_ENABLED=true`, the binary initializes a trace provider and a meter provider at startup using OTLP HTTP exporters. When `CYODA_OTEL_ENABLED=false` (default), no OTel SDK is initialized and no spans or metrics are emitted; the global OTel provider remains a no-op.",
  "body": "# telemetry\n\n## NAME\n\ntelemetry — OpenTelemetry trace, metric, and log emission configuration.\n\n## SYNOPSIS\n\n```\nCYODA_OTEL_ENABLED=true \\\nOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \\\nOTEL_SERVICE_NAME=cyoda \\\ncyoda\n```\n\n## DESCRIPTION\n\ncyoda-go integrates the OpenTelemetry Go SDK (`go.opentelemetry.io/otel`). When `CYODA_OTEL_ENABLED=true`, the binary initializes a trace provider and a meter provider at startup using OTLP HTTP exporters. When `CYODA_OTEL_ENABLED=false` (default), no OTel SDK is initialized and no spans or metrics are emitted; the global OTel provider remains a no-op.\n\nThe instrumentation name is `github.com/cyoda-platform/cyoda-go`.\n\nThe admin port (`:9091`) always emits Prometheus-format metrics at `/metrics` regardless of `CYODA_OTEL_ENABLED`. OTel metrics and Prometheus metrics are separate emission paths.\n\n## ENV VARS\n\n**cyoda-specific:**\n\n- `CYODA_OTEL_ENABLED` — `true` to initialize the OTel SDK; `false` (default) to use no-op providers. All standard `OTEL_*` env vars are only read when this is `true`.\n- `CYODA_METRICS_BEARER` — static Bearer token required on `GET :9091/metrics`. When empty (default), `/metrics` is unauthenticated. Supports `_FILE` suffix: `CYODA_METRICS_BEARER_FILE=<path>` takes precedence over the plain var.\n- `CYODA_METRICS_REQUIRE_AUTH` — `true` to refuse startup when `CYODA_METRICS_BEARER` is unset. Default `false`. The Helm chart sets this to `true` for shared-cluster deployments.\n\n**Standard OTel env vars read by cyoda-go when `CYODA_OTEL_ENABLED=true`:**\n\n- `OTEL_EXPORTER_OTLP_ENDPOINT` — base URL of the OTLP HTTP collector (e.g. `http://localhost:4318`). Applies to both trace and metric exporters unless overridden by signal-specific vars.\n- `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` — OTLP HTTP endpoint for traces. Overrides `OTEL_EXPORTER_OTLP_ENDPOINT` for the trace exporter.\n- `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` — OTLP HTTP endpoint for metrics. Overrides `OTEL_EXPORTER_OTLP_ENDPOINT` for the metric exporter.\n- `OTEL_EXPORTER_OTLP_HEADERS` — comma-separated `key=value` headers sent with OTLP requests (e.g. for API key authentication).\n- `OTEL_EXPORTER_OTLP_TRACES_HEADERS` — per-signal override of `OTEL_EXPORTER_OTLP_HEADERS` for traces.\n- `OTEL_EXPORTER_OTLP_METRICS_HEADERS` — per-signal override of `OTEL_EXPORTER_OTLP_HEADERS` for metrics.\n- `OTEL_SERVICE_NAME` — `service.name` resource attribute. Identifies the service in traces and metrics. Default value from the OTel SDK (`unknown_service`) when unset.\n- `OTEL_TRACES_SAMPLER` — sampler selection. Supported values: (unset) → `ParentBased(AlwaysSample)` (default); `always_on` → `AlwaysSample` root; `always_off` → `NeverSample` root; `traceidratio` → `TraceIDRatioBased(OTEL_TRACES_SAMPLER_ARG)` root; `parentbased_always_on` → `ParentBased(AlwaysSample)`; `parentbased_always_off` → `ParentBased(NeverSample)`; `parentbased_traceidratio` → `ParentBased(TraceIDRatioBased(OTEL_TRACES_SAMPLER_ARG))`; unknown values → logged as WARN, fallback to `parentbased_always_on`.\n- `OTEL_TRACES_SAMPLER_ARG` — ratio for `traceidratio` and `parentbased_traceidratio` samplers. Float in `(0, 1]`. Invalid or out-of-range values: logged as WARN, fallback to `1.0`.\n\n## SIGNALS\n\n**Traces**\n\nTraces are exported via `otlptracehttp`. Spans are created by:\n\n- `otelhttp.NewMiddleware(\"cyoda\")` — wraps the HTTP API handler when `CYODA_OTEL_ENABLED=true`; creates one span per inbound HTTP request.\n- `otelgrpc.NewServerHandler()` — installed as a gRPC stats handler when `CYODA_OTEL_ENABLED=true`; creates one span per inbound gRPC RPC.\n- `observability.TracingTransactionManager` — decorator around the storage `TransactionManager`; creates spans for `tx.begin`, `tx.commit`, `tx.rollback`, `tx.savepoint`, `tx.rollback_to_savepoint`, `tx.release_savepoint`.\n- `observability.TracingExternalProcessingService` — decorator around the processor dispatcher; creates spans for `dispatch.processor` and `dispatch.criteria`.\n\n**Metrics**\n\nMetrics are exported via `otlpmetrichttp` with a periodic reader. The following instruments are registered:\n\n- `cyoda.tx.duration` — `Float64Histogram`, unit `s` — transaction operation duration; labeled by `op` (`begin`, `commit`, `rollback`)\n- `cyoda.tx.active` — `Int64UpDownCounter` — count of active (begun but not committed/rolled-back) transactions\n- `cyoda.tx.conflicts` — `Int64Counter` — count of transaction serialization conflicts (commit returning `spi.ErrConflict`)\n- `cyoda.dispatch.duration` — `Float64Histogram`, unit `s` — processor/criteria dispatch duration; labeled by `type` (`processor`, `criteria`)\n- `cyoda.dispatch.count` — `Int64Counter` — total processor/criteria dispatch calls; labeled by `type` (`processor`, `criteria`)\n\n**Logs**\n\ncyoda-go uses `log/slog` for structured logging. OTel log emission (OTLP log exporter) is not currently wired. Logs are written to stderr only.\n\n## ATTRIBUTE VOCABULARY\n\nCyoda-specific span attribute keys defined in `internal/observability/attrs.go`:\n\n- `entity.id` — UUID of the entity being processed\n- `entity.model` — model name of the entity\n- `entity.state` — current workflow state of the entity\n- `tx.id` — transaction UUID\n- `op` — operation name within a transaction (`begin`, `commit`, `rollback`, etc.)\n- `workflow.name` — name of the workflow definition\n- `transition.name` — name of the transition being executed\n- `state.from` — workflow state before a transition\n- `state.to` — workflow state after a transition\n- `cascade.depth` — current depth in the automated-transition cascade loop\n- `processor.name` — name of the processor being dispatched\n- `processor.execution_mode` — execution mode of the processor (`SYNC` or `ASYNC`)\n- `processor.tags` — comma-separated `calculationNodesTags` used for member routing\n- `criterion.target` — criteria target type (`TRANSITION`, `WORKFLOW`)\n- `criteria.matches` — boolean result of a criteria evaluation\n- `type` — dispatch type label for `cyoda.dispatch.duration` and `cyoda.dispatch.count` (`processor` or `criteria`)\n- `entity.count` — count of entities in a batch operation\n- `cql.name` — CQL statement name (Cassandra plugin)\n- `cql.op` — CQL operation type (Cassandra plugin)\n- `batch.size` — size of a batch operation\n- `batch.type` — type of batch\n- `version_check.reason` — reason for a version check (cluster protocol)\n- `tx.conflict` — boolean; set `true` on `tx.commit` spans when a serialization conflict is recorded\n- `tx.savepoint_id` — savepoint identifier on savepoint-related spans\n\nStandard OTel semantic convention attributes set on the resource:\n\n- `service.name` — from `OTEL_SERVICE_NAME`\n- `service.instance.id` — from `CYODA_NODE_ID` (set to the gossip node ID in cluster mode; empty string in single-node mode)\n\n## TRACE CONTEXT PROPAGATION\n\ncyoda-go uses **W3C Trace Context** (`traceparent`, `tracestate`) and **W3C Baggage** for context propagation, via `propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})` set as the global text map propagator.\n\n**HTTP**: `otelhttp.NewMiddleware` extracts `traceparent` and `tracestate` from inbound HTTP request headers automatically. Outbound requests (inter-node cluster dispatch via `HTTPForwarder`) do not currently inject trace context.\n\n**gRPC**: `otelgrpc.NewServerHandler()` extracts trace context from inbound gRPC metadata automatically.\n\n**Messaging / internal**: `observability.InjectTraceContext(ctx, headers)` writes `traceparent` and `tracestate` into a `map[string]string` carrier. `observability.ExtractTraceContext(baseCtx, headers)` restores the remote span context. Both use the global text map propagator.\n\n## METRICS ENDPOINT\n\n**GET :9091/metrics**\n\nServes Prometheus-format metrics (text exposition format). The handler is `promhttp.Handler()` from `github.com/prometheus/client_golang`. Port is `CYODA_ADMIN_PORT` (default `9091`); bind address is `CYODA_ADMIN_BIND_ADDRESS` (default `127.0.0.1`).\n\nThis endpoint uses Prometheus client registry — it is separate from the OTel metric exporter. OTel metrics are pushed to the OTLP endpoint; Prometheus metrics are pulled from `:9091/metrics`.\n\nThe default metrics exposed are those registered by the `prometheus/client_golang` default registerer, which includes Go runtime metrics (GC, goroutine count, memory) and process metrics (CPU, open FDs). cyoda-go does not currently register additional custom Prometheus metrics beyond what the default registerer provides.\n\n## AUTHENTICATION\n\nWhen `CYODA_METRICS_BEARER` is non-empty, `GET :9091/metrics` requires:\n\n```\nAuthorization: Bearer <CYODA_METRICS_BEARER value>\n```\n\nThe comparison is constant-time to prevent timing attacks. `GET :9091/livez` and `GET :9091/readyz` remain unauthenticated regardless of `CYODA_METRICS_BEARER`.\n\nWhen `CYODA_METRICS_BEARER` is empty (default), `/metrics` is unauthenticated. This is the expected posture when the admin listener is bound to loopback (`CYODA_ADMIN_BIND_ADDRESS=127.0.0.1`) and access is controlled at the network level.\n\n## SAMPLER\n\nThe trace sampler is runtime-configurable via:\n\n- `POST :8080/api/admin/trace-sampler` — replaces the sampler atomically; requires `Authorization: Bearer <token>`\n- `GET :8080/api/admin/trace-sampler` — returns the current sampler config\n\nRequest and response body:\n\n```json\n{\n  \"sampler\": \"ratio\",\n  \"ratio\": 0.1,\n  \"parent_based\": true\n}\n```\n\n- `sampler` — `\"always\"`, `\"never\"`, or `\"ratio\"`\n- `ratio` — float in `(0, 1]`; required and only valid when `sampler=\"ratio\"`\n- `parent_based` — boolean; when `true`, the sampler is wrapped with `ParentBased()`\n\nThe sampler is backed by `atomic.Pointer[samplerState]`. Reads on the sampling hot path pay no mutex cost. The runtime-replaced sampler takes effect immediately for all new spans.\n\nOn startup, `SamplerConfigFromEnv()` is called to seed the sampler from `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG` before the `TracerProvider` is constructed.\n\n## EXAMPLES\n\n**Start with OTel enabled, exporting to a local OTLP collector:**\n\n```\nCYODA_OTEL_ENABLED=true \\\nOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \\\nOTEL_SERVICE_NAME=cyoda-dev \\\nOTEL_TRACES_SAMPLER=parentbased_traceidratio \\\nOTEL_TRACES_SAMPLER_ARG=0.1 \\\ncyoda\n```\n\n**Start with OTel enabled via Docker, exporting to Jaeger:**\n\n```\ndocker run --rm \\\n  -p 127.0.0.1:8080:8080 \\\n  -p 127.0.0.1:9090:9090 \\\n  -p 127.0.0.1:9091:9091 \\\n  -e CYODA_STORAGE_BACKEND=memory \\\n  -e CYODA_OTEL_ENABLED=true \\\n  -e OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4318 \\\n  -e OTEL_SERVICE_NAME=cyoda \\\n  ghcr.io/cyoda-platform/cyoda:latest\n```\n\n**Scrape Prometheus metrics from the admin port:**\n\n```\ncurl -s http://localhost:9091/metrics | grep \"^go_\"\n```\n\n**Scrape metrics with bearer auth:**\n\n```\ncurl -s \\\n  -H \"Authorization: Bearer $CYODA_METRICS_BEARER\" \\\n  http://localhost:9091/metrics\n```\n\n**Get current sampler configuration:**\n\n```\ncurl -s \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  http://localhost:8080/api/admin/trace-sampler\n```\n\n**Set sampler to 10% ratio sampling:**\n\n```\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sampler\":\"ratio\",\"ratio\":0.1,\"parent_based\":true}' \\\n  http://localhost:8080/api/admin/trace-sampler\n```\n\n## SEE ALSO\n\n- config\n- cli.serve\n- cli.health\n- config.auth\n",
  "sections": [
    {
      "name": "NAME",
      "body": "telemetry — OpenTelemetry trace, metric, and log emission configuration."
    },
    {
      "name": "SYNOPSIS",
      "body": "```\nCYODA_OTEL_ENABLED=true \\\nOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \\\nOTEL_SERVICE_NAME=cyoda \\\ncyoda\n```"
    },
    {
      "name": "DESCRIPTION",
      "body": "cyoda-go integrates the OpenTelemetry Go SDK (`go.opentelemetry.io/otel`). When `CYODA_OTEL_ENABLED=true`, the binary initializes a trace provider and a meter provider at startup using OTLP HTTP exporters. When `CYODA_OTEL_ENABLED=false` (default), no OTel SDK is initialized and no spans or metrics are emitted; the global OTel provider remains a no-op.\n\nThe instrumentation name is `github.com/cyoda-platform/cyoda-go`.\n\nThe admin port (`:9091`) always emits Prometheus-format metrics at `/metrics` regardless of `CYODA_OTEL_ENABLED`. OTel metrics and Prometheus metrics are separate emission paths."
    },
    {
      "name": "ENV VARS",
      "body": "**cyoda-specific:**\n\n- `CYODA_OTEL_ENABLED` — `true` to initialize the OTel SDK; `false` (default) to use no-op providers. All standard `OTEL_*` env vars are only read when this is `true`.\n- `CYODA_METRICS_BEARER` — static Bearer token required on `GET :9091/metrics`. When empty (default), `/metrics` is unauthenticated. Supports `_FILE` suffix: `CYODA_METRICS_BEARER_FILE=<path>` takes precedence over the plain var.\n- `CYODA_METRICS_REQUIRE_AUTH` — `true` to refuse startup when `CYODA_METRICS_BEARER` is unset. Default `false`. The Helm chart sets this to `true` for shared-cluster deployments.\n\n**Standard OTel env vars read by cyoda-go when `CYODA_OTEL_ENABLED=true`:**\n\n- `OTEL_EXPORTER_OTLP_ENDPOINT` — base URL of the OTLP HTTP collector (e.g. `http://localhost:4318`). Applies to both trace and metric exporters unless overridden by signal-specific vars.\n- `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` — OTLP HTTP endpoint for traces. Overrides `OTEL_EXPORTER_OTLP_ENDPOINT` for the trace exporter.\n- `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` — OTLP HTTP endpoint for metrics. Overrides `OTEL_EXPORTER_OTLP_ENDPOINT` for the metric exporter.\n- `OTEL_EXPORTER_OTLP_HEADERS` — comma-separated `key=value` headers sent with OTLP requests (e.g. for API key authentication).\n- `OTEL_EXPORTER_OTLP_TRACES_HEADERS` — per-signal override of `OTEL_EXPORTER_OTLP_HEADERS` for traces.\n- `OTEL_EXPORTER_OTLP_METRICS_HEADERS` — per-signal override of `OTEL_EXPORTER_OTLP_HEADERS` for metrics.\n- `OTEL_SERVICE_NAME` — `service.name` resource attribute. Identifies the service in traces and metrics. Default value from the OTel SDK (`unknown_service`) when unset.\n- `OTEL_TRACES_SAMPLER` — sampler selection. Supported values: (unset) → `ParentBased(AlwaysSample)` (default); `always_on` → `AlwaysSample` root; `always_off` → `NeverSample` root; `traceidratio` → `TraceIDRatioBased(OTEL_TRACES_SAMPLER_ARG)` root; `parentbased_always_on` → `ParentBased(AlwaysSample)`; `parentbased_always_off` → `ParentBased(NeverSample)`; `parentbased_traceidratio` → `ParentBased(TraceIDRatioBased(OTEL_TRACES_SAMPLER_ARG))`; unknown values → logged as WARN, fallback to `parentbased_always_on`.\n- `OTEL_TRACES_SAMPLER_ARG` — ratio for `traceidratio` and `parentbased_traceidratio` samplers. Float in `(0, 1]`. Invalid or out-of-range values: logged as WARN, fallback to `1.0`."
    },
    {
      "name": "SIGNALS",
      "body": "**Traces**\n\nTraces are exported via `otlptracehttp`. Spans are created by:\n\n- `otelhttp.NewMiddleware(\"cyoda\")` — wraps the HTTP API handler when `CYODA_OTEL_ENABLED=true`; creates one span per inbound HTTP request.\n- `otelgrpc.NewServerHandler()` — installed as a gRPC stats handler when `CYODA_OTEL_ENABLED=true`; creates one span per inbound gRPC RPC.\n- `observability.TracingTransactionManager` — decorator around the storage `TransactionManager`; creates spans for `tx.begin`, `tx.commit`, `tx.rollback`, `tx.savepoint`, `tx.rollback_to_savepoint`, `tx.release_savepoint`.\n- `observability.TracingExternalProcessingService` — decorator around the processor dispatcher; creates spans for `dispatch.processor` and `dispatch.criteria`.\n\n**Metrics**\n\nMetrics are exported via `otlpmetrichttp` with a periodic reader. The following instruments are registered:\n\n- `cyoda.tx.duration` — `Float64Histogram`, unit `s` — transaction operation duration; labeled by `op` (`begin`, `commit`, `rollback`)\n- `cyoda.tx.active` — `Int64UpDownCounter` — count of active (begun but not committed/rolled-back) transactions\n- `cyoda.tx.conflicts` — `Int64Counter` — count of transaction serialization conflicts (commit returning `spi.ErrConflict`)\n- `cyoda.dispatch.duration` — `Float64Histogram`, unit `s` — processor/criteria dispatch duration; labeled by `type` (`processor`, `criteria`)\n- `cyoda.dispatch.count` — `Int64Counter` — total processor/criteria dispatch calls; labeled by `type` (`processor`, `criteria`)\n\n**Logs**\n\ncyoda-go uses `log/slog` for structured logging. OTel log emission (OTLP log exporter) is not currently wired. Logs are written to stderr only."
    },
    {
      "name": "ATTRIBUTE VOCABULARY",
      "body": "Cyoda-specific span attribute keys defined in `internal/observability/attrs.go`:\n\n- `entity.id` — UUID of the entity being processed\n- `entity.model` — model name of the entity\n- `entity.state` — current workflow state of the entity\n- `tx.id` — transaction UUID\n- `op` — operation name within a transaction (`begin`, `commit`, `rollback`, etc.)\n- `workflow.name` — name of the workflow definition\n- `transition.name` — name of the transition being executed\n- `state.from` — workflow state before a transition\n- `state.to` — workflow state after a transition\n- `cascade.depth` — current depth in the automated-transition cascade loop\n- `processor.name` — name of the processor being dispatched\n- `processor.execution_mode` — execution mode of the processor (`SYNC` or `ASYNC`)\n- `processor.tags` — comma-separated `calculationNodesTags` used for member routing\n- `criterion.target` — criteria target type (`TRANSITION`, `WORKFLOW`)\n- `criteria.matches` — boolean result of a criteria evaluation\n- `type` — dispatch type label for `cyoda.dispatch.duration` and `cyoda.dispatch.count` (`processor` or `criteria`)\n- `entity.count` — count of entities in a batch operation\n- `cql.name` — CQL statement name (Cassandra plugin)\n- `cql.op` — CQL operation type (Cassandra plugin)\n- `batch.size` — size of a batch operation\n- `batch.type` — type of batch\n- `version_check.reason` — reason for a version check (cluster protocol)\n- `tx.conflict` — boolean; set `true` on `tx.commit` spans when a serialization conflict is recorded\n- `tx.savepoint_id` — savepoint identifier on savepoint-related spans\n\nStandard OTel semantic convention attributes set on the resource:\n\n- `service.name` — from `OTEL_SERVICE_NAME`\n- `service.instance.id` — from `CYODA_NODE_ID` (set to the gossip node ID in cluster mode; empty string in single-node mode)"
    },
    {
      "name": "TRACE CONTEXT PROPAGATION",
      "body": "cyoda-go uses **W3C Trace Context** (`traceparent`, `tracestate`) and **W3C Baggage** for context propagation, via `propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})` set as the global text map propagator.\n\n**HTTP**: `otelhttp.NewMiddleware` extracts `traceparent` and `tracestate` from inbound HTTP request headers automatically. Outbound requests (inter-node cluster dispatch via `HTTPForwarder`) do not currently inject trace context.\n\n**gRPC**: `otelgrpc.NewServerHandler()` extracts trace context from inbound gRPC metadata automatically.\n\n**Messaging / internal**: `observability.InjectTraceContext(ctx, headers)` writes `traceparent` and `tracestate` into a `map[string]string` carrier. `observability.ExtractTraceContext(baseCtx, headers)` restores the remote span context. Both use the global text map propagator."
    },
    {
      "name": "METRICS ENDPOINT",
      "body": "**GET :9091/metrics**\n\nServes Prometheus-format metrics (text exposition format). The handler is `promhttp.Handler()` from `github.com/prometheus/client_golang`. Port is `CYODA_ADMIN_PORT` (default `9091`); bind address is `CYODA_ADMIN_BIND_ADDRESS` (default `127.0.0.1`).\n\nThis endpoint uses Prometheus client registry — it is separate from the OTel metric exporter. OTel metrics are pushed to the OTLP endpoint; Prometheus metrics are pulled from `:9091/metrics`.\n\nThe default metrics exposed are those registered by the `prometheus/client_golang` default registerer, which includes Go runtime metrics (GC, goroutine count, memory) and process metrics (CPU, open FDs). cyoda-go does not currently register additional custom Prometheus metrics beyond what the default registerer provides."
    },
    {
      "name": "AUTHENTICATION",
      "body": "When `CYODA_METRICS_BEARER` is non-empty, `GET :9091/metrics` requires:\n\n```\nAuthorization: Bearer <CYODA_METRICS_BEARER value>\n```\n\nThe comparison is constant-time to prevent timing attacks. `GET :9091/livez` and `GET :9091/readyz` remain unauthenticated regardless of `CYODA_METRICS_BEARER`.\n\nWhen `CYODA_METRICS_BEARER` is empty (default), `/metrics` is unauthenticated. This is the expected posture when the admin listener is bound to loopback (`CYODA_ADMIN_BIND_ADDRESS=127.0.0.1`) and access is controlled at the network level."
    },
    {
      "name": "SAMPLER",
      "body": "The trace sampler is runtime-configurable via:\n\n- `POST :8080/api/admin/trace-sampler` — replaces the sampler atomically; requires `Authorization: Bearer <token>`\n- `GET :8080/api/admin/trace-sampler` — returns the current sampler config\n\nRequest and response body:\n\n```json\n{\n  \"sampler\": \"ratio\",\n  \"ratio\": 0.1,\n  \"parent_based\": true\n}\n```\n\n- `sampler` — `\"always\"`, `\"never\"`, or `\"ratio\"`\n- `ratio` — float in `(0, 1]`; required and only valid when `sampler=\"ratio\"`\n- `parent_based` — boolean; when `true`, the sampler is wrapped with `ParentBased()`\n\nThe sampler is backed by `atomic.Pointer[samplerState]`. Reads on the sampling hot path pay no mutex cost. The runtime-replaced sampler takes effect immediately for all new spans.\n\nOn startup, `SamplerConfigFromEnv()` is called to seed the sampler from `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG` before the `TracerProvider` is constructed."
    },
    {
      "name": "EXAMPLES",
      "body": "**Start with OTel enabled, exporting to a local OTLP collector:**\n\n```\nCYODA_OTEL_ENABLED=true \\\nOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \\\nOTEL_SERVICE_NAME=cyoda-dev \\\nOTEL_TRACES_SAMPLER=parentbased_traceidratio \\\nOTEL_TRACES_SAMPLER_ARG=0.1 \\\ncyoda\n```\n\n**Start with OTel enabled via Docker, exporting to Jaeger:**\n\n```\ndocker run --rm \\\n  -p 127.0.0.1:8080:8080 \\\n  -p 127.0.0.1:9090:9090 \\\n  -p 127.0.0.1:9091:9091 \\\n  -e CYODA_STORAGE_BACKEND=memory \\\n  -e CYODA_OTEL_ENABLED=true \\\n  -e OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4318 \\\n  -e OTEL_SERVICE_NAME=cyoda \\\n  ghcr.io/cyoda-platform/cyoda:latest\n```\n\n**Scrape Prometheus metrics from the admin port:**\n\n```\ncurl -s http://localhost:9091/metrics | grep \"^go_\"\n```\n\n**Scrape metrics with bearer auth:**\n\n```\ncurl -s \\\n  -H \"Authorization: Bearer $CYODA_METRICS_BEARER\" \\\n  http://localhost:9091/metrics\n```\n\n**Get current sampler configuration:**\n\n```\ncurl -s \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  http://localhost:8080/api/admin/trace-sampler\n```\n\n**Set sampler to 10% ratio sampling:**\n\n```\ncurl -s -X POST \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sampler\":\"ratio\",\"ratio\":0.1,\"parent_based\":true}' \\\n  http://localhost:8080/api/admin/trace-sampler\n```"
    },
    {
      "name": "SEE ALSO",
      "body": "- config\n- cli.serve\n- cli.health\n- config.auth"
    }
  ],
  "see_also": [
    "config",
    "cli.serve",
    "cli.health",
    "config.auth"
  ],
  "stability": "evolving",
  "actions": []
}
