diff --git a/kustomize/base/kustomization.yaml b/kustomize/base/kustomization.yaml index 8a70c60..1fab72a 100644 --- a/kustomize/base/kustomization.yaml +++ b/kustomize/base/kustomization.yaml @@ -55,6 +55,13 @@ resources: # Ingress - ingress.yaml +# Replica overrides +replicas: + - name: plane-api-wl + count: 5 + - name: plane-worker-wl + count: 5 + # Default image tags images: - name: makeplane/backend-commercial diff --git a/kustomize/components/otel-observability/configmap.yaml b/kustomize/components/otel-observability/configmap.yaml new file mode 100644 index 0000000..d3e87fb --- /dev/null +++ b/kustomize/components/otel-observability/configmap.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: plane-otel-vars + namespace: plane +data: + # OpenTelemetry instrumentation for the backend-api workload. + # + # These keys are PLACEHOLDERS — their real values are sourced from the overlay's + # vars.yaml (overlay-vars ConfigMap) via the OTEL replacements in the overlay + # kustomization.yaml. Leave a key empty here; set its value in vars.yaml. + # + # Optionality: the whole block is opt-in (enable this component in the overlay). + # To leave an individual variable unset, delete its line here AND the matching + # replacement in the overlay kustomization.yaml. + OTEL_ENABLED: "" + OTEL_EXPORTER_OTLP_ENDPOINT: "" + OTEL_EXPORTER_OTLP_PROTOCOL: "" + OTEL_SERVICE_NAME: "" + # Set deployment.environment.name (the CURRENT semconv key — NOT the legacy + # deployment.environment) so the API's environment tag matches the node/pi/ + # browser services and cross-service "environment=" filtering works in Tempo. + OTEL_RESOURCE_ATTRIBUTES: "" + OTEL_DEBUG_CONSOLE: "" + # Trace sampling. Two independent gotchas: + # 1. Ratio: the app defaults to parentbased_traceidratio @ 0.1 (10%), so 90% + # of root requests are NOT exported (their logs show trace_flags=0). + # 2. Parent-based: ANY parentbased_* sampler DEFERS to an incoming traceparent's + # sampled flag and IGNORES the ARG ratio. With FRONTEND_OTEL_ENABLED=1 the + # browser/proxy sends a traceparent; if it didn't sample (flag=0), the API + # span is DROPPED regardless of ARG=1.0 — the classic "only GET traffic + # (rootspans) shows up, no POST/user-action traces" symptom. + # => For a test/load/debug env, use always_on (captures every span the service + # sees, ignores upstream decisions). For prod, use parentbased_traceidratio + # with a sane ARG AND make sure browser tracing samples consistently. + OTEL_TRACES_SAMPLER: "" + OTEL_TRACES_SAMPLER_ARG: "" + # Browser/client tracing for web/admin/space. The API serves these to browsers + # via its public instance endpoint (is_otel_enabled/otlp_endpoint/otlp_headers), + # so they only need to be set on the API — which already consumes this ConfigMap. + # Read ONLY by the API; a no-op env on silo/live/space. + FRONTEND_OTEL_ENABLED: "" + FRONTEND_OTLP_ENDPOINT: "" + FRONTEND_OTLP_HEADERS: "" diff --git a/kustomize/components/otel-observability/kustomization.yaml b/kustomize/components/otel-observability/kustomization.yaml new file mode 100644 index 0000000..6cafaba --- /dev/null +++ b/kustomize/components/otel-observability/kustomization.yaml @@ -0,0 +1,113 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +# ============================================================ +# OTEL OBSERVABILITY (backend-api) +# ============================================================ +# Injects OpenTelemetry environment variables into the Plane workloads by +# appending the plane-otel-vars ConfigMap to each container's envFrom. +# +# SCOPE — only env-driven OTel consumers are wired here: +# - api (Django wsgi/asgi, OTEL_* env) +# - worker (Celery worker — Celery task spans + DB/Redis/HTTP) +# - beat-worker (Celery beat) +# - automation-consumer (Django mgmt cmd; OTel bootstraps via plane.celery import) +# - outbox-poller (Django mgmt cmd; same) +# - silo (Node startOtel, OTEL_* env) +# - live (Node startOtel, OTEL_* env) +# - space (React Router SSR initServerTelemetry, OTEL_ENABLED / +# OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_SERVICE_NAME env) +# All five Django processes bootstrap OTel because plane/__init__.py imports the +# Celery app, which calls configure_otel() at import. +# web and admin are NOT wired: their only OTel is BROWSER/client tracing, which +# is configured from the Plane instance config (is_otel_enabled, otlp_endpoint) +# served by the API via the FRONTEND_OTEL_* keys — not from per-workload env — so +# OTEL_* env on them is a no-op. +# +# VALUES LIVE IN vars.yaml: the keys in configmap.yaml are placeholders; the +# actual values are supplied per-overlay from overlay-vars (vars.yaml) via the +# OTEL replacements added in the overlay's kustomization.yaml. +# +# PER-SERVICE service.name: the shared plane-otel-vars ConfigMap carries +# OTEL_SERVICE_NAME for the api (from vars.yaml). Every other workload gets the +# SAME ConfigMap but an inline env OTEL_SERVICE_NAME= that overrides it +# (inline env wins over envFrom), so each reports its own service.name. The name +# matches the container name so it lines up with the promtail service_name label +# for trace->logs correlation. +# +# OPTIONAL / OPT-IN: the env vars are added ONLY when this component is enabled +# in the overlay. To leave OTEL off entirely, do not enable this component — no +# OTEL env vars are then set on the api container. To drop a single variable, +# remove its key from configmap.yaml and its replacement from the overlay. +# +# ORDERING: this envFrom entry is appended LAST. Enable this component AFTER +# custom-ca so the OTEL ConfigMap lands after the CA env ConfigMap. +# +# Usage — enable in your overlay's kustomization.yaml and add the OTEL +# replacements + vars (see overlays/phoenix for a worked example): +# components: +# - ../../components/otel-observability +# ============================================================ + +resources: + - configmap.yaml + +patches: + - path: patches/append-otel-envfrom.json + target: + kind: Deployment + name: plane-api-wl + - path: patches/append-otel-silo.json + target: + kind: Deployment + name: plane-silo-wl + - path: patches/append-otel-live.json + target: + kind: Deployment + name: plane-live-wl + - path: patches/append-otel-space.json + target: + kind: Deployment + name: plane-space-wl + - path: patches/append-otel-worker.json + target: + kind: Deployment + name: plane-worker-wl + - path: patches/append-otel-beat-worker.json + target: + kind: Deployment + name: plane-beat-worker-wl + - path: patches/append-otel-automation-consumer.json + target: + kind: Deployment + name: plane-automation-consumer-wl + - path: patches/append-otel-outbox-poller.json + target: + kind: Deployment + name: plane-outbox-poller-wl + - path: patches/append-otel-external-api.json + target: + kind: Deployment + name: plane-external-api-wl + - path: patches/append-otel-webhook-consumer.json + target: + kind: Deployment + name: plane-webhook-consumer-wl + - path: patches/append-otel-worker-importers.json + target: + kind: Deployment + name: plane-worker-importers-wl + # Plane Intelligence (pi) — logs-only OTel (configure_otel in pi.celery_app; + # reads the standard OTEL_* env, exports logs over OTLP/HTTP to /v1/logs). + - path: patches/append-otel-pi-api.json + target: + kind: Deployment + name: plane-pi-api-wl + - path: patches/append-otel-pi-beat.json + target: + kind: Deployment + name: plane-pi-beat-wl + - path: patches/append-otel-pi-worker.json + target: + kind: Deployment + name: plane-pi-worker-wl diff --git a/kustomize/components/otel-observability/patches/append-otel-automation-consumer.json b/kustomize/components/otel-observability/patches/append-otel-automation-consumer.json new file mode 100644 index 0000000..21058e9 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-automation-consumer.json @@ -0,0 +1,21 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { + "configMapRef": { + "name": "plane-otel-vars" + } + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ + { + "name": "OTEL_SERVICE_NAME", + "value": "automation-consumer" + } + ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-beat-worker.json b/kustomize/components/otel-observability/patches/append-otel-beat-worker.json new file mode 100644 index 0000000..ea19ba8 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-beat-worker.json @@ -0,0 +1,21 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { + "configMapRef": { + "name": "plane-otel-vars" + } + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ + { + "name": "OTEL_SERVICE_NAME", + "value": "beat-worker" + } + ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-envfrom.json b/kustomize/components/otel-observability/patches/append-otel-envfrom.json new file mode 100644 index 0000000..d502b62 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-envfrom.json @@ -0,0 +1,11 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { + "configMapRef": { + "name": "plane-otel-vars" + } + } + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-external-api.json b/kustomize/components/otel-observability/patches/append-otel-external-api.json new file mode 100644 index 0000000..cbd35ff --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-external-api.json @@ -0,0 +1,12 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { "configMapRef": { "name": "plane-otel-vars" } } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env/-", + "value": { "name": "OTEL_SERVICE_NAME", "value": "external-api" } + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-live.json b/kustomize/components/otel-observability/patches/append-otel-live.json new file mode 100644 index 0000000..157ffb0 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-live.json @@ -0,0 +1,21 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { + "configMapRef": { + "name": "plane-otel-vars" + } + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ + { + "name": "OTEL_SERVICE_NAME", + "value": "live" + } + ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-outbox-poller.json b/kustomize/components/otel-observability/patches/append-otel-outbox-poller.json new file mode 100644 index 0000000..12b4e5c --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-outbox-poller.json @@ -0,0 +1,21 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { + "configMapRef": { + "name": "plane-otel-vars" + } + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ + { + "name": "OTEL_SERVICE_NAME", + "value": "outbox-poller" + } + ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-pi-api.json b/kustomize/components/otel-observability/patches/append-otel-pi-api.json new file mode 100644 index 0000000..4538fb3 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-pi-api.json @@ -0,0 +1,12 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { "configMapRef": { "name": "plane-otel-vars" } } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ { "name": "OTEL_SERVICE_NAME", "value": "pi-api" } ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-pi-beat.json b/kustomize/components/otel-observability/patches/append-otel-pi-beat.json new file mode 100644 index 0000000..20b3a02 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-pi-beat.json @@ -0,0 +1,12 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { "configMapRef": { "name": "plane-otel-vars" } } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ { "name": "OTEL_SERVICE_NAME", "value": "pi-beat" } ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-pi-worker.json b/kustomize/components/otel-observability/patches/append-otel-pi-worker.json new file mode 100644 index 0000000..726c92f --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-pi-worker.json @@ -0,0 +1,12 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { "configMapRef": { "name": "plane-otel-vars" } } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ { "name": "OTEL_SERVICE_NAME", "value": "pi-worker" } ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-silo.json b/kustomize/components/otel-observability/patches/append-otel-silo.json new file mode 100644 index 0000000..07b3b83 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-silo.json @@ -0,0 +1,21 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { + "configMapRef": { + "name": "plane-otel-vars" + } + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ + { + "name": "OTEL_SERVICE_NAME", + "value": "silo" + } + ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-space.json b/kustomize/components/otel-observability/patches/append-otel-space.json new file mode 100644 index 0000000..8d39142 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-space.json @@ -0,0 +1,21 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { + "configMapRef": { + "name": "plane-otel-vars" + } + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ + { + "name": "OTEL_SERVICE_NAME", + "value": "space" + } + ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-webhook-consumer.json b/kustomize/components/otel-observability/patches/append-otel-webhook-consumer.json new file mode 100644 index 0000000..1ea70e8 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-webhook-consumer.json @@ -0,0 +1,12 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { "configMapRef": { "name": "plane-otel-vars" } } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ { "name": "OTEL_SERVICE_NAME", "value": "webhook-consumer" } ] + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-worker-importers.json b/kustomize/components/otel-observability/patches/append-otel-worker-importers.json new file mode 100644 index 0000000..6a38c6c --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-worker-importers.json @@ -0,0 +1,12 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { "configMapRef": { "name": "plane-otel-vars" } } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env/-", + "value": { "name": "OTEL_SERVICE_NAME", "value": "worker-importers" } + } +] diff --git a/kustomize/components/otel-observability/patches/append-otel-worker.json b/kustomize/components/otel-observability/patches/append-otel-worker.json new file mode 100644 index 0000000..5f516a7 --- /dev/null +++ b/kustomize/components/otel-observability/patches/append-otel-worker.json @@ -0,0 +1,21 @@ +[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/envFrom/-", + "value": { + "configMapRef": { + "name": "plane-otel-vars" + } + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/env", + "value": [ + { + "name": "OTEL_SERVICE_NAME", + "value": "worker" + } + ] + } +] diff --git a/kustomize/overlays/default/kustomization.yaml b/kustomize/overlays/default/kustomization.yaml index 9ca3c5d..16b866f 100644 --- a/kustomize/overlays/default/kustomization.yaml +++ b/kustomize/overlays/default/kustomization.yaml @@ -45,6 +45,11 @@ components: # draw.io diagram editor (cluster-internal) - ../../components/draw-io + # Optional: OpenTelemetry env vars for the backend-api. Enable AFTER custom-ca, + # add the OTEL_* vars to vars.yaml and the OTEL replacements to this file + # (see overlays/phoenix for a worked example). + # - ../../components/otel-observability + # Optional: harden all workloads with non-root security context # - ../../components/nonroot-security-context diff --git a/kustomize/overlays/managed/kustomization.yaml b/kustomize/overlays/managed/kustomization.yaml index ff6cfa5..7811a2c 100644 --- a/kustomize/overlays/managed/kustomization.yaml +++ b/kustomize/overlays/managed/kustomization.yaml @@ -52,6 +52,11 @@ components: # Paste your CA cert (PEM) into ../../components/custom-ca/customCA.crt, then uncomment: # - ../../components/custom-ca + # Optional: OpenTelemetry env vars for the backend-api. Enable AFTER custom-ca, + # add the OTEL_* vars to vars.yaml and the OTEL replacements to this file + # (see overlays/phoenix for a worked example). + # - ../../components/otel-observability + # Optional: external OpenSearch with username/password auth # - ../../components/opensearch-external-auth diff --git a/kustomize/overlays/phoenix/kustomization.yaml b/kustomize/overlays/phoenix/kustomization.yaml index 0f912a9..25a9d16 100644 --- a/kustomize/overlays/phoenix/kustomization.yaml +++ b/kustomize/overlays/phoenix/kustomization.yaml @@ -53,6 +53,10 @@ components: # and custom-ca: it has no envFrom (like iframely) and its third-party image should # not be patched by those components, which target all plane-enterprise Deployments. - ../../components/draw-io + # OpenTelemetry env vars for the backend-api. Comes after custom-ca so its + # ConfigMap is appended last to the api envFrom. Values are sourced from + # vars.yaml via the OTEL replacements below. + - ../../components/otel-observability # Uncomment these if you want to deploy the infrastructure components locally # - ../../components/postgres # - ../../components/redis @@ -983,3 +987,127 @@ replacements: name: plane-opensearch-secrets fieldPaths: - stringData.OPENSEARCH_INDEX_PREFIX + + # ====== OpenTelemetry (backend-api) ====== + # Drive plane-otel-vars (otel-observability component) from vars.yaml. + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.OTEL_ENABLED + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.OTEL_ENABLED + + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.OTEL_EXPORTER_OTLP_ENDPOINT + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.OTEL_EXPORTER_OTLP_ENDPOINT + + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.OTEL_EXPORTER_OTLP_PROTOCOL + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.OTEL_EXPORTER_OTLP_PROTOCOL + + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.OTEL_SERVICE_NAME + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.OTEL_SERVICE_NAME + + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.OTEL_RESOURCE_ATTRIBUTES + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.OTEL_RESOURCE_ATTRIBUTES + + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.OTEL_DEBUG_CONSOLE + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.OTEL_DEBUG_CONSOLE + + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.OTEL_TRACES_SAMPLER + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.OTEL_TRACES_SAMPLER + + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.OTEL_TRACES_SAMPLER_ARG + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.OTEL_TRACES_SAMPLER_ARG + + # Browser/client tracing (served by the API to web/admin/space browsers). + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.FRONTEND_OTEL_ENABLED + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.FRONTEND_OTEL_ENABLED + + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.FRONTEND_OTLP_ENDPOINT + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.FRONTEND_OTLP_ENDPOINT + + - source: + kind: ConfigMap + name: overlay-vars + fieldPath: data.FRONTEND_OTLP_HEADERS + targets: + - select: + kind: ConfigMap + name: plane-otel-vars + fieldPaths: + - data.FRONTEND_OTLP_HEADERS diff --git a/kustomize/overlays/phoenix/vars.yaml.example b/kustomize/overlays/phoenix/vars.yaml.example index 7fdb2d4..30d7ca2 100644 --- a/kustomize/overlays/phoenix/vars.yaml.example +++ b/kustomize/overlays/phoenix/vars.yaml.example @@ -104,3 +104,50 @@ data: # MCP server: internal K8s service URL used for server-to-server Plane API calls # Follows the pattern: http://..svc.cluster.local: MCP_PLANE_INTERNAL_BASE_URL: "http://plane-api.plane-ns.svc.cluster.local:8000" + + # ============================================================ + # OpenTelemetry (backend-api) — used by the otel-observability component. + # Optional: only applied when ../../components/otel-observability is enabled. + # Delete a line here AND its replacement to leave that env var unset. + # ============================================================ + OTEL_ENABLED: "1" + OTEL_EXPORTER_OTLP_ENDPOINT: "https://telemetry.plane.town" + OTEL_EXPORTER_OTLP_PROTOCOL: "grpc" + OTEL_SERVICE_NAME: "api" # per-service: api, silo, live, etc. + # developer=.. is a placeholder — set it before applying. + # k8s.namespace.name lets Grafana's trace->logs map the span to this namespace's + # logs (must match the overlay's deploy namespace). Add k8s.pod.name via the + # downward API if you want pod-exact correlation. + # Use deployment.environment.name (current semconv key, matches node/pi/browser + # services) — NOT the legacy deployment.environment, which breaks cross-service + # environment filtering in Tempo. + # k8s.cluster.name tags every span with the source cluster so traces are + # filterable per cluster in Tempo (e.g. {resource.k8s.cluster.name="plane-eks-dev"}), + # consistent with the metrics/logs cluster label. Set it to the cluster this + # overlay deploys to. + OTEL_RESOURCE_ATTRIBUTES: "k8s.cluster.name=,deployment.environment.name=kustomize,developer=,k8s.namespace.name=plane-ns" + # Debug: print spans to stdout to confirm the app is emitting/exporting them. + # Set to "" (or remove this line + its replacement) once tracing is confirmed. + OTEL_DEBUG_CONSOLE: "1" + # Trace sampling — always_on for a test/load/debug env: captures every span and + # does NOT defer to an upstream traceparent's sampled flag (parentbased_* does, + # which silently drops browser-initiated POST traces — the "only GET shows up" + # symptom). For prod use parentbased_traceidratio + an ARG (e.g. 0.1). ARG is + # ignored by always_on; kept for an easy prod switch-back. + OTEL_TRACES_SAMPLER: "always_on" + OTEL_TRACES_SAMPLER_ARG: "1.0" + # ---- Browser/client tracing (web/admin/space) ---- + # Set on the API ONLY; the API serves these to browsers via its public instance + # endpoint. Browser tracing turns on when FRONTEND_OTEL_ENABLED=1 AND + # FRONTEND_OTLP_ENDPOINT is non-empty. + FRONTEND_OTEL_ENABLED: "0" + # PUBLIC endpoint the BROWSER posts to (OTLP/HTTP — the client appends + # /v1/traces). MUST be internet-reachable AND CORS-enabled for the Plane web + # origin. NOT the backend gRPC path; needs an HTTP+CORS collector route. + FRONTEND_OTLP_ENDPOINT: "" + # A non-empty header is REQUIRED for cross-origin browser tracing: it forces + # the OTLP web exporter to use XHR instead of navigator.sendBeacon. sendBeacon + # always sends credentials (mode 'include'), which CORS rejects against the + # collector's wildcard 'Access-Control-Allow-Origin: *'; XHR sends none, so the + # wildcard is accepted. Value is arbitrary; format "key=value,key2=value2". + FRONTEND_OTLP_HEADERS: "x-otlp-browser=1"