Skip to content

Loki sink: Inconsistent handling of null values between static and dynamic structured_metadata fields #23426

@jmealo

Description

@jmealo

A note for the community

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Problem

Loki sink: Inconsistent handling of null values between static and dynamic structured_metadata fields

Problem

The Loki sink handles null values inconsistently between static (templated) and dynamic (expanded with *) structured_metadata fields. This creates unexpected "<null>" string values in Loki that interfere with queries.

Current Behavior

  1. Dynamic fields (using * expansion): Null values are correctly dropped

    structured_metadata:
      "metadata_*": "{{ metadata }}"  # null fields are omitted ✓
  2. Static fields (using templates): Null values become the string "<null>"

    structured_metadata:
      request_id: "{{ request_id }}"  # missing field → "<null>" ✗

Expected Behavior

Both static and dynamic structured_metadata fields should handle null values consistently by omitting the field entirely rather than sending "<null>".

Impact

This causes several issues:

  1. Query failures: | request_id != "" doesn't match logs where request_id is literally "<null>"
  2. Storage waste: Storing meaningless "<null>" strings
  3. User confusion: Unexpected string values instead of missing fields

Steps to Reproduce

  1. Configure Vector with Loki sink:

    sinks:
      loki:
        type: loki
        structured_metadata:
          # Static field
          request_id: "{{ request_id }}"
          # Dynamic field  
          "dynamic_*": "{{ metadata }}"
  2. Send an event without request_id field

  3. Query Loki: {service="test"} | request_id != ""

  4. Observe that the query returns no results because request_id contains the string "<null>"

Relevant Code

The issue is in sink/loki/sink.rs:

  • pair_expansion() function correctly checks for "<null>" and skips dynamic fields
  • build_structured_metadata() doesn't perform this check for static fields after template rendering
// This check only happens for dynamic fields:
if val == "<null>" {
    warn!("Encountered \"null\" value for dynamic pair. key: {}", key);
    continue;
}

Proposed Solution

Add a null check in build_structured_metadata() after template rendering:

let value_s = value.unwrap();

// Skip fields that rendered as <null>
if value_s == "<null>" {
    warn!("Skipping structured metadata field '{}' with <null> value", key_s);
    continue;
}

Version Information

  • Vector version: 0.48.0 (and earlier)
  • Loki version: 3.0+

Additional Context

This inconsistency also affects labels in the same way. The same fix should be applied to both build_labels() and build_structured_metadata().

Related code locations:

  • src/sinks/loki/sink.rs: EventEncoder::build_structured_metadata()
  • src/common/expansion.rs: pair_expansion()

Configuration


Version

0.48.0

Debug Output


Example Data

No response

Additional Context

No response

References

Related #23040

Metadata

Metadata

Assignees

No one assigned

    Labels

    sink: lokiAnything `loki` sink relatedtype: bugA code related bug.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions