OpenTelemetry OpenTelemetry

Collect Logs and Infrastructure Metrics with Receivers

Use filelog, hostmetrics, kubeletstats, and prometheus receivers, and derive metrics from traces.

14 min Intermediate

Applications that use an OpenTelemetry SDK push their own telemetry, but much of what you need to observe doesn’t: log files, host resources, the kubelet, and services that already expose Prometheus metrics. The Collector pulls these in through receivers. This guide shows the most useful ones, all as configuration. Component option names change between releases, so check each receiver’s README for the version you run. All of these are in the contrib and Kubernetes distributions.

Log files with filelog

The filelog receiver tails files and turns lines into log records.

receivers:
  filelog:
    include:
      - /var/log/myapp/*.log
    exclude:
      - /var/log/myapp/*.gz
    start_at: end            # or "beginning" to read existing content
    include_file_path: true
    operators:
      - type: json_parser
        timestamp:
          parse_from: attributes.time
          layout: '%Y-%m-%dT%H:%M:%S.%LZ'
        severity:
          parse_from: attributes.level

Operators form a small parsing pipeline that runs on each entry. Common ones are json_parser, regex_parser, move, add, remove, and recombine (to join multi-line stack traces back into one record). A multi-line example:

    operators:
      - type: recombine
        combine_field: body
        is_first_entry: body matches "^\\d{4}-\\d{2}-\\d{2}"
        source_identifier: attributes["log.file.path"]

To survive restarts without re-reading or skipping data, give the receiver a storage extension:

extensions:
  file_storage:
    directory: /var/lib/otelcol/storage

receivers:
  filelog:
    include: [/var/log/myapp/*.log]
    storage: file_storage

service:
  extensions: [file_storage]

On Kubernetes, container logs live under /var/log/pods/*/*/*.log. The Helm chart’s logsCollection preset configures this receiver, mounts the host path, and parses the container runtime format for you, so prefer it over writing the receiver by hand.

Host metrics with hostmetrics

On a VM or a node, the hostmetrics receiver reads CPU, memory, disk, filesystem, and network figures from the operating system:

receivers:
  hostmetrics:
    collection_interval: 30s
    scrapers:
      cpu:
      memory:
      load:
      disk:
      filesystem:
        exclude_mount_points:
          mount_points: [/dev/*, /proc/*, /sys/*, /run/*]
          match_type: regexp
      network:
      paging:

Run it in an agent on each host. In a container, it reports the container’s view unless you mount the host’s filesystem and set root_path, so check the receiver README before relying on it there. Add the resourcedetection processor to label metrics with host, cloud, or Kubernetes details:

processors:
  resourcedetection:
    detectors: [env, system]
    timeout: 5s
    override: false

Kubernetes metrics

Node and pod resource usage comes from the kubelet through kubeletstats, run as a DaemonSet:

receivers:
  kubeletstats:
    collection_interval: 30s
    auth_type: serviceAccount
    endpoint: ${env:K8S_NODE_NAME}:10250
    insecure_skip_verify: true
    metric_groups: [node, pod, container]

Pass the node name through the downward API:

env:
  - name: K8S_NODE_NAME
    valueFrom:
      fieldRef:
        fieldPath: spec.nodeName

insecure_skip_verify is common because kubelet certificates are often self-signed; use proper CA configuration where you can. The service account needs permission to read nodes/stats, which the Helm preset grants.

Cluster-level state (deployment replicas, pod phase, node conditions) comes from the k8s_cluster receiver, and Kubernetes events from k8sobjects. Run both once per cluster, in a single-replica Deployment, or you will collect duplicates. Both are available as Helm presets (clusterMetrics and kubernetesEvents).

Scrape Prometheus endpoints

The prometheus receiver accepts a standard Prometheus scrape configuration, so existing targets move over without rewriting them:

receivers:
  prometheus:
    config:
      scrape_configs:
        - job_name: node-exporter
          scrape_interval: 30s
          static_configs:
            - targets: ["node-exporter:9100"]
        - job_name: kubernetes-pods
          kubernetes_sd_configs:
            - role: pod
          relabel_configs:
            - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
              action: keep
              regex: "true"

Two gotchas: in a Collector config, write a literal dollar sign in a Prometheus regex or replacement as $$, because the Collector otherwise tries to expand it as a variable; and if you run several replicas that all scrape the same targets, you’ll get duplicates. The OpenTelemetry Operator’s Target Allocator exists to shard targets across Collector replicas, as does running one scraper replica.

Use many receivers, one pipeline

Receivers feed pipelines of the same signal type:

service:
  pipelines:
    metrics:
      receivers: [otlp, hostmetrics, kubeletstats, prometheus]
      processors: [memory_limiter, resourcedetection, batch]
      exporters: [otlphttp/prometheus]
    logs:
      receivers: [otlp, filelog]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/loki]

You can also split into several pipelines per signal (metrics/infra, metrics/apps) to apply different processors to each source.

Metrics from traces with the spanmetrics connector

If your services emit traces but not request metrics, the spanmetrics connector derives request counts, error counts, and latency histograms from spans. A connector is an exporter in one pipeline and a receiver in another:

connectors:
  spanmetrics:
    histogram:
      explicit:
        buckets: [10ms, 50ms, 100ms, 250ms, 500ms, 1s, 2.5s, 5s]
    dimensions:
      - name: http.request.method
      - name: http.response.status_code

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp/tempo, spanmetrics]
    metrics/spanmetrics:
      receivers: [spanmetrics]
      processors: [batch]
      exporters: [otlphttp/prometheus]

Every dimension multiplies the number of time series, so add only attributes with a small, bounded set of values. Never use user IDs or URLs with identifiers. Put the connector before any sampling so that the numbers reflect all traffic (see the sampling guide).

Verify

Add the debug exporter with verbosity: detailed to a copy of the pipeline and run the Collector in the foreground. You should see log records with the parsed severity, or metric names such as system.cpu.time, k8s.pod.cpu.usage, or traces.span.metrics.calls (the naming can differ by version). If nothing appears, check file permissions for filelog, the node name variable for kubeletstats, and that every receiver is listed in a pipeline.

Popular Guides