Skip to main content
The OpenTelemetry Collector runs as a DaemonSet, one pod per node, and ships every pod’s stdout and stderr to Rootprint over OTLP. Each record carries the pod, namespace, node, and container it came from.

Prerequisites

  • A running Rootprint instance. Substitute its host for <your-rootprint>.
  • An ingest API key scoped to otel-logs-v0_9. Create one in Settings → API keys with Create ingest key, then substitute it for <your-ingest-api-key>. See API keys.
  • A Kubernetes cluster with kubectl and helm configured against it.
1

Add the OpenTelemetry Helm repo

2

Create values.yaml

Save this as values.yaml. Replace <your-rootprint> and <your-ingest-api-key>.
  • processors and exclude are replaced, not merged. List every entry you want, including the preset-injected memory_limiter, k8s_attributes and batch.
  • file_log and otlp_http need Collector v0.149 or newer.
3

Install the chart

Confirm the DaemonSet runs one pod per node:
If Rootprint runs in this cluster, install the Collector into Rootprint’s namespace so the exclude covers both. Otherwise Rootprint’s and Quickwit’s own logs feed back into the index.
4

Send a test log line

Clean up afterwards with kubectl delete pod rootprint-smoke-test.
5

Verify in Rootprint

Open Logs, pick otel-logs-v0_9 from the index selector, and search for hello from rootprint. Allow up to a minute. resource_attributes.k8s.pod.name reads rootprint-smoke-test.

What you get in Rootprint

The k8s.* keys are OTLP resource attributes, so query them under resource_attributes: The transform sets a log level only on records that arrive without one. It matches error, fatal, panic, or exception as ERROR, warn, warning, or deprecated as WARN, and everything else as INFO. DEBUG is never inferred.

Choose what to collect

logsCollection tails every pod on the node. Exclude what you don’t want before you point it at a busy cluster.

Workloads that already send OTLP

A service instrumented with an OpenTelemetry SDK emits structured records carrying traceId, spanId and its own severityNumber, and usually prints the same events to stdout as well. Tail that stdout too and you store both copies, the second one flat and uncorrelated. Pick one path per workload.
Exclude the namespaces whose services report over OTLP:
To filter on something the path doesn’t carry, add a filter processor after k8s_attributes and drop on resource_attributes.

Collect selectively instead

To have workloads opt in, use annotation-based discovery and drop the file_log block. The preset collects every pod unless you set its default annotation to "false":
Annotate the pods to collect:
For per-container and per-pod settings, see the upstream guide.

Multi-line logs

The preset’s container operator rejoins lines split by the container runtime, not multi-line output from the application. A stack trace arrives as one record per line. Add a recombine operator. Restate the container parser too, because operators is replaced, not merged:
is_first_entry marks where a record starts: here, any line that doesn’t start with whitespace, which covers indented stack frames. Match a timestamp or level prefix instead if that fits your output better.

Cluster events (optional)

To also collect cluster events such as scheduling, image pulls, and OOMKills, deploy a second Collector with the kubernetesEvents preset. Use a single-replica Deployment, not a DaemonSet, so each event arrives once:
Install it under a separate release name, such as rootprint-otel-events.

Troubleshooting

  • helm install fails with [ERROR] 'image.repository' must be set: add the image.repository block from the values above.
  • A DaemonSet pod stays Pending with didn't have free ports: another Collector holds the default hostPorts. Disable them as in the values above.
  • Every event appears twice: a workload both exports OTLP and prints to stdout. Compare attributes.log.file.path:* with NOT attributes.log.file.path:* to see the split, then exclude that namespace.
  • Logs arrive without k8s.* attributes: the kubernetesAttributes preset needs RBAC to read pod metadata. The chart creates the ClusterRole. If you disabled RBAC creation, grant get, list, and watch on pods and namespaces.
  • 401/403/413/415: see Response codes.