Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Detect fields based on per-tenant configuration and put them into structured metadata at ingest time #15188

Merged
merged 11 commits into from
Jan 7, 2025

Conversation

chaudum
Copy link
Contributor

@chaudum chaudum commented Nov 29, 2024

What this PR does / why we need it:

This PR introduces a new feature that allows for extraction of "fields" into structured metadata at ingest time.

Fields can either be regular labels, structured metadata keys, or keys from logfmt or json formatted log lines.

The fields are defined in a per-tenant configuration as map[string][]string, where the key is the target key of the structured metadata, and the value is the list of source fields in given order and the order given above.

Example configuration:

limits_config:
  discover_generic_fields:
    trace_id:
      - "trace_id"
      - "TRACE_ID"
      - "traceID"
      - "TraceID"
    org_id:
      - "org_id"
      - "tenant_id"
      - "user_id"

Update: The fields moved into a "fields" objects:

limits_config:
  discover_generic_fields:
    fields:
      trace_id:
        - "trace_id"
        - "TRACE_ID"
        - "traceID"
        - "TraceID"
      org_id:
        - "org_id"
        - "tenant_id"
        - "user_id"

While parsing of log lines comes with a certain penalty at ingest time (increased latency and CPU usage on distributors), the idea is to extract certain fields once to avoid parsing the log lines every single time at query time. This is mainly useful in combination with bloom filters.

Discussion

Should the value of the config map support jsonpath expression, such as

limits_config:
  discover_generic_fields:
    ticket_id:
      - "message.ticket.id"

Where the log line looks like this:

{"timestamp": 1733128051000, "message": {"ticket": {"id": "2024-d95f87018cdb1f10"}}}

✔️ jsonpath support has been implemented with d216856

Checklist

  • Reviewed the CONTRIBUTING.md guide (required)
  • Documentation added
  • Tests updated
  • Title matches the required conventional commits format, see here
    • Note that Promtail is considered to be feature complete, and future development for logs collection will be in Grafana Alloy. As such, feat PRs are unlikely to be accepted unless a case can be made for the feature actually being a bug fix to existing behavior.
  • Changes that require user attention or interaction to upgrade are documented in docs/sources/setup/upgrade/_index.md
  • If the change is deprecating or removing a configuration option, update the deprecated-config.yaml and deleted-config.yaml files respectively in the tools/deprecated-config-checker directory. Example PR

@github-actions github-actions bot added the type/docs Issues related to technical documentation; the Docs Squad uses this label across many repositories label Nov 29, 2024
@chaudum chaudum force-pushed the chaudum/traceid-detection branch 3 times, most recently from d1c8c1b to da361dd Compare December 2, 2024 06:51
@chaudum chaudum marked this pull request as ready for review December 2, 2024 07:30
@chaudum chaudum requested a review from a team as a code owner December 2, 2024 07:30
@shantanualsi shantanualsi self-requested a review December 2, 2024 08:06
@shantanualsi
Copy link
Contributor

eventually I think we can deprecate the explicit discover_service_name and merge it to discover_generic_fields itself.. levels is a bit different as it explicitly uses detected_level as the SM label name..

Copy link
Collaborator

@JoaoBraveCoding JoaoBraveCoding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall lgtm, just one comment

pkg/distributor/field_detection.go Show resolved Hide resolved
@trevorwhitney
Copy link
Collaborator

@shantanualsi but discover_service_name creates service_name as an indexed label, this only populates structured metadata, correct?

@chaudum why put this in Loki rather than have this sort of thing be the responsibility of the agent?

@Nachtfalkeaw
Copy link

Hello,
I like this idea however I have an open issue where I like to automatically parse akll fields and then define the known fields to labels or structured_metadata:

grafana/alloy#2156

@chaudum
Copy link
Contributor Author

chaudum commented Dec 9, 2024

@chaudum why put this in Loki rather than have this sort of thing be the responsibility of the agent?

Yes, ideally this is done on the agent.
However, there are cases where the operator of Loki is not in control of the agents and updating/changing client configuration is not easy to achieve.

Copy link
Collaborator

@trevorwhitney trevorwhitney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall looks good, a few small nits.

@@ -454,8 +454,9 @@ func (d *Distributor) Push(ctx context.Context, req *logproto.PushRequest) (*log

now := time.Now()
validationContext := d.validator.getValidationContextForTime(now, tenantID)
levelDetector := newLevelDetector(validationContext)
levelDetector := newFieldDetector(validationContext)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no longer doing the work of a levelDetecter, let's rename this variable

},
},
{
name: "logline (json) matches",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about multiple matches (ie. org_id and tenant_id)? a test documenting that behavior would be nice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a good call! The behaviour was different between json and logfmt lines. json matches the first field from the config, logfmt matches the first matching field in the log line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with 167f350

@periklis
Copy link
Collaborator

periklis commented Dec 10, 2024

Considering the fact that this detection of fields happens on both push paths (Push, OTLP) how does this feature interact with the otlp_config where users can and I would dare say should actually list the OTLP resource/scope/log attributes to be extracted as structured metadata?

Note: Leaving the resource atributes to labels out here because the presented feature is only about structured metadata.

@chaudum
Copy link
Contributor Author

chaudum commented Dec 10, 2024

Considering the fact that this detection of fields happens on both push paths (Push, OTLP) how does this feature interact with the otlp_config where users can and I would dare say should actually list the OTLP resource/scope/log attributes to be extracted as structured metadata?

Note: Leaving the resource atributes to labels out here because the presented feature is only about structured metadata.

I see it as an addition. The otlp_config is used to transform OTel push payloads into Loki push payloads, and the discover_generic_fields is used to extract parsed fields from JSON or logfmt logs lines into structured metadata (if not already present in logs labels or structured metadata).

Copy link
Collaborator

@JoaoBraveCoding JoaoBraveCoding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from a code POV lgtm

@chaudum
Copy link
Contributor Author

chaudum commented Dec 11, 2024

There was a suggestion to slightly change the configuration schema to allow for possible future extension to extract all fields of a log line, e.g.

limits_config:
  discover_generic_fields:
    auto:
      format: json | logfmt | pattern
      pattern: "<_> FOO:<foo> <_>"
    fields:
      trace_id:
        - "trace_id"
        - "TRACE_ID"
        - "traceID"
        - "TraceID"
      org_id:
        - "org_id"
        - "tenant_id"
        - "user_id"

The auto object is subject for future implementations. The field definitions that are currently under discover_generic_fields move one level down into a fields object.

@chaudum chaudum force-pushed the chaudum/traceid-detection branch from ac0eb8b to 01d71ce Compare December 18, 2024 20:50
Copy link
Contributor

github-actions bot commented Dec 18, 2024

💻 Deploy preview deleted.

@chaudum chaudum requested a review from cyriltovena December 19, 2024 07:15
@chaudum chaudum force-pushed the chaudum/traceid-detection branch from ce21e0a to 34a74b3 Compare January 7, 2025 09:55
Copy link
Collaborator

@slim-bean slim-bean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@chaudum chaudum merged commit 7033091 into main Jan 7, 2025
61 checks passed
@chaudum chaudum deleted the chaudum/traceid-detection branch January 7, 2025 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size/L type/docs Issues related to technical documentation; the Docs Squad uses this label across many repositories
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants