LinkMesh

Search docs, blog and changelog

ENDE

Snippets · Sources

Parse JSON log lines into attributes

Applications that log JSON lines already did the hard part; the collector only has to read the fields. The json_parser operator on the filelog receiver turns every top-level field into a log attribute, types included: numbers stay numbers and booleans stay booleans.

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:
      # Every top-level JSON field becomes a log attribute; the original
      # line stays in the body.
      - type: json_parser
        parse_from: body
        parse_to: attributes
        on_error: send

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

Library › Processors › JSON Log Parser

LinkMesh adds these operators to the source's receiver (filelog/input_src_00000501):

Pushed to the collector
operators:
  - parse_from: body
    type: json_parser

The same, as an API call (see automation recipes):

API call
curl -X POST "$LINKMESH/pipelines" \
  -H "Authorization: Bearer $LMSAT" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "JSON Log Parser",
  "processors": [
    {
      "config": {},
      "enabled": true,
      "order": 1,
      "templateSlug": "json-log-parser"
    }
  ]
}'

The rest of the collector config above (memory_limiter, batch, otlphttp) is not part of this template: in LinkMesh those are separate sources, processors and destinations on the route.

Generated from LinkMesh's template catalogue by linkmesh-server snippet-render 1.464.3, not written by hand.

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.

3 log records in, 3 log records out

  1. Before

    body
    {"level":"info","msg":"user signed in","user":"u-4711","duration_ms":12}

    After

    body
    {"level":"info","msg":"user signed in","user":"u-4711","duration_ms":12}
    attributes.log.file.name
    input.log
    attributes.duration_ms
    12
    attributes.level
    info
    attributes.msg
    user signed in
    attributes.user
    u-4711
  2. Before

    body
    {"level":"error","msg":"payment provider timeout","order":"1002","retry":true}

    After

    body
    {"level":"error","msg":"payment provider timeout","order":"1002","retry":true}
    attributes.log.file.name
    input.log
    attributes.retry
    true
    attributes.level
    error
    attributes.msg
    payment provider timeout
    attributes.order
    1002
  3. Before

    body
    WARN legacy line that is not JSON

    After

    body
    WARN legacy line that is not JSON
    attributes.log.file.name
    input.log

The gotcha

A line that is not JSON is not dropped. With on_error: send it is shipped unparsed, with its raw text as the body and no attributes, and the collector logs an error for it: in our test the third line, plain text, arrived as-is next to two parsed ones. Mixed-format files produce a log error per plain line; on_error: send_quiet ships them without the noise.

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