LinkMesh

Search docs, blog and changelog

ENDE

Snippets · Sources

Map your log levels to OpenTelemetry severity

Severity filters, error dashboards and alert rules read severity_number, a number from 1 to 24. This config sets it from the application's level field with the severity_parser operator; the mapping adds level names the defaults do not know (warning, err, crit, emerg).

Signals
Logs
Category
CollectionParsing
Source
Log files (JSON lines)
Components
filelogbatchmemory_limiterotlphttp
Destination
OTLP/HTTP endpoint

OpenTelemetry Collector

OpenTelemetry Collector YAML
extensions:
  file_storage:
    directory: /var/lib/otelcol/storage
    create_directory: true

receivers:
  filelog:
    include: [/var/log/myapp/*.json.log]
    start_at: end
    storage: file_storage
    operators:
      - type: json_parser
        parse_from: body
        parse_to: attributes
        on_error: send
      # Sets severity_number and severity_text from the level field. The
      # mapping adds the values this application uses that the defaults miss.
      - type: severity_parser
        parse_from: attributes.level
        mapping:
          warn: warning
          error: [err, crit]
          fatal: emerg

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 25
  batch:
    timeout: 5s

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

service:
  extensions: [file_storage]
  pipelines:
    logs:
      receivers: [filelog]
      processors: [memory_limiter, batch]
      exporters: [otlphttp]

LinkMesh

LinkMesh has no built-in template for this job. Add it as a custom source in raw mode, with filelog as the component type and this body - the same lines as under filelog in the collector config:

Custom source body (filelog)
include: [/var/log/myapp/*.json.log]
start_at: end
storage: file_storage
operators:
  - type: json_parser
    parse_from: body
    parse_to: attributes
    on_error: send
  - type: severity_parser
    parse_from: attributes.level
    mapping:
      warn: warning
      error: [err, crit]
      fatal: emerg

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

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.

6 log records in, 6 log records out

  1. Before

    body
    {"level":"debug","msg":"cache miss"}

    After

    body
    {"level":"debug","msg":"cache miss"}
    severity
    debug
    severity_number
    5
    attributes.log.file.name
    input.log
    attributes.level
    debug
    attributes.msg
    cache miss
  2. Before

    body
    {"level":"info","msg":"order accepted"}

    After

    body
    {"level":"info","msg":"order accepted"}
    severity
    info
    severity_number
    9
    attributes.log.file.name
    input.log
    attributes.level
    info
    attributes.msg
    order accepted
  3. Before

    body
    {"level":"warning","msg":"slow provider"}

    After

    body
    {"level":"warning","msg":"slow provider"}
    severity
    warning
    severity_number
    13
    attributes.log.file.name
    input.log
    attributes.msg
    slow provider
    attributes.level
    warning
  4. Before

    body
    {"level":"err","msg":"payment timeout"}

    After

    body
    {"level":"err","msg":"payment timeout"}
    severity
    err
    severity_number
    17
    attributes.level
    err
    attributes.log.file.name
    input.log
    attributes.msg
    payment timeout
  5. Before

    body
    {"level":"crit","msg":"disk full"}

    After

    body
    {"level":"crit","msg":"disk full"}
    severity
    crit
    severity_number
    17
    attributes.msg
    disk full
    attributes.log.file.name
    input.log
    attributes.level
    crit
  6. Before

    body
    {"level":"notice","msg":"config reloaded"}

    After

    body
    {"level":"notice","msg":"config reloaded"}
    severity
    notice
    attributes.log.file.name
    input.log
    attributes.level
    notice
    attributes.msg
    config reloaded

The gotcha

A level the mapping does not know keeps its text and gets no number. In our test "notice" arrived with severity_text notice and no severity_number, so to a severity filter it is a record without severity - which the keep-error-logs config drops and the filter-by-severity config keeps. List every level your application writes.

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: Sources