Log Parsing User Guide

See Log Parsing Architecture for why and when to configure parsing, and Log Parsing Configuration for the full reference for each function. This page walks through the workflow of actually writing and shipping a change, start to finish.

The general workflow

Every change to this pipeline follows the same shape, regardless of which function you’re writing:

  1. Collect a representative sample line from the source you’re configuring for — copy it exactly from a real log event rather than retyping it, so you catch the actual spacing, quoting, and delimiters.

  2. Choose the right function for the job — see Why configure log parsing if you’re not sure which of Grammar, Transform, or Relabel applies.

  3. Build and test the pattern externally before touching Helm values — a dissect debugger or grok debugger, since this pipeline has no built-in live preview (Limitations).

  4. Scope it with conditions so it only runs against the logs it’s meant for — see Functions and conditions.

  5. Pick relabel or transform based on what it needs to see, not where you place it — relabel always runs before Grammar/JSON auto-parsing, transform always after, regardless of config-list position; see Relabel, Transform priority order.

  6. Validate in a lower environment, then deploy with a logs-parser Helm upgrade — there’s no hot-reload, so this is the only way the change takes effect.

  7. Confirm the result in the Logs Explorer against logs ingested after the rollout — see Logs Search Syntax.

The rest of this page works through that sequence for four common tasks.

Extracting structured fields from a plain-text source

Say an internal service logs lines like this, and the automatic heuristic lumps the whole thing into one generic facet:

2024-01-16T10:30:00Z ERROR Connection failed

A grok pattern with named captures gets you a ts, level, and msg facet instead:

logs-parser:
  kf_parsing_config:
    config: |-
      - parser:
          grok:
            args:
              - patterns:
                  - '%{TIMESTAMP_ISO8601:ts} %{LOGLEVEL:level} %{GREEDYDATA:msg}'
            conditions:
              - matcher: "#source"
                value: "my-internal-service"
                op: "=="
yaml

Test the pattern against a real sample line in a grok debugger first, confirming each named group captures what you expect, before adding the conditions block and deploying. See Grammar: dissect and grok patterns for when to reach for dissect instead — it’s faster and simpler for a fixed-delimiter format.

Promoting a facet to a filterable label

Once a facet is extracted (by Grammar, above, or by JSON auto-parsing), it’s searchable but not a first-class, always-indexed field. If you find yourself filtering or grouping dashboards by a facet often enough that it deserves to be a label, promote it with transform:

logs-parser:
  kf_parsing_config:
    config: |-
      ...
      - transform:
          args:
            - action: "facet_to_label_map"
            - sourceLabels: "@eventSource"
            - targetLabel: "source"
          conditions:
            - matcher: "#source"
              op: "=="
              value: "awsLogSource"
yaml

Use transform here, not relabel — transform always runs after extraction, so eventSource is already a facet by the time this rule evaluates, regardless of where you place it in the config list; relabel would run too early to see it. See Promote facets sparingly before doing this for every facet you extract.

Tagging every log from one source with a fixed label

To mark every log from a given source with a constant value — an environment, a team, a tier — use relabel’s `replace action with a regex that always matches:

logs-parser:
  kf_parsing_config:
    config: |-
      - relabel:
          args:
            - action: "replace"
            - regex: ".*"
            - replacement: "production"
            - targetLabel: "env"
          conditions:
            - matcher: "#source"
              value: "my-internal-service"
              op: "=="
yaml

Without the conditions block, this would tag every log line the pipeline processes with env=production — see Scope every function with conditions.

Dropping noisy log lines you don’t want indexed

A source emitting high-volume, low-value lines — health-check pings, synthetic monitoring noise — can be excluded entirely with the drop action. If the condition depends on a facet your own config extracts (here, path from an earlier Grammar step or JSON auto-parsing), write it as transform, not relabel — relabel always runs before extraction and would never see path, regardless of where you place it in the list:

logs-parser:
  kf_parsing_config:
    config: |-
      - transform:
          args:
            - action: "drop"
            - sourceLabels: "@path"
            - regex: "/healthz"
yaml

See Relabel, Transform priority order for why the choice between the two keywords matters here. Because drop is irreversible once deployed, test the condition’s regex against real sample lines first, and read Drop and keep are destructive actions before shipping it.

Next steps