shreemaan-abhishek opened a new pull request, #13562:
URL: https://github.com/apache/apisix/pull/13562

   ### Description
   
   When `loki-logger` is configured with a dynamic label value (e.g. a global 
rule with `"log_labels": {"job": "apisix", "service_name": "$service_name"}`), 
the label leaked across services: one service's `service_name` got pinned onto 
every other service's logs, and the value froze after the first request.
   
   Two defects combined:
   
   1. **The shared plugin `conf.log_labels` was mutated in place.** While 
resolving variables, the resolved literal was written back into 
`conf.log_labels`. After the first request `"$service_name"` became a literal, 
and `core.utils.resolve_var` returns `n_resolved == 0` for a literal, so it was 
never re-resolved again, freezing the value.
   
   2. **Labels were resolved once per batch processor and shared by all 
entries.** The resolution and the flush closure sat *after* the 
`batch_processor_manager:add_entry` early-return, so they ran only for the 
request that first created the processor. The closure built a single Loki 
stream and put every batched line into it under one label set.
   
   This is worst with a **global rule**, which has exactly one `conf` (the 
batch processor is keyed by `plugin.conf_version(conf)`), so one shared 
processor per worker froze the label for all subsequent requests from any 
service.
   
   ### Fix
   
   - **Clone** `conf.log_labels` before resolving, so the shared conf is never 
mutated.
   - **Resolve per request, before the early-return**, and stamp the result 
onto the entry (`entry.loki_labels`), mirroring the existing 
`entry.loki_log_time`. The entry is the only channel through the batch 
processor.
   - At flush time, **group entries into multiple Loki streams** keyed by their 
resolved label set, so each request is logged under its own labels. In Loki a 
stream is one unique label set, and the push payload supports multiple stream 
objects — the old code forced everything into one.
   - The grouping key sorts label names and joins `key=value` pairs with a NUL 
separator (avoids relying on JSON key ordering).
   - Internal fields (`loki_log_time`, `loki_labels`) are stripped from the 
entry before encoding the log line.
   
   No schema change; pure runtime behavior.
   
   ### Tests
   
   Added `t/plugin/loki-logger.t` TEST 17–20: one route with 
`log_labels.service = "$http_x_service_name"`, hit twice with different header 
values against the same conf, then assert Loki has a distinct stream per value 
and each line sits under its own label. Fails on the old frozen-label code, 
passes on the fix. These are integration tests needing a live Loki at 
`127.0.0.1:3100` (like the existing TEST 4/7/14).
   
   ### Checklist
   
   - [x] I have explained the need for this PR and the problem it solves
   - [x] I have explained the changes or the new features added to this PR
   - [x] I have added tests corresponding to this change
   - [x] I have updated the documentation to reflect this change (no doc change 
needed — the documented behavior was already correct; this fix makes the 
implementation match it)
   - [x] I have verified that the code passes lint (`luacheck`)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to