LinkMesh

Search docs, blog and changelog

ENDE

Snippets · Processors

Drop health-check logs and spans

Liveness and readiness probes hit every pod every few seconds, and each hit can produce a log line and a span that nobody will ever read. This filter processor drops log records and spans whose url.path is a health, readiness or liveness endpoint. The regex is anchored, so /api/health-insurance/quotes is kept.

Signals
LogsTraces
Category
Reduction
Source
OTLP
Components
otlpbatchfiltermemory_limiterotlphttp
Destination
OTLP/HTTP endpoint

OpenTelemetry Collector

OpenTelemetry Collector YAML
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 25
  # Drops probe traffic: log records and spans whose url.path is a health,
  # readiness or liveness endpoint.
  filter/drop-health-checks:
    error_mode: ignore
    logs:
      log_record:
        - IsMatch(attributes["url.path"], "^/(health|healthz|ready|readyz|live|livez)$")
    traces:
      span:
        - IsMatch(attributes["url.path"], "^/(health|healthz|ready|readyz|live|livez)$")
  batch:
    timeout: 5s

exporters:
  otlphttp:
    endpoint: https://your-backend:4318

service:
  pipelines:
    logs:
      receivers: [otlp]
      processors: [memory_limiter, filter/drop-health-checks, batch]
      exporters: [otlphttp]
    traces:
      receivers: [otlp]
      processors: [memory_limiter, filter/drop-health-checks, batch]
      exporters: [otlphttp]

LinkMesh

LinkMesh has no built-in template for this job. Add it as a custom processor in raw mode, with filter as the component type and this body - the same lines as under filter/drop-health-checks in the collector config:

Custom processor body (filter)
error_mode: ignore
logs:
  log_record:
    - IsMatch(attributes["url.path"], "^/(health|healthz|ready|readyz|live|livez)$")
traces:
  span:
    - IsMatch(attributes["url.path"], "^/(health|healthz|ready|readyz|live|livez)$")

How custom components are validated and rolled out: create a custom processor.

Download config.yamlValidated with otelcol-contrib v0.161.0 on 2026-10-26

Complete config files, each checked with the collector's own validate command (otelcol-contrib v0.161.0). Replace https://your-backend:4318 with your own OTLP endpoint before you run it.

Before and after

Example telemetry in, and what this exact config sent out when we ran it in otelcol-contrib v0.161.0. CI re-runs it on every change and fails if the output differs. Changed values are highlighted.

5 log records in, 3 log records out

  1. Before

    body
    GET /healthz 200
    attributes.http.request.method
    GET
    attributes.url.path
    /healthz
    attributes.http.response.status_code
    200
    resource.service.name
    checkout

    After

    Dropped

  2. Before

    body
    GET /readyz 200
    attributes.http.request.method
    GET
    attributes.url.path
    /readyz
    attributes.http.response.status_code
    200
    resource.service.name
    checkout

    After

    Dropped

  3. Before

    body
    POST /orders 201
    attributes.http.request.method
    POST
    attributes.url.path
    /orders
    attributes.http.response.status_code
    201
    resource.service.name
    checkout

    After

    body
    POST /orders 201
    attributes.http.request.method
    POST
    attributes.url.path
    /orders
    attributes.http.response.status_code
    201
    resource.service.name
    checkout
  4. Before

    body
    GET /healthz 200
    resource.service.name
    checkout

    After

    body
    GET /healthz 200
    resource.service.name
    checkout
  5. Before

    body
    GET /api/health-insurance/quotes 200
    attributes.url.path
    /api/health-insurance/quotes
    resource.service.name
    checkout

    After

    body
    GET /api/health-insurance/quotes 200
    attributes.url.path
    /api/health-insurance/quotes
    resource.service.name
    checkout

3 spans in, 2 spans out

  1. Before

    span
    GET /healthz
    attributes.url.path
    /healthz
    resource.service.name
    checkout

    After

    Dropped

  2. Before

    span
    GET /healthz
    attributes.http.target
    /healthz
    resource.service.name
    checkout

    After

    span
    GET /healthz
    attributes.http.target
    /healthz
    resource.service.name
    checkout
  3. Before

    span
    POST /orders
    attributes.url.path
    /orders
    resource.service.name
    checkout

    After

    span
    POST /orders
    attributes.url.path
    /orders
    resource.service.name
    checkout

The gotcha

It matches the url.path attribute, and only that. In our test two probes got through: a log line that carried "GET /healthz" only in its body, and a span from older instrumentation that set http.target instead of url.path. 5 log records in, 3 out; 3 spans in, 2 out. Check which attribute your instrumentation sets before you rely on it.

Manage this across a fleet

LinkMesh manages collector configuration across a fleet from one self-hosted control plane, so a change like this is made once rather than host by host. The first 25 Collectors are free after a no-card registration in the OpenSight Customer Portal (5 without one).

Doing this in LinkMesh: Drop noisy logs