From 3ceff444e8bda25e065c8980717a237e98bb6d89 Mon Sep 17 00:00:00 2001 From: szibis Date: Mon, 5 Oct 2026 23:03:28 +0200 Subject: [PATCH] test: differential parity run against Loki and a ranked gap inventory bench/parity: a differential runner for the proxy against Loki on an isolated stack with identical data. It builds a corpus from Loki v3.7.7's own LogQL test suites (selectors moved onto the seeded data), the repository's parity cases, a sweep generated from what Loki reports for the window (labels, values, detected fields and the stages Explore and Logs Drilldown build on them, the metadata endpoints, patterns, format_query, tail) and the Grafana Explore and Logs Drilldown requests captured by bench/visual, replayed through Grafana. It proves both sides healthy and equal before and after (per-slice line counts, Loki's blank window, known and unchanged Loki and VictoriaLogs restart counts, no VictoriaLogs failure in the proxy log), diffs every answer semantically with and without categorize-labels, reconfirms samples of every difference, and clusters the rest by endpoint, query shape and kind of difference. --keep-bodies and --rediff let a later diff rule be applied to a recorded run offline. The run on main at fcaccfcc is recorded in conformance/registry/generated/parity-discovery.json; every cluster is claimed by exactly one registry case with a gap block (Loki reference, proxy behaviour, impact, effort, code area, planned test), recorded deviations by the case id the diff names. conformance/scripts/parity_gaps.py ranks them into conformance/reports/parity-gaps.md and fails the gate on an unclaimed or doubly claimed cluster, a deviation that names no recorded case, or `status: fixed` without `fixed_by`. bench/visual/stack.py takes --overlay for a further compose file; the parity overlay raises Loki's gRPC message limits so per-stream metric answers are not dropped between Loki's querier and frontend. --- .github/workflows/ci.yaml | 3 + CHANGELOG.md | 39 + bench/parity/README.md | 88 + bench/parity/cluster.py | 224 + bench/parity/corpus.py | 418 + bench/parity/differ.py | 527 + bench/parity/docker-compose.parity.yml | 21 + bench/parity/publish.py | 71 + bench/parity/run.py | 675 + bench/parity/tests/test_parity.py | 334 + bench/parity/tests/test_parity_gaps.py | 106 + bench/visual/stack.py | 12 +- conformance/README.md | 8 +- conformance/registry/README.md | 26 + .../data-probing-and-freshness.yaml | 3 + .../behaviours/heavy-metric-fetch-bounds.yaml | 1 + .../behaviours/loki-compatible-profile.yaml | 3 + .../behaviours/metric-series-identity.yaml | 2 + .../parsed-label-series-identity.yaml | 2 + .../parser-error-and-label-collision.yaml | 4 + .../behaviours/service-name-derivation.yaml | 1 + .../severity-detected-level-derivation.yaml | 2 + .../severity-exposure-surfaces.yaml | 1 + .../window-bounds-and-step-alignment.yaml | 1 + .../drilldown/detected-field-values.yaml | 37 + .../detected-fields-type-inference.yaml | 36 + .../detected-labels-sampled-cardinality.yaml | 34 + .../drilldown/patterns-mined-differently.yaml | 38 + .../drilldown/plugin-legend-sync-race.yaml | 37 + ...drilldown-7d-breakdown-response-limit.yaml | 49 + .../drilldown-breakdown-keeps-busiest.yaml | 36 + .../structured-metadata-splits-series.yaml | 73 + ...re-parser-metric-duplicates-extracted.yaml | 50 + .../parsed-identity/dynamic-key-parsers.yaml | 66 + .../regexp-partial-match-series-count.yaml | 11 + .../detected-fields-no-extracted-suffix.yaml | 36 + .../existence-filter-on-sanitized-key.yaml | 44 + .../grafana-stats-error-as-warning.yaml | 35 + ...n-extraction-list-exposes-only-listed.yaml | 42 + .../json-extraction-list-missing-key.yaml | 34 + .../json-line-stored-without-line.yaml | 11 +- .../profiles/json-number-label-text.yaml | 45 + .../keep-removes-derived-service-name.yaml | 42 + .../keep-structured-metadata-key.yaml | 37 + .../profiles/label-filter-on-line-field.yaml | 9 + .../label-filter-regexp-anchoring.yaml | 9 + .../profiles/metadata-default-lookback.yaml | 31 + .../profiles/multi-tenant-volume-merge.yaml | 32 + .../structured-metadata-label-collision.yaml | 30 + .../profiles/volume-bytes-accounting.yaml | 37 + .../volume-detected-level-extension.yaml | 38 + .../detected-metadata-proxy-cache.yaml | 36 + .../quality/index-stats-bytes-and-chunks.yaml | 34 + .../index-stats-loki-chunk-accounting.yaml | 37 + .../quality/index-stats-streams-differ.yaml | 39 + .../quality/label-cache-aged-labels-kept.yaml | 33 + .../quality/label-values-chunk-window.yaml | 35 + .../cases/quality/stats-volume-freshness.yaml | 38 + .../semantics/absent-over-time-semantics.yaml | 43 + .../semantics/binary-ignoring-matching.yaml | 37 + .../semantics/count-values-unsupported.yaml | 34 + .../semantics/eager-pipeline-validation.yaml | 34 + .../semantics/extracted-suffix-collision.yaml | 128 + .../first-last-over-time-translation.yaml | 45 + .../instant-result-type-missing.yaml | 34 + .../cases/semantics/ip-filter-precision.yaml | 35 + .../label-filter-expression-syntax.yaml | 47 + .../semantics/label-replace-new-label.yaml | 35 + .../line-filter-or-alternatives.yaml | 40 + .../log-entries-differ-same-count.yaml | 38 + ...logfmt-existence-filter-ungrouped-sum.yaml | 39 + .../non-sum-vector-aggregation-unreduced.yaml | 70 + .../semantics/parenthesized-log-range.yaml | 79 + .../parse-error-labels-on-log-lines.yaml | 43 + .../semantics/parse-error-text-parity.yaml | 168 + .../cases/semantics/pattern-line-filter.yaml | 40 + .../proxy-accepts-invalid-logql.yaml | 46 + .../semantics/proxy-rejects-valid-logql.yaml | 88 + .../stddev-stdvar-grouped-values.yaml | 42 + .../semantics/tail-history-start-limit.yaml | 37 + .../semantics/topk-bottomk-range-series.yaml | 43 + .../semantics/unpack-non-string-values.yaml | 37 + .../semantics/unwrap-conversion-error.yaml | 37 + .../semantics/unwrap-range-last-point.yaml | 42 + .../semantics/unwrap-sample-validity.yaml | 104 + .../detected-level-filter-json-level.yaml | 53 + .../detected-level-stage-side-effects.yaml | 41 + .../capabilities-floor-vs-matrix.yaml | 36 + .../versions/format-if-before-vl-1.35.yaml | 35 + .../otlp-severity-before-vl-1.35.yaml | 35 + .../registry/generated/parity-discovery.json | 18433 ++++++++++++++++ conformance/registry/generated/wiring.json | 65 +- conformance/reports/gaps.md | 20 +- conformance/reports/parity-gaps.md | 87 + conformance/scripts/parity_gaps.py | 194 + scripts/ci/check_conformance.py | 11 +- 96 files changed, 24258 insertions(+), 20 deletions(-) create mode 100644 bench/parity/README.md create mode 100644 bench/parity/cluster.py create mode 100644 bench/parity/corpus.py create mode 100644 bench/parity/differ.py create mode 100644 bench/parity/docker-compose.parity.yml create mode 100644 bench/parity/publish.py create mode 100644 bench/parity/run.py create mode 100644 bench/parity/tests/test_parity.py create mode 100644 bench/parity/tests/test_parity_gaps.py create mode 100644 conformance/registry/cases/drilldown/detected-field-values.yaml create mode 100644 conformance/registry/cases/drilldown/detected-fields-type-inference.yaml create mode 100644 conformance/registry/cases/drilldown/detected-labels-sampled-cardinality.yaml create mode 100644 conformance/registry/cases/drilldown/patterns-mined-differently.yaml create mode 100644 conformance/registry/cases/drilldown/plugin-legend-sync-race.yaml create mode 100644 conformance/registry/cases/limits/drilldown-7d-breakdown-response-limit.yaml create mode 100644 conformance/registry/cases/limits/drilldown-breakdown-keeps-busiest.yaml create mode 100644 conformance/registry/cases/metric-identity/structured-metadata-splits-series.yaml create mode 100644 conformance/registry/cases/parsed-identity/bare-parser-metric-duplicates-extracted.yaml create mode 100644 conformance/registry/cases/parsed-identity/dynamic-key-parsers.yaml create mode 100644 conformance/registry/cases/profiles/detected-fields-no-extracted-suffix.yaml create mode 100644 conformance/registry/cases/profiles/existence-filter-on-sanitized-key.yaml create mode 100644 conformance/registry/cases/profiles/grafana-stats-error-as-warning.yaml create mode 100644 conformance/registry/cases/profiles/json-extraction-list-exposes-only-listed.yaml create mode 100644 conformance/registry/cases/profiles/json-extraction-list-missing-key.yaml create mode 100644 conformance/registry/cases/profiles/json-number-label-text.yaml create mode 100644 conformance/registry/cases/profiles/keep-removes-derived-service-name.yaml create mode 100644 conformance/registry/cases/profiles/keep-structured-metadata-key.yaml create mode 100644 conformance/registry/cases/profiles/metadata-default-lookback.yaml create mode 100644 conformance/registry/cases/profiles/multi-tenant-volume-merge.yaml create mode 100644 conformance/registry/cases/profiles/volume-bytes-accounting.yaml create mode 100644 conformance/registry/cases/profiles/volume-detected-level-extension.yaml create mode 100644 conformance/registry/cases/quality/detected-metadata-proxy-cache.yaml create mode 100644 conformance/registry/cases/quality/index-stats-bytes-and-chunks.yaml create mode 100644 conformance/registry/cases/quality/index-stats-loki-chunk-accounting.yaml create mode 100644 conformance/registry/cases/quality/index-stats-streams-differ.yaml create mode 100644 conformance/registry/cases/quality/label-cache-aged-labels-kept.yaml create mode 100644 conformance/registry/cases/quality/label-values-chunk-window.yaml create mode 100644 conformance/registry/cases/quality/stats-volume-freshness.yaml create mode 100644 conformance/registry/cases/semantics/absent-over-time-semantics.yaml create mode 100644 conformance/registry/cases/semantics/binary-ignoring-matching.yaml create mode 100644 conformance/registry/cases/semantics/count-values-unsupported.yaml create mode 100644 conformance/registry/cases/semantics/eager-pipeline-validation.yaml create mode 100644 conformance/registry/cases/semantics/extracted-suffix-collision.yaml create mode 100644 conformance/registry/cases/semantics/first-last-over-time-translation.yaml create mode 100644 conformance/registry/cases/semantics/instant-result-type-missing.yaml create mode 100644 conformance/registry/cases/semantics/ip-filter-precision.yaml create mode 100644 conformance/registry/cases/semantics/label-filter-expression-syntax.yaml create mode 100644 conformance/registry/cases/semantics/label-replace-new-label.yaml create mode 100644 conformance/registry/cases/semantics/line-filter-or-alternatives.yaml create mode 100644 conformance/registry/cases/semantics/log-entries-differ-same-count.yaml create mode 100644 conformance/registry/cases/semantics/logfmt-existence-filter-ungrouped-sum.yaml create mode 100644 conformance/registry/cases/semantics/non-sum-vector-aggregation-unreduced.yaml create mode 100644 conformance/registry/cases/semantics/parenthesized-log-range.yaml create mode 100644 conformance/registry/cases/semantics/parse-error-labels-on-log-lines.yaml create mode 100644 conformance/registry/cases/semantics/parse-error-text-parity.yaml create mode 100644 conformance/registry/cases/semantics/pattern-line-filter.yaml create mode 100644 conformance/registry/cases/semantics/proxy-accepts-invalid-logql.yaml create mode 100644 conformance/registry/cases/semantics/proxy-rejects-valid-logql.yaml create mode 100644 conformance/registry/cases/semantics/stddev-stdvar-grouped-values.yaml create mode 100644 conformance/registry/cases/semantics/tail-history-start-limit.yaml create mode 100644 conformance/registry/cases/semantics/topk-bottomk-range-series.yaml create mode 100644 conformance/registry/cases/semantics/unpack-non-string-values.yaml create mode 100644 conformance/registry/cases/semantics/unwrap-conversion-error.yaml create mode 100644 conformance/registry/cases/semantics/unwrap-range-last-point.yaml create mode 100644 conformance/registry/cases/semantics/unwrap-sample-validity.yaml create mode 100644 conformance/registry/cases/severity/detected-level-filter-json-level.yaml create mode 100644 conformance/registry/cases/severity/detected-level-stage-side-effects.yaml create mode 100644 conformance/registry/cases/versions/capabilities-floor-vs-matrix.yaml create mode 100644 conformance/registry/cases/versions/format-if-before-vl-1.35.yaml create mode 100644 conformance/registry/cases/versions/otlp-severity-before-vl-1.35.yaml create mode 100644 conformance/registry/generated/parity-discovery.json create mode 100644 conformance/reports/parity-gaps.md create mode 100644 conformance/scripts/parity_gaps.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 39fc7a40c..830771ec2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -668,6 +668,9 @@ jobs: - name: Visual-proof harness unit tests (plan, gate and comment, publish) run: python3 -m unittest discover -s bench/visual/tests + - name: Differential parity harness unit tests (corpus, diff, clusters) + run: python3 -m unittest discover -s bench/parity/tests + - name: Registry report for this change if: github.event_name == 'pull_request' run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a75e4f0f..8e7ec9a57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **Differential parity run against Loki (`bench/parity`) and a ranked list of + every known difference (`conformance/reports/parity-gaps.md`).** On an + isolated stack with identical data in Loki and VictoriaLogs (proven before + and after: equal line counts per 10-minute slice, Loki out of its + fresh-stack blank window, Loki and VictoriaLogs restart counts known and + unchanged, no VictoriaLogs failure in the proxy log), the runner sends a + corpus of 5,671 requests (10,886 with both encodings) to Loki and to the + proxy, query endpoints with and without `categorize-labels`: LogQL from Loki + v3.7.7's own test suites moved onto the seeded data, the repository's parity + cases, a sweep generated from what Loki reports for the window (every label + and value, every service's detected fields with the stages Explore and Logs + Drilldown build on them, index/stats, volume, detected_*, patterns, + format_query, tail), and every request Grafana Explore and Logs Drilldown + sent on the visual-proof pages, replayed through Grafana against both + datasources. It diffs the answers semantically (stats and execution metadata + ignored, stream and series order ignored where Loki leaves it unspecified, + entry order checked against the direction, ties at the line limit ignored), + asks again for samples of every difference so one Loki does not reproduce is + not counted, and clusters the rest by endpoint, query shape and kind of + difference. Every cluster of the recorded run is claimed by exactly one + registry case (a `gap:` block with the Loki reference, the proxy's + behaviour, user impact, effort, code area and the planned test; recorded + deviations by the case id the diff names); the conformance gate fails when a + cluster is unclaimed or a deviation names no recorded case, and a fix can + retire a gap with `status: fixed` and `fixed_by:`. The recorded run measured + main at fcaccfcc, before #652 rewrote the tail and log-row label conversion: + 600 gap signatures and 5 recorded-deviation clusters in 5,243 differing + requests, claimed by 66 cases (47 open, 4 owner-accepted deviations, 13 + documented, 2 Loki or plugin artifacts). Among the open ones: + max/min/count/avg over a range function returned unreduced on `query_range` + (still present after #652), `unwrap` samples with empty-string values, an + ungrouped `sum` over `| logfmt | key!=""` returning no series, `_extracted` + suffixes missing from query results, native tail without the start/limit + history (still present after #652), and parser gaps (`or` line filters, + parenthesized log ranges). Tooling and registry only; the proxy is + unchanged. + ## [1.107.2] - 2026-10-05 ### Changed diff --git a/bench/parity/README.md b/bench/parity/README.md new file mode 100644 index 000000000..fe0facd81 --- /dev/null +++ b/bench/parity/README.md @@ -0,0 +1,88 @@ +# Differential parity run: the proxy against Loki + +Finds every way the proxy's answers differ from Loki's on identical data, and +clusters the differences into gap signatures that the conformance registry +must account for. It discovers and registers gaps; it fixes nothing. + +| piece | what | +|---|---| +| `corpus.py` | builds the request corpus from four sources: LogQL from Loki's own test suites (`--loki-src`, a checkout of `grafana/loki` at the reference version; selectors are moved onto the seeded data, sharding/template fixtures dropped), the repository's parity cases (`test/e2e-compat`, `query-semantics-matrix.json`, `bench/ab/shapes.json`, every registry case's request), a seeded sweep generated from what Loki reports for the window (every label and value, every service's detected fields with the stages Explore and Logs Drilldown build on them, the metadata endpoints, patterns, format_query and tail), and every request Grafana Explore and Logs Drilldown sent on the `bench/visual` pages (`--captures`). | +| `run.py` | proves both sides healthy and the data equal, then sends every request to Loki and the proxy (query endpoints twice: with `X-Loki-Response-Encoding-Flags: categorize-labels`, as Grafana sends them, and without), replays the Grafana requests through Grafana's API against the Loki and proxy datasources, and diffs each pair (`differ.py`). | +| `differ.py` | the semantic diff: status and error text, result type, log entries matched by timestamp and line (stream order ignored, entry order inside a stream checked against the query's direction; ties at the line limit's boundary timestamp ignored, Loki breaks them arbitrarily), per-entry labels and label categories, metric series sets (including one label set returned as several series) and values (relative tolerance 1e-6; scalars compared as numbers), name/value sets of the metadata endpoints, detected_fields types/parsers/cardinality (5 % for Loki's sketch), index/stats streams and entries. Stats blocks and execution metadata are ignored. Label names are generalised to their class (`__error__`, `*_extracted`, ``, ``, ...) so one cause clusters once. Recorded deviations carry their registry case id and are never ranked. | +| `cluster.py` | one cluster per differing request: endpoint class, query shape (outer operation, parser, stages and the kinds of filter used: line, label, pattern), the request's most fundamental differing facet (status before series before labels before values) with error texts reduced to a template. Ranked by user impact (explore- and drilldown-visible first), then distinct corpus queries. Writes `clusters.json` and `report.md`. | +| `publish.py` | records a run in `conformance/registry/generated/parity-discovery.json`; `conformance/scripts/parity_gaps.py` ranks the registry's gap cases with those counts into `conformance/reports/parity-gaps.md`, and the conformance gate fails while a discovered cluster is accounted for by no registry case. | + +## Run + +On an isolated stack (own compose project and ports; never the shared e2e stack): + +```bash +OUT= +python3 bench/visual/stack.py up --out $OUT --project parity --port-offset 1500 \ + --hours 1.5 --loki-hours 1.5 --overlay bench/parity/docker-compose.parity.yml +END=$(python3 -c "import json;print(json.load(open('$OUT/state.json'))['end'])") +# Grafana's own requests (optional source): the bench/visual capture of the default pages +(cd bench/visual && npm install && GRAFANA_URL=http://127.0.0.1:34502 VP_OUT=$OUT/cap VP_END=$END \ + VP_PAGES=$(python3 -c "import json;print(','.join(p['id'] for p in json.load(open('spec.json'))['pages'] if p['kind']!='tail'))") \ + VP_RANGES=15m,1h npx playwright test) +python3 bench/parity/corpus.py --out $OUT/corpus.json --loki-src \ + --seeded http://127.0.0.1:34601 --end $END --captures $OUT/cap/data +python3 bench/parity/run.py --state $OUT/state.json --corpus $OUT/corpus.json --out $OUT/run +python3 bench/parity/publish.py $OUT/run --ref +python3 conformance/scripts/parity_gaps.py --check # every cluster registered? +python3 bench/visual/stack.py down --out $OUT +``` + +`docker-compose.parity.yml` raises Loki's gRPC message limits from the e2e +config's 8 MiB to 100 MiB: a per-stream metric answer over an hour of +generator data is 13-26 MiB between Loki's querier and frontend, and with +8 MiB Loki drops the answer and the client waits for `query_timeout`. Ports: +`--port-offset 1500` puts Loki on 34601, VictoriaLogs on 34928, the proxies +on 34700/34702 and Grafana on 34502. + +## Proof that both sides are healthy + +`run.py` refuses to compare (exit 2) unless Loki, VictoriaLogs and the proxy +are ready, Loki and VictoriaLogs count the same lines in every 10-minute slice +of the window, Loki's range-metric path is out of its fresh-stack blank window +(one series per service it lists), and the Loki and VictoriaLogs restart +counts can be read. It fails (exit 3) when a restart count changed during the +run, or when the proxy log shows VictoriaLogs failing during it: 5xx answers, +or a fallback or partial answer after one. A 4xx from VictoriaLogs is a query +the proxy translated wrongly, so it is counted in `health.json` but does not +invalidate the comparison: it is the difference being measured. + +A request whose Loki answer carries warnings, fails with 5xx or times out, or +whose proxy answer is marked partial is `blocked`, never a difference; two +empty answers are `vacuous` and prove nothing. After the corpus, up to three +requests of every difference signature are sent again (`--no-confirm` skips +it, `--confirm-only` reconfirms an existing run): a signature none of whose +samples differs again is `blocked` as not reproducible, so a Loki answer that +moves between identical requests is never registered as a gap. An index/stats +difference counts as Loki's chunk accounting only when Loki's own +`count_over_time` (entries) or `/series` (streams) over the window equals the +proxy's number. + +## Recorded deviations and offline re-diffs + +A difference the diff marks as a recorded deviation carries the id of the +registry case that records it (`differ.DOCUMENTED`): index/stats bytes and +chunks, volume byte values of matched series, detected_labels cardinality, and +detected_fields `service` / `service.name` instead of Loki's `_extracted` +(only those two). `parity_gaps.py --check` fails when the id is not a registry +case whose status is documented, owner-kept, upstream or fixed. + +`--keep-bodies` stores both answers of every request in +`RUN_DIR/bodies.jsonl.gz`; `--rediff` recomputes `results.jsonl` from them +under the current diff rules without sending a request, so a change to +`differ.py` can be applied to a recorded run. + +A fix PR retires a gap without a full rerun: set the case's `gap.status` to +`fixed` with `fixed_by:` (the PR number or commit); its clusters stay claimed +and the report lists it apart until the next recorded run no longer shows them. + +## Tests + +```bash +python3 -m unittest discover -s bench/parity/tests +``` diff --git a/bench/parity/cluster.py b/bench/parity/cluster.py new file mode 100644 index 000000000..e071e07fb --- /dev/null +++ b/bench/parity/cluster.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 +"""Cluster per-request differences into gap signatures and rank them. + + cluster.py RUN_DIR [--top 40] reads RUN_DIR/results.jsonl, writes clusters.json and report.md + +Each differing request belongs to exactly one cluster. Its signature is +(endpoint class, query shape, kind, detail): the query shape is the outer +operation plus the parser, stages and filter kinds the query uses (line +filter, label filter, pattern filter, keep/drop, unwrap, ...), and kind/detail +come from the request's most fundamental differing facet (status before +series before labels before values), with error texts reduced to a template +and label names to their class. Two requests share a cluster only when they +differ from Loki the same way on the same kind of query. + +Rank: user impact first (explore- and drilldown-visible before the Grafana +datasource path, API-only and edge cases), then how many distinct corpus +queries hit the signature. A facet the diff marks as a recorded deviation +carries its registry case id in the detail; such clusters are listed apart and +not ranked, and conformance/scripts/parity_gaps.py fails when the id is not a +registry case whose status says the difference is recorded. +""" +import argparse +import hashlib +import json +import os +import re +import sys + +IMPACT_ORDER = ("explore-visible", "drilldown-visible", "grafana-datasource", "api-only", "edge") +# Which surface shows an endpoint's answer (the registry's consumer lists, narrowed to the first one that renders it). +ENDPOINT_IMPACT = { + "query_range": "explore-visible", "query": "grafana-datasource", "labels": "explore-visible", + "label_values": "explore-visible", "series": "grafana-datasource", "tail": "explore-visible", + "detected_fields": "drilldown-visible", "detected_field_values": "drilldown-visible", + "detected_labels": "drilldown-visible", "patterns": "drilldown-visible", "volume": "drilldown-visible", + "volume_range": "drilldown-visible", "index_stats": "drilldown-visible", "format_query": "api-only", +} +STAGES = ("json", "logfmt", "pattern", "regexp", "unpack", "line_format", "label_format", "keep", "drop", + "unwrap", "decolorize", "detected_level") + + +def shape(query): + """A coarse query shape: parser and stages, label filter, aggregation.""" + if not query: + return "-" + parts = [s for s in STAGES if re.search(rf"\|\s*{s}\b", query)] + if re.search(r"\|\s*[A-Za-z_][\w.]*\s*(=~|!~|!=|>=|<=|==|=|>|<)", query): + parts.append("label-filter") + if re.search(r"\|[=~]|[}\"`]\s*![=~]\s*[\"`]", query): + parts.append("line-filter") + if re.search(r"\|>|!>", query): + parts.append("pattern-filter") + agg = re.match(r"\s*([a-z_]+)", query) + head = agg.group(1) if agg and not query.lstrip().startswith("{") else "logs" + return head + ("[" + ",".join(parts) + "]" if parts else "") + + +def endpoint_class(row): + if row["endpoint"] == "ds_query": + return "grafana:ds_query" + if row["endpoint"] == "resource": + return "grafana:" + row.get("resource_endpoint", "resource") + return row["endpoint"] + + +def impact_of(row): + if row["endpoint"] in ("ds_query", "resource"): + page = row.get("origin", "") + return "drilldown-visible" if page.startswith("dd-") else "explore-visible" + base = ENDPOINT_IMPACT.get(row["endpoint"], "api-only") + if row["endpoint"] in ("query_range", "query") and row.get("encoding") == "plain" and base != "api-only": + return "grafana-datasource" if row["endpoint"] == "query" else base + return base + + +def signature_id(signature): + return hashlib.sha1(json.dumps(signature).encode()).hexdigest()[:8] + + +# The facet that names a request's difference, most fundamental first: a request +# whose status differs also differs in everything after it. +PRIORITY = ("status", "error-text", "result-type", "shape", "order", "entries", "series", "values-set", "names", "field", + "labels", "category", "line", "values", "points", "stats", "cardinality", "patterns", "format", + "field-type", "field-parsers", "field-cardinality") + + +def query_shape(row): + """The shape of the request's LogQL (a Grafana resource call has none).""" + if row.get("resource"): + return "-" + return shape((row.get("params") or {}).get("query")) + + +def error_template(text): + """An error message with its query-specific parts (quoted text, numbers) masked.""" + text = re.sub(r"(\"[^\"]*\"|'[^']*'|`[^`]*`)", "Q", str(text).lower()) + text = re.sub(r"\d+(\.\d+)?", "N", text) + return " ".join(text.split())[:80] + + +def primary(facets): + """(facet, others): the first undocumented facet by PRIORITY, or the first documented one.""" + ranked = sorted(facets, key=lambda f: (bool(f.get("documented")), + PRIORITY.index(f["kind"]) if f["kind"] in PRIORITY else 99)) + return ranked[0], ranked[1:] + + +def detail_of(item): + detail = item["detail"] + if item["kind"] in ("status", "error-text"): + proxy_text = item["example"].split(" | proxy ", 1)[-1] + detail = f"{detail}: {error_template(proxy_text)}" + if item.get("documented"): + detail = f"{detail} [{item['documented']}]" + return detail + + +def signature_of(row): + """A differing request's signature: endpoint class, query shape, primary facet kind and detail.""" + item, _ = primary(row["facets"]) + return [endpoint_class(row), query_shape(row), item["kind"], detail_of(item)] + + +def cluster(rows): + """One cluster per differing request: see signature_of.""" + clusters = {} + for row in rows: + if row.get("verdict") != "diff" or not row["facets"]: + continue + item, others = primary(row["facets"]) + query = (row.get("params") or {}).get("query") or row.get("resource") or row.get("origin") or row["id"] + signature = signature_of(row) + key = signature_id(signature) + c = clusters.setdefault(key, { + "id": key, "signature": signature, "documented": item.get("documented"), + "requests": 0, "queries": set(), "sources": {}, "encodings": {}, "shapes": {}, + "impacts": {}, "also": {}, "examples": []}) + c["requests"] += 1 + c["queries"].add(query) + for field, value in (("sources", row["source"]), ("encodings", row.get("encoding", "-")), + ("shapes", query_shape(row)), + ("impacts", impact_of(row))): + c[field][value] = c[field].get(value, 0) + 1 + for other in others: + name = f"{other['kind']}: {other['detail']}" + c["also"][name] = c["also"].get(name, 0) + 1 + if len(c["examples"]) < 3 and not any(e["query"] == query for e in c["examples"]): + c["examples"].append({"query": query, "endpoint": row["endpoint"], "encoding": row.get("encoding"), + "origin": row.get("origin", ""), "excerpt": item["example"]}) + out = [] + for c in clusters.values(): + c["queries"] = len(c["queries"]) + c["impact"] = min(c["impacts"], key=IMPACT_ORDER.index) + out.append(c) + out.sort(key=lambda c: (bool(c["documented"]), IMPACT_ORDER.index(c["impact"]), -c["queries"], -c["requests"])) + for rank, c in enumerate([c for c in out if not c["documented"]], 1): + c["rank"] = rank + return out + + +def summary(rows): + total = {} + for row in rows: + s = total.setdefault(row["source"], {"requests": 0, "same": 0, "diff": 0, "vacuous": 0, "blocked": 0}) + s["requests"] += 1 + s[row.get("verdict", "blocked")] += 1 + return total + + +def markdown(clusters, stats, health, top=40): + lines = ["# Proxy vs Loki differential run", ""] + if health: + lines += ["## Both sides healthy", "", "```", json.dumps(health, indent=1, sort_keys=True)[:3000], "```", ""] + lines += ["## Requests", "", "| source | requests | same | differ | both empty | blocked |", "|---|---:|---:|---:|---:|---:|"] + for source, s in sorted(stats.items()): + lines.append(f"| {source} | {s['requests']} | {s['same']} | {s['diff']} | {s['vacuous']} | {s['blocked']} |") + gaps = [c for c in clusters if not c["documented"]] + lines += ["", f"## Gap signatures ({len(gaps)})", "", + "| # | id | impact | endpoint | head | kind | detail | queries | requests | example |", + "|---:|---|---|---|---|---|---|---:|---:|---|"] + for c in gaps[:top]: + example = c["examples"][0] + lines.append(f"| {c['rank']} | `{c['id']}` | {c['impact']} | " + " | ".join(cell(s, 90) for s in c["signature"]) + + f" | {c['queries']} | {c['requests']} | `{cell(example['query'])}` — {cell(example['excerpt'])} |") + documented = [c for c in clusters if c["documented"]] + if documented: + lines += ["", "## Documented deviations (not gaps)", "", "| id | rule | endpoint | detail | requests |", + "|---|---|---|---|---:|"] + for c in documented: + lines.append(f"| `{c['id']}` | {c['documented']} | {c['signature'][0]} | {cell(c['signature'][3])} | {c['requests']} |") + return "\n".join(lines) + "\n" + + +def cell(text, limit=160): + text = str(text).replace("|", "\\|").replace("\n", " ").replace("`", "'") + return text if len(text) <= limit else text[: limit - 3] + "..." + + +def load_rows(path): + with open(path) as handle: + return [json.loads(line) for line in handle if line.strip()] + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("run_dir") + ap.add_argument("--top", type=int, default=40) + a = ap.parse_args() + rows = load_rows(os.path.join(a.run_dir, "results.jsonl")) + health_path = os.path.join(a.run_dir, "health.json") + health = json.load(open(health_path)) if os.path.exists(health_path) else {} + clusters = cluster(rows) + stats = summary(rows) + with open(os.path.join(a.run_dir, "clusters.json"), "w") as handle: + json.dump({"summary": stats, "clusters": clusters}, handle, indent=1, sort_keys=True) + with open(os.path.join(a.run_dir, "report.md"), "w") as handle: + handle.write(markdown(clusters, stats, health, a.top)) + print(f"{len(rows)} requests, {sum(1 for c in clusters if not c['documented'])} gap signatures, " + f"{sum(1 for c in clusters if c['documented'])} documented; {a.run_dir}/report.md") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bench/parity/corpus.py b/bench/parity/corpus.py new file mode 100644 index 000000000..8efa2e1a1 --- /dev/null +++ b/bench/parity/corpus.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python3 +"""Build the differential query corpus: every request the runner sends to Loki and the proxy. + + corpus.py --out corpus.json [--loki-src DIR] [--captures DIR ...] [--seeded URL --end UNIX] + +Sources (each entry records its `source`): + loki-tests LogQL strings from Loki's own test suites (--loki-src: a checkout of + grafana/loki at the reference version, pkg/logql at least). Stream + selectors are rewritten onto the seeded data (a JSON, logfmt, plain + or mixed stream, picked by the parser the query uses); sharding and + template fixtures are dropped, and ranges longer than the seeded + hour are clamped to [1h]. + repo the repository's own parity cases: LogQL in test/e2e-compat/*_test.go, + test/e2e-compat/query-semantics-matrix.json, bench/ab/shapes.json and + every conformance/registry case's request. Selectors naming data the + seeded stack does not hold are rewritten the same way. + seeded generated from what Loki itself reports for the seeded window + (--seeded LOKI_URL): every label and its values, every service's + detected fields, and per field the stages Explore and Logs Drilldown + build (filters, keep/drop, extraction lists, line_format, grouping, + unwrap), plus the metadata endpoints (labels, values, series, + index/stats, volume, volume_range, detected_*, patterns, + format_query, tail). + grafana every request Grafana Explore and Logs Drilldown sent on the + bench/visual pages (--captures: capture output directories); they are + replayed through Grafana's own API against the Loki and proxy + datasources, so the corpus covers the exact datasource path. + +Times are placeholders ($start, $end, $step, $start_ns, $end_ns) that run.py fills +from the seeded window. +""" +import argparse +import glob +import hashlib +import json +import os +import re +import sys +import urllib.parse +import urllib.request + +HERE = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(HERE, "..", "..")) + +# The stream the rewritten query lands on, by the parser it uses (test/e2e-compat/log-generator.py). +SELECTORS = { + "json": '{service_name="api-gateway"}', + "logfmt": '{service_name="payment-service"}', + "pattern": '{service_name="nginx-ingress"}', + "regexp": '{service_name="nginx-ingress"}', + "unpack": '{service_name="api-gateway"}', + "otel": '{service_name="otel-collector"}', + "mixed": '{namespace="prod"}', +} +# Label values the seeded data holds; a selector using anything else is rewritten. +SEEDED_LABELS = {"app", "service_name", "namespace", "cluster", "env", "level", "pod", "container", "version", + "component", "role", "db_name", "db", "gpu", "instrumentation"} +SEEDED_VALUES = { + "namespace": {"prod", "data", "batch", "ml", "ingress-nginx", "monitoring"}, + "env": {"production"}, + "cluster": {"us-east-1", "us-west-2"}, +} +MATCHER = re.compile(r'\s*([A-Za-z_][A-Za-z0-9_.]*)\s*(=~|!~|!=|=)\s*("(?:[^"\\]|\\.)*"|`[^`]*`)\s*') +LOGQL_START = re.compile( + r'^\s*(\{|(sum|avg|min|max|count|topk|bottomk|stddev|stdvar|sort|sort_desc|rate|count_over_time|bytes_rate|' + r'bytes_over_time|absent_over_time|label_replace|quantile_over_time|avg_over_time|sum_over_time|max_over_time|' + r'min_over_time|first_over_time|last_over_time|stddev_over_time|stdvar_over_time|rate_counter|approx_topk)' + r'\s*(by|without)?\s*[\(\{])') +STRING = re.compile(r'`([^`]*)`|"((?:[^"\\\n]|\\.)*)"') +# Loki test fixtures that are not queries a client sends: sharding plans, templates, JSON lines. +NOT_A_QUERY = ("downstream<", "++", "shard=", "", "{{", "__count_min_sketch__", "__quantile_sketch", + "variants(", "approx_topk") + + +def scan_selectors(query): + """(start, end) spans of every stream selector in a LogQL string, skipping string literals.""" + spans, i, n = [], 0, len(query) + while i < n: + c = query[i] + if c in "\"`": + close = query.find(c, i + 1) + while c == '"' and close != -1 and query[close - 1] == "\\" and query[close - 2] != "\\": + close = query.find(c, close + 1) + i = n if close == -1 else close + 1 + continue + if c == "{": + j, ok = i + 1, False + while True: + m = MATCHER.match(query, j) + if not m: + break + ok, j = True, m.end() + if j < n and query[j] == ",": + j += 1 + continue + break + k = j + while k < n and query[k] == " ": + k += 1 + if ok and k < n and query[k] == "}": + spans.append((i, k + 1)) + i = k + 1 + continue + i += 1 + return spans + + +def selector_matchers(text): + return [(m.group(1), m.group(2), m.group(3)[1:-1]) for m in MATCHER.finditer(text.strip("{} "))] + + +def selector_seeded(text): + """True when every matcher names data the seeded stack holds.""" + matchers = selector_matchers(text) + if not matchers: + return False + for name, op, value in matchers: + if name not in SEEDED_LABELS: + return False + if op == "=" and name in SEEDED_VALUES and value not in SEEDED_VALUES[name]: + return False + if op == "=" and name in ("app", "service_name") and not re.fullmatch(r"[a-z0-9-]+", value): + return False + if op == "=" and name in ("app", "service_name") and re.search(r"\d{6,}|compat|test|<", value): + return False + return True + + +def parser_of(query): + for name in ("json", "logfmt", "pattern", "regexp", "unpack"): + if re.search(rf"\|\s*{name}\b", query): + return name + return "mixed" + + +def rewrite(query, force=False): + """The query with every stream selector moved onto the seeded data (unless already seeded).""" + spans = scan_selectors(query) + if not spans: + return None + target = SELECTORS[parser_of(query)] + out, last = [], 0 + for start, end in spans: + out.append(query[last:start]) + sel = query[start:end] + out.append(sel if (not force and selector_seeded(sel)) else target) + last = end + out.append(query[last:]) + return "".join(out) + + +def is_metric(query): + return not query.lstrip("( ").startswith("{") + + +def extract_strings(paths): + found = set() + for path in paths: + with open(path, errors="ignore") as handle: + text = handle.read() + for m in STRING.finditer(text): + raw = m.group(1) if m.group(1) is not None else m.group(2) + if m.group(2) is not None: + try: + raw = json.loads('"' + m.group(2) + '"') + except ValueError: + continue + if not raw or len(raw) > 600 or "\n\n" in raw or any(x in raw for x in NOT_A_QUERY): + continue + if "%" in raw and re.search(r"%[sdvq]", raw): + continue # a Go format string, filled at test time + if LOGQL_START.match(raw) and scan_selectors(raw): + found.add(" ".join(raw.split())) + return found + + +def entry(source, endpoint, params, origin="", headers=None, transport="api", **extra): + body = json.dumps([endpoint, params, headers or {}, transport, extra], sort_keys=True) + out = {"id": hashlib.sha1(body.encode()).hexdigest()[:12], "source": source, "endpoint": endpoint, + "params": params, "origin": origin, "transport": transport} + if headers: + out["headers"] = headers + out.update(extra) + return out + + +def query_entries(source, query, origin=""): + """The requests one LogQL query becomes: a log range query, or a metric range and instant query.""" + if is_metric(query): + return [entry(source, "query_range", {"query": query, "start": "$start", "end": "$end", "step": "$step"}, origin), + entry(source, "query", {"query": query, "time": "$end"}, origin)] + return [entry(source, "query_range", {"query": query, "start": "$start", "end": "$end", "limit": "100", + "direction": "backward"}, origin)] + + +RANGE = re.compile(r"\[(\d+)([smhdwy])\]") +UNIT_S = {"s": 1, "m": 60, "h": 3600, "d": 86400, "w": 604800, "y": 31536000} + + +def clamp_ranges(query, limit_s=3600): + """Range selectors longer than the seeded window read every line once per step on Loki and prove nothing more.""" + return RANGE.sub(lambda m: m.group(0) if int(m.group(1)) * UNIT_S[m.group(2)] <= limit_s else "[1h]", query) + + +def loki_tests(src): + paths = glob.glob(os.path.join(src, "pkg", "logql", "**", "*_test.go"), recursive=True) + out, seen = [], set() + for raw in sorted(extract_strings(paths)): + query = rewrite(raw, force=True) + query = clamp_ranges(query) if query else query + if not query or query in seen: + continue + seen.add(query) + out += query_entries("loki-tests", query, origin=raw) + return out + + +# Grafana template variables in the repository's shapes, replaced by what Grafana sends for an hour at 60s steps. +GRAFANA_VARS = (("$__auto", "1m"), ("$__interval", "1m"), ("$__range", "1h")) + + +def repo_queries(): + out, seen = [], set() + + def add(source, raw, origin): + for var, value in GRAFANA_VARS: + raw = raw.replace(var, value) + query = rewrite(raw) + if query and query not in seen: + seen.add(query) + out.extend(query_entries(source, query, origin)) + + for raw in sorted(extract_strings(glob.glob(os.path.join(ROOT, "test", "e2e-compat", "*_test.go")))): + add("repo", raw, "test/e2e-compat") + matrix = os.path.join(ROOT, "test", "e2e-compat", "query-semantics-matrix.json") + for case in json.load(open(matrix))["cases"]: + add("repo", case["query"], f"query-semantics-matrix:{case['id']}") + shapes = json.load(open(os.path.join(ROOT, "bench", "ab", "shapes.json"))) + for name, group in shapes["sets"].items(): + for shape in group["shapes"]: + if shape.get("query"): + add("repo", shape["query"], f"shapes:{name}:{shape['name']}") + for path in sorted(glob.glob(os.path.join(ROOT, "conformance", "registry", "cases", "**", "*.yaml"), recursive=True)): + text = open(path).read() + found = re.search(r"^\s+query:\s*'(.*)'\s*$", text, re.M) or re.search(r'^\s+query:\s*"(.*)"\s*$', text, re.M) + if found: + add("repo", found.group(1).replace("''", "'"), "registry:" + os.path.relpath(path, ROOT)) + return out + + +# ---------------------------------------------------------------- seeded + +def loki_get(base, path, params, timeout=60): + url = f"{base}{path}?{urllib.parse.urlencode(params)}" + req = urllib.request.Request(url, headers={"X-Scope-OrgID": "0", "Cache-Control": "no-cache"}) + with urllib.request.urlopen(req, timeout=timeout) as response: + return json.loads(response.read()) + + +def quote(value): + return json.dumps(value) + + +def field_queries(selector, field, parsers, numeric): + """The stages Explore and Logs Drilldown build on one detected field.""" + parser = "logfmt" if parsers == ["logfmt"] else "json" if parsers else "" + stage = f" | {parser}" if parser else "" + q = [f"{selector}{stage} | {field}!=\"\"", + f"{selector}{stage} | {field}=~\".+\"", + f"{selector}{stage} | keep {field}", + f"{selector}{stage} | drop {field}", + f"{selector}{stage} | line_format \"{{{{.{field}}}}}\"", + f"sum by ({field}) (count_over_time({selector}{stage} [5m]))", + f"sum by ({field}) (count_over_time({selector}{stage} | {field}!=\"\" [5m]))", + f"sum(count_over_time({selector}{stage} | {field}!=\"\" [5m]))"] + if parser == "json": + q.append(f"{selector} | json {field}=\"{field}\"") + if parser == "logfmt": + q.append(f"{selector} | logfmt {field}") + if numeric: + q += [f"avg_over_time({selector}{stage} | unwrap {field} | __error__=\"\" [5m])", + f"sum by (level) (max_over_time({selector}{stage} | unwrap {field} | __error__=\"\" [5m]))", + f"quantile_over_time(0.9, {selector}{stage} | unwrap {field} | __error__=\"\" [5m]) by (service_name)"] + return q + + +def seeded(loki, end, window=3600, max_fields=14): + start = end - window + ns = {"start": str(start * 10 ** 9), "end": str(end * 10 ** 9)} + out = [] + + def add(endpoint, params, origin, **extra): + out.append(entry("seeded", endpoint, params, origin, **extra)) + + labels = loki_get(loki, "/loki/api/v1/labels", ns).get("data") or [] + add("labels", {"start": "$start_ns", "end": "$end_ns"}, "all labels") + add("labels", {"start": "$start_ns", "end": "$end_ns", "query": '{namespace="prod"}'}, "labels with query") + services = [] + for name in labels: + add("label_values", {"name": name, "start": "$start_ns", "end": "$end_ns"}, f"values of {name}") + add("label_values", {"name": name, "start": "$start_ns", "end": "$end_ns", "query": '{env="production"}'}, + f"values of {name} with query") + if name == "service_name": + services = loki_get(loki, f"/loki/api/v1/label/{name}/values", ns).get("data") or [] + for match in ('{namespace="prod"}', '{service_name="api-gateway"}', '{env="production", level="error"}'): + add("series", {"match[]": match, "start": "$start_ns", "end": "$end_ns"}, "series") + add("detected_labels", {"query": '{env="production"}', "start": "$start_ns", "end": "$end_ns"}, "detected_labels") + add("index_stats", {"query": '{env="production"}', "start": "$start_ns", "end": "$end_ns"}, "index/stats") + for target in ("service_name", "level", "namespace", "detected_level", "pod", "cluster"): + add("volume", {"query": '{env="production"}', "start": "$start_ns", "end": "$end_ns", "limit": "100", + "targetLabels": target, "aggregateBy": "series"}, f"volume by {target}") + add("volume_range", {"query": '{env="production"}', "start": "$start_ns", "end": "$end_ns", "step": "$step", + "limit": "100", "targetLabels": target, "aggregateBy": "series"}, f"volume_range by {target}") + for query in ('{env="production"} |= "error"', '{namespace="prod"} | json | status>=500'): + add("format_query", {"query": query}, "format_query") + for service in services: + selector = "{" + f"service_name={quote(service)}" + "}" + add("index_stats", {"query": selector, "start": "$start_ns", "end": "$end_ns"}, f"index/stats {service}") + add("volume", {"query": selector, "start": "$start_ns", "end": "$end_ns", "limit": "100", + "targetLabels": "detected_level", "aggregateBy": "series"}, f"volume detected_level {service}") + add("detected_labels", {"query": selector, "start": "$start_ns", "end": "$end_ns"}, f"detected_labels {service}") + add("patterns", {"query": selector, "start": "$start_ns", "end": "$end_ns", "step": "$step"}, f"patterns {service}") + add("tail", {"query": selector, "start": "$start_ns", "limit": "100"}, f"tail history {service}") + for level in ("info", "warn", "error", "debug"): + out.extend(query_entries("seeded", f"{selector} | detected_level={quote(level)}", f"detected_level {service}")) + out.extend(query_entries("seeded", f"sum by (detected_level) (count_over_time({selector}[5m]))", + f"level volume {service}")) + out.extend(query_entries("seeded", f"{selector} | json | __error__!=\"\"", f"json errors {service}")) + out.extend(query_entries("seeded", f"{selector} | logfmt | __error__!=\"\"", f"logfmt errors {service}")) + out.extend(query_entries("seeded", f"{selector} | json | keep service_name, level", f"keep {service}")) + out.extend(query_entries("seeded", f"{selector} | json msg=\"msg\"", f"json msg {service}")) + add("detected_fields", {"query": selector, "start": "$start_ns", "end": "$end_ns"}, f"detected_fields {service}") + try: + fields = loki_get(loki, "/loki/api/v1/detected_fields", dict(ns, query=selector)).get("fields") or [] + except (OSError, ValueError) as error: + print(f"[corpus] detected_fields {service}: {error}", file=sys.stderr) + continue + for field in fields[:max_fields]: + name = field.get("label") + add("detected_field_values", {"name": name, "query": selector, "start": "$start_ns", "end": "$end_ns"}, + f"field values {service}.{name}") + numeric = field.get("type") in ("int", "float", "duration", "bytes") + for query in field_queries(selector, name, field.get("parsers") or [], numeric): + out.extend(query_entries("seeded", query, f"field {service}.{name}")) + return out + + +# ---------------------------------------------------------------- grafana captures + +def grafana_requests(capture_dirs, loki_uid="vp-loki"): + """Every request the Loki datasource answered in bench/visual captures, as Grafana API calls.""" + out, seen = [], set() + for directory in capture_dirs: + for path in sorted(glob.glob(os.path.join(directory, "**", "*.json"), recursive=True)): + try: + doc = json.load(open(path)) + except (OSError, ValueError): + continue + if not isinstance(doc, dict) or "records" not in doc: + continue + page = os.path.relpath(os.path.dirname(path), directory).replace(os.sep, " ") + for record in doc["records"]: + url = record.get("url", "") + if "/api/ds/query" in url: + request = record.get("request") or {} + queries = [q for q in request.get("queries") or [] + if (q.get("datasource") or {}).get("uid") == loki_uid] + if not queries: + continue + body = {"queries": queries, "from": request.get("from"), "to": request.get("to")} + key = json.dumps(body, sort_keys=True) + if key in seen: + continue + seen.add(key) + out.append(entry("grafana", "ds_query", {}, page, transport="grafana", body=body)) + elif f"/api/datasources/uid/{loki_uid}/resources/" in url: + resource = url.split("/resources/", 1)[1].split("&_=")[0] + if resource in seen: + continue + seen.add(resource) + out.append(entry("grafana", "resource", {}, page, transport="grafana", resource=resource)) + return out + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--out", required=True) + ap.add_argument("--loki-src", default="", help="checkout of grafana/loki at the reference version") + ap.add_argument("--captures", action="append", default=[], help="bench/visual capture output directory") + ap.add_argument("--seeded", default="", help="Loki URL of the seeded stack, to generate the seeded corpus") + ap.add_argument("--end", type=int, default=0, help="end of the seeded window (unix seconds)") + ap.add_argument("--no-repo", action="store_true") + a = ap.parse_args() + entries = [] + if a.loki_src: + entries += loki_tests(a.loki_src) + if not a.no_repo: + entries += repo_queries() + if a.seeded: + if not a.end: + ap.error("--seeded needs --end") + entries += seeded(a.seeded, a.end) + if a.captures: + entries += grafana_requests(a.captures) + unique = {} + for item in entries: + unique.setdefault(item["id"], item) + with open(a.out, "w") as handle: + json.dump({"entries": list(unique.values())}, handle, indent=1, sort_keys=True) + by_source = {} + for item in unique.values(): + by_source[item["source"]] = by_source.get(item["source"], 0) + 1 + print(f"corpus: {len(unique)} requests " + json.dumps(by_source, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bench/parity/differ.py b/bench/parity/differ.py new file mode 100644 index 000000000..ade99190f --- /dev/null +++ b/bench/parity/differ.py @@ -0,0 +1,527 @@ +"""Semantic diff of one Loki answer against the proxy's answer to the same request. + +Pure functions, no I/O, so the rules are unit-tested (tests/test_parity.py). + +`diff(endpoint, loki, proxy)` returns a list of facets, one per distinct way +the answers differ. A facet is a dict: + + kind what differs: status, error-text, result-type, shape, order, + entries, line, labels, category, series, values, points, names, + field, stats, ... + detail a generalised description (label names replaced by their class), + stable across queries that fail for the same reason, so facets + cluster into gap signatures + example a short concrete excerpt (Loki vs proxy) for the report + documented the conformance registry case id of a recorded deviation + (status documented, owner-kept or upstream), or absent. Only the + ids in DOCUMENTED are used, and each covers exactly the difference + its case describes; conformance/scripts/parity_gaps.py fails on an + id the registry does not hold. + +What is ignored, because it says how an answer was produced rather than what +it is: `stats` blocks, `warnings` (a request whose Loki answer carries +warnings is blocked by run.py before it reaches the diff), execution +metadata, the order of streams, series and names (Loki leaves it +unspecified; the order of entries inside a stream is compared against the +query's direction), and log entries at the boundary timestamp of a result +that hit the line limit (Loki breaks ties at the limit arbitrarily). Error +texts are compared after collapsing whitespace and a trailing period only. +""" +import json +import math +import re + +# Label names whose presence or absence is a known, named mechanism; every +# other name is generalised to its class so one root cause clusters once. +SPECIAL_LABELS = ("__error__", "__error_details__", "detected_level", "service_name", "level", + "__stream_shard__", "__line__", "__timestamp__") +REL_TOL = 1e-6 + +# Registry case ids of the deviations the diff marks as documented. Each id names a case under +# conformance/registry/cases/ whose gap status is documented, owner-kept or upstream. +DOC_INDEX_STATS_BYTES = "quality/index-stats-bytes-and-chunks" +DOC_INDEX_STATS_LOKI_CHUNKS = "quality/index-stats-loki-chunk-accounting" +DOC_VOLUME_BYTES = "profiles/volume-bytes-accounting" +DOC_DETECTED_LABELS_CARDINALITY = "drilldown/detected-labels-sampled-cardinality" +DOC_DETECTED_FIELDS_SERVICE = "profiles/detected-fields-no-extracted-suffix" +DOCUMENTED = (DOC_INDEX_STATS_BYTES, DOC_INDEX_STATS_LOKI_CHUNKS, DOC_VOLUME_BYTES, DOC_DETECTED_LABELS_CARDINALITY, + DOC_DETECTED_FIELDS_SERVICE) +# The owner's decision covers only the service name: detected_fields lists `service` / `service.name` where Loki +# lists their `_extracted` collision. Every other `_extracted` field is an ordinary difference. +SERVICE_EXTRACTED = ("service_extracted", "service_name_extracted") + + +def label_class(name, stream_labels=()): + """The class a label name falls in: itself when special, else its origin.""" + if name in SPECIAL_LABELS: + return name + if name.endswith("_extracted"): + return "*_extracted" + if name in stream_labels: + return "" + if "." in name: + return "" + return "" + + +def classes(names, stream_labels=()): + return sorted({label_class(n, stream_labels) for n in names}) + + +def facet(kind, detail, example, documented=None): + out = {"kind": kind, "detail": detail, "example": example} + assert documented is None or documented in DOCUMENTED, documented + if documented: + out["documented"] = documented + return out + + +def short(value, limit=240): + text = value if isinstance(value, str) else json.dumps(value, sort_keys=True, default=str) + return text if len(text) <= limit else text[: limit - 3] + "..." + + +def error_text(body): + if isinstance(body, dict): + return str(body.get("error") or body.get("message") or body.get("_text") or "") + return str(body or "") + + +def norm_error(text): + """An error message without the parts that legitimately vary: whitespace and quoting.""" + text = re.sub(r"\s+", " ", str(text)).strip() + return text.rstrip(".") + + +def size_class(loki_n, proxy_n): + if loki_n == proxy_n: + return "equal" + if proxy_n == 0: + return "proxy-empty" + if loki_n == 0: + return "loki-empty" + return "proxy-fewer" if proxy_n < loki_n else "proxy-more" + + +def same_number(a, b): + try: + x, y = float(a), float(b) + except (TypeError, ValueError): + return a == b + if math.isnan(x) and math.isnan(y): + return True + return math.isclose(x, y, rel_tol=REL_TOL, abs_tol=1e-9) + + +def same_sample(a, b): + """A scalar or string result ([ts, value]): timestamps and values compared as numbers (1 equals 1.0).""" + if not isinstance(a, list) or not isinstance(b, list) or len(a) != len(b): + return a == b + return all(same_number(x, y) for x, y in zip(a, b)) + + +# ---------------------------------------------------------------- log streams + +def entries(body): + """Every log entry: (ts, line, stream labels, {category: {k: v}}).""" + out = [] + for stream in ((body.get("data") or {}).get("result") or []): + labels = stream.get("stream") or {} + for value in stream.get("values") or []: + meta = value[2] if len(value) > 2 and isinstance(value[2], dict) else {} + out.append((str(value[0]), value[1], dict(labels), meta)) + return out + + +def drop_limit_boundary(rows, limit, direction): + """Loki fills `limit` lines and breaks ties at the last timestamp arbitrarily: drop that timestamp.""" + if not limit or len(rows) < limit or not rows: + return rows + stamps = [int(r[0]) for r in rows] + edge = min(stamps) if direction != "forward" else max(stamps) + return [r for r in rows if int(r[0]) != edge] + + +def order_violations(body, direction): + """Streams whose entries are not in the query's direction (backward: newest first).""" + bad = [] + for stream in ((body.get("data") or {}).get("result") or []): + stamps = [int(v[0]) for v in stream.get("values") or []] + ordered = sorted(stamps, reverse=direction != "forward") + if stamps != ordered: + bad.append(stream.get("stream") or {}) + return bad + + +def diff_streams(loki, proxy, stream_labels=(), limit=0, direction="backward", check_order=True): + out = [] + if check_order: + bad = order_violations(proxy, direction) + if bad and not order_violations(loki, direction): + out.append(facet("order", f"entries not in {direction} order within a stream", + f"{len(bad)} streams, e.g. {short(bad[0], 160)}")) + a = drop_limit_boundary(entries(loki), limit, direction) + b = drop_limit_boundary(entries(proxy), limit, direction) + if len(a) != len(b): + out.append(facet("entries", size_class(len(a), len(b)), f"loki {len(a)} entries, proxy {len(b)}")) + index = {} + for row in b: + index.setdefault((row[0], row[1]), []).append(row) + unmatched_a = [] + label_only_loki, label_only_proxy, label_value = {}, {}, {} + cat_only_loki, cat_only_proxy = {}, {} + for row in a: + bucket = index.get((row[0], row[1])) + if not bucket: + unmatched_a.append(row) + continue + # Pair with the proxy row whose labels agree best: a line repeated in two streams. + best = max(range(len(bucket)), key=lambda i: len(set(bucket[i][2].items()) & set(row[2].items()))) + other = bucket.pop(best) + la, lb = merged_labels(row), merged_labels(other) + for k in set(la) - set(lb): + label_only_loki.setdefault(k, (row, other)) + for k in set(lb) - set(la): + label_only_proxy.setdefault(k, (row, other)) + for k in set(la) & set(lb): + if la[k] != lb[k]: + label_value.setdefault(k, (row, other)) + if row[3] or other[3]: + for cat in ("structuredMetadata", "parsed"): + ka, kb = set((row[3].get(cat) or {})), set((other[3].get(cat) or {})) + for k in ka - kb: + cat_only_loki.setdefault((cat, k), (row, other)) + for k in kb - ka: + cat_only_proxy.setdefault((cat, k), (row, other)) + unmatched_b = [r for rows in index.values() for r in rows] + if unmatched_a and unmatched_b: + stamps_b = {r[0] for r in unmatched_b} + same_ts = [r for r in unmatched_a if r[0] in stamps_b] + if same_ts: + other = next(r for r in unmatched_b if r[0] == same_ts[0][0]) + out.append(facet("line", "line text differs at the same timestamp", + f"loki {short(same_ts[0][1], 120)} | proxy {short(other[1], 120)}")) + if unmatched_a and len(a) == len(b): + out.append(facet("entries", "different entries, same count", + f"loki-only {short(unmatched_a[0][1], 120)}")) + if label_only_loki: + names = sorted(label_only_loki) + out.append(facet("labels", "loki-only " + ",".join(classes(names, stream_labels)), + f"names {names[:6]}; e.g. loki {short(merged_labels(label_only_loki[names[0]][0]), 200)}")) + if label_only_proxy: + names = sorted(label_only_proxy) + out.append(facet("labels", "proxy-only " + ",".join(classes(names, stream_labels)), + f"names {names[:6]}; e.g. proxy {short(merged_labels(label_only_proxy[names[0]][1]), 200)}")) + if label_value: + names = sorted(label_value) + row, other = label_value[names[0]] + out.append(facet("labels", "value differs " + ",".join(classes(names, stream_labels)), + f"{names[0]}: loki {short(merged_labels(row).get(names[0]), 80)} | " + f"proxy {short(merged_labels(other).get(names[0]), 80)}")) + for side, found in (("loki-only", cat_only_loki), ("proxy-only", cat_only_proxy)): + by_cat = {} + for cat, k in found: + by_cat.setdefault(cat, []).append(k) + for cat, names in sorted(by_cat.items()): + out.append(facet("category", f"{side} {cat} " + ",".join(classes(names, stream_labels)), + f"{cat} names {sorted(names)[:6]}")) + return out + + +def merged_labels(row): + """Stream labels plus categorised metadata/parsed labels: what a client sees as the entry's labels.""" + out = dict(row[2]) + for cat in ("structuredMetadata", "parsed"): + out.update(row[3].get(cat) or {}) + return out + + +# ---------------------------------------------------------------- metric series + +def series_map(body): + data = body.get("data") or {} + out = {} + for item in data.get("result") or []: + key = json.dumps(item.get("metric") or {}, sort_keys=True) + points = item.get("values") or ([item["value"]] if item.get("value") else []) + out[key] = {str(float(p[0])): p[1] for p in points} + return out + + +def malformed(m): + """Samples whose value is not a number Loki could render (Loki writes FormatFloat; NaN/Inf are spelled out).""" + bad = [] + for key, points in m.items(): + for t, v in points.items(): + try: + float(v) + except (TypeError, ValueError): + bad.append((key, t, v)) + return bad + + +def raw_series_count(body): + return len((body.get("data") or {}).get("result") or []) + + +def diff_series(loki, proxy, stream_labels=(), documented_values=None): + out = [] + a, b = series_map(loki), series_map(proxy) + dup_a, dup_b = raw_series_count(loki) - len(a), raw_series_count(proxy) - len(b) + if dup_b > dup_a: + out.append(facet("shape", "proxy returns one label set as several series", + f"proxy {raw_series_count(proxy)} series, {len(b)} distinct label sets; " + f"loki {raw_series_count(loki)} / {len(a)}")) + bad = malformed(b) + if bad and not malformed(a): + key, t, v = bad[0] + out.append(facet("shape", "proxy sample value is not a number", + f"{len(bad)} samples, e.g. {short(key, 120)} @{t}: {json.dumps(v)}")) + keys_a = {k for s in a for k in json.loads(s)} + keys_b = {k for s in b for k in json.loads(s)} + if keys_a - keys_b: + names = sorted(keys_a - keys_b) + out.append(facet("labels", "loki-only " + ",".join(classes(names, stream_labels)), f"label names {names[:6]}")) + if keys_b - keys_a: + names = sorted(keys_b - keys_a) + out.append(facet("labels", "proxy-only " + ",".join(classes(names, stream_labels)), f"label names {names[:6]}")) + only_a, only_b = sorted(set(a) - set(b)), sorted(set(b) - set(a)) + if (only_a or only_b) and not (keys_a ^ keys_b): + out.append(facet("series", size_class(len(a), len(b)) if len(a) != len(b) else "different series, same count", + f"loki {len(a)} series, proxy {len(b)}; loki-only {short(only_a[:2], 160)} " + f"proxy-only {short(only_b[:2], 160)}")) + elif len(a) != len(b): + out.append(facet("series", size_class(len(a), len(b)), f"loki {len(a)} series, proxy {len(b)}")) + missing_points = value_diffs = 0 + example = None + for key in set(a) & set(b): + pa, pb = a[key], b[key] + if set(pa) != set(pb): + missing_points += 1 + example = example or f"{key}: loki {len(pa)} points, proxy {len(pb)}" + for t in set(pa) & set(pb): + if not same_number(pa[t], pb[t]): + value_diffs += 1 + example = example or f"{key} @{t}: loki {pa[t]} proxy {pb[t]}" + break + if missing_points: + # A missing or extra sample is never byte accounting: only values are covered by documented_values. + out.append(facet("points", "timestamps differ in matched series", short(example))) + if value_diffs: + ratio = totals_ratio(a, b) + out.append(facet("values", f"values differ ({ratio})", short(example), documented_values)) + return out + + +def totals_ratio(a, b): + def total(m): + s = 0.0 + for points in m.values(): + for v in points.values(): + try: + s += float(v) + except (TypeError, ValueError): + pass + return s + ta, tb = total(a), total(b) + if not ta: + return "loki total 0" + r = tb / ta + return "proxy higher" if r > 1 + REL_TOL else "proxy lower" if r < 1 - REL_TOL else "same total" + + +# ---------------------------------------------------------------- metadata endpoints + +def name_set_diff(kind, a, b, stream_labels=(), documented=None): + out = [] + a, b = set(a), set(b) + if a - b: + names = sorted(a - b) + out.append(facet(kind, "loki-only " + ",".join(classes(names, stream_labels)), f"{names[:8]}", documented)) + if b - a: + names = sorted(b - a) + out.append(facet(kind, "proxy-only " + ",".join(classes(names, stream_labels)), f"{names[:8]}", documented)) + return out + + +def data_list(body): + data = body.get("data") if isinstance(body, dict) else body + return data if isinstance(data, list) else [] + + +def diff_labels(loki, proxy, stream_labels=()): + return name_set_diff("names", data_list(loki), data_list(proxy), stream_labels) + + +def diff_values(loki, proxy): + a, b = set(map(str, data_list(loki))), set(map(str, data_list(proxy))) + out = [] + if a != b: + out.append(facet("values-set", size_class(len(a), len(b)) if len(a) != len(b) else "different values", + f"loki-only {sorted(a - b)[:5]} proxy-only {sorted(b - a)[:5]}")) + return out + + +def diff_series_endpoint(loki, proxy, stream_labels=()): + a = {json.dumps(x, sort_keys=True) for x in data_list(loki)} + b = {json.dumps(x, sort_keys=True) for x in data_list(proxy)} + out = [] + ka = {k for s in a for k in json.loads(s)} + kb = {k for s in b for k in json.loads(s)} + out += name_set_diff("labels", ka, kb, stream_labels) + if a != b and not (ka ^ kb): + out.append(facet("series", size_class(len(a), len(b)) if len(a) != len(b) else "different series, same count", + f"loki {len(a)}, proxy {len(b)}; loki-only {short(sorted(a - b)[:1], 160)} " + f"proxy-only {short(sorted(b - a)[:1], 160)}")) + return out + + +def diff_index_stats(loki, proxy): + out = [] + for key in ("streams", "entries"): + if loki.get(key) != proxy.get(key): + ratio = f" (loki/proxy {loki.get(key) / proxy.get(key):.2f})" if proxy.get(key) else "" + out.append(facet("stats", f"{key} differ", f"loki {loki.get(key)} proxy {proxy.get(key)}{ratio}")) + for key in ("bytes", "chunks"): + if loki.get(key) != proxy.get(key): + out.append(facet("stats", f"{key} differ", f"loki {loki.get(key)} proxy {proxy.get(key)}", + DOC_INDEX_STATS_BYTES)) + return out + + +def diff_detected_fields(loki, proxy, stream_labels=()): + def fields(body): + return {f.get("label"): f for f in (body.get("fields") or [])} + a, b = fields(loki), fields(proxy) + out = [] + only_a = [k for k in set(a) - set(b)] + service = [k for k in only_a if k in SERVICE_EXTRACTED] + plain = [k for k in only_a if k not in SERVICE_EXTRACTED] + if service: + out.append(facet("field", "loki-only service *_extracted", f"{sorted(service)}", DOC_DETECTED_FIELDS_SERVICE)) + out += name_set_diff("field", plain, [], stream_labels) + out += name_set_diff("field", [], set(b) - set(a), stream_labels) + for k in sorted(set(a) & set(b)): + x, y = a[k], b[k] + if x.get("type") != y.get("type"): + out.append(facet("field-type", f"type {x.get('type')} vs {y.get('type')}", f"{k}")) + if sorted(x.get("parsers") or []) != sorted(y.get("parsers") or []): + out.append(facet("field-parsers", f"parsers {sorted(x.get('parsers') or [])} vs " + f"{sorted(y.get('parsers') or [])}", f"{k}")) + ca, cb = x.get("cardinality") or 0, y.get("cardinality") or 0 + # Loki estimates cardinality with a HyperLogLog sketch: within 5% is the same answer. + if ca and abs(ca - cb) > max(1, 0.05 * ca): + out.append(facet("field-cardinality", size_class(ca, cb), f"{k}: loki {ca} proxy {cb}")) + return dedupe(out) + + +def diff_detected_labels(loki, proxy, stream_labels=()): + def labels(body): + return {x.get("label"): x.get("cardinality") for x in (body.get("detectedLabels") or [])} + a, b = labels(loki), labels(proxy) + out = name_set_diff("names", a, b, stream_labels) + differ = [k for k in set(a) & set(b) if a[k] != b[k]] + if differ: + k = sorted(differ)[0] + out.append(facet("cardinality", "label cardinality differs", f"{k}: loki {a[k]} proxy {b[k]}", + DOC_DETECTED_LABELS_CARDINALITY)) + return out + + +def diff_patterns(loki, proxy): + a = {p.get("pattern") for p in data_list(loki)} + b = {p.get("pattern") for p in data_list(proxy)} + if a == b: + return [] + return [facet("patterns", size_class(len(a), len(b)) if len(a) != len(b) else "different patterns", + f"loki {len(a)} proxy {len(b)}; loki-only {short(sorted(a - b)[:2], 160)}")] + + +def diff_format_query(loki, proxy): + a, b = (loki.get("data") if isinstance(loki, dict) else loki), (proxy.get("data") if isinstance(proxy, dict) else proxy) + return [] if a == b else [facet("format", "formatted text differs", f"loki {short(a, 120)} | proxy {short(b, 120)}")] + + +def dedupe(facets): + seen, out = set(), [] + for f in facets: + key = (f["kind"], f["detail"]) + if key not in seen: + seen.add(key) + out.append(f) + return out + + +def result_type(body): + return ((body.get("data") or {}).get("resultType")) if isinstance(body, dict) else None + + +def diff(endpoint, loki_status, loki, proxy_status, proxy, stream_labels=(), limit=0, direction="backward"): + """Facets of the difference between two answers to one request (an empty list: equivalent).""" + if loki_status != proxy_status: + return [facet("status", f"loki {loki_status} proxy {proxy_status}", + f"loki {short(error_text(loki) or '-', 160)} | proxy {short(error_text(proxy) or '-', 160)}")] + if loki_status != 200: + if norm_error(error_text(loki)) != norm_error(error_text(proxy)): + return [facet("error-text", f"{loki_status} with different text", + f"loki {short(error_text(loki), 160)} | proxy {short(error_text(proxy), 160)}")] + return [] + if not isinstance(loki, dict) or not isinstance(proxy, dict): + return [facet("shape", "answer not JSON on one side", f"loki {short(loki, 80)} | proxy {short(proxy, 80)}")] + out = [] + if endpoint in ("query_range", "query", "tail"): + ta, tb = result_type(loki), result_type(proxy) + if endpoint != "tail" and ta != tb: + return [facet("result-type", f"{ta} vs {tb}", "")] + if endpoint == "tail" or ta == "streams": + # A tail merges frames as they arrive, so only a query answer has an entry order to compare. + out += diff_streams(loki, proxy, stream_labels, limit, direction, check_order=endpoint != "tail") + elif ta in ("matrix", "vector"): + out += diff_series(loki, proxy, stream_labels) + else: + a, b = (loki.get("data") or {}).get("result"), (proxy.get("data") or {}).get("result") + if not same_sample(a, b): + out.append(facet("values", f"{ta} differs", f"loki {short(a, 80)} proxy {short(b, 80)}")) + elif endpoint in ("volume", "volume_range"): + out += diff_series(loki, proxy, stream_labels, documented_values=DOC_VOLUME_BYTES) + elif endpoint == "labels": + out += diff_labels(loki, proxy, stream_labels) + elif endpoint in ("label_values", "detected_field_values"): + if endpoint == "detected_field_values": + loki, proxy = {"data": loki.get("values") or []}, {"data": proxy.get("values") or []} + out += diff_values(loki, proxy) + elif endpoint == "series": + out += diff_series_endpoint(loki, proxy, stream_labels) + elif endpoint == "index_stats": + out += diff_index_stats(loki, proxy) + elif endpoint == "detected_fields": + out += diff_detected_fields(loki, proxy, stream_labels) + elif endpoint == "detected_labels": + out += diff_detected_labels(loki, proxy, stream_labels) + elif endpoint == "patterns": + out += diff_patterns(loki, proxy) + elif endpoint == "format_query": + out += diff_format_query(loki, proxy) + return out + + +def vacuous(endpoint, loki_status, loki, proxy_status, proxy): + """Both sides answered 200 with nothing: proves nothing, so it is not counted as parity.""" + if loki_status != 200 or proxy_status != 200: + return False + + def empty(body): + if not isinstance(body, dict): + return not body + data = body.get("data") + if isinstance(data, dict): + return not data.get("result") + if isinstance(data, list): + return not data + for key in ("fields", "detectedLabels", "values"): + if key in body: + return not body[key] + if endpoint == "index_stats": + return not body.get("entries") + return False + return empty(loki) and empty(proxy) diff --git a/bench/parity/docker-compose.parity.yml b/bench/parity/docker-compose.parity.yml new file mode 100644 index 000000000..0050caab4 --- /dev/null +++ b/bench/parity/docker-compose.parity.yml @@ -0,0 +1,21 @@ +# Differential-run overlay (bench/parity), applied after +# bench/visual/docker-compose.visual.yml: the same Loki, with gRPC message +# limits large enough for a per-stream metric answer over the seeded window. +# +# The e2e config caps Loki's gRPC messages at 8 MiB. A metric query that +# keeps one series per stream (`rate({env="production"}[5m])`, `sum without +# (level) (...)`) over an hour of generator data answers 13-26 MiB between +# Loki's querier and its frontend; the frontend drops the answer ("received +# message larger than max") and the client waits for query_timeout (5m). That +# is a property of the test configuration, not of Loki, so it would hide every +# such query from the comparison. 100 MiB is the value Loki's own production +# examples use. `command` replaces the visual overlay's list, so its flags are +# repeated here. +services: + loki: + command: + - -config.file=/etc/loki/local-config.yaml + - -querier.query-ingesters-within=3h + - -ingester.chunks-retain-period=3h + - -server.grpc-max-recv-msg-size-bytes=104857600 + - -server.grpc-max-send-msg-size-bytes=104857600 diff --git a/bench/parity/publish.py b/bench/parity/publish.py new file mode 100644 index 000000000..0a4f31803 --- /dev/null +++ b/bench/parity/publish.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Record a differential run in the conformance registry. + + publish.py RUN_DIR --ref + +Writes conformance/registry/generated/parity-discovery.json: the run's request +counts per corpus source, its health proof, and every cluster (signature, +impact, queries, requests, shapes, up to two short examples). The registry's +report generator (conformance/scripts/parity_gaps.py) ranks the registered +differences with these counts and fails when a cluster is accounted for by no +registry case. +""" +import argparse +import glob +import json +import os +import sys + +OUT = "conformance/registry/generated/parity-discovery.json" + + +def short(text, limit=300): + text = str(text) + return text if len(text) <= limit else text[: limit - 3] + "..." + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("run_dir") + ap.add_argument("--ref", required=True) + ap.add_argument("--out", default=OUT) + a = ap.parse_args() + data = json.load(open(os.path.join(a.run_dir, "clusters.json"))) + health = json.load(open(os.path.join(a.run_dir, "health.json"))) + clusters = [] + for c in data["clusters"]: + clusters.append({ + "id": c["id"], "signature": c["signature"], "documented": c.get("documented"), "impact": c["impact"], + "queries": c["queries"], "requests": c["requests"], "sources": c["sources"], + "shapes": dict(sorted(c["shapes"].items(), key=lambda kv: -kv[1])[:5]), + "examples": [{"query": short(e["query"]), "endpoint": e["endpoint"], "encoding": e.get("encoding"), + "excerpt": short(e["excerpt"])} for e in c["examples"][:2]], + }) + before, after = health.get("before", {}), health.get("after", {}) + proof = {k: before.get(k) for k in ("lines_loki", "lines_vl", "slices", "slices_differing", "services_listed", + "services_in_loki_metric", "vl_restarts", "loki_restarts", "ok")} + proof["loki_restarts_after"] = after.get("loki_restarts") + proof["not_reproducible"] = health.get("not_reproducible") + proof["vl_restarts_after"] = after.get("vl_restarts") + proof["ok_after"] = after.get("ok") + # Runs merged into this one (an earlier pass, a rerun of one source) keep their own proof. + passes = {} + for path in sorted(glob.glob(os.path.join(a.run_dir, "health-*.json"))): + h = json.load(open(path)) + name = os.path.basename(path)[len("health-"):-len(".json")] + passes[name] = {"checked": [h.get("before", {}).get("checked"), h.get("after", {}).get("checked")], + "ok": [h.get("before", {}).get("ok"), h.get("after", {}).get("ok")], + "lines": h.get("before", {}).get("lines_loki"), + "restarts_unchanged": bool(h.get("vl_restarts_unchanged") and h.get("loki_restarts_unchanged"))} + doc = {"ref": a.ref, "recorded": before.get("checked"), "window": health.get("window"), + "health": proof, "passes": passes, "summary": data["summary"], "clusters": clusters} + os.makedirs(os.path.dirname(a.out), exist_ok=True) + with open(a.out, "w") as handle: + json.dump(doc, handle, indent=1, sort_keys=True) + handle.write("\n") + print(f"{a.out}: {len(clusters)} clusters") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bench/parity/run.py b/bench/parity/run.py new file mode 100644 index 000000000..b78ad4ab1 --- /dev/null +++ b/bench/parity/run.py @@ -0,0 +1,675 @@ +#!/usr/bin/env python3 +"""Differential runner: send every corpus request to Loki and to the proxy, diff the answers. + + run.py --state STATE.json --corpus corpus.json --out RUN_DIR [--workers 4] [--window 3600] + run.py --loki URL --proxy URL --vl URL --end UNIX --corpus corpus.json --out RUN_DIR + +STATE.json is the state file of `bench/visual/stack.py up` (an isolated stack: +Loki, VictoriaLogs, the proxy builds, Grafana with the vp-pr / vp-loki +datasources). The proxy compared is the `pr` build (`--side main` for the other). + +Before any comparison the run proves both sides healthy and the data equal, +and stops otherwise (health.json): + * Loki /ready, VictoriaLogs /health, proxy /ready answer 200; + * Loki and VictoriaLogs count the same lines in every 10-minute slice of the + window (bench/ab/stack.py counts), and the total is not zero; + * Loki's range-metric path is out of its fresh-stack blank window: a grouped + count over the window returns one series per service Loki lists; + * the VictoriaLogs and Loki containers' restart counts are recorded before and + after the run, must be known (--vl-container, --loki-container) and must not + change (a Loki that restarted answers range metrics empty for minutes); + * the proxy log (--proxy-log) must not show VictoriaLogs failing during the + run: 5xx answers, or a fallback or partial answer after one (a 4xx from + VictoriaLogs is a query the proxy translated wrongly, so it is counted and + reported but is the difference being measured, not a broken comparison). +A request whose Loki answer carries warnings or whose proxy answer is marked +partial is `blocked`, never a difference, and up to three requests of every +difference signature are asked for a second time: a signature none of whose +samples reproduces is `blocked` (not reproducible). Query requests run twice: as +Grafana sends them (`X-Loki-Response-Encoding-Flags: categorize-labels`) and +as a plain API client does. + +Outputs RUN_DIR/results.jsonl (one row per request and encoding), health.json, +and, through cluster.py, clusters.json and report.md. +""" +import argparse +import base64 +import concurrent.futures +import gzip +import importlib.util +import json +import os +import re +import socket +import subprocess +import sys +import time +import urllib.error +import urllib.parse +import urllib.request + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) +import cluster # noqa: E402 +import differ # noqa: E402 + +PATHS = { + "query_range": "/loki/api/v1/query_range", "query": "/loki/api/v1/query", "labels": "/loki/api/v1/labels", + "label_values": "/loki/api/v1/label/{name}/values", "series": "/loki/api/v1/series", + "index_stats": "/loki/api/v1/index/stats", "volume": "/loki/api/v1/index/volume", + "volume_range": "/loki/api/v1/index/volume_range", "detected_fields": "/loki/api/v1/detected_fields", + "detected_field_values": "/loki/api/v1/detected_field/{name}/values", + "detected_labels": "/loki/api/v1/detected_labels", "patterns": "/loki/api/v1/patterns", + "format_query": "/loki/api/v1/format_query", "tail": "/loki/api/v1/tail", +} +CATEGORIZE = {"X-Loki-Response-Encoding-Flags": "categorize-labels"} +TENANT = {"X-Scope-OrgID": "0"} + + +def load_ab_stack(): + spec = importlib.util.spec_from_file_location("ab_stack", os.path.join(HERE, "..", "ab", "stack.py")) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def fill(params, times): + out = {} + for key, value in params.items(): + if isinstance(value, str) and value.startswith("$"): + value = str(times[value[1:]]) + out[key] = value + return out + + +def http(method, url, headers=None, body=None, timeout=150): + data = json.dumps(body).encode() if body is not None else None + req = urllib.request.Request(url, data=data, method=method, + headers=dict(headers or {}, **({"Content-Type": "application/json"} if data else {}))) + t0 = time.time() + try: + with urllib.request.urlopen(req, timeout=timeout) as response: + raw, status, hdrs = response.read(), response.status, dict(response.headers) + except urllib.error.HTTPError as error: + raw, status, hdrs = error.read(), error.code, dict(error.headers or {}) + except (urllib.error.URLError, OSError) as error: + return 0, {"_text": f"transport error: {error}"}, {}, time.time() - t0 + try: + payload = json.loads(raw) if raw else {} + except ValueError: + payload = {"_text": raw.decode(errors="replace")[:2000]} + return status, payload, hdrs, time.time() - t0 + + +# ---------------------------------------------------------------- tail (minimal websocket client) + +def ws_tail(base, params, headers, seconds=4.0): + """Frames a /tail websocket sends in `seconds`, merged into one streams answer.""" + parsed = urllib.parse.urlparse(base) + path = PATHS["tail"] + "?" + urllib.parse.urlencode(params) + key = base64.b64encode(os.urandom(16)).decode() + lines = [f"GET {path} HTTP/1.1", f"Host: {parsed.hostname}:{parsed.port}", "Upgrade: websocket", + "Connection: Upgrade", f"Sec-WebSocket-Key: {key}", "Sec-WebSocket-Version: 13"] + lines += [f"{k}: {v}" for k, v in headers.items()] + try: + sock = socket.create_connection((parsed.hostname, parsed.port), timeout=seconds) + except OSError as error: + return 0, {"_text": f"transport error: {error}"} + sock.sendall(("\r\n".join(lines) + "\r\n\r\n").encode()) + buf = b"" + deadline = time.time() + seconds + try: + while b"\r\n\r\n" not in buf and time.time() < deadline: + chunk = sock.recv(65536) + if not chunk: + break + buf += chunk + head, _, buf = buf.partition(b"\r\n\r\n") + status = int(head.split(b" ")[1]) if head else 0 + if status != 101: + return status, {"_text": (head + buf).decode(errors="replace")[:500]} + streams, dropped = [], 0 + sock.settimeout(0.5) + while time.time() < deadline: + frame, buf = read_frame(sock, buf, deadline) + if frame is None: + continue + if frame == b"": + break + message = json.loads(frame) + streams += message.get("streams") or [] + dropped += len(message.get("dropped_entries") or []) + return 200, {"data": {"resultType": "streams", "result": streams}, "dropped": dropped} + except (OSError, ValueError) as error: + return 0, {"_text": f"tail error: {error}"} + finally: + sock.close() + + +def read_frame(sock, buf, deadline): + """(payload, rest): one text frame; None when nothing arrived yet (or the deadline passed mid-frame), b'' on close.""" + def need(n): + nonlocal buf + while len(buf) < n: + try: + chunk = sock.recv(65536) + except socket.timeout: + return False + if not chunk: + raise OSError("closed") + buf += chunk + return True + if not need(2): + return None, buf + opcode, length = buf[0] & 0x0F, buf[1] & 0x7F + offset = 2 + if length == 126: + if not need(4): + return None, buf + length, offset = int.from_bytes(buf[2:4], "big"), 4 + elif length == 127: + if not need(10): + return None, buf + length, offset = int.from_bytes(buf[2:10], "big"), 10 + while not need(offset + length): + if time.time() >= deadline: + return None, buf + time.sleep(0.05) + payload, rest = buf[offset:offset + length], buf[offset + length:] + if opcode == 8: + return b"", rest + if opcode != 1: + return None, rest + return payload, rest + + +# ---------------------------------------------------------------- Grafana frames + +def frames_body(result): + """A Grafana /api/ds/query result (one refId) as a Loki-shaped answer, so differ.py compares it.""" + if not result: + return {} + if result.get("error"): + return {"error": result["error"]} + streams, series = [], [] + for frame in result.get("frames") or []: + fields = frame["schema"]["fields"] + values = frame["data"]["values"] + names = [f.get("name") for f in fields] + if "Line" in names and "labels" in names: + col = dict(zip(names, values)) + ts = col.get("tsNs") or [str(int(t) * 10 ** 6) for t in col.get("Time", [])] + types = col.get("labelTypes") or [None] * len(ts) + for t, line, labels, kinds in zip(ts, col["Line"], col["labels"], types): + labels = labels or {} + stream = {k: v for k, v in labels.items() if not kinds or kinds.get(k) == "I"} + meta = {} + if kinds: + meta = {"structuredMetadata": {k: v for k, v in labels.items() if kinds.get(k) == "S"}, + "parsed": {k: v for k, v in labels.items() if kinds.get(k) == "P"}} + streams.append({"stream": stream, "values": [[t, line, meta]]}) + elif len(fields) >= 2 and fields[0].get("type") == "time": + for index in range(1, len(fields)): + labels = fields[index].get("labels") or {} + points = [[t / 1000, str(v)] for t, v in zip(values[0], values[index]) if v is not None] + series.append({"metric": labels, "values": points}) + if streams: + return {"data": {"resultType": "streams", "result": streams}} + return {"data": {"resultType": "matrix", "result": series}} + + +RESOURCE_ENDPOINTS = (("detected_field/", "detected_field_values"), ("detected_fields", "detected_fields"), + ("detected_labels", "detected_labels"), ("index/stats", "index_stats"), + ("index/volume_range", "volume_range"), ("index/volume", "volume"), ("patterns", "patterns"), + ("label/", "label_values"), ("labels", "labels"), ("series", "series")) + + +def resource_endpoint(resource): + for prefix, name in RESOURCE_ENDPOINTS: + if resource.startswith(prefix): + return name + return "resource" + + +# ---------------------------------------------------------------- one request + +def run_one(item, cfg): + times = cfg["times"] + rows = [] + if item["transport"] == "grafana": + rows.append(run_grafana(item, cfg)) + return rows + endpoint = item["endpoint"] + params = fill(item["params"], times) + path = PATHS[endpoint] + if "{name}" in path: + path = path.replace("{name}", urllib.parse.quote(params.pop("name"), safe="")) + encodings = ("categorize", "plain") if endpoint in ("query_range", "query", "tail") else ("plain",) + for encoding in encodings: + headers = dict(TENANT, **(CATEGORIZE if encoding == "categorize" else {}), **(item.get("headers") or {})) + if endpoint == "tail": + ls, lb = ws_tail(cfg["loki"], params, headers) + ps, pb = ws_tail(cfg["proxy"], params, headers) + lh = ph = {} + lt = pt = 0 + else: + url = "?" + urllib.parse.urlencode(params) + ls, lb, lh, lt = http("GET", cfg["loki"] + path + url, headers) + ps, pb, ph, pt = http("GET", cfg["proxy"] + path + url, headers) + row = verdict(item, endpoint, encoding, params, ls, lb, lh, ps, pb, ph, cfg, lt, pt) + loki_own = None + if endpoint == "index_stats" and row["verdict"] == "diff": + loki_own = loki_own_counts(params, cfg) + apply_loki_own_counts(row, pb, loki_own) + keep_body(cfg, row, {"endpoint": endpoint, "params": params, "loki": [ls, lb, lh], "proxy": [ps, pb, ph], + "loki_own": loki_own}) + rows.append(row) + return rows + + +def keep_body(cfg, row, record): + """With --keep-bodies, store both answers so the diff can be recomputed offline (--rediff).""" + sink = cfg.get("bodies") + if sink is not None: + sink.append(dict(record, id=row["id"], encoding=row.get("encoding"))) + + +def rediff(item, record, cfg): + """The row a stored pair of answers gives under the current diff rules (no request is sent).""" + if item["transport"] == "grafana" and item["endpoint"] == "ds_query": + (ls, lb, _), (ps, pb, _) = record["loki"], record["proxy"] + return grafana_query_verdict(item, ls, lb, ps, pb, cfg, 0, 0) + (ls, lb, lh), (ps, pb, ph) = record["loki"], record["proxy"] + row = verdict(item, record["endpoint"], record.get("encoding") or "plain", record["params"], ls, lb, lh, ps, pb, + ph, cfg) + if item["transport"] == "grafana": + row["resource"], row["resource_endpoint"] = item["resource"], record["endpoint"] + if record["endpoint"] == "index_stats" and row["verdict"] == "diff" and record.get("loki_own"): + apply_loki_own_counts(row, pb, record["loki_own"]) + return row + + +def loki_own_counts(params, cfg): + """Loki's own answers for an index/stats window: count_over_time (entries) and /series (streams).""" + start, end = int(params["start"]) // 10 ** 9, int(params["end"]) // 10 ** 9 + query = f"sum(count_over_time({params['query']}[{end - start}s]))" + status, body, _, _ = http("GET", cfg["loki"] + PATHS["query"] + "?" + urllib.parse.urlencode( + {"query": query, "time": end}), TENANT) + result = ((body.get("data") or {}).get("result") or []) if status == 200 else [] + entries = int(float(result[0]["value"][1])) if result else None + status, body, _, _ = http("GET", cfg["loki"] + PATHS["series"] + "?" + urllib.parse.urlencode( + {"match[]": params["query"], "start": params["start"], "end": params["end"]}), TENANT) + streams = len(body.get("data") or []) if status == 200 else None + return {"entries": entries, "streams": streams} + + +def apply_loki_own_counts(row, proxy, own): + """Loki's index/stats reads chunk metadata: a chunk it holds twice (flushed and retained in an ingester) counts + twice. A streams or entries difference is Loki's chunk accounting only when Loki's own /series or count_over_time + over the window equals the proxy's number; otherwise it stays a gap.""" + row["loki_own_counts"] = own + for f in row["facets"]: + key = f["detail"].split(" ")[0] + if f["kind"] == "stats" and not f.get("documented") and own.get(key) is not None \ + and own.get(key) == proxy.get(key): + f["documented"] = differ.DOC_INDEX_STATS_LOKI_CHUNKS + f["example"] += f"; Loki's own {'count_over_time' if key == 'entries' else '/series'}: {own[key]}" + + +def run_grafana(item, cfg): + g, uids = cfg["grafana"], cfg["uids"] + if item["endpoint"] == "ds_query": + def body(uid): + out = json.loads(json.dumps(item["body"])) + for q in out["queries"]: + q["datasource"]["uid"] = uid + return out + ls, lb, _, lt = http("POST", f"{g}/api/ds/query", {}, body(uids["loki"])) + ps, pb, _, pt = http("POST", f"{g}/api/ds/query", {}, body(uids["proxy"])) + row = grafana_query_verdict(item, ls, lb, ps, pb, cfg, lt, pt) + keep_body(cfg, row, {"endpoint": "ds_query", "params": {}, "loki": [ls, lb, {}], "proxy": [ps, pb, {}]}) + return row + resource = item["resource"] + endpoint = resource_endpoint(resource) + ls, lb, _, lt = http("GET", f"{g}/api/datasources/uid/{uids['loki']}/resources/{resource}") + ps, pb, _, pt = http("GET", f"{g}/api/datasources/uid/{uids['proxy']}/resources/{resource}") + row = verdict(item, endpoint, "plain", {"query": resource}, ls, lb, {}, ps, pb, {}, cfg, lt, pt) + row["resource"], row["resource_endpoint"] = resource, endpoint + keep_body(cfg, row, {"endpoint": endpoint, "params": {"query": resource}, "loki": [ls, lb, {}], + "proxy": [ps, pb, {}]}) + return row + + +def grafana_query_verdict(item, ls, lb, ps, pb, cfg, lt, pt): + """The row of one replayed /api/ds/query: every refId's frames compared as a Loki answer.""" + facets, vac = [], True + for ref in sorted(set((lb.get("results") or {})) | set((pb.get("results") or {}))): + la = frames_body((lb.get("results") or {}).get(ref)) + pa = frames_body((pb.get("results") or {}).get(ref)) + facets += differ.diff("query_range", 200 if "error" not in la else 400, la, + 200 if "error" not in pa else 400, pa, cfg["stream_labels"]) + vac = vac and differ.vacuous("query_range", 200, la, 200, pa) + query = " ; ".join(q.get("expr", "") for q in item["body"]["queries"]) + row = base_row(item, "categorize", {"query": query}, ls, ps, lt, pt) + if ls != ps or ls != 200: + facets = [differ.facet("status", f"loki {ls} proxy {ps}", differ.short(lb, 160))] if ls != ps else facets + row.update(verdict="diff" if facets else ("vacuous" if vac else "same"), facets=differ.dedupe(facets)) + return row + + +def base_row(item, encoding, params, ls, ps, lt, pt): + return {"id": item["id"], "source": item["source"], "endpoint": item["endpoint"], "origin": item.get("origin", ""), + "encoding": encoding, "params": params, "loki_status": ls, "proxy_status": ps, + "loki_s": round(lt, 3), "proxy_s": round(pt, 3)} + + +def verdict(item, endpoint, encoding, params, ls, lb, lh, ps, pb, ph, cfg, lt=0, pt=0): + row = base_row(dict(item, endpoint=endpoint), encoding, params, ls, ps, lt, pt) + warnings = (lb.get("warnings") if isinstance(lb, dict) else None) + partial = {k.lower(): v for k, v in (ph or {}).items()}.get("x-loki-vl-partial-response") + if ls == 0 or ps == 0: + row.update(verdict="blocked", why="transport error", facets=[]) + elif warnings: + row.update(verdict="blocked", why=f"Loki warnings: {differ.short(warnings, 160)}", facets=[]) + elif partial: + row.update(verdict="blocked", why=f"proxy partial response: {partial}", facets=[]) + elif ls >= 500: + # Loki failing (timeout, limit, internal) is no reference answer. + row.update(verdict="blocked", why=f"Loki failed ({ls}): {differ.short(differ.error_text(lb), 160)}", facets=[]) + else: + facets = differ.diff(endpoint, ls, lb, ps, pb, cfg["stream_labels"], + limit=int(params.get("limit") or 0), direction=params.get("direction", "backward")) + if facets: + row.update(verdict="diff", facets=facets) + else: + row.update(verdict="vacuous" if differ.vacuous(endpoint, ls, lb, ps, pb) else "same", facets=[]) + return row + + +# ---------------------------------------------------------------- confirmation + +def confirm(rows, corpus, cfg, workers, per_cluster=3): + """Ask both sides again for up to `per_cluster` requests of every difference signature. A signature none of whose + samples reproduces (Loki's answer moved between two identical requests: sharding, splitting, results cache) is + `blocked` (not reproducible), never a gap. Returns the number of rows blocked.""" + by_id = {e["id"]: e for e in corpus} + members = {} + for row in rows: + if row.get("verdict") == "diff" and row["id"] in by_id: + members.setdefault(cluster_key(row), []).append(row) + sample = {} + for key, group in members.items(): + for row in group[:per_cluster]: + sample.setdefault(row["id"], set()).add(key) + ids = sorted(sample) + reproduced = set() + with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool: + for item_id, redone in zip(ids, pool.map(lambda i: run_one(by_id[i], cfg), ids)): + for row in redone: + if row.get("verdict") == "diff": + reproduced.add(cluster_key(row)) + blocked = 0 + for key, group in members.items(): + for row in group: + if key in reproduced: + row["confirmed"] = True + else: + row["verdict"] = "blocked" + row["why"] = f"not reproducible: no sampled request of {key} differed again" + blocked += 1 + return blocked + + +def cluster_key(row): + return " / ".join(cluster.signature_of(row)) + + +# ---------------------------------------------------------------- health + +def restart_count(container): + if not container: + return None + out = subprocess.run(["docker", "inspect", container, "--format", "{{.RestartCount}}"], capture_output=True, text=True) + return int(out.stdout.strip()) if out.returncode == 0 and out.stdout.strip().isdigit() else None + + +# Proxy log counters that make a comparison invalid when they rise during a run: VictoriaLogs failing (5xx; 499 is +# the proxy cancelling a call its client gave up on) and a proxy answer produced by a fallback or a partial +# response after such a failure. A 4xx from VictoriaLogs is a query the proxy translated wrongly: it is the +# difference being measured, so it is counted but does not invalidate the run. +GATING = ("upstream_5xx", "fallback_after_5xx", "partial_after_5xx") + + +def scan_log(path): + """Counters from the proxy's JSON log (one object per line); text that is not JSON is skipped.""" + out = {"upstream_5xx": 0, "upstream_4xx": 0, "upstream_499": 0, "fallback_after_5xx": 0, + "fallback_after_4xx": 0, "partial_after_5xx": 0, "lines": 0} + if not path or not os.path.exists(path): + return {} + for line in open(path, errors="ignore"): + out["lines"] += 1 + try: + record = json.loads(line) + except ValueError: + continue + body = str(record.get("body") or "") + status = record.get("http.response.status_code") or record.get("status") + error = str(record.get("err") or record.get("error") or "") + upstream_failed = bool(re.search(r"\b5\d\d\b|timeout|deadline", error)) + if body == "upstream_request" and isinstance(status, int): + if status == 499: + out["upstream_499"] += 1 + elif status >= 500: + out["upstream_5xx"] += 1 + elif status >= 400: + out["upstream_4xx"] += 1 + elif "falling back" in body or "fallback" in body: + out["fallback_after_5xx" if upstream_failed else "fallback_after_4xx"] += 1 + elif "partial" in body.lower() and upstream_failed: + out["partial_after_5xx"] += 1 + return out + + +def log_rose(before, after): + """The gating counters that rose between two scans of the same log (a shorter log was replaced: all count).""" + if not after: + return {} + restarted = after.get("lines", 0) < before.get("lines", 0) + return {k: after.get(k, 0) - (0 if restarted else before.get(k, 0)) for k in GATING + if after.get(k, 0) - (0 if restarted else before.get(k, 0)) > 0} + + +def health(cfg): + out = {"checked": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())} + for name, url in (("loki", cfg["loki"] + "/ready"), ("proxy", cfg["proxy"] + "/ready"), ("vl", cfg["vl"] + "/health")): + status, _, _, _ = http("GET", url, TENANT, timeout=10) + out[f"{name}_ready"] = status + ab = load_ab_stack() + loki_port = int(urllib.parse.urlparse(cfg["loki"]).port) + vl_port = int(urllib.parse.urlparse(cfg["vl"]).port) + st = ab.Stack("parity", loki_port, vl_port) + start, end = cfg["times"]["start"], cfg["times"]["end"] + loki_counts, vl_counts = st.counts(start, end, 600) + out["slices"] = len(vl_counts) + out["lines_loki"], out["lines_vl"] = sum(loki_counts.values()), sum(vl_counts.values()) + out["slices_differing"] = sorted(t for t in vl_counts if loki_counts.get(t, 0) != vl_counts[t]) + services_status, services, _, _ = http("GET", cfg["loki"] + "/loki/api/v1/label/service_name/values?" + + urllib.parse.urlencode({"start": start * 10 ** 9, "end": end * 10 ** 9}), TENANT) + params = {"query": 'sum by (service_name) (count_over_time({env=~".+"}[5m]))', "start": start, "end": end, "step": 300} + qs, qb, _, _ = http("GET", cfg["loki"] + "/loki/api/v1/query_range?" + urllib.parse.urlencode(params), + dict(TENANT, **{"Cache-Control": "no-cache"})) + out["services_listed"] = len(services.get("data") or []) if services_status == 200 else None + out["services_in_loki_metric"] = len(((qb.get("data") or {}).get("result") or [])) if qs == 200 else None + out["vl_restarts"] = restart_count(cfg.get("vl_container")) + # A Loki that restarted mid-run answers range metrics empty for minutes (its fresh-stack blank window). + out["loki_restarts"] = restart_count(cfg.get("loki_container")) + out["ok"] = healthy(out) + return out + + +def healthy(out): + """Both sides ready, equal non-empty data, Loki out of its blank window, restart counts known (an unknown + restart count proves nothing: the containers must be named and inspectable).""" + return bool(out.get("loki_ready") == out.get("proxy_ready") == out.get("vl_ready") == 200 + and (out.get("lines_vl") or 0) > 0 and out.get("lines_loki") == out.get("lines_vl") + and not out.get("slices_differing") and out.get("services_listed") + and out.get("services_in_loki_metric") == out.get("services_listed") + and out.get("vl_restarts") is not None and out.get("loki_restarts") is not None) + + +def stream_labels(cfg): + times = cfg["times"] + status, body, _, _ = http("GET", cfg["loki"] + "/loki/api/v1/labels?" + urllib.parse.urlencode( + {"start": times["start_ns"], "end": times["end_ns"]}), TENANT) + return sorted(body.get("data") or []) if status == 200 else [] + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--state", default="", help="bench/visual/stack.py state.json") + ap.add_argument("--side", default="pr", choices=["pr", "main"]) + ap.add_argument("--loki", default="") + ap.add_argument("--proxy", default="") + ap.add_argument("--vl", default="") + ap.add_argument("--grafana", default="") + ap.add_argument("--end", type=int, default=0) + ap.add_argument("--window", type=int, default=3600, help="seconds before --end that queries cover") + ap.add_argument("--step", type=int, default=60) + ap.add_argument("--corpus", required=True) + ap.add_argument("--out", required=True) + ap.add_argument("--workers", type=int, default=4) + ap.add_argument("--source", action="append", default=[], help="only these corpus sources") + ap.add_argument("--endpoint", action="append", default=[], help="only these endpoints (query_range, ds_query, ...)") + ap.add_argument("--limit", type=int, default=0, help="first N corpus entries (smoke run)") + ap.add_argument("--vl-container", default="") + ap.add_argument("--loki-container", default="") + ap.add_argument("--proxy-log", default="") + ap.add_argument("--skip-health", action="store_true", help="debugging only: the report says so") + ap.add_argument("--no-confirm", action="store_true", help="do not ask twice for differing requests") + ap.add_argument("--keep-bodies", action="store_true", + help="store both answers of every request in RUN_DIR/bodies.jsonl.gz for --rediff") + ap.add_argument("--rediff", action="store_true", + help="recompute RUN_DIR/results.jsonl from RUN_DIR/bodies.jsonl.gz under the current diff " + "rules; sends no request") + ap.add_argument("--confirm-only", action="store_true", + help="confirm the differences of an existing RUN_DIR/results.jsonl instead of running the corpus") + a = ap.parse_args() + if a.state: + state = json.load(open(a.state)) + ports = state["ports"] + a.loki = a.loki or f"http://127.0.0.1:{ports['loki']}" + a.vl = a.vl or f"http://127.0.0.1:{ports['vl']}" + a.proxy = a.proxy or f"http://127.0.0.1:{ports[a.side]}" + a.grafana = a.grafana or state.get("grafana", "") + a.end = a.end or state["end"] + a.vl_container = a.vl_container or f"{state['project']}-victorialogs" + a.loki_container = a.loki_container or f"{state['project']}-loki" + a.proxy_log = a.proxy_log or os.path.join(os.path.dirname(os.path.abspath(a.state)), f"proxy-{a.side}.log") + if not (a.loki and a.proxy and a.vl and a.end): + ap.error("--state, or --loki/--proxy/--vl/--end") + os.makedirs(a.out, exist_ok=True) + if a.rediff: + return rediff_run(a) + start = a.end - a.window + cfg = {"loki": a.loki, "proxy": a.proxy, "vl": a.vl, "grafana": a.grafana, "vl_container": a.vl_container, + "loki_container": a.loki_container, + "uids": {"loki": "vp-loki", "proxy": "vp-" + a.side}, + "times": {"start": start, "end": a.end, "step": a.step, "start_ns": start * 10 ** 9, "end_ns": a.end * 10 ** 9}} + before = {} if a.skip_health else health(cfg) + before["log_before"] = scan_log(a.proxy_log) + if not a.skip_health and not before["ok"]: + json.dump({"before": before}, open(os.path.join(a.out, "health.json"), "w"), indent=1) + print("both sides are not proven healthy and equal; nothing compared: " + json.dumps(before), file=sys.stderr) + return 2 + cfg["stream_labels"] = stream_labels(cfg) + corpus = json.load(open(a.corpus))["entries"] + if a.source: + corpus = [e for e in corpus if e["source"] in a.source] + if a.endpoint: + corpus = [e for e in corpus if e["endpoint"] in a.endpoint] + if not a.grafana: + corpus = [e for e in corpus if e["transport"] != "grafana"] + if a.limit: + corpus = corpus[: a.limit] + t0 = time.time() + results = os.path.join(a.out, "results.jsonl") + if a.keep_bodies: + cfg["bodies"] = [] + if not a.confirm_only: + with open(results, "w") as handle, concurrent.futures.ThreadPoolExecutor(max_workers=a.workers) as pool: + done = 0 + for rows in pool.map(lambda e: run_one(e, cfg), corpus): + for row in rows: + handle.write(json.dumps(row, sort_keys=True) + "\n") + handle.flush() + done += 1 + if done % 200 == 0: + print(f"[run] {done}/{len(corpus)} ({time.time() - t0:.0f}s)", file=sys.stderr, flush=True) + if a.keep_bodies: + with gzip.open(os.path.join(a.out, "bodies.jsonl.gz"), "wt") as handle: + for record in cfg.pop("bodies"): + handle.write(json.dumps(record) + "\n") + unconfirmed = None + if not a.no_confirm: + rows = cluster.load_rows(results) + unconfirmed = confirm(rows, corpus, cfg, a.workers) + with open(results, "w") as handle: + for row in rows: + handle.write(json.dumps(row, sort_keys=True) + "\n") + print(f"[run] confirmation: {unconfirmed} differences did not reproduce", file=sys.stderr, flush=True) + after = {} if a.skip_health else health(cfg) + after["log_after"] = scan_log(a.proxy_log) + report = {"before": before, "after": after, "skip_health": a.skip_health, "corpus_entries": len(corpus), + "seconds": round(time.time() - t0), "loki": a.loki, "proxy": a.proxy, "window": [start, a.end], + "not_reproducible": unconfirmed, "stream_labels": cfg.get("stream_labels") or []} + report["vl_restarts_unchanged"] = before.get("vl_restarts") is not None and \ + before.get("vl_restarts") == after.get("vl_restarts") + report["loki_restarts_unchanged"] = before.get("loki_restarts") is not None and \ + before.get("loki_restarts") == after.get("loki_restarts") + report["proxy_log_rose"] = log_rose(before.get("log_before") or {}, after.get("log_after") or {}) + json.dump(report, open(os.path.join(a.out, "health.json"), "w"), indent=1, sort_keys=True) + rows = cluster.load_rows(os.path.join(a.out, "results.jsonl")) + clusters = cluster.cluster(rows) + stats = cluster.summary(rows) + json.dump({"summary": stats, "clusters": clusters}, open(os.path.join(a.out, "clusters.json"), "w"), indent=1, + sort_keys=True) + with open(os.path.join(a.out, "report.md"), "w") as handle: + handle.write(cluster.markdown(clusters, stats, report)) + print(f"[run] {len(rows)} requests in {report['seconds']}s; " + f"{sum(1 for c in clusters if not c['documented'])} gap signatures; {a.out}/report.md") + if not a.skip_health and not (after.get("ok") and report["vl_restarts_unchanged"] + and report["loki_restarts_unchanged"] and not report["proxy_log_rose"]): + print("health changed during the run (or the proxy log shows VictoriaLogs failures): results are not " + "proof; see health.json", file=sys.stderr) + return 3 + return 0 + + +def rediff_run(a): + """--rediff: the stored answers under the current rules; the confirmation and health of the original run stand.""" + corpus = {e["id"]: e for e in json.load(open(a.corpus))["entries"]} + previous = {(r["id"], r.get("encoding")): r for r in cluster.load_rows(os.path.join(a.out, "results.jsonl"))} + health_path = os.path.join(a.out, "health.json") + cfg = {"stream_labels": (json.load(open(health_path)).get("stream_labels") or []) + if os.path.exists(health_path) else []} + rows = [] + with gzip.open(os.path.join(a.out, "bodies.jsonl.gz"), "rt") as handle: + for line in handle: + record = json.loads(line) + row = rediff(corpus[record["id"]], record, cfg) + old = previous.get((record["id"], record.get("encoding")), {}) + if old.get("verdict") == "blocked": # blocked stays blocked (transport, Loki failure, not reproducible) + row.update(verdict="blocked", why=old.get("why"), facets=[]) + rows.append(row) + with open(os.path.join(a.out, "results.jsonl"), "w") as handle: + for row in rows: + handle.write(json.dumps(row, sort_keys=True) + "\n") + clusters, stats = cluster.cluster(rows), cluster.summary(rows) + json.dump({"summary": stats, "clusters": clusters}, open(os.path.join(a.out, "clusters.json"), "w"), indent=1, + sort_keys=True) + print(f"[rediff] {len(rows)} rows, {sum(1 for c in clusters if not c['documented'])} gap signatures") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bench/parity/tests/test_parity.py b/bench/parity/tests/test_parity.py new file mode 100644 index 000000000..e4f0bffe0 --- /dev/null +++ b/bench/parity/tests/test_parity.py @@ -0,0 +1,334 @@ +"""Unit tests for the differential runner: corpus rewriting, semantic diff, clustering.""" +import json +import os +import socket +import sys +import time +import unittest + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(HERE, "..")) +import cluster # noqa: E402 +import corpus # noqa: E402 +import differ # noqa: E402 +import run # noqa: E402 + + +def streams(*rows, result_type="streams"): + """rows: (labels, ts, line[, meta]).""" + result = [] + for row in rows: + value = [row[1], row[2]] + ([row[3]] if len(row) > 3 else []) + result.append({"stream": row[0], "values": [value]}) + return {"status": "success", "data": {"resultType": result_type, "result": result}} + + +def matrix(*series): + return {"status": "success", "data": {"resultType": "matrix", + "result": [{"metric": m, "values": v} for m, v in series]}} + + +class CorpusTest(unittest.TestCase): + def test_selector_rewrite_keeps_pipeline_and_skips_strings(self): + q = '{app="foo"} | json | line_format "{{.a}} {x=\\"y\\"}" | b="{c=\\"d\\"}"' + out = corpus.rewrite(q, force=True) + self.assertTrue(out.startswith('{service_name="api-gateway"} | json | line_format')) + self.assertIn('{{.a}}', out) + + def test_selector_inside_metric_and_binary_rewritten_per_parser(self): + out = corpus.rewrite('sum(rate({job="a"} | logfmt [1m])) / sum(rate({job="b"}[1m]))', force=True) + self.assertEqual(out.count('{service_name="payment-service"}'), 2) + + def test_seeded_selector_kept_unless_forced(self): + self.assertEqual(corpus.rewrite('{namespace="prod", level="error"}'), '{namespace="prod", level="error"}') + self.assertEqual(corpus.rewrite('{app="compat-anchoring-123"}'), '{namespace="prod"}') + self.assertEqual(corpus.rewrite('{namespace="tns"} | json'), '{service_name="api-gateway"} | json') + + def test_extract_skips_fixtures_and_format_strings(self, tmp=None): + import tempfile + with tempfile.NamedTemporaryFile("w", suffix="_test.go", delete=False) as handle: + handle.write('q := `sum(rate({app="foo"}[1m]))`\n' + 'x := "downstream>"\n' + 'f := fmt.Sprintf(`{app="%s"}`, x)\n' + 'j := `{"a": 1}`\n') + try: + found = corpus.extract_strings([handle.name]) + finally: + os.unlink(handle.name) + self.assertEqual(found, {'sum(rate({app="foo"}[1m]))'}) + + def test_repo_queries_have_no_grafana_variables(self): + self.assertFalse([e for e in corpus.repo_queries() if "$__" in e["params"]["query"]]) + + def test_query_entries_metric_gets_range_and_instant(self): + kinds = [e["endpoint"] for e in corpus.query_entries("x", 'sum(rate({a="b"}[1m]))')] + self.assertEqual(kinds, ["query_range", "query"]) + self.assertEqual([e["endpoint"] for e in corpus.query_entries("x", '{a="b"} |= "x"')], ["query_range"]) + + +class DifferTest(unittest.TestCase): + def test_equal_streams_in_any_order_are_the_same(self): + a = streams(({"app": "x"}, "1", "l1"), ({"app": "y"}, "2", "l2")) + b = streams(({"app": "y"}, "2", "l2"), ({"app": "x"}, "1", "l1")) + self.assertEqual(differ.diff("query_range", 200, a, 200, b), []) + + def test_missing_parse_error_labels_cluster_by_name(self): + a = streams(({"app": "x", "__error__": "JSONParserErr", "__error_details__": "bad"}, "1", "l1")) + b = streams(({"app": "x"}, "1", "l1")) + facets = differ.diff("query_range", 200, a, 200, b, stream_labels=["app"]) + self.assertEqual([f["detail"] for f in facets], ["loki-only __error__,__error_details__"]) + + def test_parsed_keys_generalise_to_one_class(self): + a = streams(({"app": "x", "method": "GET"}, "1", "l1")) + b = streams(({"app": "x", "status": "200"}, "1", "l1")) + details = sorted(f["detail"] for f in differ.diff("query_range", 200, a, 200, b, ["app"])) + self.assertEqual(details, ["loki-only ", "proxy-only "]) + + def test_limit_boundary_ties_are_ignored(self): + a = streams(({"a": "1"}, "5", "new"), ({"a": "1"}, "3", "tie-a")) + b = streams(({"a": "1"}, "5", "new"), ({"a": "1"}, "3", "tie-b")) + self.assertEqual(differ.diff("query_range", 200, a, 200, b, limit=2), []) + self.assertTrue(differ.diff("query_range", 200, a, 200, b, limit=100)) + + def test_entry_count_classified(self): + a = streams(({"a": "1"}, "5", "x"), ({"a": "1"}, "4", "y")) + facets = differ.diff("query_range", 200, a, 200, streams()) + self.assertEqual(facets[0]["detail"], "proxy-empty") + + def test_categories_compared_per_entry(self): + a = streams(({"a": "1"}, "5", "x", {"structuredMetadata": {"trace_id": "t"}, "parsed": {"k": "v"}})) + b = streams(({"a": "1"}, "5", "x", {"structuredMetadata": {"trace_id": "t", "k": "v"}})) + details = [f["detail"] for f in differ.diff("query_range", 200, a, 200, b, ["a"])] + self.assertIn("loki-only parsed ", details) + self.assertIn("proxy-only structuredMetadata ", details) + + def test_metric_values_and_series(self): + a = matrix(({"level": "info"}, [[60, "2"], [120, "3"]])) + b = matrix(({"level": "info"}, [[60, "2"], [120, "4"]])) + facets = differ.diff("query_range", 200, a, 200, b) + self.assertEqual(facets[0]["kind"], "values") + self.assertIn("proxy higher", facets[0]["detail"]) + c = matrix(({"level": "info"}, [[60, "2"]]), ({"level": "warn"}, [[60, "1"]])) + self.assertEqual(differ.diff("query_range", 200, c, 200, matrix(({"level": "info"}, [[60, "2"]])))[0]["detail"], + "proxy-fewer") + + def test_non_numeric_proxy_sample_is_a_shape_difference(self): + a = matrix(({"l": "a"}, [[60, "1"]])) + b = matrix(({"l": "a"}, [[60, ""]])) + self.assertEqual(differ.diff("query_range", 200, a, 200, b)[0]["detail"], "proxy sample value is not a number") + + def test_clamp_ranges(self): + self.assertEqual(corpus.clamp_ranges('rate({a="b"}[5h]) / rate({a="b"}[30m])'), + 'rate({a="b"}[1h]) / rate({a="b"}[30m])') + + def test_status_and_error_text(self): + self.assertEqual(differ.diff("query", 400, {"_text": "parse error"}, 200, {"data": {}})[0]["kind"], "status") + self.assertEqual(differ.diff("query", 400, {"_text": "parse error at 1"}, 400, {"error": "parse error at 1"}), []) + self.assertEqual(differ.diff("query", 400, {"_text": "a"}, 400, {"error": "b"})[0]["kind"], "error-text") + + def test_documented_deviations_marked(self): + loki = {"fields": [{"label": "service_extracted", "type": "string"}, {"label": "x", "type": "int"}]} + proxy = {"fields": [{"label": "x", "type": "int"}]} + facets = differ.diff("detected_fields", 200, loki, 200, proxy) + self.assertEqual(facets[0]["documented"], differ.DOC_DETECTED_FIELDS_SERVICE) + stats = differ.diff("index_stats", 200, {"streams": 1, "entries": 2, "bytes": 3, "chunks": 1}, + 200, {"streams": 1, "entries": 2, "bytes": 4, "chunks": 0}) + self.assertTrue(all(f.get("documented") == differ.DOC_INDEX_STATS_BYTES for f in stats)) + + def test_detected_fields_exemption_covers_only_the_service_name(self): + loki = {"fields": [{"label": "service_name_extracted"}, {"label": "level_extracted"}]} + facets = differ.diff("detected_fields", 200, loki, 200, {"fields": []}) + documented = [f for f in facets if f.get("documented")] + plain = [f for f in facets if not f.get("documented")] + self.assertEqual([f["example"] for f in documented], ["['service_name_extracted']"]) + self.assertEqual([f["detail"] for f in plain], ["loki-only *_extracted"]) + + def test_volume_exemption_covers_bytes_only(self): + a = matrix(({"service_name": "a"}, [[60, "100"], [120, "100"]])) + b = matrix(({"service_name": "a"}, [[60, "90"]])) + facets = {f["kind"]: f for f in differ.diff("volume_range", 200, a, 200, b)} + self.assertEqual(facets["values"]["documented"], differ.DOC_VOLUME_BYTES) + self.assertNotIn("documented", facets["points"]) + fewer = differ.diff("volume", 200, matrix(({"a": "1"}, [[60, "1"]]), ({"a": "2"}, [[60, "1"]])), + 200, matrix(({"a": "1"}, [[60, "1"]]))) + self.assertFalse(any(f.get("documented") for f in fewer)) + + def test_documented_ids_are_registry_cases(self): + for case in differ.DOCUMENTED: + self.assertTrue(os.path.exists(os.path.join(HERE, "..", "..", "..", "conformance", "registry", "cases", + case + ".yaml")), case) + + def test_duplicate_label_sets_are_counted(self): + a = matrix(({"l": "a"}, [[60, "1"]])) + b = matrix(({"l": "a"}, [[60, "1"]]), ({"l": "a"}, [[60, "1"]])) + self.assertEqual(differ.diff("query_range", 200, a, 200, b)[0]["detail"], + "proxy returns one label set as several series") + + def test_entry_order_follows_direction(self): + backward = streams(({"a": "1"}, "5", "x"), result_type="streams") + backward["data"]["result"][0]["values"] = [["5", "x"], ["3", "y"]] + forward = json.loads(json.dumps(backward)) + forward["data"]["result"][0]["values"] = [["3", "y"], ["5", "x"]] + self.assertEqual(differ.diff("query_range", 200, backward, 200, backward), []) + facets = differ.diff("query_range", 200, backward, 200, forward) + self.assertEqual(facets[0]["kind"], "order") + self.assertEqual(differ.diff("query_range", 200, forward, 200, forward, direction="forward"), []) + self.assertEqual(differ.diff("tail", 200, backward, 200, forward), []) + + def test_scalar_compared_as_numbers(self): + a = {"data": {"resultType": "scalar", "result": [1700000000, "1"]}} + b = {"data": {"resultType": "scalar", "result": [1700000000.0, "1.0"]}} + self.assertEqual(differ.diff("query", 200, a, 200, b), []) + c = {"data": {"resultType": "scalar", "result": [1700000000, "2"]}} + self.assertEqual(differ.diff("query", 200, a, 200, c)[0]["detail"], "scalar differs") + + def test_vacuous(self): + self.assertTrue(differ.vacuous("query_range", 200, streams(), 200, streams())) + self.assertFalse(differ.vacuous("query_range", 200, streams(({"a": "1"}, "1", "x")), 200, streams())) + + +class ClusterTest(unittest.TestCase): + def row(self, query, detail, endpoint="query_range", encoding="categorize", source="seeded", documented=None): + f = differ.facet("labels", detail, "ex", documented) + return {"id": query, "source": source, "endpoint": endpoint, "encoding": encoding, "params": {"query": query}, + "verdict": "diff", "facets": [f]} + + def test_same_signature_clusters_and_ranks_by_impact_then_queries(self): + rows = [self.row('{a="1"} | json', "loki-only __error__"), self.row('{a="2"} | json', "loki-only __error__"), + self.row('{a="3"}', "x", endpoint="format_query"), + self.row('{a="4"}', "y", endpoint="detected_fields", documented=differ.DOC_DETECTED_FIELDS_SERVICE)] + clusters = cluster.cluster(rows) + self.assertEqual(clusters[0]["queries"], 2) + self.assertEqual(clusters[0]["impact"], "explore-visible") + self.assertEqual(clusters[0]["rank"], 1) + self.assertEqual(clusters[1]["impact"], "api-only") + self.assertTrue(clusters[-1]["documented"]) + self.assertNotIn("rank", clusters[-1]) + + def test_one_cluster_per_request_by_primary_facet(self): + row = self.row('sum(rate({a="1"}[1m]))', "x") + row["facets"] = [differ.facet("values", "values differ (proxy higher)", "v"), + differ.facet("status", "loki 200 proxy 400", "loki - | proxy cannot parse \"abc\" at 12")] + clusters = cluster.cluster([row]) + self.assertEqual(len(clusters), 1) + self.assertEqual(clusters[0]["signature"], + ["query_range", "sum", "status", "loki 200 proxy 400: cannot parse Q at N"]) + self.assertEqual(clusters[0]["also"], {"values: values differ (proxy higher)": 1}) + + def test_documented_facet_is_primary_only_when_alone(self): + f1 = differ.facet("stats", "bytes differ", "b", differ.DOC_INDEX_STATS_BYTES) + f2 = differ.facet("stats", "entries differ", "e") + self.assertEqual(cluster.primary([f1, f2])[0]["detail"], "entries differ") + self.assertEqual(cluster.primary([f1])[0]["detail"], "bytes differ") + + def test_signature_separates_filter_kinds(self): + def row(query): + return {"id": query, "source": "s", "endpoint": "query_range", "params": {"query": query}, + "verdict": "diff", "facets": [differ.facet("entries", "proxy-empty", "e")]} + sigs = {cluster.signature_of(row(q))[1] for q in ('{a="1"} !> "<_> x"', '{a="1"} | b!=""', '{a="1"} |= "x"')} + self.assertEqual(sigs, {"logs[pattern-filter]", "logs[label-filter]", "logs[line-filter]"}) + doc = row('{a="1"}') + doc["facets"] = [differ.facet("stats", "bytes differ", "e", differ.DOC_INDEX_STATS_BYTES)] + self.assertTrue(cluster.signature_of(doc)[3].endswith(f"[{differ.DOC_INDEX_STATS_BYTES}]")) + + def test_shape(self): + self.assertEqual(cluster.shape('sum by (a) (count_over_time({a="1"} | json | b!="" [5m]))'), + "sum[json,label-filter]") + self.assertEqual(cluster.shape('{a="1"} |= "x"'), "logs[line-filter]") + + +class ConfirmTest(unittest.TestCase): + def test_signature_that_does_not_reproduce_is_blocked(self): + def row(i, detail): + return {"id": i, "source": "s", "endpoint": "query_range", "encoding": "plain", "params": {"query": "{a=\"1\"}"}, + "verdict": "diff", "facets": [differ.facet("labels", detail, "e")]} + rows = [row("a", "stable"), row("b", "flaky")] + corpus = [{"id": "a"}, {"id": "b"}] + again = {"a": [row("a", "stable")], "b": [dict(row("b", "flaky"), verdict="same", facets=[])]} + original = run.run_one + run.run_one = lambda item, cfg: again[item["id"]] + try: + blocked = run.confirm(rows, corpus, {}, workers=1) + finally: + run.run_one = original + self.assertEqual(blocked, 1) + self.assertTrue(rows[0]["confirmed"]) + self.assertEqual(rows[1]["verdict"], "blocked") + + +class RunTest(unittest.TestCase): + def test_unknown_restart_count_is_not_proof(self): + good = {"loki_ready": 200, "proxy_ready": 200, "vl_ready": 200, "lines_loki": 5, "lines_vl": 5, + "slices_differing": [], "services_listed": 3, "services_in_loki_metric": 3, + "vl_restarts": 0, "loki_restarts": 0} + self.assertTrue(run.healthy(good)) + self.assertFalse(run.healthy(dict(good, vl_restarts=None))) + self.assertFalse(run.healthy(dict(good, loki_restarts=None))) + self.assertFalse(run.healthy(dict(good, services_in_loki_metric=2))) + + def test_log_scan_gates_only_on_backend_failures(self): + import tempfile + lines = [{"body": "upstream_request", "http.response.status_code": 200}, + {"body": "upstream_request", "http.response.status_code": 422}, + {"body": "upstream_request", "http.response.status_code": 499}, + {"body": "unwrap stats fast path failed, falling back to full-fetch", "err": "stats_query_range 422: x"}] + with tempfile.NamedTemporaryFile("w", suffix=".log", delete=False) as handle: + handle.write("\n".join(json.dumps(x) for x in lines) + "\nnot json\n") + try: + before = run.scan_log(handle.name) + self.assertEqual((before["upstream_4xx"], before["upstream_499"], before["fallback_after_4xx"]), (1, 1, 1)) + with open(handle.name, "a") as more: + more.write(json.dumps({"body": "upstream_request", "http.response.status_code": 503}) + "\n") + more.write(json.dumps({"body": "x falling back", "err": "stats_query_range 502: down"}) + "\n") + self.assertEqual(run.log_rose(before, run.scan_log(handle.name)), + {"upstream_5xx": 1, "fallback_after_5xx": 1}) + finally: + os.unlink(handle.name) + + def test_read_frame_gives_up_at_the_deadline(self): + left, right = socket.socketpair() + try: + left.sendall(bytes([0x81, 10]) + b"abc") # a 10-byte text frame of which 3 bytes arrive + right.settimeout(0.05) + t0 = time.time() + frame, _ = run.read_frame(right, b"", time.time() + 0.3) + self.assertIsNone(frame) + self.assertLess(time.time() - t0, 2) + finally: + left.close() + right.close() + + def test_rediff_recomputes_a_stored_pair(self): + item = {"id": "x", "source": "s", "endpoint": "query", "transport": "api", "params": {}} + record = {"id": "x", "encoding": "plain", "endpoint": "query", "params": {"query": "q"}, + "loki": [200, {"data": {"resultType": "scalar", "result": [1, "1"]}}, {}], + "proxy": [200, {"data": {"resultType": "scalar", "result": [1.0, "1.0"]}}, {}]} + row = run.rediff(item, record, {"stream_labels": []}) + self.assertEqual(row["verdict"], "same") + + +class FramesTest(unittest.TestCase): + def test_log_frame_becomes_categorised_streams(self): + result = {"frames": [{"schema": {"fields": [{"name": "labels"}, {"name": "Time"}, {"name": "Line"}, + {"name": "tsNs"}, {"name": "labelTypes"}]}, + "data": {"values": [[{"app": "x", "k": "v"}], [1], ["line"], ["1000000"], + [{"app": "I", "k": "P"}]]}}]} + body = run.frames_body(result) + row = body["data"]["result"][0] + self.assertEqual(row["stream"], {"app": "x"}) + self.assertEqual(row["values"][0][2]["parsed"], {"k": "v"}) + + def test_metric_frame_becomes_matrix(self): + result = {"frames": [{"schema": {"fields": [{"type": "time"}, {"type": "number", "labels": {"l": "a"}}]}, + "data": {"values": [[60000, 120000], [1, None]]}}]} + body = run.frames_body(result) + self.assertEqual(body["data"]["result"], [{"metric": {"l": "a"}, "values": [[60.0, "1"]]}]) + + def test_resource_endpoint(self): + self.assertEqual(run.resource_endpoint("detected_field/x/values?query=a"), "detected_field_values") + self.assertEqual(run.resource_endpoint("index/volume_range?x"), "volume_range") + self.assertEqual(run.resource_endpoint("labels?start=1"), "labels") + + +if __name__ == "__main__": + unittest.main() diff --git a/bench/parity/tests/test_parity_gaps.py b/bench/parity/tests/test_parity_gaps.py new file mode 100644 index 000000000..05c4432ab --- /dev/null +++ b/bench/parity/tests/test_parity_gaps.py @@ -0,0 +1,106 @@ +"""conformance/scripts/parity_gaps.py: registration rules for differential-run clusters.""" +import json +import os +import sys +import tempfile +import unittest + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(HERE, "..", "..", "..", "conformance", "scripts")) +import parity_gaps # noqa: E402 + + +def case(status="open", clusters=(), fixed_by="", impact="explore-visible"): + lines = ["id: x", "title: T", "gap:", f" status: {status}", f" impact: {impact}", " effort: S", + " area: 'a'", " loki: 'l'", " proxy: 'p'", " planned_test: 't'"] + if fixed_by: + lines.append(f" fixed_by: '{fixed_by}'") + lines.append(" clusters:" + ("" if clusters else " []")) + lines += [f" - {c}" for c in clusters] + return "\n".join(lines) + "\n" + + +def cluster(cid, documented=None, queries=2): + return {"id": cid, "signature": ["query_range", "logs", "labels", "x"], "documented": documented, + "queries": queries, "requests": queries * 2} + + +class ParityGapsTest(unittest.TestCase): + def setUp(self): + self.dir = tempfile.TemporaryDirectory() + self.root = os.path.join(self.dir.name, "registry") + os.makedirs(os.path.join(self.root, "cases", "t")) + self.discovery = os.path.join(self.dir.name, "discovery.json") + + def tearDown(self): + self.dir.cleanup() + + def write_case(self, name, text): + with open(os.path.join(self.root, "cases", "t", name + ".yaml"), "w") as handle: + handle.write(text) + + def write_discovery(self, clusters): + with open(self.discovery, "w") as handle: + json.dump({"clusters": clusters, "summary": {}}, handle) + + def analyse(self): + return parity_gaps.analyse(self.root, self.discovery) + + def test_absent_discovery_file_is_no_problem(self): + self.write_case("a", case(clusters=())) + found, discovery, problems = self.analyse() + self.assertEqual(problems, []) + self.assertIn("No differential run is recorded.", parity_gaps.render(found, discovery)) + + def test_unclaimed_and_double_claimed_clusters_fail(self): + self.write_case("a", case(clusters=("c1",))) + self.write_case("b", case(clusters=("c1",))) + self.write_discovery([cluster("c1"), cluster("c2")]) + problems = self.analyse()[2] + self.assertTrue(any("c1 is claimed by 2 cases" in p for p in problems)) + self.assertTrue(any("c2" in p and "no registry case" in p for p in problems)) + + def test_claim_of_a_cluster_the_run_does_not_hold_fails(self): + self.write_case("a", case(clusters=("gone",))) + self.write_discovery([]) + self.assertTrue(any("gone is not in" in p for p in self.analyse()[2])) + + def test_documented_cluster_must_name_a_recorded_case(self): + self.write_case("doc", case(status="documented")) + self.write_case("open", case(status="open")) + self.write_discovery([cluster("c1", documented="t/doc"), cluster("c2", documented="t/unknown"), + cluster("c3", documented="t/open")]) + found, _, problems = self.analyse() + self.assertFalse(any("c1" in p for p in problems)) + self.assertTrue(any("c2" in p and "no registry case" in p for p in problems)) + self.assertTrue(any("c3" in p and "gap status" in p for p in problems)) + self.assertEqual(next(c for c in found if c["id"] == "t/doc")["queries"], 2) + + def test_fixed_needs_fixed_by_and_is_listed_apart(self): + self.write_case("done", case(status="fixed", clusters=("c1",), fixed_by="#700")) + self.write_case("bad", case(status="fixed")) + self.write_discovery([cluster("c1")]) + found, discovery, problems = self.analyse() + self.assertEqual(problems, ["t/bad: status fixed needs fixed_by (the PR number or commit that fixed it)"]) + report = parity_gaps.render(found, discovery) + self.assertIn("## Fixed, awaiting the next run", report) + self.assertIn("#700", report) + + def test_ranking_open_first_then_impact_then_queries(self): + self.write_case("api", case(clusters=("c1",), impact="api-only")) + self.write_case("few", case(clusters=("c2",))) + self.write_case("many", case(clusters=("c3",))) + self.write_case("kept", case(status="owner-kept", clusters=("c4",))) + self.write_discovery([cluster("c1", queries=50), cluster("c2", queries=1), cluster("c3", queries=9), + cluster("c4", queries=99)]) + found = self.analyse()[0] + self.assertEqual([c["id"] for c in found], ["t/many", "t/few", "t/api", "t/kept"]) + + def test_unknown_status_and_impact_fail(self): + self.write_case("a", case(status="maybe", impact="somewhere")) + problems = self.analyse()[2] + self.assertEqual(len(problems), 2) + + +if __name__ == "__main__": + unittest.main() diff --git a/bench/visual/stack.py b/bench/visual/stack.py index 026518b2d..719c70f87 100644 --- a/bench/visual/stack.py +++ b/bench/visual/stack.py @@ -44,8 +44,9 @@ PORTS = dict(loki=33101, vl=33428, main=33200, pr=33202, grafana=33002) -def stack(project): - return ab.Stack(project, PORTS["loki"], PORTS["vl"], overlays=[LOKI_OVERLAY]) +def stack(project, overlays=()): + """overlays: further compose files after LOKI_OVERLAY (bench/parity raises Loki's gRPC message limits).""" + return ab.Stack(project, PORTS["loki"], PORTS["vl"], overlays=[LOKI_OVERLAY, *overlays]) def grafana_host_network(): @@ -110,8 +111,8 @@ def seed_vl_only(st, start, end, out): def up(a): os.makedirs(a.out, exist_ok=True) - st = stack(a.project) - state = dict(project=a.project, ports=PORTS, pids={}) + st = stack(a.project, a.overlay) + state = dict(project=a.project, ports=PORTS, pids={}, overlays=a.overlay) sp = os.path.join(a.out, "state.json") old = load_json(sp) if a.skip_seed and os.path.exists(sp) else {} stop_proxies(old) # a re-run replaces the proxies the earlier one started @@ -220,7 +221,7 @@ def down(a): a.cmd = "live-stop" live(a) sh("docker", "rm", "-f", f"{project}-grafana", check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - stack(project).down() + stack(project, state.get("overlays", a.overlay)).down() for tree in state.get("trees", {}).values(): ab.remove_tree(tree) ab.log("down") @@ -232,6 +233,7 @@ def main(): ap.add_argument("--out", required=True) ap.add_argument("--project", default="vp") ap.add_argument("--port-offset", type=int, default=0, help="shift every port (a second stack next to another)") + ap.add_argument("--overlay", action="append", default=[], help="a further compose file for Loki/VictoriaLogs") ap.add_argument("--main-ref", default="origin/main") ap.add_argument("--hours", type=float, default=24, help="VictoriaLogs history (hours)") ap.add_argument("--skip-seed", action="store_true", help="reuse the data and end of an earlier up (state.json)") diff --git a/conformance/README.md b/conformance/README.md index 5ee6226b3..f192fce67 100644 --- a/conformance/README.md +++ b/conformance/README.md @@ -32,6 +32,9 @@ conformance/ depend on it, and what evidence exists. [reports/translation-map.md](reports/translation-map.md) shows, per feature, what Loki gives, what VictoriaLogs gives, and what the proxy adds between them. [reports/gaps.md](reports/gaps.md) ranks what is missing. + [reports/parity-gaps.md](reports/parity-gaps.md) ranks every known difference from Loki + (registry cases with a `gap:` block), with the corpus queries of the last differential run + (`bench/parity`) that hit each one. [reports/performance.md](reports/performance.md) joins the saved A/B runs in `bench/ab/results/` with the registry items each measured shape covers, and lists every item the proxy answers slower than Loki on first load. @@ -46,7 +49,9 @@ conformance/ - a test claims a registry id that does not exist, a proxy route has no registry item, or an item points at code that no longer exists; - a generated report is stale (performance, coverage map, gaps, translation map, - roadmap, compatibility matrix); + roadmap, compatibility matrix, ranked parity gaps); +- a cluster of the last differential run (`registry/generated/parity-discovery.json`) + is accounted for by no registry case's `gap.clusters`; - a `bench/ab/shapes.json` shape covers a registry id that does not exist, covers nothing, or a saved A/B result does not match the shape sets; - a flag is missing from the places that list every flag; @@ -114,4 +119,5 @@ python3 conformance/scripts/coverage_map.py python3 conformance/scripts/wire.py python3 conformance/scripts/translation_map.py python3 conformance/scripts/gaps.py +python3 conformance/scripts/parity_gaps.py # after bench/parity/publish.py records a run ``` diff --git a/conformance/registry/README.md b/conformance/registry/README.md index ae6716324..cf2c4c1fb 100644 --- a/conformance/registry/README.md +++ b/conformance/registry/README.md @@ -42,3 +42,29 @@ python3 conformance/scripts/report.py # state and coverage it), `hybrid` (the proxy reshapes or merges a VictoriaLogs answer) or `proxy_side` (the proxy computes the result). The differential runner confirms it per query shape at runtime in a later phase; a curated value in the file wins over the generated one. + +## Recording a difference from Loki + +A case that records a known difference carries a `gap:` block, which +`conformance/scripts/parity_gaps.py` ranks into `conformance/reports/parity-gaps.md`: + +```yaml +gap: + status: open # open | owner-kept | documented | upstream | fixed + impact: explore-visible # explore-visible | drilldown-visible | grafana-datasource | api-only | edge + effort: S # S | M | L + area: 'internal/... (where the fix belongs)' + loki: 'pkg/logql/...:line (Loki v3.7.7 reference)' + proxy: 'what the proxy does instead' + planned_test: 'the test that will prove the fix and stay as its regression guard' + fixed_by: '#700' # required with status fixed: the PR or commit that removed the difference + note: 'optional: when and on which code the difference was measured' + clusters: # differential-run clusters (bench/parity) this difference accounts for + - 0a9f54e0 +``` + +Every cluster in `generated/parity-discovery.json` is claimed by exactly one case: a +cluster the diff marked as a recorded deviation by the case id it names (which must be a +case with status documented, owner-kept, upstream or fixed), every other one by a case's +`gap.clusters`. A fixed case keeps its clusters until the next recorded run no longer +shows them. The conformance gate fails otherwise. diff --git a/conformance/registry/behaviours/data-probing-and-freshness.yaml b/conformance/registry/behaviours/data-probing-and-freshness.yaml index ee0b95683..47ab8d6f6 100644 --- a/conformance/registry/behaviours/data-probing-and-freshness.yaml +++ b/conformance/registry/behaviours/data-probing-and-freshness.yaml @@ -23,3 +23,6 @@ cases: - quality/label-cache-warm - semantics/label-inventory-exact-and-incremental - semantics/label-browser-latency-cold-and-refresh + - quality/detected-metadata-proxy-cache + - quality/stats-volume-freshness + - quality/label-cache-aged-labels-kept diff --git a/conformance/registry/behaviours/heavy-metric-fetch-bounds.yaml b/conformance/registry/behaviours/heavy-metric-fetch-bounds.yaml index 6810a5cfa..3f7414cc8 100644 --- a/conformance/registry/behaviours/heavy-metric-fetch-bounds.yaml +++ b/conformance/registry/behaviours/heavy-metric-fetch-bounds.yaml @@ -42,3 +42,4 @@ cases: - limits/top-n-ranking-not-silent - semantics/sliding-window-recurrence-exact - semantics/tumbling-window-single-grid + - limits/drilldown-7d-breakdown-response-limit diff --git a/conformance/registry/behaviours/loki-compatible-profile.yaml b/conformance/registry/behaviours/loki-compatible-profile.yaml index 42bc0c504..0ea70a0dc 100644 --- a/conformance/registry/behaviours/loki-compatible-profile.yaml +++ b/conformance/registry/behaviours/loki-compatible-profile.yaml @@ -61,3 +61,6 @@ cases: - profiles/structured-metadata-label-collision # open gap, owner decision - profiles/uncategorized-streams-carry-structured-metadata - profiles/detected-fields-dotted-json-keys + - profiles/existence-filter-on-sanitized-key + - profiles/keep-structured-metadata-key + - profiles/json-extraction-list-exposes-only-listed diff --git a/conformance/registry/behaviours/metric-series-identity.yaml b/conformance/registry/behaviours/metric-series-identity.yaml index cf76dc291..5caa3d6f2 100644 --- a/conformance/registry/behaviours/metric-series-identity.yaml +++ b/conformance/registry/behaviours/metric-series-identity.yaml @@ -36,3 +36,5 @@ cases: - metric-identity/sum-is-row-aggregation - metric-identity/structured-metadata-splits-series # deviation, see state - metric-identity/dynamic-key-parser-labels # deviation, see state + - semantics/non-sum-vector-aggregation-unreduced + - semantics/stddev-stdvar-grouped-values diff --git a/conformance/registry/behaviours/parsed-label-series-identity.yaml b/conformance/registry/behaviours/parsed-label-series-identity.yaml index 788ddb19a..a359a0469 100644 --- a/conformance/registry/behaviours/parsed-label-series-identity.yaml +++ b/conformance/registry/behaviours/parsed-label-series-identity.yaml @@ -37,3 +37,5 @@ cases: - parsed-identity/unnamed-group-extracts-nothing - parsed-identity/dynamic-key-parsers # deviation with an opt-in flag, see state - parsed-identity/regexp-partial-match-series-count # unresolved pre-existing gap, see state + - parsed-identity/bare-parser-metric-duplicates-extracted + - profiles/json-number-label-text diff --git a/conformance/registry/behaviours/parser-error-and-label-collision.yaml b/conformance/registry/behaviours/parser-error-and-label-collision.yaml index ebc77fdd9..29c284034 100644 --- a/conformance/registry/behaviours/parser-error-and-label-collision.yaml +++ b/conformance/registry/behaviours/parser-error-and-label-collision.yaml @@ -50,3 +50,7 @@ cases: - semantics/json-label-spelling-probe - semantics/label-filter-before-parser-pushdown - semantics/pipeline-error-text-parity + - semantics/parse-error-labels-on-log-lines + - semantics/unwrap-sample-validity + - semantics/unwrap-conversion-error + - semantics/logfmt-existence-filter-ungrouped-sum diff --git a/conformance/registry/behaviours/service-name-derivation.yaml b/conformance/registry/behaviours/service-name-derivation.yaml index cee907e9a..ae418248e 100644 --- a/conformance/registry/behaviours/service-name-derivation.yaml +++ b/conformance/registry/behaviours/service-name-derivation.yaml @@ -34,3 +34,4 @@ cases: - service-name/selector-exact-match - service-name/group-by-service-name - service-name/label-values-list + - profiles/keep-removes-derived-service-name diff --git a/conformance/registry/behaviours/severity-detected-level-derivation.yaml b/conformance/registry/behaviours/severity-detected-level-derivation.yaml index 656e0b5ef..859169e9d 100644 --- a/conformance/registry/behaviours/severity-detected-level-derivation.yaml +++ b/conformance/registry/behaviours/severity-detected-level-derivation.yaml @@ -42,3 +42,5 @@ cases: - severity/no-signal-is-unknown - severity/normalisation-table - severity/keyword-scan-fallback + - severity/detected-level-filter-json-level + - versions/otlp-severity-before-vl-1.35 diff --git a/conformance/registry/behaviours/severity-exposure-surfaces.yaml b/conformance/registry/behaviours/severity-exposure-surfaces.yaml index c3a9a4cd2..c01f30d25 100644 --- a/conformance/registry/behaviours/severity-exposure-surfaces.yaml +++ b/conformance/registry/behaviours/severity-exposure-surfaces.yaml @@ -36,3 +36,4 @@ cases: - severity/detected-fields-contains-level - severity/label-values-detected-level - severity/label-filter-reads-stored-field # open gap, see state + - severity/detected-level-stage-side-effects diff --git a/conformance/registry/behaviours/window-bounds-and-step-alignment.yaml b/conformance/registry/behaviours/window-bounds-and-step-alignment.yaml index 0e8157811..e1d67f181 100644 --- a/conformance/registry/behaviours/window-bounds-and-step-alignment.yaml +++ b/conformance/registry/behaviours/window-bounds-and-step-alignment.yaml @@ -26,3 +26,4 @@ cases: - semantics/offset-sample-timestamps - semantics/grafana-24h-split - semantics/resolution-limit-11000 + - semantics/unwrap-range-last-point diff --git a/conformance/registry/cases/drilldown/detected-field-values.yaml b/conformance/registry/cases/drilldown/detected-field-values.yaml new file mode 100644 index 000000000..0180afcc3 --- /dev/null +++ b/conformance/registry/cases/drilldown/detected-field-values.yaml @@ -0,0 +1,37 @@ +id: drilldown/detected-field-values +title: detected_field values differ: none for service_name, other values for numeric and id fields (open) +proves: + - loki_api_v1_detected_field_name_values +consumer: drilldown +why: | + For `/detected_field/service_name/values?query={service_name="api-gateway"}` + Loki answers [api-gateway] and the proxy nothing; for numeric and high- + cardinality fields (duration_ms, trace ids) Loki answers the values of the + lines it sampled (the query's line limit) and the proxy another set. + Drilldown's field value picker shows different choices. +request: + method: GET + path: /loki/api/v1/detected_field/service_name/values + params: + query: '{service_name="api-gateway"}' + start: 'now-1h' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [values] + expect: | + Loki's values. +gap: + status: open + impact: drilldown-visible + effort: M + area: 'internal/proxy detected_field values (sampling window and stream-label fields)' + loki: 'pkg/querier detected field values (v3.7.7): values of the sampled lines' + proxy: 'empty for stream-label fields; different sample' + planned_test: 'TestCompat_DetectedFieldValues (test/e2e-compat, group drilldown)' + clusters: + - 0e9d9652 + - 4a75b280 + - 92699938 diff --git a/conformance/registry/cases/drilldown/detected-fields-type-inference.yaml b/conformance/registry/cases/drilldown/detected-fields-type-inference.yaml new file mode 100644 index 000000000..40107798d --- /dev/null +++ b/conformance/registry/cases/drilldown/detected-fields-type-inference.yaml @@ -0,0 +1,36 @@ +id: drilldown/detected-fields-type-inference +title: detected_fields infers a different type for some fields (open) +proves: + - loki_api_v1_detected_fields +consumer: drilldown +why: | + Loki types `size_bytes` (cache-redis) as int where the proxy says string, + and `retries` (worker-service) as int where the proxy says duration; + Drilldown offers different filter operators. + + In the differential run the type difference is secondary on requests whose + primary difference is the `_extracted` collision (semantics/extracted- + suffix-collision owns those clusters), so this case claims none. +request: + method: GET + path: /loki/api/v1/detected_fields + params: + query: '{service_name="cache-redis"}' + start: 'now-1h' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [fields.type] + expect: | + Loki's types. +gap: + status: open + impact: drilldown-visible + effort: S + area: 'internal/proxy detected_fields type inference' + loki: 'pkg/querier detected fields type detection (v3.7.7): int before duration before string' + proxy: 'different type' + planned_test: 'TestCompat_DetectedFieldsTypes (test/e2e-compat, group drilldown)' + clusters: [] diff --git a/conformance/registry/cases/drilldown/detected-labels-sampled-cardinality.yaml b/conformance/registry/cases/drilldown/detected-labels-sampled-cardinality.yaml new file mode 100644 index 000000000..ebcd0a09f --- /dev/null +++ b/conformance/registry/cases/drilldown/detected-labels-sampled-cardinality.yaml @@ -0,0 +1,34 @@ +id: drilldown/detected-labels-sampled-cardinality +title: detected_labels cardinality is sampled from the window's last minutes; Loki counts every stream its ingesters hold (documented) +proves: + - loki_api_v1_detected_labels +consumer: drilldown +why: | + Loki answers detected_labels from the streams its ingesters hold, whatever + the requested window (pkg/ingester/instance.go LabelsWithValues); the proxy + samples the window's last 5 minutes (metadataMaxFieldNamesWindow). The label + names must match; only the cardinality numbers are covered by this case + (bench/visual's compare rule says the same). +request: + method: GET + path: /loki/api/v1/detected_labels + params: + query: '{env="production"}' + start: 'now-1h' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [detectedLabels.cardinality] + expect: | + Same label names; cardinality differs by design. +gap: + status: documented + impact: drilldown-visible + effort: M + area: 'internal/proxy/patterns.go handleDetectedLabels' + loki: 'pkg/ingester/instance.go LabelsWithValues' + proxy: 'sampled cardinality' + planned_test: 'bench/parity diff rule (documented facet)' + clusters: [] diff --git a/conformance/registry/cases/drilldown/patterns-mined-differently.yaml b/conformance/registry/cases/drilldown/patterns-mined-differently.yaml new file mode 100644 index 000000000..7770f9bf9 --- /dev/null +++ b/conformance/registry/cases/drilldown/patterns-mined-differently.yaml @@ -0,0 +1,38 @@ +id: drilldown/patterns-mined-differently +title: Patterns come from different miners on each side (documented) +proves: + - loki_api_v1_patterns +consumer: drilldown +why: | + Loki's pattern ingester mines the lines pushed to it; the proxy (with + -patterns-autodetect-from-queries) mines the lines its queries returned. The + pattern sets differ by design and depend on history (bench/visual compares + them as history-dependent); on the seeded stack Loki had no patterns for + most services. +request: + method: GET + path: /loki/api/v1/patterns + params: + query: '{service_name="cache-redis"}' + start: 'now-1h' + end: 'now' + step: '60s' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [data] + expect: | + Comparable only in shape. +gap: + status: documented + impact: drilldown-visible + effort: L + area: 'internal/proxy/patterns.go' + loki: 'pkg/pattern (ingester-side mining)' + proxy: 'query-side mining' + planned_test: 'bench/visual history-dependent comparison (exists)' + clusters: + - 2c8e8dca + - 556a81b7 + - 7c1a4612 diff --git a/conformance/registry/cases/drilldown/plugin-legend-sync-race.yaml b/conformance/registry/cases/drilldown/plugin-legend-sync-race.yaml new file mode 100644 index 000000000..12131d5f3 --- /dev/null +++ b/conformance/registry/cases/drilldown/plugin-legend-sync-race.yaml @@ -0,0 +1,37 @@ +id: drilldown/plugin-legend-sync-race +title: Logs Drilldown crashes a breakdown page when a query answers before the panel module loads (upstream, not the proxy) +proves: + - loki_api_v1_query_range +consumer: drilldown +why: | + Logs Drilldown 2.5.x shows "Plugin failed to load" when a breakdown query is + answered before the page's first time-series panel module has loaded (legend + sync calls the panel's field-config update while the panel plugin is + missing). It happens on any datasource, Loki direct included, and only more + often with fast answers (proxy cache hits). Recorded so it is never mistaken + for a proxy difference; bench/visual's capture waits for the module. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'sum by (level) (count_over_time({service_name="api-gateway"}[$__auto]))' + start: 'now-1h' + end: 'now' + step: '5' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [ui] + expect: | + No proxy action; upstream plugin issue. +gap: + status: upstream + impact: drilldown-visible + effort: S + area: 'none (grafana-lokiexplore-app)' + loki: 'n/a (client race)' + proxy: 'n/a' + planned_test: 'bench/visual capture waits for the panel module (already)' + clusters: [] diff --git a/conformance/registry/cases/limits/drilldown-7d-breakdown-response-limit.yaml b/conformance/registry/cases/limits/drilldown-7d-breakdown-response-limit.yaml new file mode 100644 index 000000000..4636427c2 --- /dev/null +++ b/conformance/registry/cases/limits/drilldown-7d-breakdown-response-limit.yaml @@ -0,0 +1,49 @@ +id: limits/drilldown-7d-breakdown-response-limit +title: Metric queries fail on the 64 MiB buffered-response budget where Loki answers (open) +proves: + - heavy-metric-fetch-bounds + - loki_api_v1_query_range +consumer: drilldown +why: | + High-cardinality breakdowns over 7d (pod, batch_id) produce stats responses + above the proxy's 64 MiB buffered-response budget (-backend-max-buffered- + response-bytes; range_metric_compat.go maxStatsBucketResponseBytes) and + fail, where Loki returns the series capped by max_query_series with its + partial-result warning. + + The differential run hit the same budget over one hour of generator data: + `quantile_over_time(0.8, {namespace="prod"} | unwrap bytes [5m])`, + `rate_counter(...)` and `max(...)` over `| json` answer HTTP 502 `manual + metric response exceeds 67108864 bytes; narrow the query or increase + -backend-max-buffered-response-bytes` while Loki answers 200. The raw-row + paths buffer rows proportional to the data volume, which the no-raw-row rule + forbids by default. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'quantile_over_time(0.8, {namespace="prod"} | unwrap bytes [5m])' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status] + expect: | + 200 with Loki's series (partial warning over max_query_series). +gap: + status: open + impact: drilldown-visible + effort: M + area: 'internal/proxy/range_metric_compat.go:767, execution_limits.go:11 (raw-row metric paths buffer rows; push down or stream)' + loki: 'pkg/logql engine series limit (max_query_series) with partial result for Drilldown' + proxy: '502 over the 64 MiB budget' + planned_test: 'TestCompat_MetricBufferedBudget1h + TestE2E_Drilldown7dHighCardinalityBreakdown (test/e2e-compat, groups semantics and drilldown)' + clusters: + - 1e223d96 + - 6d9fc70d + - 7b521e8a + - a644d4bb diff --git a/conformance/registry/cases/limits/drilldown-breakdown-keeps-busiest.yaml b/conformance/registry/cases/limits/drilldown-breakdown-keeps-busiest.yaml new file mode 100644 index 000000000..294b9dc4b --- /dev/null +++ b/conformance/registry/cases/limits/drilldown-breakdown-keeps-busiest.yaml @@ -0,0 +1,36 @@ +id: limits/drilldown-breakdown-keeps-busiest +title: Over max_query_series a Drilldown breakdown keeps the busiest series; Loki keeps the first it meets (documented) +proves: + - series-limits-and-partial-results +consumer: drilldown +why: | + Above max_query_series Loki returns the first series its evaluation meets + with a partial-result warning; the proxy keeps the busiest series ranked by + VictoriaLogs (KNOWN_ISSUES). Every kept series has Loki's values; the kept + set can differ for equal volumes. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'sum by (pod) (count_over_time({env="production"}[5m]))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series] + expect: | + Same kept set (unspecified in Loki). +gap: + status: documented + impact: drilldown-visible + effort: S + area: 'internal/proxy series limit ranking' + loki: 'pkg/logql engine series limit' + proxy: 'busiest series kept' + planned_test: 'cases/limits/drilldown-partial-with-warning (exists)' + clusters: + - b88d1f5c diff --git a/conformance/registry/cases/metric-identity/structured-metadata-splits-series.yaml b/conformance/registry/cases/metric-identity/structured-metadata-splits-series.yaml new file mode 100644 index 000000000..5b19e5cfa --- /dev/null +++ b/conformance/registry/cases/metric-identity/structured-metadata-splits-series.yaml @@ -0,0 +1,73 @@ +id: metric-identity/structured-metadata-splits-series +title: Loki splits a stream's metric series per structured-metadata set; the proxy keeps the stream (owner-accepted deviation) +proves: + - metric-series-identity +consumer: explore +why: | + Loki's sample labels include the entry's structured metadata + (pkg/logql/log/metrics_extraction.go:102-120), so a stream whose lines carry + a per-line trace_id is many series: `rate({namespace="prod"}[1h])` is 49657 + series on Loki and 10155 on the proxy, and every + count/min/max/avg/stddev/topk over such series folds different inputs (count + by (app) 911 vs 83). The owner accepted the stream identity (state metric- + series-identity, waiver metric-identity-metadata-and-dynamic-keys, expiry + 2027-03-31). +request: + method: GET + path: /loki/api/v1/query + params: + query: 'count by (app) (count_over_time({env="production"}[5m]))' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series, values] + expect: | + Loki's series identity (deviation accepted). +gap: + status: owner-kept + impact: explore-visible + effort: L + area: 'internal/translator series identity (stats by stream); VictoriaLogs cannot group by a row''s remaining fields' + loki: 'pkg/logql/log/metrics_extraction.go:102-120 builder.Add(StructuredMetadataLabel) -> GroupedLabels' + proxy: 'stream identity' + planned_test: 'differential run cluster count (bench/parity)' + clusters: + - 02fdd315 + - 05582624 + - 13c8bdcd + - 1bd439f7 + - 229e4f30 + - 272583f4 + - 2cd52d1c + - 46d8ea74 + - 4bfe0c95 + - 4c5d2b49 + - 4e3297e0 + - 51011993 + - 528f5b7e + - 6069aa8c + - 6a4fb9d0 + - 6d606bf1 + - 6f105696 + - 7af0e9f0 + - 84eb302d + - 886cdc75 + - 8b8db278 + - 90729d32 + - 9273ee61 + - 9d1a4d17 + - 9f19b596 + - a274a06b + - a6b8ae84 + - ac314fbf + - acdff84d + - b65ce096 + - b8436a40 + - b8f96844 + - cf717de4 + - dbd817af + - e9319bcf + - eb31f562 + - f1aa2f22 diff --git a/conformance/registry/cases/parsed-identity/bare-parser-metric-duplicates-extracted.yaml b/conformance/registry/cases/parsed-identity/bare-parser-metric-duplicates-extracted.yaml new file mode 100644 index 000000000..8eb0ad336 --- /dev/null +++ b/conformance/registry/cases/parsed-identity/bare-parser-metric-duplicates-extracted.yaml @@ -0,0 +1,50 @@ +id: parsed-identity/bare-parser-metric-duplicates-extracted +title: A bare `| json` metric labels each series with every parsed key twice, once with _extracted (open) +proves: + - parsed-label-series-identity + - loki_api_v1_query_range +consumer: explore +why: | + `count_over_time({service_name="api-gateway"} | json [3m])` gives the same + series count on both sides, but every proxy series carries each parsed key + twice: `duration_ms` and `duration_ms_extracted`, `method` and + `method_extracted`, and so on. Loki adds the `_extracted` suffix only to a + parsed key that collides with a stream label or structured metadata name, so + its series have no such duplicates; Grafana's legend and series names + differ. +request: + method: GET + path: /loki/api/v1/query + params: + query: 'count_over_time({service_name="api-gateway"} | json [3m])' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series labels] + expect: | + Loki's label sets. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy exact parser series identity (bare parser metric labels)' + loki: 'pkg/logql/log/parser.go:152 (suffix only on collision)' + proxy: 'every parsed key duplicated with _extracted' + planned_test: 'TestCompat_BareParserMetricLabels (test/e2e-compat, group semantics)' + clusters: + - 1173e4c5 + - 5262a574 + - 907c7acb + - 9148c234 + - 9be22235 + - 9f2d98ad + - ac8e75c3 + - b6078924 + - cab142d9 + - cfff8628 + - d088f040 + - df222120 + - eb1010a3 + - fd91e575 diff --git a/conformance/registry/cases/parsed-identity/dynamic-key-parsers.yaml b/conformance/registry/cases/parsed-identity/dynamic-key-parsers.yaml new file mode 100644 index 000000000..6c8942c7e --- /dev/null +++ b/conformance/registry/cases/parsed-identity/dynamic-key-parsers.yaml @@ -0,0 +1,66 @@ +id: parsed-identity/dynamic-key-parsers +title: `| json` and `| logfmt` metric series keep the stream identity by default (owner-accepted deviation, opt-in flag) +proves: + - parsed-label-series-identity +consumer: explore +why: | + Loki names a metric series by every label `| json` / `| logfmt` extracted, + so `count_over_time({app="payment-service"} | logfmt [1m])` is 22279 series + on Loki and 1091 on the proxy, and sums of per-series max/avg differ (sum by + (level) (max_over_time(... | unwrap status ...)): Loki 82683, proxy 503). + Accepted deviation with -exact-parser-series-identity as the opt-in (state + parsed-label-series-identity, waiver parsed-identity-dynamic-keys). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'count_over_time({service_name="payment-service"} | logfmt [1m])' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series] + expect: | + Loki's series (deviation accepted by default). +gap: + status: owner-kept + impact: explore-visible + effort: L + area: 'internal/translator parser series identity; -exact-parser-series-identity' + loki: 'pkg/logql/log/parser.go label extraction into the sample labels' + proxy: 'stream identity unless the flag is set' + planned_test: 'differential run cluster count (bench/parity)' + clusters: + - 03f3b6e5 + - 0f386e36 + - 0f49a837 + - 1f333942 + - 241fb900 + - 273c6ca5 + - 370c5620 + - 375b5df6 + - 37898b71 + - 472453d0 + - 4d52c5cb + - 5bd75aed + - 609c0d8a + - 6bc7202e + - 7cc910c2 + - 833babd8 + - 891109a9 + - aa8cfdc8 + - c0f15837 + - c17c1437 + - c434dd07 + - c84cb5a1 + - cd477d03 + - d21214b2 + - d31150a4 + - dcf0f4ed + - e598f28d + - e6579e4c + - e7da85bf diff --git a/conformance/registry/cases/parsed-identity/regexp-partial-match-series-count.yaml b/conformance/registry/cases/parsed-identity/regexp-partial-match-series-count.yaml index 4740b7edb..725f6a9fd 100644 --- a/conformance/registry/cases/parsed-identity/regexp-partial-match-series-count.yaml +++ b/conformance/registry/cases/parsed-identity/regexp-partial-match-series-count.yaml @@ -62,3 +62,14 @@ compare: pattern matches every line: Loki 748 series all carrying mth, branch 739 all carrying mth, main 739 carrying none. That contrast is the evidence that the gap is about non-matching lines. +gap: + status: open + impact: api-only + effort: M + area: 'internal/proxy regexp capture series identity (non-matching lines)' + loki: 'pkg/logql/log/parser.go RegexpParser (no capture label for a non-matching line)' + proxy: 'about 3x Loki''s series' + planned_test: 'TestCompat_RegexpPartialMatchSeries (test/e2e-compat, group semantics)' + clusters: + - 7ba35380 + - 952bbb24 diff --git a/conformance/registry/cases/profiles/detected-fields-no-extracted-suffix.yaml b/conformance/registry/cases/profiles/detected-fields-no-extracted-suffix.yaml new file mode 100644 index 000000000..2e119896e --- /dev/null +++ b/conformance/registry/cases/profiles/detected-fields-no-extracted-suffix.yaml @@ -0,0 +1,36 @@ +id: profiles/detected-fields-no-extracted-suffix +title: detected_fields lists service / service.name without Loki's _extracted suffix (owner decision) +proves: + - loki_api_v1_detected_fields + - loki-compatible-profile +consumer: drilldown +why: | + Where structured metadata service.name collides with the stream label + service_name, Loki's detected_fields lists service_name_extracted; the proxy + lists service / service.name. The owner stamped this as the intended + behaviour (2026-04-25). It covers only those service fields: every other + `_extracted` field Loki lists (level_extracted, version_extracted, ...) is + an ordinary difference (semantics/extracted-suffix-collision). +request: + method: GET + path: /loki/api/v1/detected_fields + params: + query: '{service_name="otel-collector"}' + start: 'now-1h' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [fields.label] + expect: | + service / service.name instead of service_name_extracted (kept). +gap: + status: owner-kept + impact: drilldown-visible + effort: S + area: 'internal/proxy/label_handlers.go handleDetectedFields' + loki: 'pkg/logql/log/parser.go:25 duplicateSuffix' + proxy: 'service / service.name' + planned_test: 'bench/parity diff rule (documented facet, service fields only)' + clusters: [] diff --git a/conformance/registry/cases/profiles/existence-filter-on-sanitized-key.yaml b/conformance/registry/cases/profiles/existence-filter-on-sanitized-key.yaml new file mode 100644 index 000000000..60c38dd5c --- /dev/null +++ b/conformance/registry/cases/profiles/existence-filter-on-sanitized-key.yaml @@ -0,0 +1,44 @@ +id: profiles/existence-filter-on-sanitized-key +title: An existence filter on a sanitized key reads the stored dotted field, not the JSON line key (open) +proves: + - loki-compatible-profile + - loki_api_v1_query_range +consumer: drilldown +why: | + Loki's json parser sanitizes every key it extracts, so `| json | + service_version!=""` keeps a line whose JSON holds `service_version` (or + `service.version`, or `{"service":{"version":..}}`) and drops the rest. The + proxy translates the existence check on an OTel-shaped underscore name to + the stored structured-metadata field (`filter "service.version":!""`), so it + reads the push's metadata and ignores the key in the line: on the visual + stack Loki returned 15 entries and the proxy 0 for the Drilldown field page + of service_version. + + profiles/sanitized-json-key-filter widened every other label filter to all + spellings of the key; the existence check was left on its single key because + the Drilldown single-field fast paths recognise that exact shape. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="otel-collector"} | json | service_version!=""' + start: 'now-1h' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values] + expect: | + Loki's entries: every line whose parsed labels hold service_version. +gap: + status: open + impact: drilldown-visible + effort: M + area: 'internal/translator (label-filter key variants, parsed_key_variants.go); Drilldown single-field fast paths' + loki: 'pkg/logql/log/parser.go:141 sanitizeLabelKey on every extracted key; label filter reads the parsed label' + proxy: 'filter on the stored dotted field only' + planned_test: 'TestCompat_ExistenceFilterSanitizedKey (test/e2e-compat, group semantics)' + clusters: [] diff --git a/conformance/registry/cases/profiles/grafana-stats-error-as-warning.yaml b/conformance/registry/cases/profiles/grafana-stats-error-as-warning.yaml new file mode 100644 index 000000000..e9bdbb23f --- /dev/null +++ b/conformance/registry/cases/profiles/grafana-stats-error-as-warning.yaml @@ -0,0 +1,35 @@ +id: profiles/grafana-stats-error-as-warning +title: A failed stats query from Grafana answers 200 with warnings; Loki returns the error (documented) +proves: + - loki-compatible-profile +consumer: explore +why: | + When VictoriaLogs fails a stats_query_range request from Grafana, the proxy + returns HTTP 200 with Loki-style `warnings` and a Warning header instead of + the upstream error (KNOWN_ISSUES); Loki returns the error status and Grafana + shows the error. Proxy-only behaviour in the Loki-compatible profile. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'sum by (level) (count_over_time({env="production"}[5m]))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status, warnings] + expect: | + The error status Loki returns. +gap: + status: documented + impact: grafana-datasource + effort: S + area: 'internal/proxy Grafana-sourced stats error handling' + loki: 'pkg/querier/queryrange error propagation (no warnings conversion)' + proxy: '200 + warnings' + planned_test: 'TestCompat_GrafanaStatsErrorStatus (test/e2e-compat, fault injection)' + clusters: [] diff --git a/conformance/registry/cases/profiles/json-extraction-list-exposes-only-listed.yaml b/conformance/registry/cases/profiles/json-extraction-list-exposes-only-listed.yaml new file mode 100644 index 000000000..dbf094c3f --- /dev/null +++ b/conformance/registry/cases/profiles/json-extraction-list-exposes-only-listed.yaml @@ -0,0 +1,42 @@ +id: profiles/json-extraction-list-exposes-only-listed +title: Extraction lists (`| json msg="msg"`, `| logfmt op`) expose level from the line (open) +proves: + - loki-compatible-profile + - loki_api_v1_query_range +consumer: explore +why: | + Loki's JSON expression parser extracts only the listed keys + (JSONExpressionParser), so `| json msg="msg"` adds one parsed label. The + proxy also reports `level` from the line (the row's level reading for + detected_level leaks into the labels). + + Measured by the differential run: `{service_name="cache-redis"} | logfmt op` + reports level=info (the line's) where Loki keeps the stream label + level=debug, on 20 queries. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="api-gateway"} | json msg="msg"' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values, parsed labels] + expect: | + Parsed labels: msg only. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy stage-field exposure (extraction lists)' + loki: 'pkg/logql/log/parser.go:671-700 JSONExpressionParser.Process (listed ids only)' + proxy: 'extra parsed label level' + planned_test: 'TestCompat_JSONExtractionListExposesOnlyListed (test/e2e-compat, group semantics)' + note: 'Measured on main at fcaccfcc, before #652 rewrote the tail and log-row label conversion; recheck the behaviour on the current code before fixing.' + clusters: + - 7dac2581 diff --git a/conformance/registry/cases/profiles/json-extraction-list-missing-key.yaml b/conformance/registry/cases/profiles/json-extraction-list-missing-key.yaml new file mode 100644 index 000000000..e84b3e94c --- /dev/null +++ b/conformance/registry/cases/profiles/json-extraction-list-missing-key.yaml @@ -0,0 +1,34 @@ +id: profiles/json-extraction-list-missing-key +title: A json extraction-list key absent from the line is dropped; Loki keeps it in the metric series (open) +proves: + - parsed-label-series-identity + - loki_api_v1_query_range +consumer: api +why: | + `count_over_time({...} | json + layer7_something_specific="layer7_something_specific" [12m])` labels Loki's + series with the listed key; the proxy's series lack it. +request: + method: GET + path: /loki/api/v1/query + params: + query: 'count_over_time({service_name="api-gateway"} | json nokey="nokey" [5m])' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series labels] + expect: | + Loki's label sets. +gap: + status: open + impact: api-only + effort: S + area: 'internal/proxy stage-field exposure (extraction lists, metric path)' + loki: 'pkg/logql/log/parser.go:671-720 JSONExpressionParser.Process' + proxy: 'key missing' + planned_test: 'TestCompat_JSONExtractionListMissingKey (test/e2e-compat, group semantics)' + clusters: + - 3eddbf61 + - fcc4cda7 diff --git a/conformance/registry/cases/profiles/json-line-stored-without-line.yaml b/conformance/registry/cases/profiles/json-line-stored-without-line.yaml index 8f41e80c9..85a460724 100644 --- a/conformance/registry/cases/profiles/json-line-stored-without-line.yaml +++ b/conformance/registry/cases/profiles/json-line-stored-without-line.yaml @@ -1,5 +1,5 @@ id: profiles/json-line-stored-without-line -title: A JSON line pushed without _msg is stored without its line (open gap) +title: A JSON line pushed without _msg is stored without its line (documented) proves: - loki-compatible-profile - loki_api_v1_query_range @@ -42,3 +42,12 @@ compare: fields: [streams.values, metadata_categories] expect: | The original line and Loki's label categories (not met: see why). +gap: + status: documented + impact: explore-visible + effort: L + area: 'client push configuration (disable_message_parsing=1); docs/KNOWN_ISSUES.md' + loki: 'pkg/distributor push keeps the line verbatim' + proxy: 'line rebuilt from stored fields' + planned_test: 'cases/profiles/stage-field-exposure with disable_message_parsing pushes' + clusters: [] diff --git a/conformance/registry/cases/profiles/json-number-label-text.yaml b/conformance/registry/cases/profiles/json-number-label-text.yaml new file mode 100644 index 000000000..c781d2229 --- /dev/null +++ b/conformance/registry/cases/profiles/json-number-label-text.yaml @@ -0,0 +1,45 @@ +id: profiles/json-number-label-text +title: JSON numbers become label values in a different text form ("14.0" vs "14") (open) +proves: + - parsed-label-series-identity + - loki_api_v1_query_range +consumer: drilldown +why: | + Loki's json parser keeps a number's text from the line, so a value written + `14.0` is the label `14.0`. VictoriaLogs normalises numbers when it unpacks + them, so the proxy's `sum by (duration_s) + (count_over_time({service_name="batch-etl"} | json [5m]))` has series `14` + where Loki has `14.0` (and a different series count where both spellings + occur). Drilldown field breakdowns over such fields show different values. + + Seen on batch-etl fields (throughput_rps, duration_s, confidence): `sum by + (throughput_rps) (...)` returns 5325 series on Loki and 5422 on the proxy + over an hour, with `1.0` on Loki and `1` on the proxy. +request: + method: GET + path: /loki/api/v1/query + params: + query: 'sum by (duration_s) (count_over_time({service_name="batch-etl"} | json [5m]))' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series labels] + expect: | + Loki's value text. +gap: + status: open + impact: drilldown-visible + effort: M + area: 'internal/proxy JSON number rendering of unpacked values (VictoriaLogs unpack_json normalises)' + loki: 'pkg/logql/log/parser.go readValue (raw number bytes)' + proxy: 'normalised number text' + planned_test: 'TestCompat_JSONNumberLabelText (test/e2e-compat, group drilldown)' + clusters: + - 056ee345 + - 7938c66e + - 8c3eca09 + - a8228433 + - bd84536f + - c2523a5e diff --git a/conformance/registry/cases/profiles/keep-removes-derived-service-name.yaml b/conformance/registry/cases/profiles/keep-removes-derived-service-name.yaml new file mode 100644 index 000000000..84846bf8b --- /dev/null +++ b/conformance/registry/cases/profiles/keep-removes-derived-service-name.yaml @@ -0,0 +1,42 @@ +id: profiles/keep-removes-derived-service-name +title: `| keep` removes service_name the proxy derived for a stream without one (open) +proves: + - service-name-derivation + - loki_api_v1_query_range +consumer: explore +why: | + Loki's keep stage deletes every non-special label not named in its list, + including service_name (pkg/logql/log/keep_labels.go). The proxy derives + service_name on the read path for rows whose stream has none and adds it + after the pipeline, so `| keep a, b` still shows service_name. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{namespace="prod"} | json | keep level' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.stream] + expect: | + Only the kept labels. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy service_name derivation after keep/drop' + loki: 'pkg/logql/log/keep_labels.go:15-44 KeepLabels.Process' + proxy: 'derived service_name re-added after keep' + planned_test: 'TestCompat_KeepRemovesDerivedServiceName (test/e2e-compat, group semantics)' + note: 'Measured on main at fcaccfcc, before #652 rewrote the tail and log-row label conversion; recheck the behaviour on the current code before fixing.' + clusters: + - 0acfebdb + - 3e6bb855 + - 59ebda99 + - 7af7ff7b + - 8ae09281 diff --git a/conformance/registry/cases/profiles/keep-structured-metadata-key.yaml b/conformance/registry/cases/profiles/keep-structured-metadata-key.yaml new file mode 100644 index 000000000..c1cf02deb --- /dev/null +++ b/conformance/registry/cases/profiles/keep-structured-metadata-key.yaml @@ -0,0 +1,37 @@ +id: profiles/keep-structured-metadata-key +title: `| keep` of a structured-metadata key drops it (open) +proves: + - loki-compatible-profile + - loki_api_v1_query_range +consumer: explore +why: | + `{service_name="checkout-svc"} | keep deployment_environment` keeps the + structured-metadata label on Loki; the proxy removes it (and keeps the + derived service_name instead, profiles/keep-removes-derived-service-name). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="checkout-svc"} | keep deployment_environment' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values, structuredMetadata] + expect: | + deployment_environment kept. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy keep stage over structured metadata (Loki-compatible profile names)' + loki: 'pkg/logql/log/keep_labels.go:15-44' + proxy: 'structured-metadata key dropped' + planned_test: 'TestCompat_KeepStructuredMetadataKey (test/e2e-compat, group otel-edge)' + note: 'Measured on main at fcaccfcc, before #652 rewrote the tail and log-row label conversion; recheck the behaviour on the current code before fixing.' + clusters: + - 4e8cf844 diff --git a/conformance/registry/cases/profiles/label-filter-on-line-field.yaml b/conformance/registry/cases/profiles/label-filter-on-line-field.yaml index 50da34ee2..5a9a779be 100644 --- a/conformance/registry/cases/profiles/label-filter-on-line-field.yaml +++ b/conformance/registry/cases/profiles/label-filter-on-line-field.yaml @@ -60,3 +60,12 @@ compare: No entries, as Loki answers; the same entries as Loki once a stage exposes the key (TestCompat_StageFieldExposureLikeLoki), and the limit filled from older rows (TestCompat_LabelFilterOnLineFieldFillsLimitLikeLoki). +gap: + status: owner-kept + impact: explore-visible + effort: M + area: 'internal/proxy label-filter refill (-label-filter-refill-max-pages); metric queries on keys no stage exposes' + loki: 'pkg/logql/log/label_filter.go (filter reads only labels the pipeline set)' + proxy: 'metric-query filters/grouping on keys no stage exposes read the stored field (documented, owner kept)' + planned_test: 'TestCompat_LabelFilterOnLineField (exists, profile tests)' + clusters: [] diff --git a/conformance/registry/cases/profiles/label-filter-regexp-anchoring.yaml b/conformance/registry/cases/profiles/label-filter-regexp-anchoring.yaml index 606278b89..36f858740 100644 --- a/conformance/registry/cases/profiles/label-filter-regexp-anchoring.yaml +++ b/conformance/registry/cases/profiles/label-filter-regexp-anchoring.yaml @@ -35,3 +35,12 @@ compare: Loki returns no line for a value that only contains the pattern; the proxy returns it. The expected end state is Loki's answer; until then this case records the observed difference, not a passing assertion. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/translator label-filter regexp emission (anchor ^(?:re)$); external PR #572 open' + loki: 'pkg/logql/log/label_filter.go (regexp matcher on the whole value)' + proxy: 'substring match' + planned_test: 'TestCompat_LabelFilterRegexpAnchoring (test/e2e-compat, group semantics)' + clusters: [] diff --git a/conformance/registry/cases/profiles/metadata-default-lookback.yaml b/conformance/registry/cases/profiles/metadata-default-lookback.yaml new file mode 100644 index 000000000..5fd7ece92 --- /dev/null +++ b/conformance/registry/cases/profiles/metadata-default-lookback.yaml @@ -0,0 +1,31 @@ +id: profiles/metadata-default-lookback +title: Label endpoints without start/end default to a different lookback than Loki's 1h (documented) +proves: + - loki_api_v1_labels + - loki_api_v1_label_name_values + - loki_api_v1_series +consumer: api +why: | + Loki bounds /labels, /label/{name}/values and /series without start/end to + the last hour (pkg/loghttp/params.go defaultSince). The proxy's -metadata- + default-lookback defaults to 12h (KNOWN_ISSUES); the e2e stack sets 1h. +request: + method: GET + path: /loki/api/v1/labels + params: + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [data] + expect: | + Labels of the last hour. +gap: + status: documented + impact: api-only + effort: S + area: 'internal/config flag default -metadata-default-lookback' + loki: 'pkg/loghttp/params.go defaultSince = 1h' + proxy: '12h default' + planned_test: 'TestCompat_MetadataDefaultLookback (test/e2e-compat, group core)' + clusters: [] diff --git a/conformance/registry/cases/profiles/multi-tenant-volume-merge.yaml b/conformance/registry/cases/profiles/multi-tenant-volume-merge.yaml new file mode 100644 index 000000000..3b3b1f031 --- /dev/null +++ b/conformance/registry/cases/profiles/multi-tenant-volume-merge.yaml @@ -0,0 +1,32 @@ +id: profiles/multi-tenant-volume-merge +title: Multi-tenant volume returns per-tenant series with __tenant_id__; Loki sums the tenants' volumes (documented) +proves: + - loki_api_v1_index_volume +consumer: api +why: | + For a multi-tenant X-Scope-OrgID, Loki sums volumes of the same series + across tenants; the proxy returns each tenant's volumes with a __tenant_id__ + label, up to limit per tenant (KNOWN_ISSUES). +request: + method: GET + path: /loki/api/v1/index/volume + params: + query: '{env="production"}' + start: 'now-1h' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [data.result] + expect: | + Summed series without __tenant_id__. +gap: + status: documented + impact: api-only + effort: S + area: 'internal/proxy/volume.go multi-tenant merge' + loki: 'pkg/querier/queryrange volume merge' + proxy: 'per-tenant series' + planned_test: 'TestCompat_MultiTenantVolume (test/e2e-compat, group tail-multitenancy)' + clusters: [] diff --git a/conformance/registry/cases/profiles/structured-metadata-label-collision.yaml b/conformance/registry/cases/profiles/structured-metadata-label-collision.yaml index aea7ce589..f6c07980b 100644 --- a/conformance/registry/cases/profiles/structured-metadata-label-collision.yaml +++ b/conformance/registry/cases/profiles/structured-metadata-label-collision.yaml @@ -34,3 +34,33 @@ compare: fields: [structured_metadata_keys] expect: | structuredMetadata carries service_name_extracted, not service_name. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/proxy structured-metadata naming in log responses (collision with derived service_name)' + loki: 'pkg/logql/log/labels.go LabelsBuilder (structured metadata renamed with _extracted on a stream-label collision)' + proxy: 'structuredMetadata service_name beside the stream label' + planned_test: 'TestCompat_StructuredMetadataLabelCollision (test/e2e-compat, group otel-edge)' + note: 'Measured on main at fcaccfcc, before #652 rewrote the tail and log-row label conversion; recheck the behaviour on the current code before fixing.' + clusters: + - 0e676869 + - 1802c76f + - 1de4e9f2 + - 25b77e2f + - 265b2418 + - 269ef189 + - 343338a6 + - 3df8d769 + - 56dc3ddd + - 57ff8760 + - 5aa8ef5c + - 6650ff42 + - 6f3e6db5 + - 6f88a869 + - 829672cc + - b2133367 + - b3a784df + - cb109373 + - d32173c3 + - e6dc2163 diff --git a/conformance/registry/cases/profiles/volume-bytes-accounting.yaml b/conformance/registry/cases/profiles/volume-bytes-accounting.yaml new file mode 100644 index 000000000..a2c37eb9a --- /dev/null +++ b/conformance/registry/cases/profiles/volume-bytes-accounting.yaml @@ -0,0 +1,37 @@ +id: profiles/volume-bytes-accounting +title: Volume values count line bytes; Loki counts chunk bytes with structured metadata (documented) +proves: + - loki_api_v1_index_volume + - loki_api_v1_index_volume_range +consumer: drilldown +why: | + `/index/volume` and `/index/volume_range` report bytes like Loki, but the + proxy sums exact line bytes (VictoriaLogs `sum_len(_msg)`) while Loki's + volume is its chunk size estimate, which also counts structured metadata + (docs/KNOWN_ISSUES.md). The differential run marks only a value difference + of a matched series with this case: a missing or extra series or sample is + an ordinary difference. +request: + method: GET + path: /loki/api/v1/index/volume + params: + query: '{env="production"}' + start: 'now-1h' + end: 'now' + targetLabels: 'service_name' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [data.result values] + expect: | + Same series and samples; byte values differ by design. +gap: + status: documented + impact: drilldown-visible + effort: L + area: 'internal/proxy/volume.go; docs/KNOWN_ISSUES.md' + loki: 'pkg/storage volume index (chunk bytes incl. structured metadata)' + proxy: 'line bytes' + planned_test: 'bench/parity diff rule (documented facet)' + clusters: [] diff --git a/conformance/registry/cases/profiles/volume-detected-level-extension.yaml b/conformance/registry/cases/profiles/volume-detected-level-extension.yaml new file mode 100644 index 000000000..6596638b6 --- /dev/null +++ b/conformance/registry/cases/profiles/volume-detected-level-extension.yaml @@ -0,0 +1,38 @@ +id: profiles/volume-detected-level-extension +title: index/volume answers targetLabels=detected_level; Loki returns no volume for it (documented proxy extension) +proves: + - loki_api_v1_index_volume + - loki-compatible-profile +consumer: drilldown +why: | + Loki keeps detected_level in structured metadata and its volume index has no + entry for it, so `targetLabels=detected_level` answers no series. The proxy + answers per-level volumes (lines without a level as detected_level="") + (KNOWN_ISSUES). A behaviour Loki does not have, served in the Loki- + compatible profile. +request: + method: GET + path: /loki/api/v1/index/volume + params: + query: '{env="production"}' + start: 'now-1h' + end: 'now' + targetLabels: 'detected_level' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [data.result] + expect: | + Loki's answer (no detected_level series). +gap: + status: documented + impact: drilldown-visible + effort: S + area: 'internal/proxy/volume.go' + loki: 'pkg/storage volume index (stream labels only)' + proxy: 'per-level volumes' + planned_test: 'differential run row volume by detected_level' + clusters: + - 5e3fbf46 + - b126c04b diff --git a/conformance/registry/cases/quality/detected-metadata-proxy-cache.yaml b/conformance/registry/cases/quality/detected-metadata-proxy-cache.yaml new file mode 100644 index 000000000..f0129f06d --- /dev/null +++ b/conformance/registry/cases/quality/detected-metadata-proxy-cache.yaml @@ -0,0 +1,36 @@ +id: quality/detected-metadata-proxy-cache +title: detected_labels and detected_fields are served from proxy caches; Loki answers each request fresh (open) +proves: + - data-probing-and-freshness + - loki_api_v1_detected_labels + - loki_api_v1_detected_fields +consumer: drilldown +why: | + Loki computes detected_labels from its ingesters and detected_fields from + the queried lines on every request (no results cache for them). The proxy + caches both (cache keys in internal/proxy/cache_keys.go), so a label or + field that appears after a cached answer is missing until the entry expires, + and Drilldown's label and field lists lag behind Loki's on live data. +request: + method: GET + path: /loki/api/v1/detected_labels + params: + query: '{env="production"}' + start: 'now-15m' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [detectedLabels] + expect: | + A new label visible on the next request. +gap: + status: open + impact: drilldown-visible + effort: S + area: 'internal/proxy/cache_keys.go:48,130 (detected_* cache TTL) + recent-tail freshness bypass' + loki: 'pkg/querier detected labels/fields: no results cache in v3.7.7' + proxy: 'cached answers up to the cache TTL' + planned_test: 'TestE2E_DetectedMetadataFreshness (test/e2e-compat, group drilldown)' + clusters: [] diff --git a/conformance/registry/cases/quality/index-stats-bytes-and-chunks.yaml b/conformance/registry/cases/quality/index-stats-bytes-and-chunks.yaml new file mode 100644 index 000000000..a3cf6c60a --- /dev/null +++ b/conformance/registry/cases/quality/index-stats-bytes-and-chunks.yaml @@ -0,0 +1,34 @@ +id: quality/index-stats-bytes-and-chunks +title: index/stats bytes and chunks have no VictoriaLogs equivalent (documented) +proves: + - loki_api_v1_index_stats +consumer: drilldown +why: | + Loki's /index/stats `bytes` and `chunks` are its chunk accounting + (compressed chunk sizes, chunk count). VictoriaLogs has neither; the proxy + reports `chunks` as the stream count and `bytes` as an estimate from the + stored line lengths. The differential run marks a bytes or chunks difference + with this case only; a streams or entries difference is never covered by it. +request: + method: GET + path: /loki/api/v1/index/stats + params: + query: '{env="production"}' + start: 'now-1h' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [bytes, chunks] + expect: | + streams and entries equal; bytes and chunks differ by design. +gap: + status: documented + impact: drilldown-visible + effort: L + area: 'internal/proxy/label_handlers.go handleIndexStats; docs/KNOWN_ISSUES.md' + loki: 'pkg/storage index stats from chunk refs (bytes, chunks)' + proxy: 'chunks = streams, bytes estimated' + planned_test: 'bench/parity diff rule (documented facet)' + clusters: [] diff --git a/conformance/registry/cases/quality/index-stats-loki-chunk-accounting.yaml b/conformance/registry/cases/quality/index-stats-loki-chunk-accounting.yaml new file mode 100644 index 000000000..44c816311 --- /dev/null +++ b/conformance/registry/cases/quality/index-stats-loki-chunk-accounting.yaml @@ -0,0 +1,37 @@ +id: quality/index-stats-loki-chunk-accounting +title: Loki's index/stats counts chunks it holds twice; the proxy's counts equal Loki's own count_over_time (upstream) +proves: + - loki_api_v1_index_stats +consumer: drilldown +why: | + On a stack whose ingesters retain flushed chunks, Loki's /index/stats counts + a line once per chunk copy (entries 3x in one run) while its own + `sum(count_over_time(...))` over the window equals the proxy's entries. The + differential run marks an entries difference as Loki's chunk accounting only + when Loki's count_over_time equals the proxy's entries, and a streams + difference only when Loki's own /series count equals the proxy's streams + (checked from this change on; the recorded run did not check streams, see + quality/index-stats-streams-differ). +request: + method: GET + path: /loki/api/v1/index/stats + params: + query: '{env="production"}' + start: 'now-1h' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [streams, entries] + expect: | + Entries equal to Loki's count_over_time. +gap: + status: upstream + impact: drilldown-visible + effort: S + area: 'none (Loki chunk index accounting)' + loki: 'pkg/storage stats from chunk refs (flushed and retained chunks both counted)' + proxy: 'exact counts' + planned_test: 'bench/parity cross-check (run.py loki_counts_itself)' + clusters: [] diff --git a/conformance/registry/cases/quality/index-stats-streams-differ.yaml b/conformance/registry/cases/quality/index-stats-streams-differ.yaml new file mode 100644 index 000000000..810c78ad4 --- /dev/null +++ b/conformance/registry/cases/quality/index-stats-streams-differ.yaml @@ -0,0 +1,39 @@ +id: quality/index-stats-streams-differ +title: index/stats stream counts differ from Loki in both directions (open) +proves: + - loki_api_v1_index_stats +consumer: drilldown +why: | + Over the last hour Loki's /index/stats counted 7104 streams for + `{env="production"}` and the proxy 13510; over the Drilldown landing's 15 + minutes Loki counted 7104 and the proxy 3365. Entries agree with Loki's own + count_over_time, but the recorded run did not cross-check streams against + Loki's /series, so it is not known which side is right; the runner now + checks /series and marks the difference as Loki's chunk accounting only when + /series agrees with the proxy. +request: + method: GET + path: /loki/api/v1/index/stats + params: + query: '{env="production"}' + start: 'now-15m' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [streams] + expect: | + Loki's stream count (or Loki's /series count where Loki's stats double- + count). +gap: + status: open + impact: drilldown-visible + effort: S + area: 'internal/proxy/label_handlers.go handleIndexStats (stream counting per window)' + loki: 'pkg/storage index stats (streams from the index for the window)' + proxy: 'streams with lines in the window' + planned_test: 'bench/parity rerun with the /series cross-check, then TestCompat_IndexStatsStreams (test/e2e-compat, group drilldown)' + clusters: + - 2a68e26c + - aa98afb1 diff --git a/conformance/registry/cases/quality/label-cache-aged-labels-kept.yaml b/conformance/registry/cases/quality/label-cache-aged-labels-kept.yaml new file mode 100644 index 000000000..067051e6a --- /dev/null +++ b/conformance/registry/cases/quality/label-cache-aged-labels-kept.yaml @@ -0,0 +1,33 @@ +id: quality/label-cache-aged-labels-kept +title: Labels that age out of the window are never dropped from the merged label cache (open) +proves: + - data-probing-and-freshness + - loki_api_v1_labels +consumer: explore +why: | + Loki's /labels lists the labels of the streams in the requested window only. + The proxy merges new label names into its cached list (mergeLabelsIntoCache) + and never removes one, so a label whose last stream left the window is still + listed by the label browser. +request: + method: GET + path: /loki/api/v1/labels + params: + start: 'now-5m' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [data] + expect: | + Only labels of streams in the window. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy/label_metadata.go:1259 mergeLabelsIntoCache' + loki: 'pkg/querier/querier.go Label (window-bounded)' + proxy: 'union never shrinks' + planned_test: 'TestE2E_LabelsAgeOutOfWindow (test/e2e-compat, group core)' + clusters: [] diff --git a/conformance/registry/cases/quality/label-values-chunk-window.yaml b/conformance/registry/cases/quality/label-values-chunk-window.yaml new file mode 100644 index 000000000..55c6008b7 --- /dev/null +++ b/conformance/registry/cases/quality/label-values-chunk-window.yaml @@ -0,0 +1,35 @@ +id: quality/label-values-chunk-window +title: Loki lists label values of streams whose chunks overlap the window; the proxy only of streams with lines in it (open) +proves: + - loki_api_v1_label_name_values +consumer: explore +why: | + Over the last hour Loki listed 20256 pod values and the proxy 13510, exactly + the streams with lines in the window (index/stats streams). Loki reads label + values from index entries of chunks that overlap the window, so streams + whose chunk started before it are listed too. Users of the label browser see + fewer values than on Loki. +request: + method: GET + path: /loki/api/v1/label/pod/values + params: + start: 'now-1h' + end: 'now' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [data] + expect: | + Loki's values. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/proxy label values window (chunk-overlap semantics)' + loki: 'pkg/querier/querier.go Label (index lookups by chunk overlap)' + proxy: 'streams with lines in the window' + planned_test: 'TestCompat_LabelValuesWindowEdge (test/e2e-compat, group core)' + clusters: + - 185c0010 + - 76d3a547 diff --git a/conformance/registry/cases/quality/stats-volume-freshness.yaml b/conformance/registry/cases/quality/stats-volume-freshness.yaml new file mode 100644 index 000000000..c6a787014 --- /dev/null +++ b/conformance/registry/cases/quality/stats-volume-freshness.yaml @@ -0,0 +1,38 @@ +id: quality/stats-volume-freshness +title: index/stats and volume answers ending now are cached; Loki does not cache inside max_stats_cache_freshness (10m) (open) +proves: + - data-probing-and-freshness + - loki_api_v1_index_stats + - loki_api_v1_index_volume +consumer: drilldown +why: | + Loki's results cache skips stats and volume requests whose end lies within + max_stats_cache_freshness (default 10m, pkg/validation/limits.go:413), so + Drilldown's landing counts and volumes always include the last minutes. The + proxy caches these answers by time bucket regardless of how recent the end + is, so the landing page under-counts the newest lines until the entry + expires. +request: + method: GET + path: /loki/api/v1/index/volume + params: + query: '{env="production"}' + start: 'now-15m' + end: 'now' + targetLabels: 'service_name' + headers: + X-Scope-OrgID: "0" +compare: + mode: observed + fields: [data.result] + expect: | + Volumes including lines from the last minute. +gap: + status: open + impact: drilldown-visible + effort: S + area: 'internal/proxy/volume.go handleVolume, label_handlers.go handleIndexStats (cache freshness)' + loki: 'pkg/validation/limits.go:413-414 max_stats_cache_freshness default 10m' + proxy: 'cached regardless of end' + planned_test: 'TestE2E_StatsVolumeFreshness (test/e2e-compat, group drilldown)' + clusters: [] diff --git a/conformance/registry/cases/semantics/absent-over-time-semantics.yaml b/conformance/registry/cases/semantics/absent-over-time-semantics.yaml new file mode 100644 index 000000000..058e31c8a --- /dev/null +++ b/conformance/registry/cases/semantics/absent-over-time-semantics.yaml @@ -0,0 +1,43 @@ +id: semantics/absent-over-time-semantics +title: absent_over_time answers when data exists and stays empty when it is absent, after a parser (open) +proves: + - loki_api_v1_query_range +consumer: explore +why: | + Loki's absent_over_time returns one series (labelled from the selector's + equality matchers) only for steps without data. With a parser stage the + proxy inverts it: `absent_over_time({service_name="api-gateway"} | json + [1m])` (data present) returns 2026 series where Loki returns none, and + `absent_over_time({...} | json | nonexistant_field="foo" [1m])` (no line + matches) returns nothing where Loki returns `{service_name="api-gateway"}`. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'absent_over_time({service_name="api-gateway"} | json | nonexistant_field="foo" [1m])' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series] + expect: | + One series {service_name="api-gateway"} at every step. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/translator absent_over_time (parser pipelines)' + loki: 'pkg/logql/evaluator.go:647-664, :1320 absentLabels' + proxy: 'inverted with a parser' + planned_test: 'TestCompat_AbsentOverTimeWithParser (test/e2e-compat, group semantics)' + clusters: + - 2336e2a5 + - 4dfd84f7 + - 5eaf17b5 + - aeebcc0d + - bd172c7d + - c4f74a0e diff --git a/conformance/registry/cases/semantics/binary-ignoring-matching.yaml b/conformance/registry/cases/semantics/binary-ignoring-matching.yaml new file mode 100644 index 000000000..89c210db1 --- /dev/null +++ b/conformance/registry/cases/semantics/binary-ignoring-matching.yaml @@ -0,0 +1,37 @@ +id: semantics/binary-ignoring-matching +title: `+ ignoring(label)` between two sums joins the wrong series (open) +proves: + - loki_api_v1_query_range +consumer: explore +why: | + `sum by (cluster) (sum by (cluster) (rate({namespace="prod"}[5m])) + + ignoring(machine) sum by (cluster, machine) (rate({namespace="prod"}[5m])))` + is one series per cluster on Loki (2) with the sum of both sides; the proxy + returns 1 series with 150x Loki's value. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'sum by (cluster) (sum by (cluster) (rate({namespace="prod"}[5m])) + ignoring(machine) sum by (cluster, machine) (rate({namespace="prod"}[5m])))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series, values] + expect: | + Loki's series and values. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy/vector_matching.go (ignoring with a label absent on both sides)' + loki: 'pkg/logql/evaluator.go binary vector matching (ignoring)' + proxy: 'wrong join' + planned_test: 'TestCompat_BinaryIgnoringAbsentLabel (test/e2e-compat, group semantics)' + clusters: + - 354c7d83 + - 8fef8a78 diff --git a/conformance/registry/cases/semantics/count-values-unsupported.yaml b/conformance/registry/cases/semantics/count-values-unsupported.yaml new file mode 100644 index 000000000..fcbbdc875 --- /dev/null +++ b/conformance/registry/cases/semantics/count-values-unsupported.yaml @@ -0,0 +1,34 @@ +id: semantics/count-values-unsupported +title: count_values() is rejected with 400 where Loki answers (documented) +proves: + - loki_api_v1_query_range +consumer: api +why: | + Loki supports `count_values("label", )`; VictoriaLogs has no stats + function that groups by metric value, so the proxy returns HTTP 400 bad_data + (KNOWN_ISSUES). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'count_values("v", sum by (level) (count_over_time({env="production"}[5m])))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status] + expect: | + Loki's series per distinct value. +gap: + status: documented + impact: api-only + effort: M + area: 'internal/translator + proxy-side post-aggregation of a stats matrix' + loki: 'pkg/logql/syntax OpTypeCountValues; vector evaluator' + proxy: '400 bad_data' + planned_test: 'TestCompat_CountValues (test/e2e-compat, group semantics)' + clusters: [] diff --git a/conformance/registry/cases/semantics/eager-pipeline-validation.yaml b/conformance/registry/cases/semantics/eager-pipeline-validation.yaml new file mode 100644 index 000000000..f0bd54adf --- /dev/null +++ b/conformance/registry/cases/semantics/eager-pipeline-validation.yaml @@ -0,0 +1,34 @@ +id: semantics/eager-pipeline-validation +title: Invalid pipeline stages fail with 400 regardless of data; Loki answers 200 empty over a range with no data (documented) +proves: + - loki-compatible-profile +consumer: api +why: | + Loki builds a pipeline (and reports invalid json/logfmt expressions, + line_format/label_format templates and ip() arguments) only for a range it + reads, so a range with no data answers 200 empty. The proxy validates at + parse time and answers Loki's 400 text over any range (KNOWN_ISSUES). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="api-gateway"} | line_format "{{.a"' + start: 'now-30d' + end: 'now-29d' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status] + expect: | + 200 empty over a range without data. +gap: + status: documented + impact: edge + effort: S + area: 'internal/translator validation' + loki: 'pkg/logql/log/fmt.go template build at pipeline construction' + proxy: '400 at parse' + planned_test: 'TestCompat_EagerValidationEmptyRange (test/e2e-compat, group semantics)' + clusters: [] diff --git a/conformance/registry/cases/semantics/extracted-suffix-collision.yaml b/conformance/registry/cases/semantics/extracted-suffix-collision.yaml new file mode 100644 index 000000000..13e78e50b --- /dev/null +++ b/conformance/registry/cases/semantics/extracted-suffix-collision.yaml @@ -0,0 +1,128 @@ +id: semantics/extracted-suffix-collision +title: A parsed key named like a stream label is renamed with _extracted in query results (open; detected_fields decision unchanged) +proves: + - parser-error-and-label-collision + - loki_api_v1_query_range +consumer: explore +why: | + Loki's parsers rename a parsed key that equals a stream label or structured- + metadata name with the `_extracted` suffix, so the stored label keeps its + value (`| json` on a line holding `level` beside the stream label `level` + yields `level_extracted`). The proxy returns the parsed key under the stream + label's name (or not at all), so Explore shows one `level` where Loki shows + `level` and `level_extracted`, and grouping or filtering on + `level_extracted` finds nothing. + + This is the query-result side only. The owner's stamped decision that + /detected_fields lists `service` / `service.name` without the suffix stays + (documented deviation detected-fields-no-extracted-suffix); structured + metadata named like a stream label is profiles/structured-metadata-label- + collision. + + The same collision shows in /detected_fields: Loki lists level_extracted and + version_extracted for a JSON line holding level and version beside stream + labels of those names, and the proxy does not. Only service / service.name + is covered by the owner's detected_fields decision (profiles/detected- + fields-no-extracted-suffix); the other `_extracted` fields are this gap. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="otel-collector"} | json | logfmt | drop __error__, __error_details__' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values, parsed labels] + expect: | + Loki's parsed labels including level_extracted. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/proxy stage-field exposure (parsed-label naming)' + loki: 'pkg/logql/log/parser.go:25 duplicateSuffix; :152 json, :342 logfmt, :696 json expression' + proxy: 'parsed key merged into the stream label''s name' + planned_test: 'TestCompat_ExtractedSuffixOnParsedCollision (test/e2e-compat, group semantics)' + note: 'Measured on main at fcaccfcc, before #652 rewrote the tail and log-row label conversion; recheck the behaviour on the current code before fixing.' + clusters: + - 05318ee0 + - 087a214a + - 08fb5da3 + - 0b0f1379 + - 150024f9 + - 15548dbd + - 15a876ba + - 16eaca6c + - 19e8f424 + - 231b4102 + - 29ad1639 + - 31d74267 + - 32f36208 + - 3425b43c + - 35e2a101 + - 37eca1b4 + - 3f356b01 + - 41586c11 + - 422b5298 + - 47dddcfb + - 4ec90d6a + - 58f21a34 + - 5b2ec5e6 + - 5d3adecc + - 5f23b125 + - 60488a50 + - 6188db87 + - 66f96c46 + - 6d79474c + - 6dfea202 + - 7f750e4f + - 8c96e500 + - 8cc9e97a + - 903323d1 + - 93b5bb76 + - 975e64c1 + - 97e6ee39 + - 99c8515b + - 9c527802 + - 9f075b30 + - a300565d + - a88e7ea7 + - a9078b6e + - ab4ce12b + - ac4a8c52 + - b0dd1495 + - b166bfd3 + - b680829d + - b68afb0d + - bc7f7413 + - bfd206cd + - c7b6ed6a + - ce14ac99 + - d04917ae + - d051cfff + - d41734a9 + - d45a655e + - d4b04b0c + - d650bcfb + - d72d583b + - e9b099d2 + - e9d4fcd8 + - ea80f966 + - eaaf14ca + - ee9ebbea + - ef144320 + - ef9bfaad + - f056672a + - f1ad0a1b + - f455442d + - f809410d + - f87f7636 + - f8fa9aa2 + - fa6d53ad + - fbece7b0 + - fe307701 diff --git a/conformance/registry/cases/semantics/first-last-over-time-translation.yaml b/conformance/registry/cases/semantics/first-last-over-time-translation.yaml new file mode 100644 index 000000000..17b8b0edb --- /dev/null +++ b/conformance/registry/cases/semantics/first-last-over-time-translation.yaml @@ -0,0 +1,45 @@ +id: semantics/first-last-over-time-translation +title: first_over_time and last_over_time with offset or a line filter fail in VictoriaLogs (open) +proves: + - loki_api_v1_query_range +consumer: explore +why: | + `first_over_time({...} | logfmt | unwrap value [1s] offset 2s) by (a)` and + `last_over_time({...} |~ ".+bar" | unwrap foo [1m])` answer 200 on Loki; the + proxy sends VictoriaLogs a query it rejects (HTTP 422 `cannot parse query + arg`). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'last_over_time({service_name="payment-service"} | logfmt | unwrap latency_ms [1m] offset 2s) by (level)' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status] + expect: | + 200 with Loki's series. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/translator first/last_over_time emission (row_min/row_max with offset and filters)' + loki: 'pkg/logql/first_last_over_time.go' + proxy: '422 VictoriaLogs parse error' + planned_test: 'TestCompat_FirstLastOverTimeOffsetFilter (test/e2e-compat, group semantics)' + clusters: + - 13f557ca + - 20680725 + - 3c0a55f2 + - 56b4c549 + - 61ff16ca + - 83edd378 + - 90a46cda + - a27681a7 + - ae0d1b7b + - b91f5292 diff --git a/conformance/registry/cases/semantics/instant-result-type-missing.yaml b/conformance/registry/cases/semantics/instant-result-type-missing.yaml new file mode 100644 index 000000000..e880cf8e1 --- /dev/null +++ b/conformance/registry/cases/semantics/instant-result-type-missing.yaml @@ -0,0 +1,34 @@ +id: semantics/instant-result-type-missing +title: Some instant binary/label_replace answers carry no resultType (open) +proves: + - loki_api_v1_query +consumer: grafana-datasource +why: | + Loki answers every instant metric query with `resultType: vector`. For + `label_replace(avg by (app) (sum by (app) (rate(...)) + sum by (app) + (rate(...))), ...)` and `stdvar without (app) (...)` the proxy's 200 answer + has no resultType, which Grafana and API clients cannot decode. +request: + method: GET + path: /loki/api/v1/query + params: + query: 'stdvar without (app) (count_over_time({namespace="prod"}[1m]))' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [resultType] + expect: | + vector. +gap: + status: open + impact: grafana-datasource + effort: S + area: 'internal/proxy instant response assembly (binary/label_replace/without paths)' + loki: 'pkg/logqlmodel result types; pkg/util/marshal query response' + proxy: 'resultType missing' + planned_test: 'TestCompat_InstantResultTypeAlwaysSet (test/e2e-compat, group semantics)' + clusters: + - 307bbdd1 + - f2066991 diff --git a/conformance/registry/cases/semantics/ip-filter-precision.yaml b/conformance/registry/cases/semantics/ip-filter-precision.yaml new file mode 100644 index 000000000..646b871a4 --- /dev/null +++ b/conformance/registry/cases/semantics/ip-filter-precision.yaml @@ -0,0 +1,35 @@ +id: semantics/ip-filter-precision +title: ip() filters are approximated with regular expressions (documented) +proves: + - loki_api_v1_query_range +consumer: api +why: | + Loki matches ip() line and label filters by parsing addresses, prefixes and + ranges. The proxy translates them to VictoriaLogs regular expressions; exact + matching of all IPv6, non-octet CIDR and range forms is not established and + label ip() filters need further work (KNOWN_ISSUES). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="nginx-ingress"} |= ip("10.0.0.0/9")' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values] + expect: | + Loki's lines. +gap: + status: documented + impact: edge + effort: M + area: 'internal/proxy/postprocess.go:84 ip filtering' + loki: 'pkg/logql/log/ip.go' + proxy: 'regexp approximation' + planned_test: 'TestCompat_IPFilterForms (test/e2e-compat, group semantics)' + clusters: [] diff --git a/conformance/registry/cases/semantics/label-filter-expression-syntax.yaml b/conformance/registry/cases/semantics/label-filter-expression-syntax.yaml new file mode 100644 index 000000000..a04c3adb8 --- /dev/null +++ b/conformance/registry/cases/semantics/label-filter-expression-syntax.yaml @@ -0,0 +1,47 @@ +id: semantics/label-filter-expression-syntax +title: Comma-joined and parenthesized label-filter expressions are rejected (open) +proves: + - loki_api_v1_query_range +consumer: explore +why: | + Loki accepts `| foo>=5,bar<25ms` (comma = and) and grouped expressions `| ( + status_code < 500 and status_code > 200) or latency >= 250ms`. The proxy + fails them: VictoriaLogs parse errors from the translated filter (`cannot + parse ... probably, the whole string must be put into quotes`) or its own + `syntax error: unexpected |`. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="api-gateway"} | json | (status>=500 or duration_ms>1000), method="GET"' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status, streams.values] + expect: | + 200 with Loki's lines. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/logql parser (label filter grammar) + translator filter emission' + loki: 'pkg/logql/syntax/syntax.y labelFilter: labelFilter COMMA labelFilter, OPEN_PARENTHESIS labelFilter CLOSE_PARENTHESIS' + proxy: '400' + planned_test: 'TestCompat_LabelFilterExpressionSyntax (test/e2e-compat, group semantics)' + clusters: + - 163be53e + - 4e6094a0 + - 7b225b99 + - 7c27b83b + - 834735ce + - 967f3abc + - ac2c4509 + - adb16001 + - e67ce23c + - ed5ff2d9 + - f59a58b3 diff --git a/conformance/registry/cases/semantics/label-replace-new-label.yaml b/conformance/registry/cases/semantics/label-replace-new-label.yaml new file mode 100644 index 000000000..e8a04867c --- /dev/null +++ b/conformance/registry/cases/semantics/label-replace-new-label.yaml @@ -0,0 +1,35 @@ +id: semantics/label-replace-new-label +title: label_replace does not add a new destination label (open) +proves: + - loki_api_v1_query_range +consumer: explore +why: | + `label_replace(rate({app="api-gateway"}[5m]), "app_short", "$1", "app", + "(.*)-.*")` adds `app_short` to every series on Loki; the proxy's series + lack the new label (also through nested label_replace). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'label_replace(sum by (app) (rate({app="api-gateway"}[5m])), "app_short", "$1", "app", "(.*)-.*")' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series labels] + expect: | + app_short on every series. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy label_replace post-processing' + loki: 'pkg/logql/evaluator.go:387, :1253 label_replace evaluator' + proxy: 'destination label missing' + planned_test: 'TestCompat_LabelReplaceNewLabel (test/e2e-compat, group semantics)' + clusters: + - a9d4f85c diff --git a/conformance/registry/cases/semantics/line-filter-or-alternatives.yaml b/conformance/registry/cases/semantics/line-filter-or-alternatives.yaml new file mode 100644 index 000000000..cb82b35b2 --- /dev/null +++ b/conformance/registry/cases/semantics/line-filter-or-alternatives.yaml @@ -0,0 +1,40 @@ +id: semantics/line-filter-or-alternatives +title: Line filters with `or` alternatives are rejected (open) +proves: + - loki_api_v1_query_range +consumer: explore +why: | + Loki 3 accepts `|= "a" or "b"` and `!= "foo" or "bar"` (and `or ip(...)`) as + one line filter with alternatives. The proxy's parser stops at the second + string (`unexpected token STRING`) and returns 400. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{namespace="prod"} |= "error" or "timeout"' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status, streams.values] + expect: | + Lines holding either string. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/logql parser (line filter alternatives) + translator' + loki: 'pkg/logql/syntax/ast.go:473 newOrLineFilterExpr' + proxy: '400 parse error' + planned_test: 'TestCompat_LineFilterOrAlternatives (test/e2e-compat, group semantics)' + clusters: + - 147e07fb + - 435e2f3c + - 9d69befe + - b349505b + - c061fca0 + - dfe987e7 diff --git a/conformance/registry/cases/semantics/log-entries-differ-same-count.yaml b/conformance/registry/cases/semantics/log-entries-differ-same-count.yaml new file mode 100644 index 000000000..9d3bd19ae --- /dev/null +++ b/conformance/registry/cases/semantics/log-entries-differ-same-count.yaml @@ -0,0 +1,38 @@ +id: semantics/log-entries-differ-same-count +title: A log query returns the same number of lines with different entries (open, cause not isolated) +proves: + - loki_api_v1_query_range +consumer: explore +why: | + `{app="api-gateway",env="production"} | json | status != 200 | status != + 404` returned 100 lines on both sides, but not the same lines, and not only + at the limit's boundary timestamp (which the diff ignores). The run + reproduced it on a second request; the cause (ordering across streams at + equal timestamps, or a numeric label filter on a JSON number) is not + isolated yet. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{app="api-gateway",env="production"} | json | status != 200 | status != 404' + start: 'now-1h' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values] + expect: | + Loki's lines. +gap: + status: open + impact: explore-visible + effort: S + area: 'to be isolated (log query paging / numeric label filters)' + loki: 'pkg/logql/log/label_filter.go numeric filters; pkg/iter entry ordering' + proxy: 'different lines, same count' + planned_test: 'TestCompat_NumericLabelFilterLines (test/e2e-compat, group semantics)' + clusters: + - 5da321bc diff --git a/conformance/registry/cases/semantics/logfmt-existence-filter-ungrouped-sum.yaml b/conformance/registry/cases/semantics/logfmt-existence-filter-ungrouped-sum.yaml new file mode 100644 index 000000000..1073870c3 --- /dev/null +++ b/conformance/registry/cases/semantics/logfmt-existence-filter-ungrouped-sum.yaml @@ -0,0 +1,39 @@ +id: semantics/logfmt-existence-filter-ungrouped-sum +title: An ungrouped sum over `| logfmt | key!=""` returns no series (open) +proves: + - parser-error-and-label-collision + - loki_api_v1_query_range +consumer: explore +why: | + `sum(count_over_time({service_name="cache-redis"} | logfmt | op!="" [5m]))` + is 94170 lines on Loki and an empty matrix on the proxy; the same with `| + drop __error__`, and for every logfmt service and key tried (payment-service + amount). The grouped form (`sum by (op) (...)`), the equality filter + (`op="hget"`) and the `| json` form match Loki, so the ungrouped logfmt + stats pushdown with an existence filter loses every row. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'sum(count_over_time({service_name="cache-redis"} | logfmt | op!="" [5m]))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series, values] + expect: | + One series with Loki's counts. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy/ordered_json_metric.go setStatsPushdown / pushdownLabelFilters (logfmt, ungrouped)' + loki: 'pkg/logql/log/parser.go:398-409 logfmt label extraction; label filter on the parsed label' + proxy: 'empty matrix' + planned_test: 'TestCompat_LogfmtExistenceFilterUngroupedSum (test/e2e-compat, group semantics), cases semantics/json-filter-pushdown-ungrouped-sum sibling' + clusters: + - 0a9f54e0 diff --git a/conformance/registry/cases/semantics/non-sum-vector-aggregation-unreduced.yaml b/conformance/registry/cases/semantics/non-sum-vector-aggregation-unreduced.yaml new file mode 100644 index 000000000..3f06328db --- /dev/null +++ b/conformance/registry/cases/semantics/non-sum-vector-aggregation-unreduced.yaml @@ -0,0 +1,70 @@ +id: semantics/non-sum-vector-aggregation-unreduced +title: max, min, count and avg over a range function return one series per stream on query_range (open) +proves: + - metric-series-identity + - loki_api_v1_query_range +consumer: explore +why: | + Loki folds a vector aggregation to one series per group at every step: + `max(count_over_time({service_name="api-gateway"}[5m]))` is one series, `max + by (level) (...)` one per level. On the range path the proxy returns the + inner per-stream series unreduced (2160 series for that selector over an + hour, label sets of the streams) for max, min, count and avg with or without + `by`, including a grouping on a label no stream has (`max by (baz) (...)`: + Loki 1 series, proxy 10656) and nested inside sum (`sum(max(rate(...)))`). + sum and stddev reduce correctly; the instant path reduces but folds over + streams (see metric-identity/structured-metadata-splits-series). + + Found by the differential run (bench/parity) on origin/main; the registry's + metric-series-identity item records count/min/max/avg as fixed by #582, so + this is either a range-path regression or a path #582 did not reach. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'max(count_over_time({service_name="api-gateway"}[5m]))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series, values] + expect: | + One series, Loki's maximum per step. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/translator seriesLevelOuterAggregation (range path) + internal/proxy range stats folding' + loki: 'pkg/logql/evaluator.go:400-529 newVectorAggEvaluator / VectorAggEvaluator.Next (fold per group per step)' + proxy: 'inner per-stream series returned unreduced on query_range' + planned_test: 'TestCompat_NonSumVectorAggregationRange (test/e2e-compat, group semantics) over max/min/count/avg x by/without/none' + note: 'Measured on main at fcaccfcc; verified still present on c4ebcd98.' + clusters: + - 099e8acf + - 0b8db994 + - 0d361b0c + - 0e319b2f + - 20bb13d8 + - 2bbe720f + - 2c7140a9 + - 2ec3f6c0 + - 543a2f07 + - 77eeea84 + - 864af0b3 + - 86c51db9 + - 91b7514f + - 9259c6c6 + - bb04eea0 + - be6066e1 + - cf968b92 + - d13dd24b + - da2a39ee + - da345f70 + - db50c7bd + - ea157df5 + - efb24a18 + - feb671c2 diff --git a/conformance/registry/cases/semantics/parenthesized-log-range.yaml b/conformance/registry/cases/semantics/parenthesized-log-range.yaml new file mode 100644 index 000000000..98c41b1f1 --- /dev/null +++ b/conformance/registry/cases/semantics/parenthesized-log-range.yaml @@ -0,0 +1,79 @@ +id: semantics/parenthesized-log-range +title: A parenthesized log expression under a range function fails translation (open) +proves: + - loki_api_v1_query_range + - loki_api_v1_query +consumer: explore +why: | + LogQL allows `rate(({namespace="prod"} |= "error")[5m])` and + `count_over_time(({...} |~ "x")[1m])`; Loki answers them like the + unparenthesized form. The proxy sends VictoriaLogs a query it cannot parse + (`cannot parse query arg ... missing whitespace or ':'`, HTTP 400) on range + queries and answers instant ones with `log queries are not supported as an + instant query type`. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'count_over_time(({namespace="prod"} |~ ".+bar")[5m])' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status] + expect: | + 200 with Loki's series. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/logql parser / internal/translator (LogRangeExpr with parenthesized left side)' + loki: 'pkg/logql/syntax/syntax.y logRangeExpr: OPEN_PARENTHESIS logExpr CLOSE_PARENTHESIS RANGE' + proxy: '400 (VictoriaLogs parse error) / instant 400' + planned_test: 'TestCompat_ParenthesizedLogRange (test/e2e-compat, group semantics)' + clusters: + - 0e5f1b8d + - 154f3c5e + - 25b1f798 + - 26eda2af + - 2731385f + - 27527d05 + - 2ec911a8 + - 2fe5e780 + - 34fd43a7 + - 35c82cd3 + - 361b1ce4 + - 3f290148 + - 40bbb685 + - 4fc1fe7c + - 585e4266 + - 5d3d5b11 + - 5ea3232b + - 6813c1b2 + - 68fcec74 + - 74815f5f + - 75e5bc38 + - 87216ff6 + - 98078324 + - 9ef01b8a + - a97fb38d + - b31a48a7 + - b5e0e1c5 + - b871c258 + - bc81e845 + - c04abdc5 + - c37f606b + - c77525e2 + - d0dfe746 + - d3d0631c + - d517894b + - d8b61530 + - e0a75c1d + - e3b4a851 + - f7b13aae + - f982bf27 + - fb38c36f diff --git a/conformance/registry/cases/semantics/parse-error-labels-on-log-lines.yaml b/conformance/registry/cases/semantics/parse-error-labels-on-log-lines.yaml new file mode 100644 index 000000000..58802b215 --- /dev/null +++ b/conformance/registry/cases/semantics/parse-error-labels-on-log-lines.yaml @@ -0,0 +1,43 @@ +id: semantics/parse-error-labels-on-log-lines +title: A line a parser rejects carries __error__ and __error_details__ in Loki's log answer (open) +proves: + - parser-error-and-label-collision + - loki_api_v1_query_range +consumer: explore +why: | + Loki's json parser adds `__error__=JSONParserErr` and + `__error_details__=` to a line it cannot parse (a logfmt or + plain line under `| json`), and logfmt adds `LogfmtParserErr`; Explore shows + both labels on the row and `| __error__=""` / `!=""` select by them. + VictoriaLogs' unpack_json adds nothing for such a line and has no error + marker, so the proxy returns the row without either label and a filter on + `__error__` cannot select the rows Loki selects. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{namespace="prod"} | json | __error__!=""' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values, parsed labels] + expect: | + The rows Loki rejects, with __error__ and __error_details__ as parsed + labels. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/proxy log-row assembly (stage-field exposure) + translator parse-error predicate' + loki: 'pkg/logql/log/parser.go:97 addErrLabel(errJSON, err) -> :777-787 SetErr/SetErrorDetails; pkg/logql/log/error.go:5 errJSON' + proxy: 'no __error__ / __error_details__ on log rows' + planned_test: 'TestCompat_ParseErrorLabelsOnLogLines (test/e2e-compat, group semantics)' + note: 'Measured on main at fcaccfcc, before #652 rewrote the tail and log-row label conversion; recheck the behaviour on the current code before fixing.' + clusters: + - 9a63f6e2 + - b50e5ea5 diff --git a/conformance/registry/cases/semantics/parse-error-text-parity.yaml b/conformance/registry/cases/semantics/parse-error-text-parity.yaml new file mode 100644 index 000000000..324d7d60e --- /dev/null +++ b/conformance/registry/cases/semantics/parse-error-text-parity.yaml @@ -0,0 +1,168 @@ +id: semantics/parse-error-text-parity +title: Invalid queries fail on both sides, with different error text or status (open) +proves: + - loki_api_v1_query_range + - loki-compatible-profile +consumer: explore +why: | + Both sides reject the query, but the proxy's message is its own parser's + (`parse error : logql: expected duration ...`, `unexpected token ...`), a + VictoriaLogs message (`cannot parse query arg ...`, `missing | stats pipe`, + 422) or a proxy-specific one (`unsupported instant aggregation target`, + `sum() requires a range metric inside`) where Loki writes `parse error at + line N, col M: syntax error: unexpected ...`. Grafana shows the error text + to the user (cases profiles/grafana-shows-loki-error-text). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'sort({app="api-gateway"})' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status, error_text] + expect: | + Loki's status and text. +gap: + status: open + impact: grafana-datasource + effort: L + area: 'internal/logql parser error messages (Loki''s goyacc messages and positions) + VictoriaLogs error mapping' + loki: 'pkg/logql/syntax/parser.go ParseError (line, col) and syntax.y' + proxy: 'own / VictoriaLogs text, 422 for some' + planned_test: 'TestCompat_ParseErrorText corpus (test/e2e-compat, bad_request_status_parity_test.go)' + clusters: + - 00c1351d + - 090d292e + - 0b43cabb + - 11e197ef + - 1216c87b + - 15273179 + - 1597db8f + - 19bb4804 + - 1bce60c7 + - 1e15e3f7 + - 235b2fd7 + - 236be44b + - 2456a6d9 + - 24b236fd + - 24d72116 + - 25a45cb4 + - 26a758d3 + - 291568c8 + - 2cb1c674 + - 2fd5ec28 + - 31ae1824 + - 335a0063 + - 337a1e15 + - 3791711e + - 3801dc16 + - 380bfbcd + - 3aea3c86 + - 3b74360f + - 3cac4f43 + - 4014589d + - 42cdded9 + - 44e0a473 + - 464672f3 + - 479a3ac5 + - 47be3455 + - 4990705b + - 4a83670c + - 4b30f46e + - 4be578fa + - 4bef518e + - 4d13f90a + - 4e182695 + - 4e7de2f4 + - 4f0bd5da + - 4fb822da + - 5185e724 + - 54a23828 + - 5729447e + - 5a21650b + - 5b4749b3 + - 5dd88485 + - 60c7e720 + - 60ec9fb1 + - 61f5762d + - 702df3da + - 717fedc0 + - 74505ee8 + - 75acdfee + - 7705bcbf + - 799eb1d6 + - 79e63a4f + - 7d93601c + - 7efa02ba + - 7f7a4d03 + - 80af726a + - 80e2ee76 + - 82283849 + - 832022c8 + - 83a0bbb4 + - 8556e63f + - 85e26f96 + - 884f4336 + - 893f45f2 + - 8cce5c63 + - 8ebf872b + - 94fdc54f + - 952c4ced + - 95f2257a + - 97d18bb6 + - 9e2e6496 + - a2d0b3cb + - a57ca4cc + - a5b943b2 + - a9930b3c + - acc8d4a9 + - b0921dfc + - b1f0d548 + - b7b8e6bb + - b864cc9d + - bd69d9b0 + - bdc75f86 + - bf2a7f15 + - bfa1bb8f + - c30c82d7 + - c44d6ae3 + - c4c8efb7 + - c8035a7e + - c9b6ea43 + - ce1fc758 + - ce4dd73f + - cebdc20e + - d1da0216 + - d40e85ea + - d5db4f97 + - dabfed68 + - dae354de + - db738b79 + - e17816bf + - e1d8fb09 + - e37fdcc4 + - e4fdf6d3 + - e6255407 + - e7cdda75 + - e8b24a9b + - ea5fa199 + - ead2b08b + - ec8a8fbf + - ed6ad12f + - f09d3262 + - f2273e53 + - f22ad939 + - f5161c97 + - f65563ce + - f7f991c9 + - fa1c6399 + - fcbdc6b4 + - fd0ad64a + - fea1552d + - feee44bb diff --git a/conformance/registry/cases/semantics/pattern-line-filter.yaml b/conformance/registry/cases/semantics/pattern-line-filter.yaml new file mode 100644 index 000000000..a07e90c1d --- /dev/null +++ b/conformance/registry/cases/semantics/pattern-line-filter.yaml @@ -0,0 +1,40 @@ +id: semantics/pattern-line-filter +title: An empty pattern line filter (`|> ""`, `!> ""`) selects the opposite lines (open) +proves: + - loki_api_v1_query_range +consumer: explore +why: | + Loki's pattern filter keeps (`|>`) or drops (`!>`) the lines matching a + pattern. Only the empty pattern differs in the differential run: + `{namespace="prod"} |> ""` returns the page on the proxy where Loki returns + no line, and `!> ""` returns nothing where Loki returns the page. Non-empty + patterns (`|> "<_> GET <_>"`, `!> "<_> GET <_>"`, `!> "foo <_>"`) agree with + Loki; pattern alternatives with `or` are rejected (semantics/line-filter-or- + alternatives). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{namespace="prod"} |> ""' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values] + expect: | + Loki's lines. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/translator pattern line filter' + loki: 'pkg/logql/syntax/lex.go:26,29 (NPA, PIPE_PATTERN); pkg/logql/log/filter.go pattern filter' + proxy: 'empty pattern matches every line' + planned_test: 'TestCompat_PatternLineFilter (test/e2e-compat, group semantics)' + clusters: + - 90e7eb36 + - f131187f diff --git a/conformance/registry/cases/semantics/proxy-accepts-invalid-logql.yaml b/conformance/registry/cases/semantics/proxy-accepts-invalid-logql.yaml new file mode 100644 index 000000000..b22272b86 --- /dev/null +++ b/conformance/registry/cases/semantics/proxy-accepts-invalid-logql.yaml @@ -0,0 +1,46 @@ +id: semantics/proxy-accepts-invalid-logql +title: Queries Loki rejects with 400 are answered by the proxy (open) +proves: + - loki_api_v1_query_range +consumer: api +why: | + Loki answers HTTP 400 parse errors for `absent_over_time(...) by (foo)` + (grouping not allowed), `rate({...}[5minutes])` (unknown unit), `| bytes < + 0B`, `rate({...}[5m] @ 1000000000)` and others; the proxy answers 200 (or a + 5xx), so a client learns of a broken query only from Loki. +request: + method: GET + path: /loki/api/v1/query + params: + query: 'rate({namespace="prod"}[5minutes])' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status, error_text] + expect: | + Loki's 400 parse error. +gap: + status: open + impact: api-only + effort: S + area: 'internal/logql parser validation' + loki: 'pkg/logql/syntax/ast.go:1446,1579 grouping not allowed; duration parsing' + proxy: '200' + planned_test: 'TestCompat_InvalidLogQLRejected (test/e2e-compat, bad_request_status_parity_test.go)' + clusters: + - 16428cd1 + - 354d31ed + - 44fa34f9 + - 556649c4 + - 649f8e6b + - 7980881e + - 7ba3e83b + - a987dc81 + - b04a9c73 + - b879c156 + - cd64981a + - d4a26169 + - e46391d4 + - eec27d19 diff --git a/conformance/registry/cases/semantics/proxy-rejects-valid-logql.yaml b/conformance/registry/cases/semantics/proxy-rejects-valid-logql.yaml new file mode 100644 index 000000000..0a5698bf2 --- /dev/null +++ b/conformance/registry/cases/semantics/proxy-rejects-valid-logql.yaml @@ -0,0 +1,88 @@ +id: semantics/proxy-rejects-valid-logql +title: The proxy rejects LogQL Loki accepts: comments, negative and compound offsets, filters after a range, logfmt flags, ip() label filters (open) +proves: + - loki_api_v1_query_range + - loki_api_v1_query +consumer: api +why: | + Loki v3.7.7 accepts and answers `# comments`, `offset -1s` and `offset + 1h2m20s`, a line filter after the range (`count_over_time({...}[12h] |= + "error")`), `| logfmt --strict` / `--keep-empty`, `| addr=ip("1.2.3.4")` + label filters, `topk(k, sum by (a) (sum_over_time(... | unwrap value + [1s])))` as an instant query, and more. The proxy's parser or translator + rejects each with 400 (its own parse error, or `unsupported instant + aggregation target`). Each query family is a separate small parser fix; the + differential run lists them per cluster. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'count_over_time({namespace="prod"}[3s] offset -1s)' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status] + expect: | + 200 with Loki's answer. +gap: + status: open + impact: api-only + effort: M + area: 'internal/logql parser (lexer: comments, signed durations, flags) + translator' + loki: 'pkg/logql/syntax/lex.go:149 comments, :90-91 OpStrict/OpKeepEmpty; syntax.y offsetExpr; pkg/logql/log/ip.go' + proxy: '400' + planned_test: 'TestCompat_LogQLSurfaceAccepted (test/e2e-compat, group semantics), one subtest per family' + clusters: + - 02494ca7 + - 02fa6772 + - 048082ab + - 0c00485a + - 18f6c171 + - 2137c2ba + - 23cbc9b3 + - 27e68f5e + - 282fd2af + - 293abde9 + - 2cda3276 + - 306824bd + - 3c3ebb3b + - 4517a40b + - 46125976 + - 461b1df0 + - 4a1aab68 + - 4dc99479 + - 53933736 + - 5a65069b + - 5c22832f + - 6553b1df + - 72db0446 + - 75526081 + - 807c5ade + - 815a3690 + - 82240823 + - 8275c76c + - 852cb577 + - 892af37b + - 8dedc850 + - 998b9e72 + - 9be7d291 + - 9c93bc66 + - 9e36e9cd + - a5c93607 + - b3444d76 + - b81935ec + - bd531691 + - c0ccfa2d + - c138dca8 + - c559852c + - c59b5392 + - d2f9deab + - d34c2dbd + - dbd01e9a + - e1af2a49 + - ee3cf0bd diff --git a/conformance/registry/cases/semantics/stddev-stdvar-grouped-values.yaml b/conformance/registry/cases/semantics/stddev-stdvar-grouped-values.yaml new file mode 100644 index 000000000..e90829822 --- /dev/null +++ b/conformance/registry/cases/semantics/stddev-stdvar-grouped-values.yaml @@ -0,0 +1,42 @@ +id: semantics/stddev-stdvar-grouped-values +title: stddev and stdvar by a label compute different values (open) +proves: + - metric-series-identity + - loki_api_v1_query_range +consumer: explore +why: | + `stddev by (app) (count_over_time({env="production"}[5m]))` folds the per- + stream counts of each app: Loki 28.0 for auth-service, the proxy 1829 (and + stdvar alike), on range and instant queries. The ungrouped form matches. + Some instant forms answer without a resultType (`stdvar without (app) + (...)`). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'stddev by (app) (count_over_time({env="production"}[5m]))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [values] + expect: | + Loki's values. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/translator stddev/stdvar outer aggregation (by-group fold)' + loki: 'pkg/logql/evaluator.go VectorAggEvaluator stddev/stdvar' + proxy: 'different values' + planned_test: 'TestCompat_StddevStdvarBy (test/e2e-compat, group semantics)' + clusters: + - 44bf1425 + - 4b67fb14 + - 65210f05 + - 6864165c + - ce65447e diff --git a/conformance/registry/cases/semantics/tail-history-start-limit.yaml b/conformance/registry/cases/semantics/tail-history-start-limit.yaml new file mode 100644 index 000000000..b485b6aaf --- /dev/null +++ b/conformance/registry/cases/semantics/tail-history-start-limit.yaml @@ -0,0 +1,37 @@ +id: semantics/tail-history-start-limit +title: Native tail ignores start and limit: Grafana Live shows no history where Loki shows its last 100 lines (open) +proves: + - loki_api_v1_tail +consumer: explore +why: | + Loki's tail request takes `start` and `limit` (pkg/loghttp/tail.go:70-94) + and sends the entries since start, up to limit, before live lines, so + Grafana Live shows the last 100 entries at once. The proxy's native tail + path streams VictoriaLogs' live tail from now and ignores both, so a quiet + stream shows nothing until new lines arrive. +request: + method: GET + path: /loki/api/v1/tail + params: + query: '{service_name="api-gateway"}' + start: 'now-1h' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values] + expect: | + The last 100 entries since start, then live entries. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/proxy/tail.go openNativeTailStream (history replay before the live stream)' + loki: 'pkg/loghttp/tail.go:70-94 ParseTailQuery (limit, start); querier tailer sends history first' + proxy: 'live entries only' + planned_test: 'TestCompat_TailHistoryStartLimit (test/e2e-compat, group tail-multitenancy) + @explore-tail Playwright' + note: 'Measured on main at fcaccfcc, before #652 rewrote the tail conversion; verified still present on c4ebcd98 (after #652).' + clusters: + - e02ddf41 diff --git a/conformance/registry/cases/semantics/topk-bottomk-range-series.yaml b/conformance/registry/cases/semantics/topk-bottomk-range-series.yaml new file mode 100644 index 000000000..0afd77432 --- /dev/null +++ b/conformance/registry/cases/semantics/topk-bottomk-range-series.yaml @@ -0,0 +1,43 @@ +id: semantics/topk-bottomk-range-series +title: topk/bottomk on query_range return k series overall, not the per-step top k (open) +proves: + - loki_api_v1_query_range +consumer: explore +why: | + Loki ranks at every step, so a range topk(3, ...) returns the union of each + step's top 3 (8 series for `topk(3, sum by (app) + (count_over_time({env="production"}[5m])))` over an hour; 791 for `topk by + (container) (5, ...)`). The proxy returns 3 (5) series. Instant topk + matches. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'topk(3, sum by (app) (count_over_time({env="production"}[5m])))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series] + expect: | + Loki's per-step union. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/proxy topk/bottomk range ranking (cases limits/top-n-ranking-not-silent)' + loki: 'pkg/logql/evaluator.go:443-494 VectorAggEvaluator topk/bottomk per step' + proxy: 'global top k' + planned_test: 'TestCompat_TopkRangePerStep (test/e2e-compat, group semantics)' + clusters: + - 23526b6c + - 4f7b982a + - 8bc2e155 + - a6109627 + - aa368887 + - ab5f6f01 + - f2d36009 diff --git a/conformance/registry/cases/semantics/unpack-non-string-values.yaml b/conformance/registry/cases/semantics/unpack-non-string-values.yaml new file mode 100644 index 000000000..bbe3156b5 --- /dev/null +++ b/conformance/registry/cases/semantics/unpack-non-string-values.yaml @@ -0,0 +1,37 @@ +id: semantics/unpack-non-string-values +title: `| unpack` extracts non-string JSON values that Loki ignores (open) +proves: + - loki_api_v1_query_range +consumer: api +why: | + Loki's unpack adds only the string values of a JSON object as labels + (pkg/logql/log/parser.go:791-794), so `| unpack | status >= 400` over a line + with a numeric status selects nothing. The proxy unpacks numbers too and + counts 12195 lines where Loki counts none. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'count_over_time({service_name="api-gateway"} | unpack | status >= 400 [5m])' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series] + expect: | + No series. +gap: + status: open + impact: api-only + effort: S + area: 'internal/translator unpack (string values only)' + loki: 'pkg/logql/log/parser.go:789-820 UnpackParser.unpack' + proxy: 'numeric values unpacked' + planned_test: 'TestCompat_UnpackStringValuesOnly (test/e2e-compat, group semantics)' + clusters: + - 2b0fb55a + - 511ab651 diff --git a/conformance/registry/cases/semantics/unwrap-conversion-error.yaml b/conformance/registry/cases/semantics/unwrap-conversion-error.yaml new file mode 100644 index 000000000..9d68d63b4 --- /dev/null +++ b/conformance/registry/cases/semantics/unwrap-conversion-error.yaml @@ -0,0 +1,37 @@ +id: semantics/unwrap-conversion-error +title: An unwrap over non-numeric values fails in Loki with a 400 SampleExtractionErr; the proxy answers 200 (open) +proves: + - parser-error-and-label-collision + - loki_api_v1_query_range +consumer: explore +why: | + Loki fails `sum_over_time({app="a"} | logfmt | unwrap msg [5m])` over text + with HTTP 400 `pipeline error: 'SampleExtractionErr' for series ...` unless + `| __error__=""` drops those samples. VictoriaLogs' stats functions skip + non-numeric values, so the proxy returns 200 with the numeric samples only + (documented in KNOWN_ISSUES). +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'sum_over_time({service_name="payment-service"} | logfmt | unwrap msg [5m])' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status, error_text] + expect: | + Loki's 400 pipeline error. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/proxy range metric unwrap path (one-lookup probe like pipeline-error-text-parity)' + loki: 'pkg/logql/log/metrics_extraction.go SampleExtractionErr; logqlmodel.PipelineError' + proxy: '200, non-numeric samples skipped' + planned_test: 'TestCompat_UnwrapConversionErrorParity (test/e2e-compat, group semantics)' + clusters: [] diff --git a/conformance/registry/cases/semantics/unwrap-range-last-point.yaml b/conformance/registry/cases/semantics/unwrap-range-last-point.yaml new file mode 100644 index 000000000..2f6bb0725 --- /dev/null +++ b/conformance/registry/cases/semantics/unwrap-range-last-point.yaml @@ -0,0 +1,42 @@ +id: semantics/unwrap-range-last-point +title: A grouped unwrap range metric misses the sample at `end` (open) +proves: + - window-bounds-and-step-alignment + - loki_api_v1_query_range +consumer: explore +why: | + `sum by (level) (max_over_time({service_name="api-gateway"} | json | unwrap + status | __error__="" [5m]))` over an hour at 60s has 61 samples on Loki + (start..end inclusive) and 60 on the proxy, which drops the sample at end; + the same for sum_over_time and avg_over_time by app over `| json | unwrap + duration_ms`. + + In the differential run this shows only as a secondary difference of + requests whose primary difference is the parser series identity, so those + clusters are owned by parsed-identity/dynamic-key-parsers; this case claims + none and was measured by hand. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'sum by (level) (max_over_time({service_name="api-gateway"} | json | unwrap status | __error__="" [5m]))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [timestamps] + expect: | + 61 samples including end. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy range-metric unwrap path (end bucket)' + loki: 'pkg/logql/range_vector.go step iteration start..end inclusive' + proxy: 'last step missing' + planned_test: 'TestCompat_UnwrapRangeEndSample (test/e2e-compat, group semantics)' + clusters: [] diff --git a/conformance/registry/cases/semantics/unwrap-sample-validity.yaml b/conformance/registry/cases/semantics/unwrap-sample-validity.yaml new file mode 100644 index 000000000..9dd930588 --- /dev/null +++ b/conformance/registry/cases/semantics/unwrap-sample-validity.yaml @@ -0,0 +1,104 @@ +id: semantics/unwrap-sample-validity +title: unwrap emits samples Loki skips: a missing label gives "" values, a unit string is passed through (open) +proves: + - parser-error-and-label-collision + - loki_api_v1_query_range +consumer: explore +why: | + Loki skips a line whose unwrapped label is absent + (pkg/logql/log/metrics_extraction.go:212-216, issue 6713) and marks a value + it cannot convert with SampleExtractionErr, which `| __error__=""` drops. + The proxy answers series for an absent label with empty-string sample values + (`[1791223680, ""]`, not a number; Grafana cannot plot it and API clients + fail to parse it), returns `"86282s"` verbatim for `| unwrap ttl` over a + duration string, and sums/max-es absent labels into a `{}` series where Loki + returns none. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'max_over_time({service_name="api-gateway"} | unwrap b [2s])' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [series, values] + expect: | + No series (no line has b); numeric sample values only. +gap: + status: open + impact: explore-visible + effort: M + area: 'internal/proxy range-metric unwrap evaluation (stats pushdown over a missing/non-numeric field)' + loki: 'pkg/logql/log/metrics_extraction.go:202-231 streamLabelSampleExtractor.Process' + proxy: '"" sample values / raw unit strings / {} series' + planned_test: 'TestCompat_UnwrapMissingAndNonNumeric (test/e2e-compat, group semantics)' + clusters: + - 0adb5d8c + - 0b6060ea + - 0b885d9e + - 0ca5ac7a + - 0d7e7b08 + - 0f62f4ec + - 13cecc6e + - 188a6f9f + - 1a24c9ee + - 1d449423 + - 2eb02792 + - 33e032b9 + - 3cbd35cd + - 428bae72 + - 46a11d9a + - 4f81c662 + - 6843e15f + - 696cdca6 + - 69766a56 + - 6ff0a17b + - 7314ff41 + - 756c99ae + - 764e0a1b + - 77c25773 + - 77fcddef + - 78618043 + - 7b61ac93 + - 82790745 + - 852ba006 + - 8550f3db + - 866ac555 + - 87352721 + - 8bb55f62 + - 8cc35d5b + - 8f56d9d0 + - 9ac3197a + - 9eaa432d + - 9ecbc578 + - a36064b5 + - a5201e0b + - a5c073e4 + - abb2d873 + - abdf3fa3 + - b15fbabc + - b499246a + - b58ffb68 + - ba07c5dc + - baa868eb + - c16333ff + - c2316012 + - c273ebd1 + - d3077c47 + - d5321d1c + - dc15e297 + - ddbd51ef + - df52ecf7 + - e78502df + - e924d60f + - ea2af38c + - ed1356bf + - ed470597 + - ee497e5b + - f607c198 + - fc1254b0 diff --git a/conformance/registry/cases/severity/detected-level-filter-json-level.yaml b/conformance/registry/cases/severity/detected-level-filter-json-level.yaml new file mode 100644 index 000000000..4f61c4eac --- /dev/null +++ b/conformance/registry/cases/severity/detected-level-filter-json-level.yaml @@ -0,0 +1,53 @@ +id: severity/detected-level-filter-json-level +title: `| detected_level=` filters select different lines than Loki (JSON-level lines, stream level precedence) (open) +proves: + - severity-detected-level-derivation + - loki_api_v1_query_range +consumer: drilldown +why: | + Loki stores detected_level at ingest (structured metadata) from the stream, + metadata or line, so a filter on it selects every line of that level. The + proxy derives detected_level on the read path for the response, but + translates the filter to a stored-field predicate that misses rows whose + level is only inside the JSON line, so the Drilldown level filter shows + fewer lines than Loki. + + Measured by the differential run: `sum by (level, detected_level) + (count_over_time({env="production"} | detected_level="info" | json | drop + __error__ [1m]))` is 3408 on Loki and 3418 on the proxy at one step, and + `{service_name="cache-redis"} | detected_level="info"` (stream label + level=debug, lines level=info) is empty on Loki, which takes the stream's + level, and 99 lines on the proxy, which reads the line. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{namespace="prod"} | detected_level="warn"' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values] + expect: | + Every warn line, as Loki selects. +gap: + status: open + impact: drilldown-visible + effort: M + area: 'internal/translator/detected_level_logsql.go (filter predicate) vs internal/proxy/detected_level.go (response derivation)' + loki: 'pkg/distributor field detection (v3.7.7) stores detected_level as structured metadata; filter reads it' + proxy: 'filter predicate narrower than the read-path derivation' + planned_test: 'TestCompat_DetectedLevelFilterJSONLevel (test/e2e-compat, group drilldown)' + clusters: + - 337f7f5e + - 36eeb045 + - 44bcb9d1 + - 5e798d62 + - 80fabfe0 + - 9016b6b0 + - ce40a9c8 + - f248d966 diff --git a/conformance/registry/cases/severity/detected-level-stage-side-effects.yaml b/conformance/registry/cases/severity/detected-level-stage-side-effects.yaml new file mode 100644 index 000000000..626ff8048 --- /dev/null +++ b/conformance/registry/cases/severity/detected-level-stage-side-effects.yaml @@ -0,0 +1,41 @@ +id: severity/detected-level-stage-side-effects +title: keep/drop detected_level and a detected_level filter change other labels (open) +proves: + - severity-exposure-surfaces + - loki_api_v1_query_range +consumer: explore +why: | + `| keep detected_level` leaves only detected_level on Loki; the proxy also + keeps level and service_name. `| drop detected_level` keeps the stream label + level on Loki; the proxy drops it too. `| detected_level!=""` without a + parser makes the proxy report the logfmt keys of the line (key, msg, op, + ...) as labels; Loki adds none. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="api-gateway"} | drop detected_level' + start: 'now-15m' + end: 'now' + limit: '100' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.stream] + expect: | + Loki's labels. +gap: + status: open + impact: explore-visible + effort: S + area: 'internal/proxy/detected_level.go (level/detected_level coupling in keep/drop/filter)' + loki: 'pkg/logql/log/keep_labels.go, drop_labels.go (detected_level is ordinary structured metadata)' + proxy: 'level coupled to detected_level; parsed fields leak' + planned_test: 'TestCompat_DetectedLevelKeepDrop (test/e2e-compat, group drilldown)' + note: 'Measured on main at fcaccfcc, before #652 rewrote the tail and log-row label conversion; recheck the behaviour on the current code before fixing.' + clusters: + - 89a7162d + - ab9cc1fc + - f737ddd5 diff --git a/conformance/registry/cases/versions/capabilities-floor-vs-matrix.yaml b/conformance/registry/cases/versions/capabilities-floor-vs-matrix.yaml new file mode 100644 index 000000000..1bbc699ee --- /dev/null +++ b/conformance/registry/cases/versions/capabilities-floor-vs-matrix.yaml @@ -0,0 +1,36 @@ +id: versions/capabilities-floor-vs-matrix +title: logsql.Capabilities treats VictoriaLogs below v1.40 as unsupported while the matrix claims v1.30 (open) +proves: + - loki-compatible-profile +consumer: operator +why: | + internal/logsql/capabilities.go returns the zero capability set for versions + before v1.40 (no pipe or stats function assumed), while + test/e2e-compat/compatibility-matrix.json lists v1.30.0 onwards as + supported. A v1.30-v1.39 backend therefore runs the conservative paths (or + none) for features the matrix calls supported. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: 'sum by (level) (count_over_time({env="production"}[5m]))' + start: 'now-1h' + end: 'now' + step: '60' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [status, series] + expect: | + Same answers on every matrix version. +gap: + status: open + impact: edge + effort: M + area: 'internal/logsql/capabilities.go:47 + test/e2e-compat/compatibility-matrix.json' + loki: 'n/a (backend capability)' + proxy: 'zero capability set below v1.40' + planned_test: 'capabilities unit table per matrix version + compat-vl matrix run' + clusters: [] diff --git a/conformance/registry/cases/versions/format-if-before-vl-1.35.yaml b/conformance/registry/cases/versions/format-if-before-vl-1.35.yaml new file mode 100644 index 000000000..53be4b034 --- /dev/null +++ b/conformance/registry/cases/versions/format-if-before-vl-1.35.yaml @@ -0,0 +1,35 @@ +id: versions/format-if-before-vl-1.35 +title: `format if (...)` drops the destination field on VictoriaLogs before v1.35 (open, version-gated) +proves: + - loki-compatible-profile +consumer: explore +why: | + LogsQL `format if (...)` keeps rows the condition rejects without the + destination field only from VictoriaLogs v1.35; earlier versions drop the + field unless `skip_empty_results` is set. Translations that emit `format if` + (line_format / label_format with conditions) lose the field on v1.30-v1.34. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="api-gateway"} | json | label_format lvl="{{.level}}"' + start: 'now-15m' + end: 'now' + limit: '10' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [streams.values] + expect: | + The same labels on every matrix version. +gap: + status: open + impact: edge + effort: S + area: 'internal/translator (format emission) + logsql.Capabilities gate' + loki: 'pkg/logql/log/fmt.go label_format/line_format' + proxy: 'field missing on VictoriaLogs < v1.35' + planned_test: 'unit test for the capability fallback + one e2e group on v1.34' + clusters: [] diff --git a/conformance/registry/cases/versions/otlp-severity-before-vl-1.35.yaml b/conformance/registry/cases/versions/otlp-severity-before-vl-1.35.yaml new file mode 100644 index 000000000..ccd2f9949 --- /dev/null +++ b/conformance/registry/cases/versions/otlp-severity-before-vl-1.35.yaml @@ -0,0 +1,35 @@ +id: versions/otlp-severity-before-vl-1.35 +title: OTLP severity is not read as detected_level on VictoriaLogs v1.30-v1.34 (open, version-gated) +proves: + - severity-detected-level-derivation +consumer: explore +why: | + Loki derives detected_level from an OTLP record's severity. VictoriaLogs + before v1.35 stores OTLP severity under a different field shape, so the + proxy's read-path derivation returns `unknown` for such rows on v1.30-v1.34, + while the compatibility matrix claims those versions. +request: + method: GET + path: /loki/api/v1/query_range + params: + query: '{service_name="otel-collector"}' + start: 'now-15m' + end: 'now' + limit: '10' + headers: + X-Scope-OrgID: "0" + X-Loki-Response-Encoding-Flags: categorize-labels +compare: + mode: observed + fields: [detected_level] + expect: | + detected_level from severity on every matrix version. +gap: + status: open + impact: edge + effort: S + area: 'internal/proxy/detected_level.go + logsql capabilities' + loki: 'detected_level from OTLP severity (distributor field detection, v3.7.7)' + proxy: 'unknown on VictoriaLogs v1.30-v1.34' + planned_test: 'TestVLTrackScore version matrix row for OTLP severity (compat-vl workflow)' + clusters: [] diff --git a/conformance/registry/generated/parity-discovery.json b/conformance/registry/generated/parity-discovery.json new file mode 100644 index 000000000..32180eb6f --- /dev/null +++ b/conformance/registry/generated/parity-discovery.json @@ -0,0 +1,18433 @@ +{ + "clusters": [ + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{service_name=\"api-gateway\"} | json | region!=\"\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{service_name=\"api-gateway\"} | json | region=~\".+\"" + } + ], + "id": "e9b099d2", + "impact": "explore-visible", + "queries": 164, + "requests": 328, + "shapes": { + "logs[json,label-filter]": 328 + }, + "signature": [ + "query_range", + "logs[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 56, + "seeded": 272 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{service_name=\"api-gateway\"} | json | drop region" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{service_name=\"api-gateway\"} | json | drop status" + } + ], + "id": "eaaf14ca", + "impact": "explore-visible", + "queries": 93, + "requests": 186, + "shapes": { + "logs[json,drop]": 186 + }, + "signature": [ + "query_range", + "logs[json,drop]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 20, + "seeded": 166 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name']; e.g. proxy {\"region\": \"us-east-1\", \"service_name\": \"api-gateway\"}", + "query": "{service_name=\"api-gateway\"} | json | keep region" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name']; e.g. proxy {\"service_name\": \"api-gateway\", \"status\": \"404\"}", + "query": "{service_name=\"api-gateway\"} | json | keep status" + } + ], + "id": "8ae09281", + "impact": "explore-visible", + "queries": 73, + "requests": 146, + "shapes": { + "logs[json,keep]": 146 + }, + "signature": [ + "query_range", + "logs[json,keep]", + "labels", + "proxy-only service_name" + ], + "sources": { + "repo": 10, + "seeded": 136 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clu...", + "query": "{namespace=\"prod\"} | foo =~\".*\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clu...", + "query": "{namespace=\"prod\"} | foo=~\".*\"" + } + ], + "id": "343338a6", + "impact": "explore-visible", + "queries": 72, + "requests": 144, + "shapes": { + "logs[label-filter]": 144 + }, + "signature": [ + "query_range", + "logs[label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 6, + "repo": 2, + "seeded": 136 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{service_name=\"api-gateway\"} | json | line_format \"{{.region}}\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{service_name=\"api-gateway\"} | json | line_format \"{{.status}}\"" + } + ], + "id": "a300565d", + "impact": "explore-visible", + "queries": 70, + "requests": 140, + "shapes": { + "logs[json,line_format]": 140 + }, + "signature": [ + "query_range", + "logs[json,line_format]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4, + "seeded": 136 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"amount\": \"9262.76\", \"app\": \"payment-service\", \"cluster\": \"us-east-1\", \"container\": \"payment\", \"currency\": \"USD\", \"detected_level\": \"error\", \"env\": \"production\", \"error_code\": \"provider_error\", \"l...", + "query": "{service_name=\"payment-service\"} | logfmt | level=\"error\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"amount\": \"9262.76\", \"app\": \"payment-service\", \"cluster\": \"us-east-1\", \"container\": \"payment\", \"currency\": \"USD\", \"detected_level\": \"error\", \"env\": \"production\", \"error_code\": \"provider_error\", \"l...", + "query": "{service_name=\"payment-service\"}|logfmt|level=\"error\"" + } + ], + "id": "b0dd1495", + "impact": "explore-visible", + "queries": 57, + "requests": 114, + "shapes": { + "logs[logfmt,label-filter]": 114 + }, + "signature": [ + "query_range", + "logs[logfmt,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 4, + "repo": 2, + "seeded": 108 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"checkout-svc\", \"cluster\": \"us-east-1\", \"container\": \"checkout\", \"deployment_environment\": \"production\", \"detected_level\": \"error\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clus...", + "query": "{service_name=\"checkout-svc\"} | line_format \"{{.detected_level}}\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"checkout-svc\", \"cluster\": \"us-east-1\", \"container\": \"checkout\", \"deployment_environment\": \"production\", \"detected_level\": \"error\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clus...", + "query": "{service_name=\"checkout-svc\"} | line_format \"{{.deployment_environment}}\"" + } + ], + "id": "25b77e2f", + "impact": "explore-visible", + "queries": 40, + "requests": 80, + "shapes": { + "logs[line_format]": 80 + }, + "signature": [ + "query_range", + "logs[line_format]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2, + "seeded": 78 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{service_name=\"api-gateway\"} | json" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"checkout-svc\", \"cluster\": \"us-east-1\", \"container\": \"checkout\", \"deployment_environment\": \"production\", \"detected_level\": \"error\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clus...", + "query": "{service_name=\"checkout-svc\"} | json msg=\"msg\"" + } + ], + "id": "15a876ba", + "impact": "explore-visible", + "queries": 38, + "requests": 76, + "shapes": { + "logs[json]": 76 + }, + "signature": [ + "query_range", + "logs[json]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 2, + "repo": 12, + "seeded": 62 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 99 entries, proxy 0", + "query": "{service_name=\"api-gateway\"} | json | service_name_extracted!=\"\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 99 entries, proxy 0", + "query": "{service_name=\"api-gateway\"} | json | service_name_extracted=~\".+\"" + } + ], + "id": "bfd206cd", + "impact": "explore-visible", + "queries": 37, + "requests": 74, + "shapes": { + "logs[json,label-filter]": 74 + }, + "signature": [ + "query_range", + "logs[json,label-filter]", + "entries", + "proxy-empty" + ], + "sources": { + "seeded": 74 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 0; loki-only [\"{}\"] proxy-only []", + "query": "sum(count_over_time({service_name=\"cache-redis\"} | logfmt | op!=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 0; loki-only [\"{}\"] proxy-only []", + "query": "sum(count_over_time({service_name=\"cache-redis\"} | logfmt | msg!=\"\" [5m]))" + } + ], + "id": "0a9f54e0", + "impact": "explore-visible", + "queries": 36, + "requests": 72, + "shapes": { + "sum[logfmt,label-filter]": 72 + }, + "signature": [ + "query_range", + "sum[logfmt,label-filter]", + "series", + "proxy-empty" + ], + "sources": { + "seeded": 72 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"checkout-svc\", \"cluster\": \"us-east-1\", \"container\": \"checkout\", \"detected_level\": \"error\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_cluster_name\": \"k8s-prod-us-west-2\", \"k8s_na...", + "query": "{service_name=\"checkout-svc\"} | drop deployment_environment" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"checkout-svc\", \"cluster\": \"us-east-1\", \"container\": \"checkout\", \"deployment_environment\": \"production\", \"detected_level\": \"error\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clus...", + "query": "{service_name=\"checkout-svc\"} | drop trace_id" + } + ], + "id": "56dc3ddd", + "impact": "explore-visible", + "queries": 34, + "requests": 68, + "shapes": { + "logs[drop]": 68 + }, + "signature": [ + "query_range", + "logs[drop]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 68 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token STRING (\"bar\")", + "query": "{namespace=\"prod\"} != \"foo\" or \"bar\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token STRING (\"bar\")", + "query": "{namespace=\"prod\"} != \"foo\" or \"bar\" or \"baz\" != \"car\"" + } + ], + "id": "b349505b", + "impact": "explore-visible", + "queries": 31, + "requests": 62, + "shapes": { + "logs[line-filter]": 62 + }, + "signature": [ + "query_range", + "logs[line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token string (Q)" + ], + "sources": { + "loki-tests": 62 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name']; e.g. proxy {\"service_name\": \"cache-redis\"}", + "query": "{service_name=\"cache-redis\"} | logfmt | keep op" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name']; e.g. proxy {\"msg\": \"cache expire\", \"service_name\": \"cache-redis\"}", + "query": "{service_name=\"cache-redis\"} | logfmt | keep msg" + } + ], + "id": "59ebda99", + "impact": "explore-visible", + "queries": 31, + "requests": 62, + "shapes": { + "logs[logfmt,keep]": 62 + }, + "signature": [ + "query_range", + "logs[logfmt,keep]", + "labels", + "proxy-only service_name" + ], + "sources": { + "repo": 2, + "seeded": 60 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"app\": \"cache-redis\", \"cluster\": \"us-east-1\", \"container\": \"cache\", \"db\": \"0\", \"detected_level\": \"debug\", \"env\": \"production\", \"key\": \"cache:query:e29a39\", \"level\": \"debug\", \"level_extracted\": \"de...", + "query": "{service_name=\"cache-redis\"} | logfmt | drop op" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"app\": \"cache-redis\", \"cluster\": \"us-east-1\", \"container\": \"cache\", \"db\": \"0\", \"detected_level\": \"debug\", \"env\": \"production\", \"key\": \"cache:query:e29a39\", \"level\": \"debug\", \"level_extracted\": \"de...", + "query": "{service_name=\"cache-redis\"} | logfmt | drop msg" + } + ], + "id": "975e64c1", + "impact": "explore-visible", + "queries": 28, + "requests": 56, + "shapes": { + "logs[logfmt,drop]": 56 + }, + "signature": [ + "query_range", + "logs[logfmt,drop]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2, + "seeded": 54 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"app\": \"cache-redis\", \"cluster\": \"us-east-1\", \"container\": \"cache\", \"db\": \"0\", \"detected_level\": \"debug\", \"env\": \"production\", \"key\": \"cache:query:e29a39\", \"level\": \"debug\", \"level_extracted\": \"de...", + "query": "{service_name=\"cache-redis\"} | logfmt | line_format \"{{.op}}\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"app\": \"cache-redis\", \"cluster\": \"us-east-1\", \"container\": \"cache\", \"db\": \"0\", \"detected_level\": \"debug\", \"env\": \"production\", \"key\": \"cache:query:e29a39\", \"level\": \"debug\", \"level_extracted\": \"de...", + "query": "{service_name=\"cache-redis\"} | logfmt | line_format \"{{.msg}}\"" + } + ], + "id": "f809410d", + "impact": "explore-visible", + "queries": 27, + "requests": 54, + "shapes": { + "logs[logfmt,line_format]": 54 + }, + "signature": [ + "query_range", + "logs[logfmt,line_format]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 54 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clu...", + "query": "{namespace=\"prod\"}" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clu...", + "query": "{namespace=\"prod\"} != ip(\"127.0.0.1\") != \"foo\"" + } + ], + "id": "6f88a869", + "impact": "explore-visible", + "queries": 26, + "requests": 54, + "shapes": { + "logs": 54 + }, + "signature": [ + "query_range", + "logs", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 6, + "repo": 48 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clu...", + "query": "{namespace=\"prod\"} != \"foo\" != \"bar\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clu...", + "query": "{namespace=\"prod\"} != \"foo\" != \"bar\" != \"baz\" != \"car\"" + } + ], + "id": "269ef189", + "impact": "explore-visible", + "queries": 24, + "requests": 50, + "shapes": { + "logs[line-filter]": 50 + }, + "signature": [ + "query_range", + "logs[line-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 40, + "repo": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"checkout-svc\", \"cluster\": \"us-east-1\", \"container\": \"checkout\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clust...", + "query": "{service_name=\"checkout-svc\"} | detected_level=\"info\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"checkout-svc\", \"cluster\": \"us-east-1\", \"container\": \"checkout\", \"deployment_environment\": \"production\", \"detected_level\": \"warn\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clust...", + "query": "{service_name=\"checkout-svc\"} | detected_level=\"warn\"" + } + ], + "id": "3df8d769", + "impact": "explore-visible", + "queries": 24, + "requests": 48, + "shapes": { + "logs[detected_level,label-filter]": 48 + }, + "signature": [ + "query_range", + "logs[detected_level,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 48 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['deployment_environment']; e.g. loki {\"deployment_environment\": \"production\"}", + "query": "{service_name=\"checkout-svc\"} | keep deployment_environment" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['k8s_cluster_name']; e.g. loki {\"k8s_cluster_name\": \"k8s-prod-us-east-1\"}", + "query": "{service_name=\"checkout-svc\"} | keep k8s_cluster_name" + } + ], + "id": "4e8cf844", + "impact": "explore-visible", + "queries": 24, + "requests": 48, + "shapes": { + "logs[keep]": 48 + }, + "signature": [ + "query_range", + "logs[keep]", + "labels", + "loki-only " + ], + "sources": { + "seeded": 48 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "avg_over_time({service_name=\"api-gateway\"} | json | unwrap status | __error__=\"\" [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "avg_over_time({service_name=\"api-gateway\"} | json | unwrap duration_ms | __error__=\"\" [5m])" + } + ], + "id": "087a214a", + "impact": "explore-visible", + "queries": 23, + "requests": 46, + "shapes": { + "avg_over_time[json,unwrap,label-filter]": 46 + }, + "signature": [ + "query_range", + "avg_over_time[json,unwrap,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2, + "seeded": 44 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"level\": \"error\"}: loki 61 points, proxy 60", + "query": "sum by (level) (max_over_time({service_name=\"api-gateway\"} | json | unwrap status | __error__=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"level\": \"error\"}: loki 61 points, proxy 60", + "query": "sum by (level) (max_over_time({service_name=\"api-gateway\"} | json | unwrap duration_ms | __error__=\"\" [5m]))" + } + ], + "id": "375b5df6", + "impact": "explore-visible", + "queries": 21, + "requests": 42, + "shapes": { + "sum[json,unwrap,label-filter]": 42 + }, + "signature": [ + "query_range", + "sum[json,unwrap,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "seeded": 42 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count by (a) (max_over_time({namespace=\"prod\"} | unwrap b [2s]) by (a))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count by (a) (max_over_time({namespace=\"prod\"} | unwrap b [2s]))" + } + ], + "id": "46a11d9a", + "impact": "explore-visible", + "queries": 20, + "requests": 40, + "shapes": { + "count[unwrap]": 40 + }, + "signature": [ + "query_range", + "count[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 40 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 74383 series, proxy 15220", + "query": "rate({namespace=\"prod\"}[1h])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 50479 series, proxy 10325", + "query": "rate({namespace=\"prod\"}[1m])" + } + ], + "id": "9f19b596", + "impact": "explore-visible", + "queries": 18, + "requests": 38, + "shapes": { + "rate": 38 + }, + "signature": [ + "query_range", + "rate", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 34, + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "query_range", + "excerpt": "level: loki debug | proxy info", + "query": "{service_name=\"cache-redis\"} | logfmt op" + }, + { + "encoding": "plain", + "endpoint": "query_range", + "excerpt": "level: loki debug | proxy info", + "query": "{service_name=\"cache-redis\"} | logfmt msg" + } + ], + "id": "7dac2581", + "impact": "explore-visible", + "queries": 18, + "requests": 18, + "shapes": { + "logs[logfmt]": 18 + }, + "signature": [ + "query_range", + "logs[logfmt]", + "labels", + "value differs level" + ], + "sources": { + "seeded": 18 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unexpected type for left leg of binary operation (+): *syntax.MatchersExpr | proxy parse error at line 1, col 1: unexpected expression for binary operation", + "query": "{namespace=\"prod\"} + {namespace=\"prod\"}" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unexpected type for left leg of binary operation (/): *syntax.MatchersExpr | proxy parse error at line 1, col 1: unexpected expression for binary operation", + "query": "{namespace=\"prod\"} / sum(count_over_time({namespace=\"prod\"}[5m])) by (foo)" + } + ], + "id": "e8b24a9b", + "impact": "explore-visible", + "queries": 17, + "requests": 34, + "shapes": { + "logs": 34 + }, + "signature": [ + "query_range", + "logs", + "error-text", + "400 with different text: parse error at line N, col N: unexpected expression for binary operation" + ], + "sources": { + "loki-tests": 4, + "repo": 30 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 59420 series, proxy 12176", + "query": "count_over_time({namespace=\"prod\"}[12m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 59420 series, proxy 12176", + "query": "count_over_time({namespace=\"prod\"}[12m]) or count_over_time({namespace=\"prod\"}[12m]) > 1" + } + ], + "id": "13c8bdcd", + "impact": "explore-visible", + "queries": 16, + "requests": 34, + "shapes": { + "count_over_time": 34 + }, + "signature": [ + "query_range", + "count_over_time", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 26, + "repo": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max by (a) (max_over_time({namespace=\"prod\"} | unwrap b [2s]) by (a))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max by (a) (max_over_time({namespace=\"prod\"} | unwrap b [2s]))" + } + ], + "id": "0b885d9e", + "impact": "explore-visible", + "queries": 16, + "requests": 32, + "shapes": { + "max[unwrap]": 32 + }, + "signature": [ + "query_range", + "max[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 32 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "min by (a) (max_over_time({namespace=\"prod\"} | unwrap b [2s]) by (a))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "min by (a) (max_over_time({namespace=\"prod\"} | unwrap b [2s]))" + } + ], + "id": "4f81c662", + "impact": "explore-visible", + "queries": 16, + "requests": 32, + "shapes": { + "min[unwrap]": 32 + }, + "signature": [ + "query_range", + "min[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 32 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"service_name_extracted\": \"api-gateway\"}", + "query": "{service_name=\"api-gateway\"} | json | keep service_name_extracted" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['version_extracted']; e.g. loki {\"version_extracted\": \"v1\"}", + "query": "{service_name=\"api-gateway\"} | json | keep version_extracted" + } + ], + "id": "a9078b6e", + "impact": "explore-visible", + "queries": 16, + "requests": 32, + "shapes": { + "logs[json,keep]": 32 + }, + "signature": [ + "query_range", + "logs[json,keep]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 32 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki-only api-gateway", + "query": "{service_name=\"api-gateway\"} | json | line_format \"{{.service_name_extracted}}\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki-only v1", + "query": "{service_name=\"api-gateway\"} | json | line_format \"{{.version_extracted}}\"" + } + ], + "id": "8c96e500", + "impact": "explore-visible", + "queries": 16, + "requests": 32, + "shapes": { + "logs[json,line_format]": 32 + }, + "signature": [ + "query_range", + "logs[json,line_format]", + "entries", + "different entries, same count" + ], + "sources": { + "seeded": 32 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "tail", + "excerpt": "loki 99 entries, proxy 0", + "query": "{service_name=\"api-gateway\"}" + }, + { + "encoding": "categorize", + "endpoint": "tail", + "excerpt": "loki 99 entries, proxy 0", + "query": "{service_name=\"auth-service\"}" + } + ], + "id": "e02ddf41", + "impact": "explore-visible", + "queries": 15, + "requests": 30, + "shapes": { + "logs": 30 + }, + "signature": [ + "tail", + "logs", + "entries", + "proxy-empty" + ], + "sources": { + "seeded": 30 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level', 'service_name']; e.g. proxy {\"detected_level\": \"warn\", \"level\": \"warn\", \"service_name\": \"api-gateway\"}", + "query": "{service_name=\"api-gateway\"} | keep detected_level" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level', 'service_name']; e.g. proxy {\"detected_level\": \"warn\", \"level\": \"warn\", \"service_name\": \"auth-service\"}", + "query": "{service_name=\"auth-service\"} | keep detected_level" + } + ], + "id": "89a7162d", + "impact": "explore-visible", + "queries": 15, + "requests": 30, + "shapes": { + "logs[keep]": 30 + }, + "signature": [ + "query_range", + "logs[keep]", + "labels", + "proxy-only level,service_name" + ], + "sources": { + "seeded": 30 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 10656", + "query": "max by (baz) (bytes_over_time({namespace=\"prod\"}[3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 10656", + "query": "max by (baz) (bytes_rate({namespace=\"prod\"}[3m]))" + } + ], + "id": "91b7514f", + "impact": "explore-visible", + "queries": 14, + "requests": 28, + "shapes": { + "max": 28 + }, + "signature": [ + "query_range", + "max", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 22, + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by (a) (sum_over_time({namespace=\"prod\"} | unwrap b [2s]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by (baz) (sum_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + } + ], + "id": "9ecbc578", + "impact": "explore-visible", + "queries": 14, + "requests": 28, + "shapes": { + "sum[unwrap]": 28 + }, + "signature": [ + "query_range", + "sum[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 28 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 10656", + "query": "count by (baz) (bytes_over_time({namespace=\"prod\"}[3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 10656", + "query": "count by (baz) (bytes_rate({namespace=\"prod\"}[3m]))" + } + ], + "id": "da2a39ee", + "impact": "explore-visible", + "queries": 12, + "requests": 24, + "shapes": { + "count": 24 + }, + "signature": [ + "query_range", + "count", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 18, + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max by (a) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]) by (a))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max by (a) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]))" + } + ], + "id": "1a24c9ee", + "impact": "explore-visible", + "queries": 12, + "requests": 24, + "shapes": { + "max[logfmt,unwrap]": 24 + }, + "signature": [ + "query_range", + "max[logfmt,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 24 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "min by (a) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]) by (a))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "min by (a) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]))" + } + ], + "id": "ed1356bf", + "impact": "explore-visible", + "queries": 12, + "requests": 24, + "shapes": { + "min[logfmt,unwrap]": 24 + }, + "signature": [ + "query_range", + "min[logfmt,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 24 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['key', 'msg', 'op', 'reason', 'size_bytes', 'ttl']; e.g. proxy {\"app\": \"cache-redis\", \"cluster\": \"us-east-1\", \"container\": \"cache\", \"db\": \"0\", \"detected_level\": \"debug\", \"env\": \"production\", \"key\": \"cache:query:e29a39\", \"level\": \"debug\", \"msg\": \"cache expire\",...", + "query": "{service_name=\"cache-redis\"} | detected_level!=\"\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['key', 'msg', 'op', 'reason', 'size_bytes', 'ttl']; e.g. proxy {\"app\": \"cache-redis\", \"cluster\": \"us-east-1\", \"container\": \"cache\", \"db\": \"0\", \"detected_level\": \"debug\", \"env\": \"production\", \"key\": \"cache:query:e29a39\", \"level\": \"debug\", \"msg\": \"cache expire\",...", + "query": "{service_name=\"cache-redis\"} | detected_level=~\".+\"" + } + ], + "id": "f737ddd5", + "impact": "explore-visible", + "queries": 12, + "requests": 24, + "shapes": { + "logs[detected_level,label-filter]": 24 + }, + "signature": [ + "query_range", + "logs[detected_level,label-filter]", + "labels", + "proxy-only " + ], + "sources": { + "seeded": 24 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 791 series, proxy 5", + "query": "topk by (container)( 5, count_over_time( {namespace=\"prod\"} [5m] ) )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 3 series, proxy 2; loki-only [\"{\\\"app\\\": \\\"api-gateway\\\", \\\"cluster\\\": \\\"us-east-1\\\", \\\"container\\\": \\\"api\\\", \\\"detected_level\\\": \\\"error\\\", \\\"env\\\": \\\"production\\\", \\\"level\\\": \\\"error\\\"... proxy-only [\"{\\\"app\\\": \\\"api-gateway\\\", \\\"cluster\\\": \\\"us-east-1\\\", \\\"container\\\": \\\"api\\\", \\\"detected...", + "query": "topk(1,rate({namespace=\"prod\"}[1h]) / (rate({namespace=\"prod\"}[1h]) + rate({namespace=\"prod\"}[1h])))" + } + ], + "id": "23526b6c", + "impact": "explore-visible", + "queries": 11, + "requests": 22, + "shapes": { + "topk": 22 + }, + "signature": [ + "query_range", + "topk", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 14, + "repo": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name']; e.g. proxy {\"service_name\": \"checkout-svc\", \"trace_id\": \"56904d0510be6b6160a4b6492122a43a\"}", + "query": "{service_name=\"checkout-svc\"} | keep trace_id" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name']; e.g. proxy {\"service_name\": \"checkout-svc\", \"span_id\": \"9dd09df4b20e1413\"}", + "query": "{service_name=\"checkout-svc\"} | keep span_id" + } + ], + "id": "0acfebdb", + "impact": "explore-visible", + "queries": 11, + "requests": 22, + "shapes": { + "logs[keep]": 22 + }, + "signature": [ + "query_range", + "logs[keep]", + "labels", + "proxy-only service_name" + ], + "sources": { + "repo": 2, + "seeded": 20 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 10656", + "query": "min by (baz) (bytes_over_time({namespace=\"prod\"}[3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 10656", + "query": "min by (baz) (bytes_rate({namespace=\"prod\"}[3m]))" + } + ], + "id": "20bb13d8", + "impact": "explore-visible", + "queries": 10, + "requests": 20, + "shapes": { + "min": 20 + }, + "signature": [ + "query_range", + "min", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 16, + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by (a) (sum_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by (method) (sum_over_time({service_name=\"payment-service\"} | logfmt | unwrap duration(latency) [5m]))" + } + ], + "id": "b58ffb68", + "impact": "explore-visible", + "queries": 10, + "requests": 20, + "shapes": { + "sum[logfmt,unwrap]": 20 + }, + "signature": [ + "query_range", + "sum[logfmt,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 20 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"env\": \"production\", \"level\": \"error\", \"namespace\": \"prod\", \"pod\": \"api-gateway-7eb16-a4fd\", \"service_name\": \"api-gateway\", \"versi...", + "query": "{service_name=\"api-gateway\"} | drop detected_level" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level']; e.g. loki {\"app\": \"auth-service\", \"cluster\": \"us-east-1\", \"container\": \"auth\", \"env\": \"production\", \"level\": \"info\", \"namespace\": \"prod\", \"pod\": \"auth-service-33a6a-9bf9\", \"service_name\": \"auth-service\"}", + "query": "{service_name=\"auth-service\"} | drop detected_level" + } + ], + "id": "ab9cc1fc", + "impact": "explore-visible", + "queries": 10, + "requests": 20, + "shapes": { + "logs[drop]": 20 + }, + "signature": [ + "query_range", + "logs[drop]", + "labels", + "loki-only level" + ], + "sources": { + "seeded": 20 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted']", + "query": "avg_over_time({service_name=\"cache-redis\"} | logfmt | unwrap size_bytes | __error__=\"\" [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted']", + "query": "avg_over_time({service_name=\"payment-service\"} | logfmt | unwrap latency_ms | __error__=\"\" [5m])" + } + ], + "id": "97e6ee39", + "impact": "explore-visible", + "queries": 10, + "requests": 20, + "shapes": { + "avg_over_time[logfmt,unwrap,label-filter]": 20 + }, + "signature": [ + "query_range", + "avg_over_time[logfmt,unwrap,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 20 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 99 entries, proxy 0", + "query": "{service_name=\"checkout-svc\"} | service_name_extracted!=\"\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 99 entries, proxy 0", + "query": "{service_name=\"checkout-svc\"} | service_name_extracted=~\".+\"" + } + ], + "id": "e6dc2163", + "impact": "explore-visible", + "queries": 10, + "requests": 20, + "shapes": { + "logs[label-filter]": 20 + }, + "signature": [ + "query_range", + "logs[label-filter]", + "entries", + "proxy-empty" + ], + "sources": { + "seeded": 20 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 0", + "query": "sum by (service_name_extracted) (count_over_time({service_name=\"checkout-svc\"} | service_name_extracted!=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 0; loki-only [\"{}\"] proxy-only []", + "query": "sum(count_over_time({service_name=\"checkout-svc\"} | service_name_extracted!=\"\" [5m]))" + } + ], + "id": "6650ff42", + "impact": "explore-visible", + "queries": 10, + "requests": 20, + "shapes": { + "sum[label-filter]": 20 + }, + "signature": [ + "query_range", + "sum[label-filter]", + "series", + "proxy-empty" + ], + "sources": { + "seeded": 20 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 59420 series, proxy 12176", + "query": "bytes_over_time({namespace=\"prod\"}[12m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 50479 series, proxy 10325", + "query": "bytes_over_time({namespace=\"prod\"}[1m])" + } + ], + "id": "acdff84d", + "impact": "explore-visible", + "queries": 9, + "requests": 18, + "shapes": { + "bytes_over_time": 18 + }, + "signature": [ + "query_range", + "bytes_over_time", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 16, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count by (a) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]) by (a))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count by (a) (min_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]) by (a))" + } + ], + "id": "abdf3fa3", + "impact": "explore-visible", + "queries": 9, + "requests": 18, + "shapes": { + "count[logfmt,unwrap]": 18 + }, + "signature": [ + "query_range", + "count[logfmt,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 18 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \")\" at position 59", + "query": "{service_name=\"payment-service\"}|logfmt|addr!=ip(\"1.2.3.4\")" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \")\" at position 58", + "query": "{service_name=\"payment-service\"}|logfmt|addr=ip(\"1.2.3.4\")" + } + ], + "id": "46125976", + "impact": "explore-visible", + "queries": 9, + "requests": 18, + "shapes": { + "logs[logfmt,label-filter]": 18 + }, + "signature": [ + "query_range", + "logs[logfmt,label-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 18 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "sum by (a) (max_over_time({namespace=\"prod\"} | unwrap b [2s]) by (a))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "sum by (a) (max_over_time({namespace=\"prod\"} | unwrap b [2s]))" + } + ], + "id": "a36064b5", + "impact": "explore-visible", + "queries": 8, + "requests": 16, + "shapes": { + "sum[unwrap]": 16 + }, + "signature": [ + "query_range", + "sum[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 16 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 53652 series, proxy 10995", + "query": "sum without () (rate({namespace=\"prod\"}[5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 50479 series, proxy 10325", + "query": "sum without (app) (count_over_time({namespace=\"prod\"}[1m]))" + } + ], + "id": "886cdc75", + "impact": "explore-visible", + "queries": 8, + "requests": 16, + "shapes": { + "sum": 16 + }, + "signature": [ + "query_range", + "sum", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 10, + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "10155 samples, e.g. {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"env\": \"production\", \"l... @1791223680.0: \"\"", + "query": "max_over_time({namespace=\"prod\"} | unwrap b [2s])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max_over_time({namespace=\"prod\"} | unwrap b [2s]) by (a)" + } + ], + "id": "0f62f4ec", + "impact": "explore-visible", + "queries": 7, + "requests": 14, + "shapes": { + "max_over_time[unwrap]": 14 + }, + "signature": [ + "query_range", + "max_over_time[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 12, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"amount\": \"8626.55\", \"app\": \"payment-service\", \"cluster\": \"us-east-1\", \"container\": \"payment\", \"currency\": \"GBP\", \"detected_level\": \"error\", \"env\": \"production\", \"error_code\": \"insufficient_funds\"...", + "query": "{service_name=\"payment-service\"} | logfmt" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"amount\": \"8626.55\", \"app\": \"payment-service\", \"cluster\": \"us-east-1\", \"container\": \"payment\", \"currency\": \"GBP\", \"detected_level\": \"error\", \"env\": \"production\", \"error_code\": \"insufficient_funds\"...", + "query": "{app=\"payment-service\",env=\"production\"} | logfmt" + } + ], + "id": "58f21a34", + "impact": "explore-visible", + "queries": 7, + "requests": 14, + "shapes": { + "logs[logfmt]": 14 + }, + "signature": [ + "query_range", + "logs[logfmt]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 2, + "repo": 12 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token STRING (\"foo <_>\")", + "query": "{namespace=\"prod\"} !> \"<_> foo <_>\" or \"foo <_>\" !> \"foo <_> baz\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token STRING (\"bar\")", + "query": "{namespace=\"prod\"} !> \"foo\" or \"bar\"" + } + ], + "id": "9d69befe", + "impact": "explore-visible", + "queries": 7, + "requests": 14, + "shapes": { + "logs[pattern-filter]": 14 + }, + "signature": [ + "query_range", + "logs[pattern-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token string (Q)" + ], + "sources": { + "loki-tests": 14 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "10155 samples, e.g. {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"env\": \"production\", \"l... @1791223680.0: \"\"", + "query": "min_over_time({namespace=\"prod\"} | unwrap b [2s])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "min_over_time({namespace=\"prod\"} | unwrap b [2s]) by (a)" + } + ], + "id": "866ac555", + "impact": "explore-visible", + "queries": 6, + "requests": 12, + "shapes": { + "min_over_time[unwrap]": 12 + }, + "signature": [ + "query_range", + "min_over_time[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 12 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "sum by (a) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]) by (a))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "sum by (a) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]))" + } + ], + "id": "82790745", + "impact": "explore-visible", + "queries": 6, + "requests": 12, + "shapes": { + "sum[logfmt,unwrap]": 12 + }, + "signature": [ + "query_range", + "sum[logfmt,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 12 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 10155", + "query": "sum_over_time({namespace=\"prod\"} | unwrap b [2s])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 10155", + "query": "sum_over_time({namespace=\"prod\"} | unwrap bar [3m])" + } + ], + "id": "ba07c5dc", + "impact": "explore-visible", + "queries": 6, + "requests": 12, + "shapes": { + "sum_over_time[unwrap]": 12 + }, + "signature": [ + "query_range", + "sum_over_time[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 10, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 99 entries, proxy 0", + "query": "{service_name=\"cache-redis\"} | logfmt | level_extracted!=\"\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 99 entries, proxy 0", + "query": "{service_name=\"cache-redis\"} | logfmt | level_extracted=~\".+\"" + } + ], + "id": "ef9bfaad", + "impact": "explore-visible", + "queries": 6, + "requests": 12, + "shapes": { + "logs[logfmt,label-filter]": 12 + }, + "signature": [ + "query_range", + "logs[logfmt,label-filter]", + "entries", + "proxy-empty" + ], + "sources": { + "seeded": 12 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "ds_query", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"otel-collector\", \"cluster\": \"us-east-1\", \"component\": \"collector\", \"container\": \"otel\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"k8s_cluster_...", + "query": "{service_name=`otel-collector` }" + }, + { + "encoding": "categorize", + "endpoint": "ds_query", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"checkout-svc\", \"cluster\": \"us-east-1\", \"container\": \"checkout\", \"deployment_environment\": \"production\", \"detected_level\": \"error\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clus...", + "query": "{service_name=`checkout-svc` }" + } + ], + "id": "0e676869", + "impact": "explore-visible", + "queries": 6, + "requests": 12, + "shapes": { + "logs": 12 + }, + "signature": [ + "grafana:ds_query", + "logs", + "labels", + "loki-only *_extracted" + ], + "sources": { + "grafana": 12 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : error parsing regexp: missing closing ): `(unclosed` | proxy parse error at line 1, col 1: parse error : invalid regex: error parsing regexp: missing closing ): `(unclosed`", + "query": "{app!~\"(unclosed\"}" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : error parsing regexp: missing closing ): `(a` | proxy parse error at line 1, col 1: parse error : invalid regex: error parsing regexp: missing closing ): `(a`", + "query": "{app=~\"(a\"}" + } + ], + "id": "4be578fa", + "impact": "explore-visible", + "queries": 6, + "requests": 12, + "shapes": { + "logs": 12 + }, + "signature": [ + "query_range", + "logs", + "error-text", + "400 with different text: parse error at line N, col N: parse error : invalid regex: error parsing regexp:" + ], + "sources": { + "repo": 12 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 15220", + "query": "avg(count_over_time({namespace=\"prod\"}[1h])) by ()" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 15220", + "query": "avg(count_over_time({namespace=\"prod\"}[1h])) by (bar,foo)" + } + ], + "id": "099e8acf", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "avg": 10 + }, + "signature": [ + "query_range", + "avg", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 6, + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count by (baz) (min_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]) by (baz))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count by (foo) (max_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]))" + } + ], + "id": "8bb55f62", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "count[json,unwrap]": 10 + }, + "signature": [ + "query_range", + "count[json,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2091", + "query": "count by (foo) (bytes_over_time({service_name=\"api-gateway\"} | json [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2091", + "query": "count by (foo) (bytes_rate({service_name=\"api-gateway\"} | json [3m]))" + } + ], + "id": "2bbe720f", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "count[json]": 10 + }, + "signature": [ + "query_range", + "count[json]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 5140 series, proxy 4024", + "query": "label_replace( label_replace( rate( {namespace=\"prod\"} |= \"err\" [5m] ), \"foo\", \"$1\", \"service\", \"(.*):.*\" ), \"foo\", \"$1\", \"service\", \"(.*):.*\" )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 5140 series, proxy 4024", + "query": "label_replace( rate( {namespace=\"prod\"} |= \"err\" [5m] ), \"foo\", \"$1\", \"service\", \"(.*):.*\" )" + } + ], + "id": "05582624", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "label_replace[line-filter]": 10 + }, + "signature": [ + "query_range", + "label_replace[line-filter]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by (baz) (sum_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by (name)(rate({service_name=\"api-gateway\"} | json | unwrap foo[5m]))" + } + ], + "id": "852ba006", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "sum[json,unwrap]": 10 + }, + "signature": [ + "query_range", + "sum[json,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "sum_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms | unwrap latency | __error__!~\".*\" | foo >5[5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "sum_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms| unwrap bytes(foo) [5m] offset 5m)" + } + ], + "id": "834735ce", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "sum_over_time[json,unwrap,label-filter,line-filter]": 10 + }, + "signature": [ + "query_range", + "sum_over_time[json,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably, the whole string m" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 47: syntax error: unexpected ip | proxy parse error : logql: unexpected token \")\" at position 59", + "query": "{service_name=\"payment-service\"}|logfmt|addr<=ip(\"1.2.3.4\")" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 46: syntax error: unexpected ip | proxy parse error : logql: unexpected token \")\" at position 58", + "query": "{service_name=\"payment-service\"}|logfmt|addr=250ms or ( status_code<500 , status_code>200 ) )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error at line 1, col 1: syntax error: unexpected |", + "query": "{service_name=\"api-gateway\"} |= \"bar\" | json | ( status_code < 500 and status_code > 200) or latency >= 250ms" + } + ], + "id": "e67ce23c", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "logs[json,line-filter]": 10 + }, + "signature": [ + "query_range", + "logs[json,line-filter]", + "status", + "loki 200 proxy 400: parse error at line N, col N: syntax error: unexpected |" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 20: syntax error: unexpected STRING | proxy parse error : logql: unexpected token \"foo\" at position 24", + "query": "{namespace=\"prod\"} \"foo\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 20: syntax error: unexpected IDENTIFIER | proxy parse error : logql: unexpected token \"foo\" at position 22", + "query": "{namespace=\"prod\"} foo" + } + ], + "id": "3cac4f43", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "logs": 10 + }, + "signature": [ + "query_range", + "logs", + "error-text", + "400 with different text: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 4, + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['__error__', '__error_details__']; e.g. loki {\"__error__\": \"JSONParserErr\", \"__error_details__\": \"Value looks like object, but can't find closing '}' symbol\", \"level\": \"debug\", \"service_name\": \"cache-redis\"}", + "query": "{service_name=\"cache-redis\"} | json | keep service_name, level" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['__error__', '__error_details__']; e.g. loki {\"__error__\": \"JSONParserErr\", \"__error_details__\": \"Value looks like object, but can't find closing '}' symbol\", \"level\": \"info\", \"service_name\": \"db-postgres\"}", + "query": "{service_name=\"db-postgres\"} | json | keep service_name, level" + } + ], + "id": "9a63f6e2", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "logs[json,keep]": 10 + }, + "signature": [ + "query_range", + "logs[json,keep]", + "labels", + "loki-only __error__,__error_details__" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['__error__']; e.g. loki {\"__error__\": \"JSONParserErr\", \"app\": \"cache-redis\", \"cluster\": \"us-east-1\", \"container\": \"cache\", \"db\": \"0\", \"detected_level\": \"debug\", \"env\": \"production\", \"level\": \"debug\", \"namespace\": \"data\", ...", + "query": "{service_name=\"cache-redis\"} | json msg=\"msg\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['__error__']; e.g. loki {\"__error__\": \"JSONParserErr\", \"app\": \"db-postgres\", \"cluster\": \"us-east-1\", \"container\": \"db\", \"db_name\": \"app_prod\", \"detected_level\": \"info\", \"env\": \"production\", \"level\": \"info\", \"namespace\": \"...", + "query": "{service_name=\"db-postgres\"} | json msg=\"msg\"" + } + ], + "id": "b50e5ea5", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "logs[json]": 10 + }, + "signature": [ + "query_range", + "logs[json]", + "labels", + "loki-only __error__" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "120 samples, e.g. {\"level\": \"debug\"} @1791220800.0: \"86282s\"", + "query": "sum by (level) (max_over_time({service_name=\"cache-redis\"} | logfmt | unwrap ttl | __error__=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {\"level\": \"warn\"} @1791220800.0: \"\"", + "query": "sum by (level) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap amount | __error__=\"\" [5m]))" + } + ], + "id": "baa868eb", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "sum[logfmt,unwrap,label-filter]": 10 + }, + "signature": [ + "query_range", + "sum[logfmt,unwrap,label-filter]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level', 'service_name_extracted']; e.g. loki {\"app\": \"checkout-svc\", \"cluster\": \"us-east-1\", \"container\": \"checkout\", \"deployment_environment\": \"production\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_cluster_name\": \"k8s-prod-us-wes...", + "query": "{service_name=\"checkout-svc\"} | drop detected_level" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level', 'service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_cluster_name\": \"k8s-prod-us-e...", + "query": "{service_name=\"inventory-svc\"} | drop detected_level" + } + ], + "id": "829672cc", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "logs[drop]": 10 + }, + "signature": [ + "query_range", + "logs[drop]", + "labels", + "loki-only *_extracted,level" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"service_name_extracted\": \"checkout-svc\"}", + "query": "{service_name=\"checkout-svc\"} | keep service_name_extracted" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"service_name_extracted\": \"inventory-svc\"}", + "query": "{service_name=\"inventory-svc\"} | keep service_name_extracted" + } + ], + "id": "cb109373", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "logs[keep]": 10 + }, + "signature": [ + "query_range", + "logs[keep]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki-only checkout-svc", + "query": "{service_name=\"checkout-svc\"} | line_format \"{{.service_name_extracted}}\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki-only inventory-svc", + "query": "{service_name=\"inventory-svc\"} | line_format \"{{.service_name_extracted}}\"" + } + ], + "id": "6f3e6db5", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "logs[line_format]": 10 + }, + "signature": [ + "query_range", + "logs[line_format]", + "entries", + "different entries, same count" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['service_name_extracted']", + "query": "sum by (service_name_extracted) (count_over_time({service_name=\"checkout-svc\"} [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['service_name_extracted']", + "query": "sum by (service_name_extracted) (count_over_time({service_name=\"inventory-svc\"} [5m]))" + } + ], + "id": "5aa8ef5c", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "sum": 10 + }, + "signature": [ + "query_range", + "sum", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"level\": \"info\"}: loki 61 points, proxy 60", + "query": "sum by (level) (max_over_time({service_name=\"db-postgres\"} | logfmt | unwrap port | __error__=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"level\": \"info\"}: loki 61 points, proxy 60", + "query": "sum by (level) (max_over_time({service_name=\"db-postgres\"} | logfmt | unwrap distance | __error__=\"\" [5m]))" + } + ], + "id": "833babd8", + "impact": "explore-visible", + "queries": 5, + "requests": 10, + "shapes": { + "sum[logfmt,unwrap,label-filter]": 10 + }, + "signature": [ + "query_range", + "sum[logfmt,unwrap,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "structuredMetadata names ['service_name']", + "query": "{service_name=\"checkout-svc\"} | drop service_name_extracted" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "structuredMetadata names ['service_name']", + "query": "{service_name=\"inventory-svc\"} | drop service_name_extracted" + } + ], + "id": "1de4e9f2", + "impact": "explore-visible", + "queries": 5, + "requests": 5, + "shapes": { + "logs[drop]": 5 + }, + "signature": [ + "query_range", + "logs[drop]", + "category", + "proxy-only structuredMetadata service_name" + ], + "sources": { + "seeded": 5 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "absent_over_time( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "absent_over_time( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] offset 10d )" + } + ], + "id": "d517894b", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "absent_over_time[line-filter]": 8 + }, + "signature": [ + "query_range", + "absent_over_time[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 1127", + "query": "count(bytes_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 1127", + "query": "count(bytes_rate({service_name=\"payment-service\"} | logfmt [3m]))" + } + ], + "id": "543a2f07", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "count[logfmt]": 8 + }, + "signature": [ + "query_range", + "count[logfmt]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "count_over_time({service_name=\"api-gateway\"} | json [3m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "count_over_time({service_name=\"api-gateway\"} | json [5m])" + } + ], + "id": "9148c234", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "count_over_time[json]": 8 + }, + "signature": [ + "query_range", + "count_over_time[json]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "loki-tests": 4, + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "max by (a) (sum_over_time({namespace=\"prod\"} | unwrap b [2s]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "max by (baz) (sum_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + } + ], + "id": "6843e15f", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "max[unwrap]": 8 + }, + "signature": [ + "query_range", + "max[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2091", + "query": "max by (baz) (bytes_over_time({service_name=\"api-gateway\"} | json [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2091", + "query": "max by (foo) (bytes_rate({service_name=\"api-gateway\"} | json [3m]))" + } + ], + "id": "ea157df5", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "max[json]": 8 + }, + "signature": [ + "query_range", + "max[json]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 1127", + "query": "max by (baz) (count_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 1127", + "query": "max(bytes_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + } + ], + "id": "efb24a18", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "max[logfmt]": 8 + }, + "signature": [ + "query_range", + "max[logfmt]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max by (baz) (max_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]) by (baz))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max by (baz) (min_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]) by (baz))" + } + ], + "id": "8f56d9d0", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "max[json,unwrap]": 8 + }, + "signature": [ + "query_range", + "max[json,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 74383 series, proxy 15220", + "query": "max without () (count_over_time({namespace=\"prod\"}[1h]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 74383 series, proxy 15220", + "query": "max without (bar) (count_over_time({namespace=\"prod\"}[1h]))" + } + ], + "id": "51011993", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "max": 8 + }, + "signature": [ + "query_range", + "max", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 6, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "min by (a) (sum_over_time({namespace=\"prod\"} | unwrap b [2s]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "min by (baz) (sum_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + } + ], + "id": "7314ff41", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "min[unwrap]": 8 + }, + "signature": [ + "query_range", + "min[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2091", + "query": "min by (baz) (bytes_over_time({service_name=\"api-gateway\"} | json [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2091", + "query": "min by (foo) (bytes_rate({service_name=\"api-gateway\"} | json [3m]))" + } + ], + "id": "77eeea84", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "min[json]": 8 + }, + "signature": [ + "query_range", + "min[json]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 1127", + "query": "min by (baz) (count_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 1127", + "query": "min(bytes_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + } + ], + "id": "0b8db994", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "min[logfmt]": 8 + }, + "signature": [ + "query_range", + "min[logfmt]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "min by (baz) (max_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]) by (baz))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "min by (baz) (min_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]) by (baz))" + } + ], + "id": "756c99ae", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "min[json,unwrap]": 8 + }, + "signature": [ + "query_range", + "min[json,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({service_name=\"api-gateway\"} | json [3m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\"} | json [1m])" + } + ], + "id": "5262a574", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "rate[json]": 8 + }, + "signature": [ + "query_range", + "rate[json]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "loki-tests": 2, + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"api\"; probably, the whole string must be put into quotes", + "query": "sum by (job) ( sum_over_time( {service_name=\"api-gateway\"} |= \"level=error\" | json | avg=5 and bar<25ms | unwrap duration(latency) [5m] ) / count_over_time({service_name=\"api-gateway\"} | logfmt | label_format foo=bar[5m]) )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"api\"; probably, the whole string must be put into quotes", + "query": "sum by (job) ( sum_over_time( {service_name=\"api-gateway\"} |= \"level=error\" | json | avg=5 and bar<25ms | unwrap duration(latency) | __error__!~\".*\" [5m] ) / count_over_time({service_name=\"api-gateway\"} | logfmt | label_format foo=bar[5m]) )" + } + ], + "id": "a97fb38d", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "sum[json,logfmt,label_format,unwrap,label-filter,line-filter]": 8 + }, + "signature": [ + "query_range", + "sum[json,logfmt,label_format,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk(1,rate(({namespace=\"prod\"} |~\".+bar\")[1m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk(1,rate(({namespace=\"prod\"} |~\".+bar\")[1m])) by (app)" + } + ], + "id": "f982bf27", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "topk[line-filter]": 8 + }, + "signature": [ + "query_range", + "topk[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \")\" at position 100", + "query": "{service_name=\"payment-service\"}|logfmt|= ip(\"127.0.0.1\")|ip=\"2.3.4.5\"|ip=\"abc\"|ipaddr=ip(\"4.5.6.7\")|ip=ip(\"6.7.8.9\")" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \")\" at position 100", + "query": "{service_name=\"payment-service\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\" | logfmt | b=ip(\"127.0.0.1\")" + } + ], + "id": "0c00485a", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "logs[logfmt,label-filter,line-filter]": 8 + }, + "signature": [ + "query_range", + "logs[logfmt,label-filter,line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"}|logfmt --strict" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"} | logfmt --keep-empty msg, err" + } + ], + "id": "461b1df0", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "logs[logfmt]": 8 + }, + "signature": [ + "query_range", + "logs[logfmt]", + "status", + "loki 200 proxy 400: parse error : logql: expected number after Q" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clu...", + "query": "{namespace=\"prod\"} !> \"<_> foo <_>\" !> \"foo <_>\" !> \"foo <_> baz\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clu...", + "query": "{namespace=\"prod\"} !> \"foo <_>\"" + } + ], + "id": "1802c76f", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "logs[pattern-filter]": 8 + }, + "signature": [ + "query_range", + "logs[pattern-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 6, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "count_over_time({app=\"api-gateway\",env=\"production\"} | json | status>=200 and status<300 [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "count_over_time({app=\"api-gateway\"} | json | method=\"GET\" | status >= 200 [5m])" + } + ], + "id": "3425b43c", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "count_over_time[json,label-filter]": 8 + }, + "signature": [ + "query_range", + "count_over_time[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "quantile_over_time(0.95, {app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "quantile_over_time(0.99, {app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "ea80f966", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "quantile_over_time[json,unwrap]": 8 + }, + "signature": [ + "query_range", + "quantile_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4553\", \"env\": \"production\", \"error\": \"connection refused\", \"ip\": \"10.3.133.238\", \"level...", + "query": "{app=\"api-gateway\",env=\"production\"} | json | method=\"GET\" |= \"users\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"info\", \"duration_ms\": \"55\", \"env\": \"production\", \"ip\": \"10.5.200.251\", \"level\": \"info\", \"level_extracted\": \"inf...", + "query": "{app=\"api-gateway\",env=\"production\"} |= \"GET\" |= \"200\" | json | method=\"GET\"" + } + ], + "id": "bc7f7413", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "logs[json,label-filter,line-filter]": 8 + }, + "signature": [ + "query_range", + "logs[json,label-filter,line-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '|= ip(\"192.168.1.1/33\")' : ip: invalid pattern: \"192.168.1.1/33\" | proxy parse error : ip: invalid pattern: \"192.168.1.1/33\"", + "query": "{app=\"api-gateway\"} |= ip(\"192.168.1.1/33\")" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '|= ip(\"999.999.999.999\")' : ip: invalid pattern: \"999.999.999.999\" | proxy parse error : ip: invalid pattern: \"999.999.999.999\"", + "query": "{app=\"api-gateway\"} |= ip(\"999.999.999.999\")" + } + ], + "id": "bfa1bb8f", + "impact": "explore-visible", + "queries": 4, + "requests": 8, + "shapes": { + "logs[line-filter]": 8 + }, + "signature": [ + "query_range", + "logs[line-filter]", + "error-text", + "400 with different text: parse error : ip: invalid pattern: Q" + ], + "sources": { + "repo": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 59420 series, proxy 12176", + "query": "bytes_rate({namespace=\"prod\"}[12m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 25111 series, proxy 5155", + "query": "bytes_rate({namespace=\"prod\"}[30s])" + } + ], + "id": "a6b8ae84", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "bytes_rate": 6 + }, + "signature": [ + "query_range", + "bytes_rate", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 22279 series, proxy 1091", + "query": "count_over_time( {service_name=\"payment-service\"} | logfmt [1m] )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 22279 series, proxy 1091", + "query": "count_over_time({service_name=\"payment-service\"}|logfmt[1m])" + } + ], + "id": "aa8cfdc8", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "count_over_time[logfmt]": 6 + }, + "signature": [ + "query_range", + "count_over_time[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 4, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 5175 series, proxy 4024", + "query": "count_over_time( {namespace=\"prod\"} |= \"error\" [5m] offset 20m )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 7133 series, proxy 5560", + "query": "count_over_time({namespace=\"prod\"} |= \"error\" [1h])" + } + ], + "id": "1bd439f7", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "count_over_time[line-filter]": 6 + }, + "signature": [ + "query_range", + "count_over_time[line-filter]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "max by (a) (sum_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "max(sum_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]))" + } + ], + "id": "c16333ff", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "max[logfmt,unwrap]": 6 + }, + "signature": [ + "query_range", + "max[logfmt,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "min by (a) (sum_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "min(sum_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]))" + } + ], + "id": "3cbd35cd", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "min[logfmt,unwrap]": 6 + }, + "signature": [ + "query_range", + "min[logfmt,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy manual metric response exceeds 67108864 bytes; narrow the query or increase -backend-max-buffered-response-bytes", + "query": "quantile_over_time(0.8, {namespace=\"prod\"} | unwrap bytes [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy manual metric response exceeds 67108864 bytes; narrow the query or increase -backend-max-buffered-response-bytes", + "query": "quantile_over_time(0.8, {namespace=\"prod\"} | unwrap bytes [5m]) by (cluster)" + } + ], + "id": "1e223d96", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "quantile_over_time[unwrap]": 6 + }, + "signature": [ + "query_range", + "quantile_over_time[unwrap]", + "status", + "loki 200 proxy 502: manual metric response exceeds N bytes; narrow the query or increase -backend-ma" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 10656", + "query": "rate({namespace=\"prod\"} | unwrap bar[3m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 5155", + "query": "rate({namespace=\"prod\"} | unwrap foo [30s])" + } + ], + "id": "d3077c47", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "rate[unwrap]": 6 + }, + "signature": [ + "query_range", + "rate[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sum by (container)( count_over_time( {namespace=\"prod\"} [5m] ) )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sum without (a) ( label_replace( sum without (b) ( rate({namespace=\"prod\"}[5m]) ), \"baz\", \"buz\", \"foo\", \"(.*)\" ) )" + } + ], + "id": "293abde9", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "sum": 6 + }, + "signature": [ + "query_range", + "sum", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 4, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sum by (job) ( count_over_time({namespace=\"prod\"} |= \"level=error\"[5m]) / count_over_time({namespace=\"prod\"}[5m]) )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sum without(a) ( rate ( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) )" + } + ], + "id": "40bbb685", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "sum[line-filter]": 6 + }, + "signature": [ + "query_range", + "sum[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sum(bytes_rate(({namespace=\"prod\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")[5m])) by (foo)" + } + ], + "id": "5ea3232b", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "sum[line-filter]": 6 + }, + "signature": [ + "query_range", + "sum[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 10656", + "query": "sum(max(bytes_over_time({namespace=\"prod\"}[3m])))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 10995", + "query": "sum(max(rate({namespace=\"prod\"}[5m])) by (foo,bar)) by (foo)" + } + ], + "id": "be6066e1", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "sum": 6 + }, + "signature": [ + "query_range", + "sum", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '| logfmt --strict --keep-empty code=\"response.code\",IPAddress=\"host\"' : cannot parse expression [response.code]: unexpected char . | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"} | logfmt --keep-empty --strict code=\"response.code\", IPAddress=\"host\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 56: literal not terminated | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"} | logfmt --strict code\"" + } + ], + "id": "fea1552d", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "logs[logfmt]": 6 + }, + "signature": [ + "query_range", + "logs[logfmt]", + "error-text", + "400 with different text: parse error : logql: expected number after Q" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 5325 series, proxy 5422; loki-only [\"{\\\"throughput_rps\\\": \\\"1.0\\\"}\", \"{\\\"throughput_rps\\\": \\\"1004.0\\\"}\"] proxy-only [\"{\\\"throughput_rps\\\": \\\"0\\\"}\", \"{\\\"throughput_rps\\\": \\\"0.30000000000000004\\\"}\"]", + "query": "sum by (throughput_rps) (count_over_time({service_name=\"batch-etl\"} | json [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 9786 series, proxy 9789; loki-only [\"{\\\"duration_s\\\": \\\"0.6\\\"}\", \"{\\\"duration_s\\\": \\\"0.94\\\"}\"] proxy-only [\"{\\\"duration_s\\\": \\\"0.6000000000000001\\\"}\", \"{\\\"duration_s\\\": \\\"0.9400000000000001\\\"}\"]", + "query": "sum by (duration_s) (count_over_time({service_name=\"batch-etl\"} | json [5m]))" + } + ], + "id": "a8228433", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "sum[json]": 6 + }, + "signature": [ + "query_range", + "sum[json]", + "series", + "proxy-more" + ], + "sources": { + "seeded": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 5325 series, proxy 5422; loki-only [\"{\\\"throughput_rps\\\": \\\"1.0\\\"}\", \"{\\\"throughput_rps\\\": \\\"1004.0\\\"}\"] proxy-only [\"{\\\"throughput_rps\\\": \\\"0\\\"}\", \"{\\\"throughput_rps\\\": \\\"0.30000000000000004\\\"}\"]", + "query": "sum by (throughput_rps) (count_over_time({service_name=\"batch-etl\"} | json | throughput_rps!=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 9786 series, proxy 9789; loki-only [\"{\\\"duration_s\\\": \\\"0.6\\\"}\", \"{\\\"duration_s\\\": \\\"0.94\\\"}\"] proxy-only [\"{\\\"duration_s\\\": \\\"0.6000000000000001\\\"}\", \"{\\\"duration_s\\\": \\\"0.9400000000000001\\\"}\"]", + "query": "sum by (duration_s) (count_over_time({service_name=\"batch-etl\"} | json | duration_s!=\"\" [5m]))" + } + ], + "id": "8c3eca09", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "sum[json,label-filter]": 6 + }, + "signature": [ + "query_range", + "sum[json,label-filter]", + "series", + "proxy-more" + ], + "sources": { + "seeded": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 entries, proxy 99", + "query": "{service_name=\"cache-redis\"} | detected_level=\"info\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 entries, proxy 99", + "query": "{service_name=\"worker-service\"} | detected_level=\"warn\"" + } + ], + "id": "ce40a9c8", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "logs[detected_level,label-filter]": 6 + }, + "signature": [ + "query_range", + "logs[detected_level,label-filter]", + "entries", + "loki-empty" + ], + "sources": { + "seeded": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki-only level=info msg=\"cache miss\" key=session:f27935 ttl=13680s size_bytes=0 op=hget", + "query": "{service_name=\"cache-redis\"} | detected_level=\"debug\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki-only level=warn msg=\"job retry\" queue=analytics job_id=job_91238558 attempt=2 max_retries=5 backoff_ms=1000", + "query": "{service_name=\"worker-service\"} | detected_level=\"info\"" + } + ], + "id": "36eeb045", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "logs[detected_level,label-filter]": 6 + }, + "signature": [ + "query_range", + "logs[detected_level,label-filter]", + "entries", + "different entries, same count" + ], + "sources": { + "repo": 2, + "seeded": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"level_extracted\": \"debug\"}", + "query": "{service_name=\"cache-redis\"} | logfmt | keep level_extracted" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted']; e.g. loki {\"level_extracted\": \"error\"}", + "query": "{service_name=\"payment-service\"} | logfmt | keep level_extracted" + } + ], + "id": "f1ad0a1b", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "logs[logfmt,keep]": 6 + }, + "signature": [ + "query_range", + "logs[logfmt,keep]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki-only debug", + "query": "{service_name=\"cache-redis\"} | logfmt | line_format \"{{.level_extracted}}\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki-only error", + "query": "{service_name=\"payment-service\"} | logfmt | line_format \"{{.level_extracted}}\"" + } + ], + "id": "e9d4fcd8", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "logs[logfmt,line_format]": 6 + }, + "signature": [ + "query_range", + "logs[logfmt,line_format]", + "entries", + "different entries, same count" + ], + "sources": { + "seeded": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 2 series, proxy 1", + "query": "sum by (level_extracted) (count_over_time({service_name=\"cache-redis\"} | logfmt [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 3 series, proxy 1", + "query": "sum by (level_extracted) (count_over_time({service_name=\"payment-service\"} | logfmt [5m]))" + } + ], + "id": "d45a655e", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "sum[logfmt]": 6 + }, + "signature": [ + "query_range", + "sum[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "seeded": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 7 series, proxy 2; loki-only [\"{\\\"app\\\": \\\"db-postgres\\\"}\", \"{\\\"app\\\": \\\"inventory-svc\\\"}\"] proxy-only []", + "query": "bottomk(2, sum by (app) (count_over_time({app=~\".+\"}[5m])))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 7 series, proxy 3; loki-only [\"{\\\"app\\\": \\\"inventory-svc\\\"}\", \"{\\\"app\\\": \\\"notification-svc\\\"}\"] proxy-only []", + "query": "bottomk(3, sum by (app) (count_over_time({env=\"production\"}[5m])))" + } + ], + "id": "f2d36009", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "bottomk": 6 + }, + "signature": [ + "query_range", + "bottomk", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation quantile_over_time without unwrap | proxy quantile_over_time requires `| unwrap ` for range aggregation", + "query": "quantile_over_time(0.95, {app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation quantile_over_time without unwrap | proxy quantile_over_time requires `| unwrap ` for range aggregation", + "query": "quantile_over_time(0.95, {app=\"api-gateway\"}[5m])" + } + ], + "id": "dabfed68", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "quantile_over_time": 6 + }, + "signature": [ + "query_range", + "quantile_over_time", + "error-text", + "400 with different text: quantile_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation rate_counter without unwrap | proxy parse error : rate_counter requires | unwrap expression", + "query": "rate_counter({app=\"api-gateway\"}[5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation rate_counter without unwrap | proxy parse error : rate_counter requires | unwrap expression", + "query": "rate_counter({app=\"range-metric-counter\"}[1m])" + } + ], + "id": "ec8a8fbf", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "rate_counter": 6 + }, + "signature": [ + "query_range", + "rate_counter", + "error-text", + "400 with different text: parse error : rate_counter requires | unwrap expression" + ], + "sources": { + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 88 series, proxy 88; loki-only [\"{\\\"app\\\": \\\"api-gateway\\\", \\\"cluster\\\": \\\"us-east-1\\\", \\\"container\\\": \\\"api\\\", \\\"detected_level\\\": \\\"info\\\", \\\"env\\\": \\\"production\\\", \\\"level\\\": \\\"info\\\", ... proxy-only [\"{\\\"app\\\": \\\"api-gateway\\\", \\\"cluster\\\": \\\"us-east-1\\\", \\\"container\\\": \\\"api\\\", \\\"detect...", + "query": "topk(5, rate({namespace=\"prod\"}[5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 88 series, proxy 88; loki-only [\"{\\\"pod\\\": \\\"api-gateway-8f45e-c864\\\"}\", \"{\\\"pod\\\": \\\"api-gateway-eb48a-3883\\\"}\"] proxy-only [\"{\\\"pod\\\": \\\"api-gateway-0e3b4-9d04\\\"}\", \"{\\\"pod\\\": \\\"api-gateway-810ee-1887\\\"}\"]", + "query": "topk(5, sum by (pod) (count_over_time({service_name=\"api-gateway\"}[5m])))" + } + ], + "id": "aa368887", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "topk": 6 + }, + "signature": [ + "query_range", + "topk", + "series", + "different series, same count" + ], + "sources": { + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4225\", \"env\": \"production\", \"error\": \"user not found\", \"ip\": \"10.2.137.22\", \"level\": \"e...", + "query": "{app=\"api-gateway\",env=\"production\"} |= \"GET\" | json" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"1758\", \"env\": \"production\", \"error\": \"service unavailable\", \"ip\": \"10.5.161.217\", \"leve...", + "query": "{app=\"api-gateway\",env=\"production\"} |= \"GET\" |= \"/api\" != \"health\" | json" + } + ], + "id": "5d3adecc", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "logs[json,line-filter]": 6 + }, + "signature": [ + "query_range", + "logs[json,line-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unexpected type for left leg of binary operation (<=): *syntax.PipelineExpr | proxy parse error at line 1, col 1: unexpected expression for binary operation", + "query": "{app=\"api-gateway\"} | json <= 2" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unexpected type for left leg of binary operation (==): *syntax.PipelineExpr | proxy parse error at line 1, col 1: unexpected expression for binary operation", + "query": "{app=\"api-gateway\"} | json == 2" + } + ], + "id": "952c4ced", + "impact": "explore-visible", + "queries": 3, + "requests": 6, + "shapes": { + "logs[json,label-filter]": 6 + }, + "signature": [ + "query_range", + "logs[json,label-filter]", + "error-text", + "400 with different text: parse error at line N, col N: unexpected expression for binary operation" + ], + "sources": { + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy manual metric response exceeds 67108864 bytes; narrow the query or increase -backend-max-buffered-response-bytes", + "query": "absent_over_time({namespace=\"prod\"}[1h])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy manual metric response exceeds 67108864 bytes; narrow the query or increase -backend-max-buffered-response-bytes", + "query": "absent_over_time({namespace=\"prod\"}[5m])" + } + ], + "id": "7b521e8a", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "absent_over_time": 4 + }, + "signature": [ + "query_range", + "absent_over_time", + "status", + "loki 200 proxy 502: manual metric response exceeds N bytes; narrow the query or increase -backend-ma" + ], + "sources": { + "loki-tests": 2, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 0", + "query": "absent_over_time({service_name=\"api-gateway\"} | json | nonexistant_field=\"foo\" [1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 0", + "query": "absent_over_time({service_name=\"api-gateway\"} | json | remote_user=\"foo\" [1m])" + } + ], + "id": "4dfd84f7", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "absent_over_time[json,label-filter]": 4 + }, + "signature": [ + "query_range", + "absent_over_time[json,label-filter]", + "series", + "proxy-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unexpected literal for right leg of logical/set binary operation (or): 0.000000 | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "avg( label_replace( count_over_time({namespace=\"prod\"}[1h]) or 0, \"bar\", \"$1$2\", \"foo\", \"(.*).(.*)\" ) ) by (bar,foo)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation sum_over_time without unwrap | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "avg( label_replace( count_over_time({namespace=\"prod\"}[1h]) or sum_over_time({namespace=\"prod\"}[1h]), \"bar\", \"$1$2\", \"foo\", \"(.*).(.*)\" ) ) by (bar,foo)" + } + ], + "id": "fa1c6399", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "avg": 4 + }, + "signature": [ + "query_range", + "avg", + "error-text", + "400 with different text: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 3", + "query": "avg(min_over_time({namespace=\"prod\"} |= \"GET\" | unwrap foo[10s] offset 10m)) by (region)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 3", + "query": "avg(min_over_time({namespace=\"prod\"} |= \"GET\" | unwrap foo[10s])) by (region)" + } + ], + "id": "77fcddef", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "avg[unwrap,line-filter]": 4 + }, + "signature": [ + "query_range", + "avg[unwrap,line-filter]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "avg_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s]) by (a)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 21916", + "query": "avg_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s]) without (stream)" + } + ], + "id": "d5321d1c", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "avg_over_time[logfmt,unwrap]": 4 + }, + "signature": [ + "query_range", + "avg_over_time[logfmt,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "bottomk(2,rate(({namespace=\"prod\"} |~\".+bar\")[1m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "bottomk(3,rate(({namespace=\"prod\"} |~\".+bar\")[1m])) without (app)" + } + ], + "id": "c37f606b", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "bottomk[line-filter]": 4 + }, + "signature": [ + "query_range", + "bottomk[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected ), got DURATION (\"2m\")", + "query": "bytes_over_time({namespace=\"prod\"}[1h] offset 1h2m20s)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected ), got DURATION (\"10s\")", + "query": "bytes_over_time({namespace=\"prod\"}[3m] offset 20m10s)" + } + ], + "id": "b81935ec", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "bytes_over_time": 4 + }, + "signature": [ + "query_range", + "bytes_over_time", + "status", + "loki 200 proxy 400: parse error : logql: expected ), got duration (Q)" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "count_over_time(({namespace=\"prod\"} |~\".+bar\")[5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "count_over_time(({namespace=\"prod\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")[5m])" + } + ], + "id": "d0dfe746", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time[line-filter]": 4 + }, + "signature": [ + "query_range", + "count_over_time[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 6803 series, proxy 11036", + "query": "count_over_time({namespace=\"prod\"}[12m]) > 1" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 5771 series, proxy 9350", + "query": "count_over_time({namespace=\"prod\"}[1m]) > 1" + } + ], + "id": "6069aa8c", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time": 4 + }, + "signature": [ + "query_range", + "count_over_time", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "count_over_time({namespace=\"prod\"}[1h] |= \"error\")" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "count_over_time({namespace=\"prod\"}[5m] |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")" + } + ], + "id": "815a3690", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time[line-filter]": 4 + }, + "signature": [ + "query_range", + "count_over_time[line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: expected ), got |= (Q)" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"first\"", + "query": "first_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s] offset 2s) by (a)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"first\"", + "query": "first_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s]) by (a)" + } + ], + "id": "a27681a7", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "first_over_time[logfmt,unwrap]": 4 + }, + "signature": [ + "query_range", + "first_over_time[logfmt,unwrap]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"first\"", + "query": "first_over_time({namespace=\"prod\"} |~\".+bar\" | unwrap foo [1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"first\"", + "query": "first_over_time({namespace=\"prod\"}|~\".+bar\"| unwrap foo [1m])" + } + ], + "id": "61ff16ca", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "first_over_time[unwrap,line-filter]": 4 + }, + "signature": [ + "query_range", + "first_over_time[unwrap,line-filter]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"api\"; probably, the whole string must be put into quotes", + "query": "label_replace( sum by (job) ( sum_over_time( {service_name=\"api-gateway\"} |= \"level=error\" | json | avg=5 and bar<25ms | unwrap duration(latency) | __error__!~\".*\" [5m] ) / count_over_time({service_name=\"api-gateway\"} | logfmt | label_format foo=bar[5m]) ), \"foo\", \"$1\", \"service\", \"(.*):.*\" )" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"api\"; probably, the whole string must be put into quotes", + "query": "label_replace( sum by (job) ( sum_over_time( {service_name=\"api-gateway\"} |= \"level=error\" | json | avg=5 and bar<25ms | unwrap duration(latency) | __error__!~\".*\" [5m] offset 1h ) / count_over_time({service_name=\"api-gateway\"} | logfmt | label_format foo=bar[5m] offset 1h) ), \"foo\", \"$1\", \"servi..." + } + ], + "id": "998b9e72", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "label_replace[json,logfmt,label_format,unwrap,label-filter,line-filter]": 4 + }, + "signature": [ + "query_range", + "label_replace[json,logfmt,label_format,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"last\"", + "query": "last_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s] offset 2s) by (a)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"last\"", + "query": "last_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s]) by (a)" + } + ], + "id": "13f557ca", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "last_over_time[logfmt,unwrap]": 4 + }, + "signature": [ + "query_range", + "last_over_time[logfmt,unwrap]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]) by (a)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]) by (baz)" + } + ], + "id": "a5201e0b", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "max_over_time[logfmt,unwrap]": 4 + }, + "signature": [ + "query_range", + "max_over_time[logfmt,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 10324", + "query": "max_over_time({namespace=\"prod\"} | unwrap bar [5m] offset 5m) without (foo,bar)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 10155", + "query": "max_over_time({namespace=\"prod\"} | unwrap bar [5m]) without (foo,bar)" + } + ], + "id": "0d7e7b08", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "max_over_time[unwrap]": 4 + }, + "signature": [ + "query_range", + "max_over_time[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 1: syntax error: unexpected IDENTIFIER | proxy parse error : logql: unexpected token \"(\" at position 14", + "query": "maxby(status)(quantile_over_time(0.7,{service_name=\"payment-service\"}|logfmt|unwrapvalue[1s]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 1: syntax error: unexpected IDENTIFIER | proxy parse error : logql: unexpected token \"(\" at position 14", + "query": "maxby(status)(quantile_over_time(0.7,{service_name=\"payment-service\"}|logfmt|unwrapvalue[1s])by(baz))" + } + ], + "id": "d40e85ea", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "maxby[logfmt]": 4 + }, + "signature": [ + "query_range", + "maxby[logfmt]", + "error-text", + "400 with different text: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 725 series, proxy 607", + "query": "rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] )>-1" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 5140 series, proxy 4024", + "query": "rate({namespace=\"prod\"} |= \"error\"[5m])" + } + ], + "id": "528f5b7e", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "rate[line-filter]": 4 + }, + "signature": [ + "query_range", + "rate[line-filter]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 2, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy manual metric response exceeds 67108864 bytes; narrow the query or increase -backend-max-buffered-response-bytes", + "query": "rate_counter({namespace=\"prod\"} | unwrap foo [30s])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy manual metric response exceeds 67108864 bytes; narrow the query or increase -backend-max-buffered-response-bytes", + "query": "rate_counter({namespace=\"prod\"} | unwrap foo[30s])" + } + ], + "id": "6d9fc70d", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "rate_counter[unwrap]": 4 + }, + "signature": [ + "query_range", + "rate_counter[unwrap]", + "status", + "loki 200 proxy 502: manual metric response exceeds N bytes; narrow the query or increase -backend-ma" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "sum by (baz) (max_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]) by (baz))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "sum by (baz) (min_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]) by (baz))" + } + ], + "id": "e78502df", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "sum[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "sum[json,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unexpected literal for right leg of logical/set binary operation (or): 1.000000 | proxy unexpected literal for logical/set binary operation", + "query": "sum(count_over_time({namespace=\"prod\"}[5m])) by (foo) + 1 or 1" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unexpected literal for right leg of logical/set binary operation (or): 1.000000 | proxy unexpected literal for logical/set binary operation", + "query": "sum(count_over_time({namespace=\"prod\"}[5m])) by (foo) or 1" + } + ], + "id": "5a21650b", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "sum": 4 + }, + "signature": [ + "query_range", + "sum", + "error-text", + "400 with different text: unexpected literal for logical/set binary operation" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk(1,rate(({namespace=\"prod\"})[1h]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk(1,rate(({namespace=\"prod\"})[1m]))" + } + ], + "id": "b5e0e1c5", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "topk": 4 + }, + "signature": [ + "query_range", + "topk", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"payment\"; probably, the whole string must be put into quotes", + "query": "topk(100, sum by (a) (sum_over_time ({service_name=\"payment-service\"} | logfmt | unwrap value [1s])))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"payment\"; probably, the whole string must be put into quotes", + "query": "topk(3, sum by (a) (sum_over_time ({service_name=\"payment-service\"} | logfmt | unwrap value [1s])))" + } + ], + "id": "c559852c", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "topk[logfmt,unwrap]": 4 + }, + "signature": [ + "query_range", + "topk[logfmt,unwrap]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "topk(5,count_over_time({namespace=\"prod\"}[5m] |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")) without (foo)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "topk(5,sum(rate({namespace=\"prod\"}[5m] |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")) by (app))" + } + ], + "id": "4517a40b", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "topk[line-filter]": 4 + }, + "signature": [ + "query_range", + "topk[line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: expected ), got |= (Q)" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : parameter required for operation topk | proxy parse error : logql: topk requires a numeric k parameter", + "query": "topk(count_over_time({namespace=\"prod\"}[1h]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 8: syntax error: unexpected , | proxy parse error : logql: topk requires a numeric k parameter", + "query": "topk(-1, sum by(level)(count_over_time({env=\"production\"}[5m])))" + } + ], + "id": "25a45cb4", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "topk": 4 + }, + "signature": [ + "query_range", + "topk", + "error-text", + "400 with different text: parse error : logql: topk requires a numeric k parameter" + ], + "sources": { + "loki-tests": 2, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '!~ ip(\"127.0.0.1\") !~ \"foo\"' : ip: invalid operation | proxy parse error : ip: invalid operation", + "query": "{namespace=\"prod\"} !~ ip(\"127.0.0.1\") !~ \"foo\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '!~ ip(\"127.0.0.1\") !~ \"foo\"' : ip: invalid operation | proxy parse error : ip: invalid operation", + "query": "{namespace=\"prod\"} !~ ip(\"127.0.0.1\") or \"foo\"" + } + ], + "id": "e17816bf", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "logs": 4 + }, + "signature": [ + "query_range", + "logs", + "error-text", + "400 with different text: parse error : ip: invalid operation" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \"#\" at position 30", + "query": "{service_name=\"api-gateway\"} # |= \"bar\" | json" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \"#\" at position 30", + "query": "{service_name=\"api-gateway\"} # |~ \"\\\\w+\" | json" + } + ], + "id": "ee3cf0bd", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "logs[json,line-filter]": 4 + }, + "signature": [ + "query_range", + "logs[json,line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \")\" at position 109", + "query": "{service_name=\"api-gateway\"} |= \"bar\" | json | latency >= 250ms or ( status_code < 500 and status_code > 200)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \")\" at position 109", + "query": "{service_name=\"api-gateway\"} |= \"bar\" | json | latency >= 250ms or ( status_code < 500 and status_code > 200) | foo=\"bar\" buzz!=\"blip\", blop=~\"boop\" or fuzz==5" + } + ], + "id": "ed5ff2d9", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "logs[json,label-filter,line-filter]": 4 + }, + "signature": [ + "query_range", + "logs[json,label-filter,line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 50: syntax error: unexpected logfmt, expecting STRING or ip | proxy parse error : logql: expected STRING or RAWSTRING, got IDENT (\"logfmt\")", + "query": "{namespace=\"prod\"} |= \"error\" |= \"metrics.go\" |= logfmt" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 34: syntax error: unexpected logfmt, expecting STRING or ip | proxy parse error : logql: expected STRING or RAWSTRING, got IDENT (\"logfmt\")", + "query": "{namespace=\"prod\"} |= \"error\" |= logfmt |= \"metrics.go\"" + } + ], + "id": "bdc75f86", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "logs[line-filter]": 4 + }, + "signature": [ + "query_range", + "logs[line-filter]", + "error-text", + "400 with different text: parse error : logql: expected string or rawstring, got ident (Q)" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 53: decimal literal has no digits | proxy -", + "query": "{service_name=\"payment-service\"} | logfmt | bytes < 0B" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 60: syntax error: unexpected STRING | proxy -", + "query": "{app=\"payment-service\",level=\"warn\"} | logfmt | duration > \"1s\"" + } + ], + "id": "e46391d4", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "logs[logfmt,label-filter]": 4 + }, + "signature": [ + "query_range", + "logs[logfmt,label-filter]", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\" | logfmt --strict" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\" | logfmt --strict --keep-empty" + } + ], + "id": "892af37b", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "logs[logfmt,line-filter]": 4 + }, + "signature": [ + "query_range", + "logs[logfmt,line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: expected number after Q" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\" | logfmt --strict --keep-empty b=\"foo\" | b=ip(\"127.0.0.1\") | level=\"error\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\" | logfmt --strict b=\"foo\" | b=ip(\"127.0.0.1\") | level=\"error\"" + } + ], + "id": "c59b5392", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "logs[logfmt,label-filter,line-filter]": 4 + }, + "signature": [ + "query_range", + "logs[logfmt,label-filter,line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: expected number after Q" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 1; loki-only [\"{\\\"level\\\": \\\"info\\\"}\"] proxy-only [\"{\\\"level\\\": \\\"warn\\\"}\"]", + "query": "sum by (level) (max_over_time({service_name=\"worker-service\"} | logfmt | unwrap max_retries | __error__=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 1; loki-only [\"{\\\"level\\\": \\\"info\\\"}\"] proxy-only [\"{\\\"level\\\": \\\"warn\\\"}\"]", + "query": "sum by (level) (max_over_time({service_name=\"worker-service\"} | logfmt | unwrap backoff_ms | __error__=\"\" [5m]))" + } + ], + "id": "35e2a101", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "sum[logfmt,unwrap,label-filter]": 4 + }, + "signature": [ + "query_range", + "sum[logfmt,unwrap,label-filter]", + "series", + "different series, same count" + ], + "sources": { + "seeded": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 68453 series, proxy 14627", + "query": "avg without (level, env) (count_over_time({env=\"production\"}[5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 68453 series, proxy 14627", + "query": "avg without(app)(rate({env=\"production\"}[5m]))" + } + ], + "id": "b8436a40", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "avg": 4 + }, + "signature": [ + "query_range", + "avg", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 41: syntax error: unexpected ) | proxy avg() requires a range metric inside (e.g. rate({...}[5m]), count_over_time({...}[5m]))", + "query": "avg({app=\"api-gateway\",env=\"production\"})" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 24: syntax error: unexpected ) | proxy avg() requires a range metric inside (e.g. rate({...}[5m]), count_over_time({...}[5m]))", + "query": "avg({app=\"api-gateway\"})" + } + ], + "id": "8556e63f", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "avg": 4 + }, + "signature": [ + "query_range", + "avg", + "error-text", + "400 with different text: avg() requires a range metric inside (e.g. rate({...}[Nm]), count_over_time({..." + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "avg_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "avg_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "6dfea202", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "avg_over_time[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "avg_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation avg_over_time without unwrap | proxy avg_over_time requires `| unwrap ` for range aggregation", + "query": "avg_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation avg_over_time without unwrap | proxy avg_over_time requires `| unwrap ` for range aggregation", + "query": "avg_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "f2273e53", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "avg_over_time": 4 + }, + "signature": [ + "query_range", + "avg_over_time", + "error-text", + "400 with different text: avg_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "bytes_rate({app=\"api-gateway\"} | json [1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "bytes_rate({app=\"api-gateway\"} | json [5m])" + } + ], + "id": "9f2d98ad", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "bytes_rate[json]": 4 + }, + "signature": [ + "query_range", + "bytes_rate[json]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "count_over_time({app=\"api-gateway\"} | json | method=\"GET\" [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted', 'request_id_extracted']", + "query": "count_over_time({app=\"api-gateway\"} | json | status=\"200\" [5m])" + } + ], + "id": "1173e4c5", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time[json,label-filter]": 4 + }, + "signature": [ + "query_range", + "count_over_time[json,label-filter]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted']", + "query": "count_over_time({app=\"payment-service\",env=\"production\"} | logfmt | level!=\"debug\" [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted']", + "query": "count_over_time({app=\"payment-service\",env=\"production\"} | logfmt | level=\"error\"[5m])" + } + ], + "id": "66f96c46", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time[logfmt,label-filter]": 4 + }, + "signature": [ + "query_range", + "count_over_time[logfmt,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "first_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "first_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "231b4102", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "first_over_time[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "first_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation first_over_time without unwrap | proxy first_over_time requires `| unwrap ` for range aggregation", + "query": "first_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation first_over_time without unwrap | proxy first_over_time requires `| unwrap ` for range aggregation", + "query": "first_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "4f0bd5da", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "first_over_time": 4 + }, + "signature": [ + "query_range", + "first_over_time", + "error-text", + "400 with different text: first_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "last_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "last_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "16eaca6c", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "last_over_time[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "last_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation last_over_time without unwrap | proxy last_over_time requires `| unwrap ` for range aggregation", + "query": "last_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation last_over_time without unwrap | proxy last_over_time requires `| unwrap ` for range aggregation", + "query": "last_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "e37fdcc4", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "last_over_time": 4 + }, + "signature": [ + "query_range", + "last_over_time", + "error-text", + "400 with different text: last_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 55100 series, proxy 2160", + "query": "max_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 55100 series, proxy 2160", + "query": "max_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "d31150a4", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "max_over_time[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "max_over_time[json,unwrap]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation max_over_time without unwrap | proxy max_over_time requires `| unwrap ` for range aggregation", + "query": "max_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation max_over_time without unwrap | proxy max_over_time requires `| unwrap ` for range aggregation", + "query": "max_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "4e182695", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "max_over_time": 4 + }, + "signature": [ + "query_range", + "max_over_time", + "error-text", + "400 with different text: max_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 55100 series, proxy 2160", + "query": "min_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 55100 series, proxy 2160", + "query": "min_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "273c6ca5", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "min_over_time[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "min_over_time[json,unwrap]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation min_over_time without unwrap | proxy min_over_time requires `| unwrap ` for range aggregation", + "query": "min_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation min_over_time without unwrap | proxy min_over_time requires `| unwrap ` for range aggregation", + "query": "min_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "4990705b", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "min_over_time": 4 + }, + "signature": [ + "query_range", + "min_over_time", + "error-text", + "400 with different text: min_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\",env=\"production\"} | json | method=\"GET\"[5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\"} | json | method=\"GET\" [1m])" + } + ], + "id": "cab142d9", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "rate[json,label-filter]": 4 + }, + "signature": [ + "query_range", + "rate[json,label-filter]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "rate({app=\"api-gateway\",env=\"production\"} | json | status>=400 [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "rate({app=\"api-gateway\",env=\"production\"} | json | status>=400 | status<500 [5m])" + } + ], + "id": "d04917ae", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "rate[json,label-filter]": 4 + }, + "signature": [ + "query_range", + "rate[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\"} |= \"GET\" | json [1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\"} |~ \"POST|PUT\" | json [1m])" + } + ], + "id": "b6078924", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "rate[json,line-filter]": 4 + }, + "signature": [ + "query_range", + "rate[json,line-filter]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 25: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "rate({app=\"api-gateway\"})" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 42: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "rate({app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "95f2257a", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "rate": 4 + }, + "signature": [ + "query_range", + "rate", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 30: syntax error: unexpected IDENTIFIER, expecting ) | proxy -", + "query": "rate({app=\"api-gateway\"}[5m] @ 1000000000)" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 30: syntax error: unexpected IDENTIFIER, expecting ) | proxy -", + "query": "rate({app=\"api-gateway\"}[5m] @ start())" + } + ], + "id": "d4a26169", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "rate": 4 + }, + "signature": [ + "query_range", + "rate", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"app\": \"batch-etl\"} @1791221580.0: loki 10.791447320168809 proxy 881", + "query": "stddev by(app)(count_over_time({env=\"production\"}[5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"level\": \"error\"} @1791221580.0: loki 2.3885011067990423 proxy 162", + "query": "stddev by(level)(count_over_time({app=\"api-gateway\",env=\"production\"}[5m]))" + } + ], + "id": "6864165c", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "stddev": 4 + }, + "signature": [ + "query_range", + "stddev", + "values", + "values differ (proxy higher)" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "stddev_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "stddev_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "b166bfd3", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "stddev_over_time[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "stddev_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation stddev_over_time without unwrap | proxy stddev_over_time requires `| unwrap ` for range aggregation", + "query": "stddev_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation stddev_over_time without unwrap | proxy stddev_over_time requires `| unwrap ` for range aggregation", + "query": "stddev_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "3791711e", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "stddev_over_time": 4 + }, + "signature": [ + "query_range", + "stddev_over_time", + "error-text", + "400 with different text: stddev_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "stdvar_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "stdvar_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "ee9ebbea", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "stdvar_over_time[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "stdvar_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation stdvar_over_time without unwrap | proxy stdvar_over_time requires `| unwrap ` for range aggregation", + "query": "stdvar_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation stdvar_over_time without unwrap | proxy stdvar_over_time requires `| unwrap ` for range aggregation", + "query": "stdvar_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "f09d3262", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "stdvar_over_time": 4 + }, + "signature": [ + "query_range", + "stdvar_over_time", + "error-text", + "400 with different text: stdvar_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"app\": \"pricing-svc\"}: loki 61 points, proxy 60", + "query": "sum by (app)(sum_over_time({env=\"production\"} | json | unwrap duration_ms [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"level\": \"error\"}: loki 61 points, proxy 60", + "query": "sum by(level)(sum_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m]))" + } + ], + "id": "c17c1437", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "sum[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "sum[json,unwrap]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 24: syntax error: unexpected ) | proxy sum() requires a range metric inside (e.g. rate({...}[5m]), count_over_time({...}[5m]))", + "query": "sum({app=\"api-gateway\"})" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 32: syntax error: unexpected ) | proxy sum() requires a range metric inside (e.g. rate({...}[5m]), count_over_time({...}[5m]))", + "query": "sum by(job) ({namespace=\"prod\"})" + } + ], + "id": "b864cc9d", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "sum": 4 + }, + "signature": [ + "query_range", + "sum", + "error-text", + "400 with different text: sum() requires a range metric inside (e.g. rate({...}[Nm]), count_over_time({..." + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 55100 series, proxy 2160", + "query": "sum_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 55100 series, proxy 2160", + "query": "sum_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "c84cb5a1", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "sum_over_time[json,unwrap]": 4 + }, + "signature": [ + "query_range", + "sum_over_time[json,unwrap]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation sum_over_time without unwrap | proxy sum_over_time requires `| unwrap ` for range aggregation", + "query": "sum_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation sum_over_time without unwrap | proxy sum_over_time requires `| unwrap ` for range aggregation", + "query": "sum_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "74505ee8", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "sum_over_time": 4 + }, + "signature": [ + "query_range", + "sum_over_time", + "error-text", + "400 with different text: sum_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"http_status\": \"status\", \"ip\": ...", + "query": "{app=\"api-gateway\",env=\"production\"} | json | label_format req_method=\"method\", req_path=\"path\", http_status=\"status\"" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{app=\"api-gateway\",env=\"production\"} | json | label_format method_alias=\"{{.method}}\", status_code=\"{{.status}}\"" + } + ], + "id": "f455442d", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "logs[json,label_format]": 4 + }, + "signature": [ + "query_range", + "logs[json,label_format]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \")\" at position 44", + "query": "{namespace=\"prod\"} | addr = ip(\"10.0.0.0/8\")" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \")\" at position 48", + "query": "{namespace=\"prod\"} | addr = ip(\"192.168.0.0/16\")" + } + ], + "id": "2cda3276", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "logs[label-filter]": 4 + }, + "signature": [ + "query_range", + "logs[label-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but ca...", + "query": "sum by (level) (rate({env=\"production\"} | json [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but ca...", + "query": "sum by (status) (count_over_time({env=\"production\"} | json [5m]))" + } + ], + "id": "e4fdf6d3", + "impact": "explore-visible", + "queries": 2, + "requests": 4, + "shapes": { + "sum[json]": 4 + }, + "signature": [ + "query_range", + "sum[json]", + "error-text", + "400 with different text: pipeline error: Q for series: Qt find closing Q symbol\", app=..." + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "query_range", + "excerpt": "level: loki debug | proxy info", + "query": "{service_name=\"cache-redis\"} | logfmt | drop level_extracted" + }, + { + "encoding": "plain", + "endpoint": "query_range", + "excerpt": "level: loki info | proxy warn", + "query": "{service_name=\"worker-service\"} | logfmt | drop level_extracted" + } + ], + "id": "ab4ce12b", + "impact": "explore-visible", + "queries": 2, + "requests": 2, + "shapes": { + "logs[logfmt,drop]": 2 + }, + "signature": [ + "query_range", + "logs[logfmt,drop]", + "labels", + "value differs level" + ], + "sources": { + "seeded": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"inventory-svc\", \"cluster\": \"us-east-1\", \"container\": \"inventory\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k8s_clu...", + "query": "{namespace=\"prod\"} | decolorize" + } + ], + "id": "57ff8760", + "impact": "explore-visible", + "queries": 1, + "requests": 4, + "shapes": { + "logs[decolorize]": 4 + }, + "signature": [ + "query_range", + "logs[decolorize]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 2, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "absent_over_time(({namespace=\"prod\"} |~\".+bar\")[1m])" + } + ], + "id": "74815f5f", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time[line-filter]": 2 + }, + "signature": [ + "query_range", + "absent_over_time[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : grouping not allowed for absent_over_time aggregation | proxy -", + "query": "absent_over_time({namespace=\"prod\"}[1h]) by (foo)" + } + ], + "id": "cd64981a", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time": 2 + }, + "signature": [ + "query_range", + "absent_over_time", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 2026", + "query": "absent_over_time({service_name=\"api-gateway\"} | json [1m])" + } + ], + "id": "5eaf17b5", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time[json]": 2 + }, + "signature": [ + "query_range", + "absent_over_time[json]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 0", + "query": "absent_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json [5m])" + } + ], + "id": "c4f74a0e", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time[json,line-filter]": 2 + }, + "signature": [ + "query_range", + "absent_over_time[json,line-filter]", + "series", + "proxy-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "absent_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms | unwrap latency | __error__!~\".*\" | foo >5[5m])" + } + ], + "id": "4e6094a0", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query_range", + "absent_over_time[json,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably, the whole string m" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "avg( label_replace( count_over_time({namespace=\"prod\"}[1h]), \"bar\", \"$1$2\", \"foo\", \"(.*).(.*)\" ) ) by (bar,foo)" + } + ], + "id": "4dc99479", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "avg": 2 + }, + "signature": [ + "query_range", + "avg", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "avg( rate( ( {namespace=\"prod\"} |= \"GET\" ) [10s] ) ) by (region)" + } + ], + "id": "f7b13aae", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "avg[line-filter]": 2 + }, + "signature": [ + "query_range", + "avg[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2160", + "query": "avg(rate({service_name=\"api-gateway\"} | json [5m]))" + } + ], + "id": "0e319b2f", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "avg[json]": 2 + }, + "signature": [ + "query_range", + "avg[json]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 21916", + "query": "avg_over_time({service_name=\"payment-service\"} | logfmt | drop level | unwrap value [1s]) without (stream)" + } + ], + "id": "c273ebd1", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "avg_over_time[logfmt,drop,unwrap]": 2 + }, + "signature": [ + "query_range", + "avg_over_time[logfmt,drop,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 2", + "query": "avg_over_time({namespace=\"prod\"} | unwrap bytes [5m]) by (cluster)" + } + ], + "id": "b499246a", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "avg_over_time[unwrap]": 2 + }, + "signature": [ + "query_range", + "avg_over_time[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 9: syntax error: unexpected IDENTIFIER | proxy parse error : logql: bottomk requires a numeric k parameter", + "query": "bottomk(he,count_over_time({namespace=\"prod\"}[1h]))" + } + ], + "id": "94fdc54f", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "bottomk": 2 + }, + "signature": [ + "query_range", + "bottomk", + "error-text", + "400 with different text: parse error : logql: bottomk requires a numeric k parameter" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "bytes_over_time(({namespace=\"prod\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")[5m])" + } + ], + "id": "b871c258", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_over_time[line-filter]": 2 + }, + "signature": [ + "query_range", + "bytes_over_time[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 22937 series, proxy 1127", + "query": "bytes_over_time({service_name=\"payment-service\"} | logfmt [3m])" + } + ], + "id": "d21214b2", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_over_time[logfmt]": 2 + }, + "signature": [ + "query_range", + "bytes_over_time[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 22937 series, proxy 1127", + "query": "bytes_rate({service_name=\"payment-service\"} | logfmt [3m])" + } + ], + "id": "e598f28d", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_rate[logfmt]": 2 + }, + "signature": [ + "query_range", + "bytes_rate[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{} @1791221760.0: loki 844 proxy 170", + "query": "count by (foo) (rate({namespace=\"prod\"}[1m]))" + } + ], + "id": "02fdd315", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count": 2 + }, + "signature": [ + "query_range", + "count", + "values", + "values differ (proxy lower)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 16: syntax error: unexpected RATE, expecting IDENTIFIER or ) | proxy parse error : logql: expected ), got ( (\"(\")", + "query": "count without (rate({namespace=\"prod\"}[15s]))" + } + ], + "id": "ead2b08b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count": 2 + }, + "signature": [ + "query_range", + "count", + "error-text", + "400 with different text: parse error : logql: expected ), got ( (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2160", + "query": "count(rate({service_name=\"api-gateway\"} | json | keep foo [5m]))" + } + ], + "id": "cf968b92", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count[json,keep]": 2 + }, + "signature": [ + "query_range", + "count[json,keep]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2160", + "query": "count(rate({service_name=\"api-gateway\"} | json | label_format foo=bar [5m]))" + } + ], + "id": "2c7140a9", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count[json,label_format]": 2 + }, + "signature": [ + "query_range", + "count[json,label_format]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unexpected type for right leg of binary operation (>): *syntax.MatchersExpr | proxy parse error at line 1, col 1: unexpected expression for binary operation", + "query": "count_over_time({namespace=\"prod\"}[12m]) > {namespace=\"prod\"}" + } + ], + "id": "c9b6ea43", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time": 2 + }, + "signature": [ + "query_range", + "count_over_time", + "error-text", + "400 with different text: parse error at line N, col N: unexpected expression for binary operation" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['layer7_something_specific']", + "query": "count_over_time({service_name=\"api-gateway\"} | json layer7_something_specific=\"layer7_something_specific\" [12m])" + } + ], + "id": "3eddbf61", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json]": 2 + }, + "signature": [ + "query_range", + "count_over_time[json]", + "labels", + "loki-only " + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation count_over_time with unwrap | proxy -", + "query": "count_over_time({service_name=\"api-gateway\"} |= \"foo\" | json | unwrap foo [5m])" + } + ], + "id": "7ba3e83b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json,unwrap,line-filter]": 2 + }, + "signature": [ + "query_range", + "count_over_time[json,unwrap,line-filter]", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "count_over_time({namespace=\"prod\"}[3s] offset -1s)" + } + ], + "id": "c138dca8", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time": 2 + }, + "signature": [ + "query_range", + "count_over_time", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 49: syntax error: unexpected $end, expecting STRING | proxy parse error : logql: expected STRING or RAWSTRING, got EOF (\"\")", + "query": "count_over_time({namespace=\"prod\"} | line_format" + } + ], + "id": "7efa02ba", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[line_format]": 2 + }, + "signature": [ + "query_range", + "count_over_time[line_format]", + "error-text", + "400 with different text: parse error : logql: expected string or rawstring, got eof (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid aggregation count_over_time with unwrap | proxy -", + "query": "count_over_time({namespace=\"prod\"} |~ \"\\\\w+\" | unwrap foo[5m])" + } + ], + "id": "649f8e6b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[unwrap,line-filter]": 2 + }, + "signature": [ + "query_range", + "count_over_time[unwrap,line-filter]", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '|~ \"error\\\\\"' : error parsing regexp: trailing backslash at end of expression: `` | proxy parse error : logql: expected ), got |~ (\"|~\")", + "query": "count_over_time({namespace=\"prod\"}[1h] |~ `error\\`)" + } + ], + "id": "2cb1c674", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[line-filter]": 2 + }, + "signature": [ + "query_range", + "count_over_time[line-filter]", + "error-text", + "400 with different text: parse error : logql: expected ), got |~ (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "first_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s] offset -2s) by (a)" + } + ], + "id": "852cb577", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "first_over_time[logfmt,unwrap]": 2 + }, + "signature": [ + "query_range", + "first_over_time[logfmt,unwrap]", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "first_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms | unwrap latency | __error__!~\".*\" | foo >5[5m])" + } + ], + "id": "f59a58b3", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "first_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query_range", + "first_over_time[json,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably, the whole string m" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "label_replace( avg by (app) ( sum by (app) (rate({namespace=\"prod\"} |~\".+bar\" [1m])) + sum by (app) (rate({namespace=\"prod\"} |~\".+bar\" [1m])) / sum by (app) (rate({namespace=\"prod\"} |~\".+bar\" [1m])) ) * 2, \"new\", \"$1\", \"app\", \"f(.*)\" )" + } + ], + "id": "5a65069b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "label_replace[line-filter]": 2 + }, + "signature": [ + "query_range", + "label_replace[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "label_replace( bytes_over_time(({namespace=\"prod\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")[5m]), \"buzz\", \"$2\", \"bar\", \"(.*):(.*)\" )" + } + ], + "id": "d8b61530", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "label_replace[line-filter]": 2 + }, + "signature": [ + "query_range", + "label_replace[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 46: syntax error: unexpected ), expecting , | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "label_replace(rate({namespace=\"prod\"}[5m]),\"\")" + } + ], + "id": "f7f991c9", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "label_replace": 2 + }, + "signature": [ + "query_range", + "label_replace", + "error-text", + "400 with different text: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : invalid regex in label_replace: error parsing regexp: invalid escape sequence: `\\q` | proxy -", + "query": "label_replace(rate({namespace=\"prod\"}[5m]),\"foo\",\"$1\",\"bar\",\"^^^^x43\\\\q\")" + } + ], + "id": "a987dc81", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "label_replace": 2 + }, + "signature": [ + "query_range", + "label_replace", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "last_over_time(({namespace=\"prod\"} |~\".+bar\" | unwrap foo)[5m])" + } + ], + "id": "c04abdc5", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "last_over_time[unwrap,line-filter]": 2 + }, + "signature": [ + "query_range", + "last_over_time[unwrap,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "last_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s] offset -2s) by (a)" + } + ], + "id": "d2f9deab", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "last_over_time[logfmt,unwrap]": 2 + }, + "signature": [ + "query_range", + "last_over_time[logfmt,unwrap]", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"last\"", + "query": "last_over_time({namespace=\"prod\"}|~\".+bar\"| unwrap foo[5m])" + } + ], + "id": "90a46cda", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "last_over_time[unwrap,line-filter]": 2 + }, + "signature": [ + "query_range", + "last_over_time[unwrap,line-filter]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "last_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms | unwrap latency | __error__!~\".*\" | foo >5[5m])" + } + ], + "id": "967f3abc", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "last_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query_range", + "last_over_time[json,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably, the whole string m" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "max by (baz) (sum_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]))" + } + ], + "id": "6ff0a17b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "max[json,unwrap]": 2 + }, + "signature": [ + "query_range", + "max[json,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "max( sum(count_over_time({namespace=\"prod\"}[1h])) without (foo,bar) ) by (foo)" + } + ], + "id": "8dedc850", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "max": 2 + }, + "signature": [ + "query_range", + "max", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{} @1791221760.0: loki 14.193333333333333 proxy 4258", + "query": "max(sum by (abc) (rate({service_name=\"api-gateway\"} | json | label_format bazz=buzz [5m])))" + } + ], + "id": "609c0d8a", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "max[json,label_format]": 2 + }, + "signature": [ + "query_range", + "max[json,label_format]", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 0; loki-only [\"{}\"] proxy-only []", + "query": "max(sum by (cluster) (rate({namespace=\"prod\"}[5m]))) / count(rate({namespace=\"prod\"}[5m]))" + } + ], + "id": "e9319bcf", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "max": 2 + }, + "signature": [ + "query_range", + "max", + "series", + "proxy-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "max_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]) by (bar)" + } + ], + "id": "77c25773", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "max_over_time[json,unwrap]": 2 + }, + "signature": [ + "query_range", + "max_over_time[json,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "max_over_time({namespace=\"prod\"} | unwrap bar [5m] offset -5m) without (foo,bar)" + } + ], + "id": "82240823", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "max_over_time[unwrap]": 2 + }, + "signature": [ + "query_range", + "max_over_time[unwrap]", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "min by (baz) (sum_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]))" + } + ], + "id": "764e0a1b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "min[json,unwrap]": 2 + }, + "signature": [ + "query_range", + "min[json,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 0, col 23: syntax error: unexpected RANGE | proxy parse error : logql: expected ), got [ (\"[\")", + "query": "min({namespace=\"prod\"}[5m])" + } + ], + "id": "c8035a7e", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "min": 2 + }, + "signature": [ + "query_range", + "min", + "error-text", + "400 with different text: parse error : logql: expected ), got [ (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "min_over_time({service_name=\"payment-service\"} | logfmt | unwrap line [2s]) by (a)" + } + ], + "id": "9eaa432d", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "min_over_time[logfmt,unwrap]": 2 + }, + "signature": [ + "query_range", + "min_over_time[logfmt,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {} @1791220800.0: \"\"", + "query": "min_over_time({service_name=\"api-gateway\"} | json | unwrap bar [3m]) by (baz)" + } + ], + "id": "0ca5ac7a", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "min_over_time[json,unwrap]": 2 + }, + "signature": [ + "query_range", + "min_over_time[json,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : parameter required for operation quantile_over_time | proxy invalid quantile query", + "query": "quantile_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms| unwrap latency [5m])" + } + ], + "id": "24d72116", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "quantile_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query_range", + "quantile_over_time[json,unwrap,label-filter,line-filter]", + "error-text", + "400 with different text: invalid quantile query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] )" + } + ], + "id": "fb38c36f", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[line-filter]": 2 + }, + "signature": [ + "query_range", + "rate[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "rate(({namespace=\"prod\"} |~\".+bar\" | unwrap bar)[1m])" + } + ], + "id": "585e4266", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[unwrap,line-filter]": 2 + }, + "signature": [ + "query_range", + "rate[unwrap,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "rate(({namespace=\"prod\"} |~\".+bar\")[1m])" + } + ], + "id": "2ec911a8", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[line-filter]": 2 + }, + "signature": [ + "query_range", + "rate[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 0, col 24: missing closing ']' in duration | proxy parse error : logql: expected duration: logql: expected DURATION, got NUMBER (\"5\")", + "query": "rate({namespace=\"prod\"}[5)" + } + ], + "id": "a9930b3c", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate": 2 + }, + "signature": [ + "query_range", + "rate", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got number (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 0, col 24: unknown unit \"minutes\" in duration \"5minutes\" | proxy invalid range metric query", + "query": "rate({namespace=\"prod\"}[5minutes])" + } + ], + "id": "61f5762d", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate": 2 + }, + "signature": [ + "query_range", + "rate", + "error-text", + "400 with different text: invalid range metric query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "rate({namespace=\"prod\"}[3s] offset -1s)" + } + ], + "id": "048082ab", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate": 2 + }, + "signature": [ + "query_range", + "rate", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "rate({service_name=\"api-gateway\"} | json | unwrap foo | latency >= 250ms or bytes > 42B or ( status_code < 500 and status_code > 200) or source = ip(\"\") and user = \"me\" [1m])" + } + ], + "id": "807c5ade", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json,unwrap,label-filter]": 2 + }, + "signature": [ + "query_range", + "rate[json,unwrap,label-filter]", + "status", + "loki 200 proxy 400: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 51866", + "query": "rate({service_name=\"api-gateway\"} | json | unwrap foo | latency >= 250ms or bytes=~\".*\" [1m])" + } + ], + "id": "87352721", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json,unwrap,label-filter]": 2 + }, + "signature": [ + "query_range", + "rate[json,unwrap,label-filter]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "rate({service_name=\"api-gateway\"} | json | label_format foo=bar [5m])" + } + ], + "id": "fa6d53ad", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json,label_format]": 2 + }, + "signature": [ + "query_range", + "rate[json,label_format]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sort(rate(({namespace=\"prod\"} |~\".+bar\")[1m]))" + } + ], + "id": "d3d0631c", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sort[line-filter]": 2 + }, + "signature": [ + "query_range", + "sort[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sort(sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) ))" + } + ], + "id": "75e5bc38", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sort[line-filter]": 2 + }, + "signature": [ + "query_range", + "sort[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : sort and sort_desc doesn't allow grouping by | proxy invalid range metric query", + "query": "sort(sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) )) by (app)" + } + ], + "id": "bd69d9b0", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sort[line-filter]": 2 + }, + "signature": [ + "query_range", + "sort[line-filter]", + "error-text", + "400 with different text: invalid range metric query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy backend returned 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sort_desc(sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) ))" + } + ], + "id": "9ef01b8a", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sort_desc[line-filter]": 2 + }, + "signature": [ + "query_range", + "sort_desc[line-filter]", + "status", + "loki 200 proxy 400: backend returned N: cannot parse query arg [\u2026]: missing whitespace or Q between " + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : sort and sort_desc doesn't allow grouping by | proxy invalid range metric query", + "query": "sort_desc(sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) )) by (app)" + } + ], + "id": "4bef518e", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sort_desc[line-filter]": 2 + }, + "signature": [ + "query_range", + "sort_desc[line-filter]", + "error-text", + "400 with different text: invalid range metric query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "stddev(count_over_time(({namespace=\"prod\"} |~\".+bar\")[1m]))" + } + ], + "id": "154f3c5e", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "stddev[line-filter]": 2 + }, + "signature": [ + "query_range", + "stddev[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 26: syntax error: unexpected ) | proxy missing `| stats ...` pipe in the query [_time:[2026-10-05T17:20:00.000000000Z,2026-10-05T18:20:59.999999999Z] namespace:=prod]", + "query": "stddev({namespace=\"prod\"})" + } + ], + "id": "75acdfee", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "stddev": 2 + }, + "signature": [ + "query_range", + "stddev", + "status", + "loki 400 proxy 422: missing Q pipe in the query [_time:[N-N-NtN:N:Nz,N-N-NtN:N:Nz] namespace:=prod]" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "stdvar without (app) (count_over_time(({namespace=\"prod\"} |~\".+bar\")[1m]))" + } + ], + "id": "35c82cd3", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "stdvar[line-filter]": 2 + }, + "signature": [ + "query_range", + "stdvar[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"cluster\": \"us-west-2\"} @1791221760.0: loki 10.08 proxy 1512", + "query": "sum by (cluster) (sum by (cluster) (rate({namespace=\"prod\"} [5m])) + ignoring(machine) sum by (cluster,machine) (rate({namespace=\"prod\"} [5m])))" + } + ], + "id": "354c7d83", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum": 2 + }, + "signature": [ + "query_range", + "sum", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "sum by (container) (min_over_time({service_name=\"api-gateway\"} |= \"foo\" or \"bar\" | logfmt | unwrap duration [1m])) / sum by (container) (max_over_time({service_name=\"api-gateway\"} |~ \"(foo|bar)\" | json | unwrap duration [1m]))" + } + ], + "id": "147e07fb", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,logfmt,unwrap,line-filter]": 2 + }, + "signature": [ + "query_range", + "sum[json,logfmt,unwrap,line-filter]", + "status", + "loki 200 proxy 400: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{} @1791221760.0: loki 3258.236346011562 proxy 33800", + "query": "sum by (job) ( count_over_time({namespace=\"prod\"} |= \"level=error\"[5m]) / count_over_time({namespace=\"prod\"}[5m]) ) * 100" + } + ], + "id": "4c5d2b49", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[line-filter]": 2 + }, + "signature": [ + "query_range", + "sum[line-filter]", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by(name)(rate({service_name=\"api-gateway\"} | json | line_format \"something else\" | unwrap foo[5m]))" + } + ], + "id": "ea2af38c", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,line_format,unwrap]": 2 + }, + "signature": [ + "query_range", + "sum[json,line_format,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unsupported parameter for operation sum(3, | proxy parse error : logql: expected ), got , (\",\")", + "query": "sum(3 ,count_over_time({namespace=\"prod\"}[1h]))" + } + ], + "id": "d5db4f97", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum": 2 + }, + "signature": [ + "query_range", + "sum", + "error-text", + "400 with different text: parse error : logql: expected ), got , (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "sum(count_over_time({namespace=\"prod\"}[5m] |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")) by (foo)" + } + ], + "id": "72db0446", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[line-filter]": 2 + }, + "signature": [ + "query_range", + "sum[line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: expected ), got |= (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : unexpected type for right leg of binary operation (-): *syntax.MatchersExpr | proxy parse error at line 1, col 1: unexpected expression for binary operation", + "query": "sum(count_over_time({namespace=\"prod\"}[5m])) by (foo) - {namespace=\"prod\"}" + } + ], + "id": "3801dc16", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum": 2 + }, + "signature": [ + "query_range", + "sum", + "error-text", + "400 with different text: parse error at line N, col N: unexpected expression for binary operation" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "sum(count_over_time({service_name=\"payment-service\"} | logfmt --strict [5m] offset 10m))" + } + ], + "id": "23cbc9b3", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt]": 2 + }, + "signature": [ + "query_range", + "sum[logfmt]", + "status", + "loki 200 proxy 400: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"last\"", + "query": "sum(last_over_time({service_name=\"payment-service\"} | logfmt | unwrap total_count [1h]) by (foo)) or vector(0)" + } + ], + "id": "83edd378", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,unwrap]": 2 + }, + "signature": [ + "query_range", + "sum[logfmt,unwrap]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2160", + "query": "sum(max(rate({service_name=\"api-gateway\"} | json | label_format foo=bar [5m])))" + } + ], + "id": "db50c7bd", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,label_format]": 2 + }, + "signature": [ + "query_range", + "sum[json,label_format]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 0; loki-only [\"{}\"] proxy-only []", + "query": "sum(rate({namespace=\"prod\"} |= \"foo\" != \"foo\"[1s]) or vector(1))" + } + ], + "id": "ac314fbf", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[line-filter]": 2 + }, + "signature": [ + "query_range", + "sum[line-filter]", + "series", + "proxy-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 36: syntax error: unexpected $end, expecting STRING | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "sum(rate({namespace=\"prod\"} | msg=~" + } + ], + "id": "4014589d", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[label-filter]": 2 + }, + "signature": [ + "query_range", + "sum[label-filter]", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 40: syntax error: unexpected $end | proxy parse error : logql: expected ), got EOF (\"\")", + "query": "sum(rate({namespace=\"prod\"}[5m])-1 > 30" + } + ], + "id": "8cce5c63", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum": 2 + }, + "signature": [ + "query_range", + "sum", + "error-text", + "400 with different text: parse error : logql: expected ), got eof (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : parameter 50 not supported for operation sum_over_time | proxy parse error at line 1, col 15: syntax error: unexpected NUMBER, expecting NUMBER or { or (", + "query": "sum_over_time(50,{service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms| unwrap latency [5m])" + } + ], + "id": "479a3ac5", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query_range", + "sum_over_time[json,unwrap,label-filter,line-filter]", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected number, expecting number " + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : grouping not allowed for sum_over_time aggregation | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "sum_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms| unwrap latency [5m]) by (foo)" + } + ], + "id": "8ebf872b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query_range", + "sum_over_time[json,unwrap,label-filter,line-filter]", + "error-text", + "400 with different text: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably, the whole string m" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk by (name)(10,sum(rate({namespace=\"prod\"}[5m])))" + } + ], + "id": "53933736", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "topk": 2 + }, + "signature": [ + "query_range", + "topk", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"api\"; probably, the whole string must be put into quotes", + "query": "topk( 10, sum by (ip) ( sum_over_time({service_name=\"api-gateway\"} | json | unwrap bytes(bytes)[1m]) ) )" + } + ], + "id": "98078324", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "topk[json,unwrap]": 2 + }, + "signature": [ + "query_range", + "topk[json,unwrap]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy stats backend 400: cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk(5,sum(rate(({namespace=\"prod\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")[5m])) by (app))" + } + ], + "id": "e0a75c1d", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "topk[line-filter]": 2 + }, + "signature": [ + "query_range", + "topk[line-filter]", + "status", + "loki 200 proxy 400: stats backend N: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"}|logfmt --strict --keep-empty|length>5d" + } + ], + "id": "c0ccfa2d", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[logfmt,label-filter]": 2 + }, + "signature": [ + "query_range", + "logs[logfmt,label-filter]", + "status", + "loki 200 proxy 400: parse error : logql: expected number after Q" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token STRING (\"foo\")", + "query": "{namespace=\"prod\"} != ip(\"127.0.0.1\") or \"foo\"" + } + ], + "id": "306824bd", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs": 2 + }, + "signature": [ + "query_range", + "logs", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token string (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \"#\" at position 37", + "query": "{service_name=\"api-gateway\"} | json #" + } + ], + "id": "18f6c171", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json]": 2 + }, + "signature": [ + "query_range", + "logs[json]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token STRING (\"200\")", + "query": "{service_name=\"api-gateway\"} |= \"bar\" | json |= \"500\" or \"200\"" + } + ], + "id": "435e2f3c", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[json,line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token string (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error at line 1, col 1: syntax error: unexpected |", + "query": "{service_name=\"nginx-ingress\"} |= \"bar\" | pattern \" bar \" | (duration > 1s or status!= 200) and method!=\"POST\"" + } + ], + "id": "ac2c4509", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[pattern,line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[pattern,line-filter]", + "status", + "loki 200 proxy 400: parse error at line N, col N: syntax error: unexpected |" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \")\" at position 118", + "query": "{service_name=\"api-gateway\"} |= \"bar\" | unpack | json | latency >= 250ms or ( status_code < 500 and status_code > 200)" + } + ], + "id": "3c3ebb3b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,unpack,label-filter,line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[json,unpack,label-filter,line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error at line 1, col 1: unexpected expression for binary operation", + "query": "{namespace=\"prod\"} |= \"foo\" or ip(\"127.0.0.1\")" + } + ], + "id": "dfe987e7", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[line-filter]", + "status", + "loki 200 proxy 400: parse error at line N, col N: unexpected expression for binary operation" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : ip: invalid operation", + "query": "{namespace=\"prod\"} |~ ip(\"127.0.0.1\") or \"foo\"" + } + ], + "id": "9be7d291", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[line-filter]", + "status", + "loki 200 proxy 400: parse error : ip: invalid operation" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 entries, proxy 99", + "query": "{namespace=\"prod\"} |> \"\"" + } + ], + "id": "90e7eb36", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[pattern-filter]": 2 + }, + "signature": [ + "query_range", + "logs[pattern-filter]", + "entries", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy parse error : logql: unexpected token \"#\" at position 20", + "query": "{namespace=\"prod\"} #|~ \"\\\\w+\"" + } + ], + "id": "bd531691", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 22: syntax error: unexpected $end, expecting STRING or ip | proxy parse error : logql: expected STRING or RAWSTRING, got EOF (\"\")", + "query": "{namespace=\"prod\"} |~" + } + ], + "id": "090d292e", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[line-filter]", + "error-text", + "400 with different text: parse error : logql: expected string or rawstring, got eof (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 83: syntax error: unexpected unwrap | proxy -", + "query": "{namespace=\"prod\"} |~ \"\\\\w+\" | latency > 1h0.0m0s or foo == 4.00 and bar =\"foo\" | unwrap foo" + } + ], + "id": "eec27d19", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[unwrap,label-filter,line-filter]", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 35: literal not terminated | proxy cannot parse query arg [\u2026]:", + "query": "{namespace=\"prod\"}|LOGFMT --strict\"" + } + ], + "id": "dae354de", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs": 2 + }, + "signature": [ + "query_range", + "logs", + "error-text", + "400 with different text: cannot parse query arg [\u2026]:" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '|= ip(\"b\")' : ip: invalid pattern: \"b\" | proxy parse error : logql: expected number after '-'", + "query": "{service_name=\"payment-service\"}|logfmt --strict --keep-empty|=ip(\"b\")" + } + ], + "id": "11e197ef", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[logfmt,line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[logfmt,line-filter]", + "error-text", + "400 with different text: parse error : logql: expected number after Q" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '|= ip(\"b\")' : ip: invalid pattern: \"b\" | proxy parse error : ip: invalid pattern: \"b\"", + "query": "{service_name=\"payment-service\"}|logfmt|=ip(\"b\")" + } + ], + "id": "799eb1d6", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[logfmt,line-filter]": 2 + }, + "signature": [ + "query_range", + "logs[logfmt,line-filter]", + "error-text", + "400 with different text: parse error : ip: invalid pattern: Q" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 99 entries, proxy 0", + "query": "{namespace=\"prod\"} !> \"\"" + } + ], + "id": "f131187f", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[pattern-filter]": 2 + }, + "signature": [ + "query_range", + "logs[pattern-filter]", + "entries", + "proxy-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 1 series, proxy 2; loki-only [] proxy-only [\"{\\\"level\\\": \\\"info\\\"}\"]", + "query": "sum by (level) (max_over_time({service_name=\"cache-redis\"} | logfmt | unwrap size_bytes | __error__=\"\" [5m]))" + } + ], + "id": "5bd75aed", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,unwrap,label-filter]": 2 + }, + "signature": [ + "query_range", + "sum[logfmt,unwrap,label-filter]", + "series", + "proxy-more" + ], + "sources": { + "seeded": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "60 samples, e.g. {\"level\": \"error\"} @1791220800.0: \"\"", + "query": "sum by (level) (max_over_time({service_name=\"frontend-ssr\"} | json | unwrap status | __error__=\"\" [5m]))" + } + ], + "id": "8550f3db", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,unwrap,label-filter]": 2 + }, + "signature": [ + "query_range", + "sum[json,unwrap,label-filter]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "seeded": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "ds_query", + "excerpt": "names ['__error__', 'level_extracted', 'service_name_extracted']; e.g. loki {\"__error__\": \"JSONParserErr\", \"app\": \"cache-redis\", \"cluster\": \"us-east-1\", \"container\": \"cache\", \"db\": \"0\", \"detected_level\": \"debug\", \"env\": \"production\", \"level\": \"debug\", \"namespace\": \"data\", ...", + "query": "{env=\"production\"} | json pipeline, level" + } + ], + "id": "9c527802", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json]": 2 + }, + "signature": [ + "grafana:ds_query", + "logs[json]", + "labels", + "loki-only *_extracted,__error__" + ], + "sources": { + "grafana": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "ds_query", + "excerpt": "names ['level_extracted', 'service_name_extracted']; e.g. loki {\"app\": \"otel-collector\", \"cluster\": \"us-east-1\", \"component\": \"collector\", \"container\": \"otel\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"dropped\": \"0\", \"env\": \"production...", + "query": "{env=\"production\"} | json | pipeline=`logs/loki`" + } + ], + "id": "f87f7636", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,label-filter]": 2 + }, + "signature": [ + "grafana:ds_query", + "logs[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "grafana": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "ds_query", + "excerpt": "names ['__error__', '__error_details__', 'level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"__error__\": \"JSONParserErr\", \"__error_details__\": \"Value looks like object, but can't find closing '}' symbol\", \"app\": \"cache-redis\", \"cluster\": \"us-east-1\", \"container\": \"cache...", + "query": "{env=\"production\"} | json | line_format \"{{.message}}\"" + } + ], + "id": "c7b6ed6a", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,line_format]": 2 + }, + "signature": [ + "grafana:ds_query", + "logs[json,line_format]", + "labels", + "loki-only *_extracted,__error__,__error_details__" + ], + "sources": { + "grafana": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 37: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "absent_over_time({app=\"api-gateway\"})" + } + ], + "id": "464672f3", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time": 2 + }, + "signature": [ + "query_range", + "absent_over_time", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 36: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "bytes_over_time({app=\"api-gateway\"})" + } + ], + "id": "feee44bb", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_over_time": 2 + }, + "signature": [ + "query_range", + "bytes_over_time", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 68453 series, proxy 14627", + "query": "count without (cluster) (count_over_time({env=\"production\"}[5m]))" + } + ], + "id": "6a4fb9d0", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count": 2 + }, + "signature": [ + "query_range", + "count", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "count_over_time({app=\"api-gateway\",env=\"production\"} |= \"GET\" | json | status>=200 | status<400 [5m])" + } + ], + "id": "d72d583b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json,label-filter,line-filter]": 2 + }, + "signature": [ + "query_range", + "count_over_time[json,label-filter,line-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['duration_ms_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted', 'request_id_extracted']", + "query": "count_over_time({app=\"api-gateway\"} != \"error\" | json [1m])" + } + ], + "id": "907c7acb", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json,line-filter]": 2 + }, + "signature": [ + "query_range", + "count_over_time[json,line-filter]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 0, col 44: not a valid duration string: \"WINDOW\" | proxy parse error : logql: expected duration: logql: expected DURATION, got IDENT (\"WINDOW\")", + "query": "count_over_time({app=\"api-gateway\"} | json [WINDOW])" + } + ], + "id": "2456a6d9", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json]": 2 + }, + "signature": [ + "query_range", + "count_over_time[json]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got ident (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 0, col 59: not a valid duration string: \"WINDOW\" | proxy parse error : logql: expected duration: logql: expected DURATION, got IDENT (\"WINDOW\")", + "query": "count_over_time({app=\"api-gateway\"} | json | method=\"GET\" [WINDOW])" + } + ], + "id": "4e7de2f4", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json,label-filter]": 2 + }, + "signature": [ + "query_range", + "count_over_time[json,label-filter]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got ident (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '|~ \"a{2,1}\"' : error parsing regexp: invalid repeat count: `{2,1}` | proxy cannot parse query arg [\u2026]: invalid regexp \"_msg\":\"a{\u2026}\": error parsing regexp: invalid repeat count: `{\u2026}`", + "query": "count_over_time({app=\"api-gateway\"} |~ \"a{2,1}\" [1m])" + } + ], + "id": "3b74360f", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[line-filter]": 2 + }, + "signature": [ + "query_range", + "count_over_time[line-filter]", + "error-text", + "400 with different text: cannot parse query arg [\u2026]: invalid regexp Q:Q: error parsing regexp: invalid re" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 36: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "count_over_time({app=\"api-gateway\"})" + } + ], + "id": "5dd88485", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time": 2 + }, + "signature": [ + "query_range", + "count_over_time", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 0, col 50: not a valid duration string: \"WINDOW\" | proxy parse error : logql: expected duration: logql: expected DURATION, got IDENT (\"WINDOW\")", + "query": "count_over_time({app=\"payment-service\"} | logfmt [WINDOW])" + } + ], + "id": "335a0063", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[logfmt]": 2 + }, + "signature": [ + "query_range", + "count_over_time[logfmt]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got ident (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 0 series, proxy 12195", + "query": "count_over_time({service_name=\"api-gateway\"} | unpack | status >= 400 [5m])" + } + ], + "id": "511ab651", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[unpack,label-filter]": 2 + }, + "signature": [ + "query_range", + "count_over_time[unpack,label-filter]", + "series", + "loki-empty" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"level\": \"error\"}: loki 61 points, proxy 60", + "query": "max by (level)(max_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m]))" + } + ], + "id": "7cc910c2", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "max[json,unwrap]": 2 + }, + "signature": [ + "query_range", + "max[json,unwrap]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki - | proxy manual metric response exceeds 67108864 bytes; narrow the query or increase -backend-max-buffered-response-bytes", + "query": "max by(app)(quantile_over_time(0.99, {env=\"production\"} | json | unwrap duration_ms [5m]))" + } + ], + "id": "a644d4bb", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "max[json,unwrap]": 2 + }, + "signature": [ + "query_range", + "max[json,unwrap]", + "status", + "loki 200 proxy 502: manual metric response exceeds N bytes; narrow the query or increase -backend-ma" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 10932 series, proxy 1643", + "query": "max_over_time({app=\"api-gateway\"} | json | method=\"GET\" | unwrap duration_ms [5m])" + } + ], + "id": "c434dd07", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "max_over_time[json,unwrap,label-filter]": 2 + }, + "signature": [ + "query_range", + "max_over_time[json,unwrap,label-filter]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 20: syntax error: unexpected -, expecting NUMBER or { or ( | proxy parse error at line 1, col 1: invalid parameter for quantile_over_time: expected range [0, 1] but got -0.1", + "query": "quantile_over_time(-0.1, {app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "c4c8efb7", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "quantile_over_time[json,unwrap]": 2 + }, + "signature": [ + "query_range", + "quantile_over_time[json,unwrap]", + "error-text", + "400 with different text: parse error at line N, col N: invalid parameter for quantile_over_time: expected" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 0, col 33: not a valid duration string: \"WINDOW\" | proxy parse error : logql: expected duration: logql: expected DURATION, got IDENT (\"WINDOW\")", + "query": "rate({app=\"api-gateway\"} | json [WINDOW])" + } + ], + "id": "ed6ad12f", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json]": 2 + }, + "signature": [ + "query_range", + "rate[json]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got ident (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 22279 series, proxy 1091", + "query": "rate({app=\"payment-service\"} | logfmt [1m])" + } + ], + "id": "0f49a837", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[logfmt]": 2 + }, + "signature": [ + "query_range", + "rate[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted']", + "query": "rate({app=\"payment-service\"} | logfmt | drop msg [5m])" + } + ], + "id": "05318ee0", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[logfmt,drop]": 2 + }, + "signature": [ + "query_range", + "rate[logfmt,drop]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but ca...", + "query": "rate({env=\"production\"} | json | __error__!=\"\" [5m])" + } + ], + "id": "15273179", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json,label-filter]": 2 + }, + "signature": [ + "query_range", + "rate[json,label-filter]", + "error-text", + "400 with different text: pipeline error: Q for series: Qt find closing Q symbol\", app=..." + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 25: syntax error: unexpected ) | proxy missing `| stats ...` pipe in the query [_time:[2026-10-05T17:20:00.000000000Z,2026-10-05T18:20:59.999999999Z] app:=\"api-gateway\"]", + "query": "sort({app=\"api-gateway\"})" + } + ], + "id": "a2d0b3cb", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sort": 2 + }, + "signature": [ + "query_range", + "sort", + "status", + "loki 400 proxy 422: missing Q pipe in the query [_time:[N-N-NtN:N:Nz,N-N-NtN:N:Nz] app:=Q]" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 30: syntax error: unexpected ) | proxy missing `| stats ...` pipe in the query [_time:[2026-10-05T17:20:00.000000000Z,2026-10-05T18:20:59.999999999Z] app:=\"api-gateway\"]", + "query": "sort_desc({app=\"api-gateway\"})" + } + ], + "id": "1e15e3f7", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sort_desc": 2 + }, + "signature": [ + "query_range", + "sort_desc", + "status", + "loki 400 proxy 422: missing Q pipe in the query [_time:[N-N-NtN:N:Nz,N-N-NtN:N:Nz] app:=Q]" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"app\": \"batch-etl\"} @1791221580.0: loki 116.45533526397855 proxy 881", + "query": "stdvar by(app)(count_over_time({env=\"production\"}[5m]))" + } + ], + "id": "44bf1425", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "stdvar": 2 + }, + "signature": [ + "query_range", + "stdvar", + "values", + "values differ (proxy higher)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but ca...", + "query": "sum by (level) (count_over_time({namespace=\"prod\"}|json|level!=\"\"[2m]))" + } + ], + "id": "9e2e6496", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,label-filter]": 2 + }, + "signature": [ + "query_range", + "sum[json,label-filter]", + "error-text", + "400 with different text: pipeline error: Q for series: Qt find closing Q symbol\", app=..." + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 0, col 78: missing closing ']' in duration | proxy parse error : logql: expected duration: logql: expected DURATION, got EOF (\"\")", + "query": "sum by (level, detected_level) (count_over_time({app=~\".+\"} | drop __error__ [" + } + ], + "id": "db738b79", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[drop]": 2 + }, + "signature": [ + "query_range", + "sum[drop]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got eof (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error : stage '|~ \"a{2,1}\"' : error parsing regexp: invalid repeat count: `{2,1}` | proxy cannot parse query arg [\u2026]: invalid regexp \"_msg\":\"a{\u2026}\": error parsing regexp: invalid repeat count: `{\u2026}`", + "query": "sum(count_over_time({app=\"api-gateway\"} |~ \"a{2,1}\" [1m]))" + } + ], + "id": "fd0ad64a", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[line-filter]": 2 + }, + "signature": [ + "query_range", + "sum[line-filter]", + "error-text", + "400 with different text: cannot parse query arg [\u2026]: invalid regexp Q:Q: error parsing regexp: invalid re" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 82: syntax error: unexpected unwrap, expecting IDENTIFIER or ( | proxy parse error : syntax error: unexpected unwrap", + "query": "sum_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms | unwrap status [5m])" + } + ], + "id": "ce4dd73f", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum_over_time[json,unwrap]": 2 + }, + "signature": [ + "query_range", + "sum_over_time[json,unwrap]", + "error-text", + "400 with different text: parse error : syntax error: unexpected unwrap" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 18823 series, proxy 775", + "query": "sum_over_time({app=\"payment-service\",env=\"production\"} | logfmt | unwrap latency_ms [5m])" + } + ], + "id": "cd477d03", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum_over_time[logfmt,unwrap]": 2 + }, + "signature": [ + "query_range", + "sum_over_time[logfmt,unwrap]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 3 series, proxy 58", + "query": "topk(3, avg by(app)(rate({env=\"production\"}[5m])))" + } + ], + "id": "8bc2e155", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "topk": 2 + }, + "signature": [ + "query_range", + "topk", + "series", + "proxy-more" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy manual metric response exceeds 67108864 bytes; narrow the query or increase -backend-max-buffered-response-bytes", + "query": "topk(3, rate({cluster=\"us-east-1\"} | json [5m]))" + } + ], + "id": "44fa34f9", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "topk[json]": 2 + }, + "signature": [ + "query_range", + "topk[json]", + "status", + "loki 400 proxy 502: manual metric response exceeds N bytes; narrow the query or increase -backend-ma" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 28: syntax error: unexpected ) | proxy missing `| stats ...` pipe in the query [_time:[2026-10-05T17:20:00.000000000Z,2026-10-05T18:20:59.999999999Z] app:=\"api-gateway\"]", + "query": "topk(5, {app=\"api-gateway\"})" + } + ], + "id": "337a1e15", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "topk": 2 + }, + "signature": [ + "query_range", + "topk", + "status", + "loki 400 proxy 422: missing Q pipe in the query [_time:[N-N-NtN:N:Nz,N-N-NtN:N:Nz] app:=Q]" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{app=\"api-gateway\",env=\"production\"} | decolorize | json" + } + ], + "id": "fbece7b0", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,decolorize]": 2 + }, + "signature": [ + "query_range", + "logs[json,decolorize]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"4564\", \"env\": \"production\", \"error\": \"gateway timeout\", \"ip\": \"10.5.68.203\", \"level\": \"...", + "query": "{app=\"api-gateway\",env=\"production\"} | decolorize | json | status>=200" + } + ], + "id": "b680829d", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,decolorize,label-filter]": 2 + }, + "signature": [ + "query_range", + "logs[json,decolorize,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 56: syntax error: unexpected IDENTIFIER | proxy parse error : syntax error: unexpected IDENT", + "query": "{app=\"api-gateway\",env=\"production\"} | json | distinct method" + } + ], + "id": "47be3455", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json]": 2 + }, + "signature": [ + "query_range", + "logs[json]", + "error-text", + "400 with different text: parse error : syntax error: unexpected ident" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name']; e.g. proxy {\"method\": \"GET\", \"path\": \"/api/v1/users\", \"service_name\": \"api-gateway\", \"status\": \"404\"}", + "query": "{app=\"api-gateway\",env=\"production\"} | json | drop trace_id | keep method, path, status" + } + ], + "id": "3e6bb855", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,keep,drop]": 2 + }, + "signature": [ + "query_range", + "logs[json,keep,drop]", + "labels", + "proxy-only service_name" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name']; e.g. proxy {\"method\": \"GET\", \"service_name\": \"api-gateway\", \"status\": \"200\"}", + "query": "{app=\"api-gateway\",env=\"production\"} | json | method=\"GET\" | status>=200 | status<400 | drop trace_id | keep method, status" + } + ], + "id": "7af7ff7b", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,keep,drop,label-filter]": 2 + }, + "signature": [ + "query_range", + "logs[json,keep,drop,label-filter]", + "labels", + "proxy-only service_name" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted', 'version_extracted']; e.g. loki {\"app\": \"api-gateway\", \"cluster\": \"us-east-1\", \"container\": \"api\", \"detected_level\": \"error\", \"duration_ms\": \"147\", \"env\": \"production\", \"error\": \"rate limit exceeded\", \"ip\": \"10.5.222.31\", \"level\"...", + "query": "{app=\"api-gateway\",env=\"production\"} | json | path=~\"/api/.*\" | status>200 | drop trace_id" + } + ], + "id": "5b2ec5e6", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,drop,label-filter]": 2 + }, + "signature": [ + "query_range", + "logs[json,drop,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki-only {\"service\": {\"name\": \"api-gateway\"}, \"method\": \"PATCH\", \"path\": \"/api/v2/recommendations\", \"status\": 500, \"duration_m...", + "query": "{app=\"api-gateway\",env=\"production\"} | json | status != 200 | status != 404" + } + ], + "id": "5da321bc", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,label-filter]": 2 + }, + "signature": [ + "query_range", + "logs[json,label-filter]", + "entries", + "different entries, same count" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 30: syntax error: unexpected ip | proxy parse error : logql: unexpected token \")\" at position 51", + "query": "{app=\"api-gateway\"} | json | ip(\"not-a-valid-cidr\")" + } + ], + "id": "31ae1824", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json]": 2 + }, + "signature": [ + "query_range", + "logs[json]", + "error-text", + "400 with different text: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 34: syntax error: unexpected $end, expecting STRING | proxy parse error : logql: expected STRING or RAWSTRING, got EOF (\"\")", + "query": "{app=\"api-gateway\"} | line_format" + } + ], + "id": "a5b943b2", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[line_format]": 2 + }, + "signature": [ + "query_range", + "logs[line_format]", + "error-text", + "400 with different text: parse error : logql: expected string or rawstring, got eof (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 23: syntax error: unexpected IDENTIFIER | proxy parse error : syntax error: unexpected IDENT", + "query": "{app=\"nginx\"} | stats invalid_function()" + } + ], + "id": "5729447e", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs": 2 + }, + "signature": [ + "query_range", + "logs", + "error-text", + "400 with different text: parse error : syntax error: unexpected ident" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name']; e.g. proxy {\"level\": \"error\", \"msg\": \"payment failed\", \"service_name\": \"payment-service\"}", + "query": "{app=\"payment-service\",env=\"production\"} | logfmt | level=\"error\" | keep level, msg" + } + ], + "id": "f056672a", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[logfmt,keep,label-filter]": 2 + }, + "signature": [ + "query_range", + "logs[logfmt,keep,label-filter]", + "labels", + "proxy-only service_name" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['level_extracted', 'service_name_extracted']; e.g. loki {\"app\": \"notification-svc\", \"cluster\": \"us-east-1\", \"container\": \"notification\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"duration_ms\": \"66\", \"env\": \"production\", \"instrum...", + "query": "{cluster=\"us-east-1\"} | detected_level=\"error\" or detected_level=\"info\" or detected_level=\"warn\" | json | logfmt | drop __error__, __error_details__" + } + ], + "id": "ef144320", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,logfmt,drop,detected_level,label-filter]": 2 + }, + "signature": [ + "query_range", + "logs[json,logfmt,drop,detected_level,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 19: syntax error: unexpected NUMBER, expecting STRING or ip | proxy parse error : logql: expected STRING or RAWSTRING, got NUMBER (\"2\")", + "query": "{level=\"info\"} != 2" + } + ], + "id": "79e63a4f", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs": 2 + }, + "signature": [ + "query_range", + "logs", + "error-text", + "400 with different text: parse error : logql: expected string or rawstring, got number (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "bytes_over_time({app=\"api-gateway\",env=\"production\"} | json | status >= 500 [5m])" + } + ], + "id": "0b0f1379", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_over_time[json,label-filter]": 2 + }, + "signature": [ + "query_range", + "bytes_over_time[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "bytes_rate({app=\"api-gateway\",env=\"production\"} | json | status >= 500 [5m])" + } + ], + "id": "903323d1", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_rate[json,label-filter]": 2 + }, + "signature": [ + "query_range", + "bytes_rate[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 45: syntax error: unexpected ) | proxy missing `| stats ...` pipe in the query [_time:[2026-10-05T17:20:00.000000000Z,2026-10-05T18:20:59.999999999Z] app:=\"api-gateway\" env:=production]", + "query": "topk(2, {app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "a57ca4cc", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "topk": 2 + }, + "signature": [ + "query_range", + "topk", + "status", + "loki 400 proxy 422: missing Q pipe in the query [_time:[N-N-NtN:N:Nz,N-N-NtN:N:Nz] app:=Q env:=produ" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 42: syntax error: unexpected ) | proxy missing `| stats ...` pipe in the query [_time:[2026-10-05T17:20:00.000000000Z,2026-10-05T18:20:59.999999999Z] app:=\"api-gateway\" env:=production]", + "query": "sort({app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "c30c82d7", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sort": 2 + }, + "signature": [ + "query_range", + "sort", + "status", + "loki 400 proxy 422: missing Q pipe in the query [_time:[N-N-NtN:N:Nz,N-N-NtN:N:Nz] app:=Q env:=produ" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 48: syntax error: unexpected ) | proxy missing `| stats ...` pipe in the query [_time:[2026-10-05T17:20:00.000000000Z,2026-10-05T18:20:59.999999999Z] app:=\"api-gateway\" env:=production]", + "query": "bottomk(1, {app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "44e0a473", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "bottomk": 2 + }, + "signature": [ + "query_range", + "bottomk", + "status", + "loki 400 proxy 422: missing Q pipe in the query [_time:[N-N-NtN:N:Nz,N-N-NtN:N:Nz] app:=Q env:=produ" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 47: syntax error: unexpected ) | proxy missing `| stats ...` pipe in the query [_time:[2026-10-05T17:20:00.000000000Z,2026-10-05T18:20:59.999999999Z] app:=\"api-gateway\" env:=production]", + "query": "sort_desc({app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "b7b8e6bb", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sort_desc": 2 + }, + "signature": [ + "query_range", + "sort_desc", + "status", + "loki 400 proxy 422: missing Q pipe in the query [_time:[N-N-NtN:N:Nz,N-N-NtN:N:Nz] app:=Q env:=produ" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki parse error at line 1, col 46: syntax error: unexpected $end | proxy parse error at line 1, col 1: syntax error: unexpected |", + "query": "{app=\"api-gateway\",env=\"production\"} | json |" + } + ], + "id": "ce1fc758", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json]": 2 + }, + "signature": [ + "query_range", + "logs[json]", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected |" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"level\": \"info\"} @1791221580.0: loki 2976 proxy 2968", + "query": "sum by (level) (count_over_time({env=\"production\"} | logfmt | level=`info` | drop __error__ [1m]))" + } + ], + "id": "d650bcfb", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,drop,label-filter]": 2 + }, + "signature": [ + "query_range", + "sum[logfmt,drop,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"detected_level\": \"info\", \"level\": \"info\"} @1791221580.0: loki 2976 proxy 2968", + "query": "sum by (level, detected_level) (count_over_time({env=\"production\"} | detected_level=\"info\" | json | drop __error__ [1m]))" + } + ], + "id": "44bcb9d1", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,drop,detected_level,label-filter]": 2 + }, + "signature": [ + "query_range", + "sum[json,drop,detected_level,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 22279 series, proxy 18956; loki-only [\"{\\\"job_id\\\": \\\"job_0007f479\\\"}\", \"{\\\"job_id\\\": \\\"job_002519df\\\"}\"] proxy-only []", + "query": "sum by (job_id) (count_over_time({env=\"production\"} | detected_level=\"info\" | logfmt | job_id!=\"\" [1m]))" + } + ], + "id": "80fabfe0", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,detected_level,label-filter]": 2 + }, + "signature": [ + "query_range", + "sum[logfmt,detected_level,label-filter]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{} @1791221580.0: loki 142961 proxy 142616", + "query": "sum(count_over_time({env=\"production\"} | detected_level=\"info\" | json | logfmt | drop __error__, __error_details__ [1h]))" + } + ], + "id": "337f7f5e", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,logfmt,drop,detected_level,label-filter]": 2 + }, + "signature": [ + "query_range", + "sum[json,logfmt,drop,detected_level,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "names ['service_name_extracted']; e.g. loki {\"app\": \"notification-svc\", \"cluster\": \"us-east-1\", \"container\": \"notification\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"env\": \"production\", \"instrumentation\": \"otel\", \"k...", + "query": "{env=\"production\"} | regexp \"(?PGET|POST|PUT|DELETE)\"" + } + ], + "id": "8cc9e97a", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[regexp]": 2 + }, + "signature": [ + "query_range", + "logs[regexp]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "{\"lvl\": \"error\"} @1791221580.0: loki 149 proxy 182", + "query": "sum by (lvl) (count_over_time({env=\"production\"} | logfmt | label_format lvl=level [1m]))" + } + ], + "id": "d4b04b0c", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,label_format]": 2 + }, + "signature": [ + "query_range", + "sum[logfmt,label_format]", + "values", + "values differ (same total)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query_range", + "excerpt": "loki 6850 series, proxy 55100", + "query": "count_over_time({app=\"api-gateway\"} | regexp \"(?PGET|POST|PUT)\" [5m])" + } + ], + "id": "952bbb24", + "impact": "explore-visible", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[regexp]": 2 + }, + "signature": [ + "query_range", + "count_over_time[regexp]", + "series", + "proxy-more" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "label_values", + "excerpt": "loki-only ['api-gateway-006d1-de0e', 'api-gateway-009d9-3bd4', 'api-gateway-009e0-ced6', 'api-gateway-00b51-f84e', 'api-gateway-00ef4-8f7f'] proxy-only []", + "query": "values of pod" + } + ], + "id": "185c0010", + "impact": "explore-visible", + "queries": 1, + "requests": 1, + "shapes": { + "-": 1 + }, + "signature": [ + "label_values", + "-", + "values-set", + "proxy-fewer" + ], + "sources": { + "seeded": 1 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "label_values", + "excerpt": "loki-only ['api-gateway-006d1-de0e', 'api-gateway-009d9-3bd4', 'api-gateway-009e0-ced6', 'api-gateway-00b51-f84e', 'api-gateway-00ef4-8f7f'] proxy-only []", + "query": "{env=\"production\"}" + } + ], + "id": "76d3a547", + "impact": "explore-visible", + "queries": 1, + "requests": 1, + "shapes": { + "logs": 1 + }, + "signature": [ + "label_values", + "logs", + "values-set", + "proxy-fewer" + ], + "sources": { + "seeded": 1 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "query_range", + "excerpt": "{\"app\": \"pricing-svc\"}: loki 61 points, proxy 60", + "query": "avg by(app)(avg_over_time({env=\"production\"} | json | unwrap duration_ms [5m]))" + } + ], + "id": "4d52c5cb", + "impact": "explore-visible", + "queries": 1, + "requests": 1, + "shapes": { + "avg[json,unwrap]": 1 + }, + "signature": [ + "query_range", + "avg[json,unwrap]", + "values", + "values differ (proxy higher)" + ], + "sources": { + "repo": 1 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "volume", + "excerpt": "loki 0 series, proxy 4", + "query": "{env=\"production\"}" + }, + { + "encoding": "plain", + "endpoint": "volume", + "excerpt": "loki 0 series, proxy 3", + "query": "{service_name=\"api-gateway\"}" + } + ], + "id": "b126c04b", + "impact": "drilldown-visible", + "queries": 16, + "requests": 16, + "shapes": { + "logs": 16 + }, + "signature": [ + "volume", + "logs", + "series", + "loki-empty" + ], + "sources": { + "seeded": 16 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "detected_field_values", + "excerpt": "loki-only ['api-gateway'] proxy-only []", + "query": "{service_name=\"api-gateway\"}" + }, + { + "encoding": "plain", + "endpoint": "detected_field_values", + "excerpt": "loki-only ['auth-service'] proxy-only []", + "query": "{service_name=\"auth-service\"}" + } + ], + "id": "4a75b280", + "impact": "drilldown-visible", + "queries": 13, + "requests": 24, + "shapes": { + "logs": 24 + }, + "signature": [ + "detected_field_values", + "logs", + "values-set", + "proxy-empty" + ], + "sources": { + "seeded": 24 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "detected_fields", + "excerpt": "['level_extracted', 'version_extracted']", + "query": "{service_name=\"api-gateway\"}" + }, + { + "encoding": "plain", + "endpoint": "detected_fields", + "excerpt": "['level_extracted']", + "query": "{service_name=\"auth-service\"}" + } + ], + "id": "37eca1b4", + "impact": "drilldown-visible", + "queries": 13, + "requests": 13, + "shapes": { + "logs": 13 + }, + "signature": [ + "detected_fields", + "logs", + "field", + "loki-only *_extracted" + ], + "sources": { + "seeded": 13 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "detected_field_values", + "excerpt": "loki-only ['102', '108', '110', '112', '117'] proxy-only ['105', '107', '109', '1125', '116']", + "query": "{service_name=\"api-gateway\"}" + }, + { + "encoding": "plain", + "endpoint": "detected_field_values", + "excerpt": "loki-only [] proxy-only ['wrong_password']", + "query": "{service_name=\"auth-service\"}" + } + ], + "id": "92699938", + "impact": "drilldown-visible", + "queries": 10, + "requests": 38, + "shapes": { + "logs": 38 + }, + "signature": [ + "detected_field_values", + "logs", + "values-set", + "proxy-more" + ], + "sources": { + "seeded": 38 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "detected_field_values", + "excerpt": "loki-only ['0190b86a5e83d908', '03eb081958e57172', '08deb9ba8897674f', '0adb741ac3ce9204', '0e216083312d699c'] proxy-only ['02ca40cab24ceeb4', '039a4167447f6545', '052b300284b3b4e7', '07b8550bc3e25b6d', '07fe9d241ef6e002']", + "query": "{service_name=\"api-gateway\"}" + }, + { + "encoding": "plain", + "endpoint": "detected_field_values", + "excerpt": "loki-only ['005329445e0f', '03b1f9730840', '04c31ee2f98f', '07a1389add2b', '0db8de3cbc4d'] proxy-only ['0117dabde421', '02cf39dfecea', '06be02c13b9d', '06f5e290c989', '10d6cca57afa']", + "query": "{service_name=\"auth-service\"}" + } + ], + "id": "0e9d9652", + "impact": "drilldown-visible", + "queries": 10, + "requests": 23, + "shapes": { + "logs": 23 + }, + "signature": [ + "detected_field_values", + "logs", + "values-set", + "different values" + ], + "sources": { + "seeded": 23 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "patterns", + "excerpt": "loki 0 proxy 2; loki-only []", + "query": "{service_name=\"batch-etl\"}" + }, + { + "encoding": "plain", + "endpoint": "patterns", + "excerpt": "loki 0 proxy 3; loki-only []", + "query": "{service_name=\"checkout-svc\"}" + } + ], + "id": "556a81b7", + "impact": "drilldown-visible", + "queries": 10, + "requests": 10, + "shapes": { + "logs": 10 + }, + "signature": [ + "patterns", + "logs", + "patterns", + "loki-empty" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "index_stats", + "excerpt": "loki 7104 proxy 13510 (loki/proxy 0.53)", + "query": "{env=\"production\"}" + }, + { + "encoding": "plain", + "endpoint": "index_stats", + "excerpt": "loki 2990 proxy 1992 (loki/proxy 1.50)", + "query": "{service_name=\"api-gateway\"}" + } + ], + "id": "aa98afb1", + "impact": "drilldown-visible", + "queries": 4, + "requests": 4, + "shapes": { + "logs": 4 + }, + "signature": [ + "index_stats", + "logs", + "stats", + "streams differ" + ], + "sources": { + "seeded": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "patterns", + "excerpt": "loki 11 proxy 4; loki-only [\"level=debug msg=\\\"cache expire\\\" key=<_> ttl=<_> reason=ttl_reset\", \"level=debug msg=\\\"cache hit\\\" key=<_> ttl=<_> size_bytes=<_> op=<_>\"]", + "query": "{service_name=\"cache-redis\"}" + }, + { + "encoding": "plain", + "endpoint": "patterns", + "excerpt": "loki 26 proxy 4; loki-only [\"level=info msg=\\\"payment processed\\\" tx_id=<_> amount=<_> currency=CAD provider=adyen latency_ms=<_> user_id=<_>\", \"level=info msg=\\\"payment processed\\\" tx...", + "query": "{service_name=\"payment-service\"}" + } + ], + "id": "2c8e8dca", + "impact": "drilldown-visible", + "queries": 3, + "requests": 3, + "shapes": { + "logs": 3 + }, + "signature": [ + "patterns", + "logs", + "patterns", + "proxy-fewer" + ], + "sources": { + "seeded": 3 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "patterns", + "excerpt": "loki 0 proxy 6; loki-only []", + "query": "patterns?end=2026-10-05T18%3A20%3A00.000Z&query=%7Bservice_name%3D%22otel-collector%22%7D&start=2026-10-05T18%3A05%3A00.000Z&step=2s" + }, + { + "encoding": "plain", + "endpoint": "patterns", + "excerpt": "loki 0 proxy 5; loki-only []", + "query": "patterns?end=2026-10-05T18%3A20%3A00.000Z&query=%7Bservice_name%3D%22otel-collector%22%7D&start=2026-10-05T17%3A20%3A00.000Z&step=5s" + } + ], + "id": "7c1a4612", + "impact": "drilldown-visible", + "queries": 2, + "requests": 2, + "shapes": { + "-": 2 + }, + "signature": [ + "patterns", + "-", + "patterns", + "loki-empty" + ], + "sources": { + "grafana": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "detected_fields", + "excerpt": "['level_extracted']", + "query": "detected_fields?end=2026-10-05T18%3A20%3A00.000Z&query=%7Bservice_name%3D%22otel-collector%22%7D%20%20%20%20%20%7C%20json%20%20%7C%20logfmt%20%7C%20drop%20__error__%2C%20__error_details__%20&start=2026-10-05T18%3A05%3A00.000Z" + }, + { + "encoding": "plain", + "endpoint": "detected_fields", + "excerpt": "['level_extracted']", + "query": "detected_fields?end=2026-10-05T18%3A20%3A00.000Z&query=%7Bservice_name%3D%22otel-collector%22%7D%20%20%20%20%20%7C%20json%20%20%7C%20logfmt%20%7C%20drop%20__error__%2C%20__error_details__%20&start=2026-10-05T17%3A20%3A00.000Z" + } + ], + "id": "f8fa9aa2", + "impact": "drilldown-visible", + "queries": 2, + "requests": 2, + "shapes": { + "-": 2 + }, + "signature": [ + "detected_fields", + "-", + "field", + "loki-only *_extracted" + ], + "sources": { + "grafana": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "index_stats", + "excerpt": "loki 7104 proxy 3365 (loki/proxy 2.11)", + "query": "index/stats?query=%7Benv%3D%22production%22%7D&start=1791223500000000000&end=1791224400000000000" + }, + { + "encoding": "plain", + "endpoint": "index_stats", + "excerpt": "loki 7104 proxy 13510 (loki/proxy 0.53)", + "query": "index/stats?query=%7Benv%3D%22production%22%7D&start=1791220800000000000&end=1791224400000000000" + } + ], + "id": "2a68e26c", + "impact": "drilldown-visible", + "queries": 2, + "requests": 2, + "shapes": { + "-": 2 + }, + "signature": [ + "index_stats", + "-", + "stats", + "streams differ" + ], + "sources": { + "grafana": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "ds_query", + "excerpt": "names ['level_extracted', 'service_name_extracted']; e.g. loki {\"app\": \"otel-collector\", \"cluster\": \"us-east-1\", \"component\": \"collector\", \"container\": \"otel\", \"deployment_environment\": \"production\", \"detected_level\": \"info\", \"dropped\": \"0\", \"env\": \"production...", + "query": "{service_name=\"otel-collector\"} | json | logfmt | drop __error__, __error_details__ " + } + ], + "id": "6d79474c", + "impact": "drilldown-visible", + "queries": 1, + "requests": 2, + "shapes": { + "logs[json,logfmt,drop]": 2 + }, + "signature": [ + "grafana:ds_query", + "logs[json,logfmt,drop]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "grafana": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "ds_query", + "excerpt": "loki 1 series, proxy 0", + "query": "sum by (service_name_extracted) (count_over_time({service_name=\"otel-collector\"} | service_name_extracted!=\"\" [$__auto]))" + } + ], + "id": "b3a784df", + "impact": "drilldown-visible", + "queries": 1, + "requests": 2, + "shapes": { + "sum[label-filter]": 2 + }, + "signature": [ + "grafana:ds_query", + "sum[label-filter]", + "series", + "proxy-empty" + ], + "sources": { + "grafana": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "volume_range", + "excerpt": "loki 0 series, proxy 4", + "query": "{env=\"production\"}" + } + ], + "id": "5e3fbf46", + "impact": "drilldown-visible", + "queries": 1, + "requests": 1, + "shapes": { + "logs": 1 + }, + "signature": [ + "volume_range", + "logs", + "series", + "loki-empty" + ], + "sources": { + "seeded": 1 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "plain", + "endpoint": "volume_range", + "excerpt": "loki 6000 series, proxy 6000; loki-only [\"{\\\"pod\\\": \\\"checkout-svc-012b5-80c0\\\"}\", \"{\\\"pod\\\": \\\"checkout-svc-039e7-80f3\\\"}\"] proxy-only [\"{\\\"pod\\\": \\\"api-gateway-0277d-9793\\\"}\", \"{\\\"pod\\\": \\\"api-gateway-028a8-8a99\\\"}\"]", + "query": "{env=\"production\"}" + } + ], + "id": "b88d1f5c", + "impact": "drilldown-visible", + "queries": 1, + "requests": 1, + "shapes": { + "logs": 1 + }, + "signature": [ + "volume_range", + "logs", + "series", + "different series, same count" + ], + "sources": { + "seeded": 1 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "avg_over_time({service_name=\"api-gateway\"} | json | unwrap status | __error__=\"\" [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "avg_over_time({service_name=\"api-gateway\"} | json | unwrap duration_ms | __error__=\"\" [5m])" + } + ], + "id": "15548dbd", + "impact": "grafana-datasource", + "queries": 23, + "requests": 46, + "shapes": { + "avg_over_time[json,unwrap,label-filter]": 46 + }, + "signature": [ + "query", + "avg_over_time[json,unwrap,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2, + "seeded": 44 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"level\": \"error\"} @1791224400.0: loki 82683 proxy 503", + "query": "sum by (level) (max_over_time({service_name=\"api-gateway\"} | json | unwrap status | __error__=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"level\": \"error\"} @1791224400.0: loki 395305 proxy 4996", + "query": "sum by (level) (max_over_time({service_name=\"api-gateway\"} | json | unwrap duration_ms | __error__=\"\" [5m]))" + } + ], + "id": "891109a9", + "impact": "grafana-datasource", + "queries": 22, + "requests": 44, + "shapes": { + "sum[json,unwrap,label-filter]": 44 + }, + "signature": [ + "query", + "sum[json,unwrap,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "seeded": 44 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 49657 series, proxy 10155", + "query": "rate({namespace=\"prod\"}[1h])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 992 series, proxy 171", + "query": "rate({namespace=\"prod\"}[1m])" + } + ], + "id": "2cd52d1c", + "impact": "grafana-datasource", + "queries": 18, + "requests": 38, + "shapes": { + "rate": 38 + }, + "signature": [ + "query", + "rate", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 34, + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 9971 series, proxy 2024", + "query": "count_over_time({namespace=\"prod\"}[12m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 9971 series, proxy 2024", + "query": "count_over_time({namespace=\"prod\"}[12m]) or count_over_time({namespace=\"prod\"}[12m]) > 1" + } + ], + "id": "b8f96844", + "impact": "grafana-datasource", + "queries": 16, + "requests": 34, + "shapes": { + "count_over_time": 34 + }, + "signature": [ + "query", + "count_over_time", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 26, + "repo": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 2646 proxy 500", + "query": "count by (baz) (bytes_over_time({namespace=\"prod\"}[3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 2646 proxy 500", + "query": "count by (baz) (bytes_rate({namespace=\"prod\"}[3m]))" + } + ], + "id": "7af0e9f0", + "impact": "grafana-datasource", + "queries": 13, + "requests": 26, + "shapes": { + "count": 26 + }, + "signature": [ + "query", + "count", + "values", + "values differ (proxy lower)" + ], + "sources": { + "loki-tests": 20, + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count by (baz) (max_over_time({namespace=\"prod\"} | unwrap bar [3m]) by (baz))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count by (baz) (max_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + } + ], + "id": "a5c073e4", + "impact": "grafana-datasource", + "queries": 10, + "requests": 20, + "shapes": { + "count[unwrap]": 20 + }, + "signature": [ + "query", + "count[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 20 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted']", + "query": "avg_over_time({service_name=\"cache-redis\"} | logfmt | unwrap size_bytes | __error__=\"\" [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted']", + "query": "avg_over_time({service_name=\"payment-service\"} | logfmt | unwrap latency_ms | __error__=\"\" [5m])" + } + ], + "id": "d051cfff", + "impact": "grafana-datasource", + "queries": 10, + "requests": 20, + "shapes": { + "avg_over_time[logfmt,unwrap,label-filter]": 20 + }, + "signature": [ + "query", + "avg_over_time[logfmt,unwrap,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 20 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 0", + "query": "sum by (service_name_extracted) (count_over_time({service_name=\"checkout-svc\"} | service_name_extracted!=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 0; loki-only [\"{}\"] proxy-only []", + "query": "sum(count_over_time({service_name=\"checkout-svc\"} | service_name_extracted!=\"\" [5m]))" + } + ], + "id": "265b2418", + "impact": "grafana-datasource", + "queries": 10, + "requests": 20, + "shapes": { + "sum[label-filter]": 20 + }, + "signature": [ + "query", + "sum[label-filter]", + "series", + "proxy-empty" + ], + "sources": { + "seeded": 20 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 9971 series, proxy 2024", + "query": "bytes_over_time({namespace=\"prod\"}[12m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 992 series, proxy 171", + "query": "bytes_over_time({namespace=\"prod\"}[1m])" + } + ], + "id": "b65ce096", + "impact": "grafana-datasource", + "queries": 9, + "requests": 18, + "shapes": { + "bytes_over_time": 18 + }, + "signature": [ + "query", + "bytes_over_time", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 16, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 2 series, proxy 1", + "query": "sum by (cluster) (sum by (cluster) (rate({namespace=\"prod\"} [5m])) + ignoring(machine) sum by (cluster,machine) (rate({namespace=\"prod\"} [5m])))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 4154 series, proxy 840", + "query": "sum without () (rate({namespace=\"prod\"}[5m]))" + } + ], + "id": "8fef8a78", + "impact": "grafana-datasource", + "queries": 9, + "requests": 18, + "shapes": { + "sum": 18 + }, + "signature": [ + "query", + "sum", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 12, + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "max by (baz) (max_over_time({namespace=\"prod\"} | unwrap bar [3m]) by (baz))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "max by (baz) (max_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + } + ], + "id": "ee497e5b", + "impact": "grafana-datasource", + "queries": 8, + "requests": 16, + "shapes": { + "max[unwrap]": 16 + }, + "signature": [ + "query", + "max[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 16 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "min by (baz) (max_over_time({namespace=\"prod\"} | unwrap bar [3m]) by (baz))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "min by (baz) (max_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + } + ], + "id": "0b6060ea", + "impact": "grafana-datasource", + "queries": 8, + "requests": 16, + "shapes": { + "min[unwrap]": 16 + }, + "signature": [ + "query", + "min[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 16 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by (baz) (sum_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum(avg_over_time({namespace=\"prod\"} | unwrap bar[3m]))" + } + ], + "id": "7b61ac93", + "impact": "grafana-datasource", + "queries": 8, + "requests": 16, + "shapes": { + "sum[unwrap]": 16 + }, + "signature": [ + "query", + "sum[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 16 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 2 series, proxy 0", + "query": "sum by (level_extracted) (count_over_time({service_name=\"cache-redis\"} | logfmt | level_extracted!=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 0; loki-only [\"{}\"] proxy-only []", + "query": "sum(count_over_time({service_name=\"cache-redis\"} | logfmt | level_extracted!=\"\" [5m]))" + } + ], + "id": "ce14ac99", + "impact": "grafana-datasource", + "queries": 6, + "requests": 12, + "shapes": { + "sum[logfmt,label-filter]": 12 + }, + "signature": [ + "query", + "sum[logfmt,label-filter]", + "series", + "proxy-empty" + ], + "sources": { + "seeded": 12 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "absent_over_time( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "absent_over_time( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] offset 10d )" + } + ], + "id": "0e5f1b8d", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "absent_over_time[line-filter]": 10 + }, + "signature": [ + "query", + "absent_over_time[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count by (baz) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]) by (baz))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "count(max_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]) by (baz))" + } + ], + "id": "33e032b9", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "count[logfmt,unwrap]": 10 + }, + "signature": [ + "query", + "count[logfmt,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 96", + "query": "count by (foo) (bytes_over_time({service_name=\"api-gateway\"} | json [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 96", + "query": "count by (foo) (bytes_rate({service_name=\"api-gateway\"} | json [3m]))" + } + ], + "id": "d13dd24b", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "count[json]": 10 + }, + "signature": [ + "query", + "count[json]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 387 series, proxy 305", + "query": "label_replace( label_replace( rate( {namespace=\"prod\"} |= \"err\" [5m] ), \"foo\", \"$1\", \"service\", \"(.*):.*\" ), \"foo\", \"$1\", \"service\", \"(.*):.*\" )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 387 series, proxy 305", + "query": "label_replace( rate( {namespace=\"prod\"} |= \"err\" [5m] ), \"foo\", \"$1\", \"service\", \"(.*):.*\" )" + } + ], + "id": "4e3297e0", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "label_replace[line-filter]": 10 + }, + "signature": [ + "query", + "label_replace[line-filter]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "500 samples, e.g. {\"app\": \"frontend-ssr\", \"cluster\": \"us-west-2\", \"container\": \"frontend\", \"detected_level\": \"warn\", \"env\": \"production... @1791224400.0: \"\"", + "query": "max_over_time({namespace=\"prod\"} | unwrap bar [3m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "max_over_time({namespace=\"prod\"} | unwrap bar [3m]) by (baz)" + } + ], + "id": "f607c198", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "max_over_time[unwrap]": 10 + }, + "signature": [ + "query", + "max_over_time[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 8, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "sum_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms | unwrap latency | __error__!~\".*\" | foo >5[5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "sum_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms| unwrap bytes(foo) [5m] offset 5m)" + } + ], + "id": "7c27b83b", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "sum_over_time[json,unwrap,label-filter,line-filter]": 10 + }, + "signature": [ + "query", + "sum_over_time[json,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably, the whole string m" + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk(1,rate(({namespace=\"prod\"} |~\".+bar\")[1m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk(1,rate(({namespace=\"prod\"} |~\".+bar\")[1m])) by (app)" + } + ], + "id": "34fd43a7", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "topk[line-filter]": 10 + }, + "signature": [ + "query", + "topk[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "2 samples, e.g. {\"level\": \"debug\"} @1791224400.0: \"86378s\"", + "query": "sum by (level) (max_over_time({service_name=\"cache-redis\"} | logfmt | unwrap ttl | __error__=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {\"level\": \"warn\"} @1791224400.0: \"\"", + "query": "sum by (level) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap amount | __error__=\"\" [5m]))" + } + ], + "id": "b15fbabc", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "sum[logfmt,unwrap,label-filter]": 10 + }, + "signature": [ + "query", + "sum[logfmt,unwrap,label-filter]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['service_name_extracted']", + "query": "sum by (service_name_extracted) (count_over_time({service_name=\"checkout-svc\"} [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['service_name_extracted']", + "query": "sum by (service_name_extracted) (count_over_time({service_name=\"inventory-svc\"} [5m]))" + } + ], + "id": "b2133367", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "sum": 10 + }, + "signature": [ + "query", + "sum", + "labels", + "loki-only *_extracted" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"level\": \"info\"} @1791224400.0: loki 9590165 proxy 64981", + "query": "sum by (level) (max_over_time({service_name=\"db-postgres\"} | logfmt | unwrap port | __error__=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"level\": \"info\"} @1791224400.0: loki 11077670 proxy 498813", + "query": "sum by (level) (max_over_time({service_name=\"db-postgres\"} | logfmt | unwrap distance | __error__=\"\" [5m]))" + } + ], + "id": "0f386e36", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "sum[logfmt,unwrap,label-filter]": 10 + }, + "signature": [ + "query", + "sum[logfmt,unwrap,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "seeded": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['service']", + "query": "label_replace(label_replace(sum by(app)(count_over_time({env=\"production\"}[5m])), \"service\", \"$1\", \"app\", \"(.*)\"), \"short\", \"$1\", \"service\", \"^([^-]+)\")" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['app_short']", + "query": "label_replace(rate({app=\"api-gateway\"}[5m]), \"app_short\", \"$1\", \"app\", \"(.*)-.*\")" + } + ], + "id": "a9d4f85c", + "impact": "grafana-datasource", + "queries": 5, + "requests": 10, + "shapes": { + "label_replace": 10 + }, + "signature": [ + "query", + "label_replace", + "labels", + "loki-only " + ], + "sources": { + "repo": 10 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 2 series, proxy 2; loki-only [\"{\\\"app\\\": \\\"api-gateway\\\", \\\"cluster\\\": \\\"us-east-1\\\", \\\"container\\\": \\\"api\\\", \\\"detected_level\\\": \\\"info\\\", \\\"env\\\": \\\"production\\\", \\\"level\\\": \\\"info\\\", ... proxy-only [\"{\\\"app\\\": \\\"api-gateway\\\", \\\"cluster\\\": \\\"us-east-1\\\", \\\"container\\\": \\\"api\\\", \\\"detected...", + "query": "topk(2, count_over_time({namespace=\"prod\"}[3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 3 series, proxy 3; loki-only [\"{\\\"app\\\": \\\"auth-service\\\"}\"] proxy-only [\"{\\\"app\\\": \\\"ml-serving\\\"}\"]", + "query": "topk(3, sum by (app) (count_over_time({env=\"production\"}[5m])))" + } + ], + "id": "a6109627", + "impact": "grafana-datasource", + "queries": 5, + "requests": 9, + "shapes": { + "topk": 9 + }, + "signature": [ + "query", + "topk", + "series", + "different series, same count" + ], + "sources": { + "loki-tests": 2, + "repo": 7 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 3.8877701029059346 proxy 19.010832102412603", + "query": "avg(count_over_time({namespace=\"prod\"}[1h])) by ()" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 3.8877701029059346 proxy 19.010832102412603", + "query": "avg(count_over_time({namespace=\"prod\"}[1h])) by (bar,foo)" + } + ], + "id": "84eb302d", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "avg": 8 + }, + "signature": [ + "query", + "avg", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 6, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 1180 proxy 54", + "query": "count(bytes_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 1180 proxy 54", + "query": "count(bytes_rate({service_name=\"payment-service\"} | logfmt [3m]))" + } + ], + "id": "1f333942", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "count[logfmt]": 8 + }, + "signature": [ + "query", + "count[logfmt]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "count_over_time({service_name=\"api-gateway\"} | json [3m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "count_over_time({service_name=\"api-gateway\"} | json [5m])" + } + ], + "id": "ac8e75c3", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "count_over_time[json]": 8 + }, + "signature": [ + "query", + "count_over_time[json]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "loki-tests": 4, + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 96", + "query": "max by (baz) (bytes_over_time({service_name=\"api-gateway\"} | json [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 96", + "query": "max by (foo) (bytes_rate({service_name=\"api-gateway\"} | json [3m]))" + } + ], + "id": "864af0b3", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "max[json]": 8 + }, + "signature": [ + "query", + "max[json]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 1 proxy 66", + "query": "max by (baz) (count_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 143 proxy 8186", + "query": "max(bytes_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + } + ], + "id": "370c5620", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "max[logfmt]": 8 + }, + "signature": [ + "query", + "max[logfmt]", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "max by (baz) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "max by (baz) (min_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]))" + } + ], + "id": "1d449423", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "max[logfmt,unwrap]": 8 + }, + "signature": [ + "query", + "max[logfmt,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 49657 series, proxy 1", + "query": "max without () (count_over_time({namespace=\"prod\"}[1h]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 49657 series, proxy 10155", + "query": "max without (bar) (count_over_time({namespace=\"prod\"}[1h]))" + } + ], + "id": "9273ee61", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "max": 8 + }, + "signature": [ + "query", + "max", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 6, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 96", + "query": "min by (baz) (bytes_over_time({service_name=\"api-gateway\"} | json [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 96", + "query": "min by (foo) (bytes_rate({service_name=\"api-gateway\"} | json [3m]))" + } + ], + "id": "86c51db9", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "min[json]": 8 + }, + "signature": [ + "query", + "min[json]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 93 proxy 118", + "query": "min by (baz) (bytes_over_time({namespace=\"prod\"}[3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 0.5166666666666667 proxy 0.6555555555555556", + "query": "min by (baz) (bytes_rate({namespace=\"prod\"}[3m]))" + } + ], + "id": "a274a06b", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "min": 8 + }, + "signature": [ + "query", + "min", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 1 proxy 2", + "query": "min by (baz) (count_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 107 proxy 237", + "query": "min(bytes_over_time({service_name=\"payment-service\"} | logfmt [3m]))" + } + ], + "id": "dcf0f4ed", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "min[logfmt]": 8 + }, + "signature": [ + "query", + "min[logfmt]", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "min by (baz) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "min by (baz) (min_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]))" + } + ], + "id": "dc15e297", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "min[logfmt,unwrap]": 8 + }, + "signature": [ + "query", + "min[logfmt,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "500 samples, e.g. {\"app\": \"frontend-ssr\", \"cluster\": \"us-east-1\", \"container\": \"frontend\", \"detected_level\": \"error\", \"env\": \"productio... @1791224400.0: \"\"", + "query": "min_over_time({namespace=\"prod\"} | unwrap bar [3m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "min_over_time({namespace=\"prod\"} | unwrap bar [3m]) by (baz)" + } + ], + "id": "e924d60f", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "min_over_time[unwrap]": 8 + }, + "signature": [ + "query", + "min_over_time[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({service_name=\"api-gateway\"} | json [3m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\"} | json [1m])" + } + ], + "id": "df222120", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "rate[json]": 8 + }, + "signature": [ + "query", + "rate[json]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "loki-tests": 2, + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "sum by (baz) (max_over_time({namespace=\"prod\"} | unwrap bar [3m]) by (baz))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "sum by (baz) (max_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + } + ], + "id": "78618043", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "sum[unwrap]": 8 + }, + "signature": [ + "query", + "sum[unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "sum by (job) ( sum_over_time( {service_name=\"api-gateway\"} |= \"level=error\" | json | avg=5 and bar<25ms | unwrap duration(latency) [5m] ) / count_over_time({service_name=\"api-gateway\"} | logfmt | label_format foo=bar[5m]) )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "sum by (job) ( sum_over_time( {service_name=\"api-gateway\"} |= \"level=error\" | json | avg=5 and bar<25ms | unwrap duration(latency) | __error__!~\".*\" [5m] ) / count_over_time({service_name=\"api-gateway\"} | logfmt | label_format foo=bar[5m]) )" + } + ], + "id": "361b1ce4", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "sum[json,logfmt,label_format,unwrap,label-filter,line-filter]": 8 + }, + "signature": [ + "query", + "sum[json,logfmt,label_format,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: log queries are not supported as an instant query type, please change your query" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by (method) (sum_over_time({service_name=\"payment-service\"} | logfmt | unwrap duration(latency) [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum(max_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]) by (baz))" + } + ], + "id": "69766a56", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "sum[logfmt,unwrap]": 8 + }, + "signature": [ + "query", + "sum[logfmt,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sum(bytes_rate(({namespace=\"prod\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")[5m])) by (foo)" + } + ], + "id": "68fcec74", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "sum[line-filter]": 8 + }, + "signature": [ + "query", + "sum[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "count_over_time({app=\"api-gateway\",env=\"production\"} | json | status>=200 and status<300 [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "count_over_time({app=\"api-gateway\"} | json | method=\"GET\" | status >= 200 [5m])" + } + ], + "id": "6188db87", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "count_over_time[json,label-filter]": 8 + }, + "signature": [ + "query", + "count_over_time[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "quantile_over_time(0.95, {app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "quantile_over_time(0.99, {app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "29ad1639", + "impact": "grafana-datasource", + "queries": 4, + "requests": 8, + "shapes": { + "quantile_over_time[json,unwrap]": 8 + }, + "signature": [ + "query", + "quantile_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 8 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 9971 series, proxy 2024", + "query": "bytes_rate({namespace=\"prod\"}[12m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 496 series, proxy 86", + "query": "bytes_rate({namespace=\"prod\"}[30s])" + } + ], + "id": "6d606bf1", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "bytes_rate": 6 + }, + "signature": [ + "query", + "bytes_rate", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 446 series, proxy 18", + "query": "count_over_time( {service_name=\"payment-service\"} | logfmt [1m] )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 446 series, proxy 18", + "query": "count_over_time({service_name=\"payment-service\"}|logfmt[1m])" + } + ], + "id": "c0f15837", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "count_over_time[logfmt]": 6 + }, + "signature": [ + "query", + "count_over_time[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 4, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 366 series, proxy 307", + "query": "count_over_time( {namespace=\"prod\"} |= \"error\" [5m] offset 20m )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 4754 series, proxy 3717", + "query": "count_over_time({namespace=\"prod\"} |= \"error\" [1h])" + } + ], + "id": "eb31f562", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "count_over_time[line-filter]": 6 + }, + "signature": [ + "query", + "count_over_time[line-filter]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 0, col 24: unknown unit \"minutes\" in duration \"5minutes\" | proxy -", + "query": "rate({namespace=\"prod\"}[5minutes])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 30: syntax error: unexpected IDENTIFIER, expecting ) | proxy -", + "query": "rate({app=\"api-gateway\"}[5m] @ 1000000000)" + } + ], + "id": "16428cd1", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "rate": 6 + }, + "signature": [ + "query", + "rate", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2, + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 500", + "query": "rate({namespace=\"prod\"} | unwrap bar[3m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 86", + "query": "rate({namespace=\"prod\"} | unwrap foo [30s])" + } + ], + "id": "2eb02792", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "rate[unwrap]": 6 + }, + "signature": [ + "query", + "rate[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "sum by (container)( count_over_time( {namespace=\"prod\"} [5m] ) )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "sum without (a) ( label_replace( sum without (b) ( rate({namespace=\"prod\"}[5m]) ), \"baz\", \"buz\", \"foo\", \"(.*)\" ) )" + } + ], + "id": "282fd2af", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "sum": 6 + }, + "signature": [ + "query", + "sum", + "status", + "loki 200 proxy 400: log queries are not supported as an instant query type, please change your query" + ], + "sources": { + "loki-tests": 4, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum(max(bytes_over_time({namespace=\"prod\"}[3s])))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum(max(rate({namespace=\"prod\"}[1s])))" + } + ], + "id": "cf717de4", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "sum": 6 + }, + "signature": [ + "query", + "sum", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 500", + "query": "sum_over_time({namespace=\"prod\"} | unwrap bar [3m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 840", + "query": "sum_over_time({namespace=\"prod\"} | unwrap bytes[5m])" + } + ], + "id": "696cdca6", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "sum_over_time[unwrap]": 6 + }, + "signature": [ + "query", + "sum_over_time[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 4, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 45 series, proxy 5", + "query": "topk by (container)( 5, count_over_time( {namespace=\"prod\"} [5m] ) )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 49657 series, proxy 10", + "query": "topk(10,count_over_time({namespace=\"prod\"}[1h])) without (bar)" + } + ], + "id": "4f7b982a", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "topk": 6 + }, + "signature": [ + "query", + "topk", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 2 series, proxy 1", + "query": "sum by (level_extracted) (count_over_time({service_name=\"cache-redis\"} | logfmt [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 3 series, proxy 1", + "query": "sum by (level_extracted) (count_over_time({service_name=\"payment-service\"} | logfmt [5m]))" + } + ], + "id": "b68afb0d", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "sum[logfmt]": 6 + }, + "signature": [ + "query", + "sum[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "seeded": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation quantile_over_time without unwrap | proxy quantile_over_time requires `| unwrap ` for range aggregation", + "query": "quantile_over_time(0.95, {app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation quantile_over_time without unwrap | proxy quantile_over_time requires `| unwrap ` for range aggregation", + "query": "quantile_over_time(0.95, {app=\"api-gateway\"}[5m])" + } + ], + "id": "884f4336", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "quantile_over_time": 6 + }, + "signature": [ + "query", + "quantile_over_time", + "error-text", + "400 with different text: quantile_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation rate_counter without unwrap | proxy parse error : rate_counter requires | unwrap expression", + "query": "rate_counter({app=\"api-gateway\"}[5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation rate_counter without unwrap | proxy parse error : rate_counter requires | unwrap expression", + "query": "rate_counter({app=\"range-metric-counter\"}[1m])" + } + ], + "id": "60ec9fb1", + "impact": "grafana-datasource", + "queries": 3, + "requests": 6, + "shapes": { + "rate_counter": 6 + }, + "signature": [ + "query", + "rate_counter", + "error-text", + "400 with different text: parse error : rate_counter requires | unwrap expression" + ], + "sources": { + "repo": 6 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 2 series, proxy 2; loki-only [\"{\\\"app\\\": \\\"batch-etl\\\"}\", \"{\\\"app\\\": \\\"db-postgres\\\"}\"] proxy-only [\"{\\\"app\\\": \\\"inventory-svc\\\"}\", \"{\\\"app\\\": \\\"otel-collector\\\"}\"]", + "query": "bottomk(2, sum by (app) (count_over_time({app=~\".+\"}[5m])))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 3 series, proxy 3; loki-only [\"{\\\"app\\\": \\\"inventory-svc\\\"}\"] proxy-only [\"{\\\"app\\\": \\\"pricing-svc\\\"}\"]", + "query": "bottomk(3, sum by (app) (count_over_time({env=\"production\"}[5m])))" + } + ], + "id": "ab5f6f01", + "impact": "grafana-datasource", + "queries": 3, + "requests": 5, + "shapes": { + "bottomk": 5 + }, + "signature": [ + "query", + "bottomk", + "series", + "different series, same count" + ], + "sources": { + "repo": 5 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 0", + "query": "absent_over_time({service_name=\"api-gateway\"} | json | nonexistant_field=\"foo\" [1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 0", + "query": "absent_over_time({service_name=\"api-gateway\"} | json | remote_user=\"foo\" [1m])" + } + ], + "id": "2336e2a5", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "absent_over_time[json,label-filter]": 4 + }, + "signature": [ + "query", + "absent_over_time[json,label-filter]", + "series", + "proxy-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : unexpected literal for right leg of logical/set binary operation (or): 0.000000 | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "avg( label_replace( count_over_time({namespace=\"prod\"}[1h]) or 0, \"bar\", \"$1$2\", \"foo\", \"(.*).(.*)\" ) ) by (bar,foo)" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation sum_over_time without unwrap | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "avg( label_replace( count_over_time({namespace=\"prod\"}[1h]) or sum_over_time({namespace=\"prod\"}[1h]), \"bar\", \"$1$2\", \"foo\", \"(.*).(.*)\" ) ) by (bar,foo)" + } + ], + "id": "5185e724", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "avg": 4 + }, + "signature": [ + "query", + "avg", + "error-text", + "400 with different text: log queries are not supported as an instant query type, please change your query" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 3", + "query": "avg(min_over_time({namespace=\"prod\"} |= \"GET\" | unwrap foo[10s] offset 10m)) by (region)" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 3", + "query": "avg(min_over_time({namespace=\"prod\"} |= \"GET\" | unwrap foo[10s])) by (region)" + } + ], + "id": "428bae72", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "avg[unwrap,line-filter]": 4 + }, + "signature": [ + "query", + "avg[unwrap,line-filter]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "bottomk(2,rate(({namespace=\"prod\"} |~\".+bar\")[1m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "bottomk(3,rate(({namespace=\"prod\"} |~\".+bar\")[1m])) without (app)" + } + ], + "id": "b31a48a7", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "bottomk[line-filter]": 4 + }, + "signature": [ + "query", + "bottomk[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected ), got DURATION (\"2m\")", + "query": "bytes_over_time({namespace=\"prod\"}[1h] offset 1h2m20s)" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected ), got DURATION (\"10s\")", + "query": "bytes_over_time({namespace=\"prod\"}[3m] offset 20m10s)" + } + ], + "id": "e1af2a49", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "bytes_over_time": 4 + }, + "signature": [ + "query", + "bytes_over_time", + "status", + "loki 200 proxy 400: parse error : logql: expected ), got duration (Q)" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "count_over_time(({namespace=\"prod\"} |~\".+bar\")[5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "count_over_time(({namespace=\"prod\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")[5m])" + } + ], + "id": "e3b4a851", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time[line-filter]": 4 + }, + "signature": [ + "query", + "count_over_time[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1130 series, proxy 1834", + "query": "count_over_time({namespace=\"prod\"}[12m]) > 1" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 97 series, proxy 155", + "query": "count_over_time({namespace=\"prod\"}[1m]) > 1" + } + ], + "id": "46d8ea74", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time": 4 + }, + "signature": [ + "query", + "count_over_time", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "count_over_time({namespace=\"prod\"}[1h] |= \"error\")" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "count_over_time({namespace=\"prod\"}[5m] |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")" + } + ], + "id": "8275c76c", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time[line-filter]": 4 + }, + "signature": [ + "query", + "count_over_time[line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: expected ), got |= (Q)" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"first\"", + "query": "first_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s] offset 2s) by (a)" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"first\"", + "query": "first_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s]) by (a)" + } + ], + "id": "56b4c549", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "first_over_time[logfmt,unwrap]": 4 + }, + "signature": [ + "query", + "first_over_time[logfmt,unwrap]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"first\"", + "query": "first_over_time({namespace=\"prod\"} |~\".+bar\" | unwrap foo [1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"first\"", + "query": "first_over_time({namespace=\"prod\"}|~\".+bar\"| unwrap foo [1m])" + } + ], + "id": "ae0d1b7b", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "first_over_time[unwrap,line-filter]": 4 + }, + "signature": [ + "query", + "first_over_time[unwrap,line-filter]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "", + "query": "label_replace( avg by (app) ( sum by (app) (rate({namespace=\"prod\"} |~\".+bar\" [1m])) + sum by (app) (rate({namespace=\"prod\"} |~\".+bar\" [1m])) / sum by (app) (rate({namespace=\"prod\"} |~\".+bar\" [1m])) ) * 2, \"new\", \"$1\", \"app\", \"f(.*)\" )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "", + "query": "label_replace( bytes_over_time(({namespace=\"prod\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")[5m]), \"buzz\", \"$2\", \"bar\", \"(.*):(.*)\" )" + } + ], + "id": "307bbdd1", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "label_replace[line-filter]": 4 + }, + "signature": [ + "query", + "label_replace[line-filter]", + "result-type", + "vector vs None" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "", + "query": "label_replace( sum by (job) ( sum_over_time( {service_name=\"api-gateway\"} |= \"level=error\" | json | avg=5 and bar<25ms | unwrap duration(latency) | __error__!~\".*\" [5m] ) / count_over_time({service_name=\"api-gateway\"} | logfmt | label_format foo=bar[5m]) ), \"foo\", \"$1\", \"service\", \"(.*):.*\" )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "", + "query": "label_replace( sum by (job) ( sum_over_time( {service_name=\"api-gateway\"} |= \"level=error\" | json | avg=5 and bar<25ms | unwrap duration(latency) | __error__!~\".*\" [5m] offset 1h ) / count_over_time({service_name=\"api-gateway\"} | logfmt | label_format foo=bar[5m] offset 1h) ), \"foo\", \"$1\", \"servi..." + } + ], + "id": "f2066991", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "label_replace[json,logfmt,label_format,unwrap,label-filter,line-filter]": 4 + }, + "signature": [ + "query", + "label_replace[json,logfmt,label_format,unwrap,label-filter,line-filter]", + "result-type", + "vector vs None" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"last\"", + "query": "last_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s] offset 2s) by (a)" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"last\"", + "query": "last_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s]) by (a)" + } + ], + "id": "3c0a55f2", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "last_over_time[logfmt,unwrap]": 4 + }, + "signature": [ + "query", + "last_over_time[logfmt,unwrap]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "max by (baz) (sum_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "max(sum_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + } + ], + "id": "ed470597", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "max[unwrap]": 4 + }, + "signature": [ + "query", + "max[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 4154 proxy 840", + "query": "max(count(rate({namespace=\"prod\"}[5m])))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 2077 proxy 420", + "query": "max(count(rate({namespace=\"prod\"}[5m]))) / 2" + } + ], + "id": "4bfe0c95", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "max": 4 + }, + "signature": [ + "query", + "max", + "values", + "values differ (proxy lower)" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 0.011758947199486438 proxy 0.05815079365079365", + "query": "max(sum by (cluster) (rate({namespace=\"prod\"}[5m]))) / count(rate({namespace=\"prod\"}[5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"app\": \"pricing-svc\"} @1791224400.0: loki 0.0033333333333333335 proxy 0.1", + "query": "max by(app)(rate({env=\"production\"}[5m]))" + } + ], + "id": "90729d32", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "max": 4 + }, + "signature": [ + "query", + "max", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 2, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 850", + "query": "max_over_time({namespace=\"prod\"} | unwrap bar [5m] offset 5m) without (foo,bar)" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 840", + "query": "max_over_time({namespace=\"prod\"} | unwrap bar [5m]) without (foo,bar)" + } + ], + "id": "8cc35d5b", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "max_over_time[unwrap]": 4 + }, + "signature": [ + "query", + "max_over_time[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 1: syntax error: unexpected IDENTIFIER | proxy parse error : logql: unexpected token \"(\" at position 14", + "query": "maxby(status)(quantile_over_time(0.7,{service_name=\"payment-service\"}|logfmt|unwrapvalue[1s]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 1: syntax error: unexpected IDENTIFIER | proxy parse error : logql: unexpected token \"(\" at position 14", + "query": "maxby(status)(quantile_over_time(0.7,{service_name=\"payment-service\"}|logfmt|unwrapvalue[1s])by(baz))" + } + ], + "id": "f5161c97", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "maxby[logfmt]": 4 + }, + "signature": [ + "query", + "maxby[logfmt]", + "error-text", + "400 with different text: parse error : logql: unexpected token Q at position N" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "min by (baz) (sum_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "min(sum_over_time({namespace=\"prod\"} | unwrap bar [3m]))" + } + ], + "id": "abb2d873", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "min[unwrap]": 4 + }, + "signature": [ + "query", + "min[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "rate(({namespace=\"prod\"} |~\".+bar\")[1m])" + } + ], + "id": "25b1f798", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "rate[line-filter]": 4 + }, + "signature": [ + "query", + "rate[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sort(rate(({namespace=\"prod\"} |~\".+bar\")[1m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sort(sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) ))" + } + ], + "id": "3f290148", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sort[line-filter]": 4 + }, + "signature": [ + "query", + "sort[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "sum by (baz) (max_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "sum by (baz) (min_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]))" + } + ], + "id": "df52ecf7", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum[logfmt,unwrap]": 4 + }, + "signature": [ + "query", + "sum[logfmt,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "sum by (job) ( count_over_time({namespace=\"prod\"} |= \"level=error\"[5m]) / count_over_time({namespace=\"prod\"}[5m]) )" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "sum without(a) ( rate ( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) )" + } + ], + "id": "27527d05", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum[line-filter]": 4 + }, + "signature": [ + "query", + "sum[line-filter]", + "status", + "loki 200 proxy 400: log queries are not supported as an instant query type, please change your query" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by (name)(rate({service_name=\"api-gateway\"} | json | unwrap foo[5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 34", + "query": "sum(rate({service_name=\"api-gateway\"} | json | unwrap response_latency_seconds [1m]))" + } + ], + "id": "c2316012", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum[json,unwrap]": 4 + }, + "signature": [ + "query", + "sum[json,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : unexpected literal for right leg of logical/set binary operation (or): 1.000000 | proxy unexpected literal for logical/set binary operation", + "query": "sum(count_over_time({namespace=\"prod\"}[5m])) by (foo) + 1 or 1" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : unexpected literal for right leg of logical/set binary operation (or): 1.000000 | proxy unexpected literal for logical/set binary operation", + "query": "sum(count_over_time({namespace=\"prod\"}[5m])) by (foo) or 1" + } + ], + "id": "7f7a4d03", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum": 4 + }, + "signature": [ + "query", + "sum", + "error-text", + "400 with different text: unexpected literal for logical/set binary operation" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk(1,rate(({namespace=\"prod\"})[1h]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "topk(1,rate(({namespace=\"prod\"})[1m]))" + } + ], + "id": "4fc1fe7c", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "topk": 4 + }, + "signature": [ + "query", + "topk", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy unsupported instant aggregation target", + "query": "topk(100, sum by (a) (sum_over_time ({service_name=\"payment-service\"} | logfmt | unwrap value [1s])))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy unsupported instant aggregation target", + "query": "topk(3, sum by (a) (sum_over_time ({service_name=\"payment-service\"} | logfmt | unwrap value [1s])))" + } + ], + "id": "9e36e9cd", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "topk[logfmt,unwrap]": 4 + }, + "signature": [ + "query", + "topk[logfmt,unwrap]", + "status", + "loki 200 proxy 400: unsupported instant aggregation target" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "topk(5,count_over_time({namespace=\"prod\"}[5m] |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")) without (foo)" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "topk(5,sum(rate({namespace=\"prod\"}[5m] |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")) by (app))" + } + ], + "id": "6553b1df", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "topk[line-filter]": 4 + }, + "signature": [ + "query", + "topk[line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: expected ), got |= (Q)" + ], + "sources": { + "loki-tests": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : parameter required for operation topk | proxy parse error : logql: topk requires a numeric k parameter", + "query": "topk(count_over_time({namespace=\"prod\"}[1h]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 8: syntax error: unexpected , | proxy parse error : logql: topk requires a numeric k parameter", + "query": "topk(-1, sum by(level)(count_over_time({env=\"production\"}[5m])))" + } + ], + "id": "97d18bb6", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "topk": 4 + }, + "signature": [ + "query", + "topk", + "error-text", + "400 with different text: parse error : logql: topk requires a numeric k parameter" + ], + "sources": { + "loki-tests": 2, + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 801 series, proxy 804; loki-only [\"{\\\"throughput_rps\\\": \\\"0.6\\\"}\", \"{\\\"throughput_rps\\\": \\\"1.0\\\"}\"] proxy-only [\"{\\\"throughput_rps\\\": \\\"0.6000000000000001\\\"}\", \"{\\\"throughput_rps\\\": \\\"1\\\"}\"]", + "query": "sum by (throughput_rps) (count_over_time({service_name=\"batch-etl\"} | json [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1550 series, proxy 1552; loki-only [\"{\\\"confidence\\\": \\\"0.5004\\\"}\", \"{\\\"confidence\\\": \\\"0.5005\\\"}\"] proxy-only [\"{\\\"confidence\\\": \\\"0.5004000000000001\\\"}\", \"{\\\"confidence\\\": \\\"0.5005000000000001\\\"}\"]", + "query": "sum by (confidence) (count_over_time({service_name=\"ml-serving\"} | json [5m]))" + } + ], + "id": "c2523a5e", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum[json]": 4 + }, + "signature": [ + "query", + "sum[json]", + "series", + "proxy-more" + ], + "sources": { + "seeded": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 801 series, proxy 804; loki-only [\"{\\\"throughput_rps\\\": \\\"0.6\\\"}\", \"{\\\"throughput_rps\\\": \\\"1.0\\\"}\"] proxy-only [\"{\\\"throughput_rps\\\": \\\"0.6000000000000001\\\"}\", \"{\\\"throughput_rps\\\": \\\"1\\\"}\"]", + "query": "sum by (throughput_rps) (count_over_time({service_name=\"batch-etl\"} | json | throughput_rps!=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1550 series, proxy 1552; loki-only [\"{\\\"confidence\\\": \\\"0.5004\\\"}\", \"{\\\"confidence\\\": \\\"0.5005\\\"}\"] proxy-only [\"{\\\"confidence\\\": \\\"0.5004000000000001\\\"}\", \"{\\\"confidence\\\": \\\"0.5005000000000001\\\"}\"]", + "query": "sum by (confidence) (count_over_time({service_name=\"ml-serving\"} | json | confidence!=\"\" [5m]))" + } + ], + "id": "bd84536f", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum[json,label-filter]": 4 + }, + "signature": [ + "query", + "sum[json,label-filter]", + "series", + "proxy-more" + ], + "sources": { + "seeded": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 1; loki-only [\"{\\\"level\\\": \\\"info\\\"}\"] proxy-only [\"{\\\"level\\\": \\\"warn\\\"}\"]", + "query": "sum by (level) (max_over_time({service_name=\"worker-service\"} | logfmt | unwrap max_retries | __error__=\"\" [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 1; loki-only [\"{\\\"level\\\": \\\"info\\\"}\"] proxy-only [\"{\\\"level\\\": \\\"warn\\\"}\"]", + "query": "sum by (level) (max_over_time({service_name=\"worker-service\"} | logfmt | unwrap backoff_ms | __error__=\"\" [5m]))" + } + ], + "id": "19e8f424", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum[logfmt,unwrap,label-filter]": 4 + }, + "signature": [ + "query", + "sum[logfmt,unwrap,label-filter]", + "series", + "different series, same count" + ], + "sources": { + "seeded": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 5302 series, proxy 1120", + "query": "avg without (level, env) (count_over_time({env=\"production\"}[5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 5302 series, proxy 1120", + "query": "avg without(app)(rate({env=\"production\"}[5m]))" + } + ], + "id": "f1aa2f22", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "avg": 4 + }, + "signature": [ + "query", + "avg", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 41: syntax error: unexpected ) | proxy avg() requires a range metric inside (e.g. rate({...}[5m]), count_over_time({...}[5m]))", + "query": "avg({app=\"api-gateway\",env=\"production\"})" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 24: syntax error: unexpected ) | proxy avg() requires a range metric inside (e.g. rate({...}[5m]), count_over_time({...}[5m]))", + "query": "avg({app=\"api-gateway\"})" + } + ], + "id": "54a23828", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "avg": 4 + }, + "signature": [ + "query", + "avg", + "error-text", + "400 with different text: avg() requires a range metric inside (e.g. rate({...}[Nm]), count_over_time({..." + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "avg_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "avg_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "41586c11", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "avg_over_time[json,unwrap]": 4 + }, + "signature": [ + "query", + "avg_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation avg_over_time without unwrap | proxy avg_over_time requires `| unwrap ` for range aggregation", + "query": "avg_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation avg_over_time without unwrap | proxy avg_over_time requires `| unwrap ` for range aggregation", + "query": "avg_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "f65563ce", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "avg_over_time": 4 + }, + "signature": [ + "query", + "avg_over_time", + "error-text", + "400 with different text: avg_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "bytes_rate({app=\"api-gateway\"} | json [1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "bytes_rate({app=\"api-gateway\"} | json [5m])" + } + ], + "id": "eb1010a3", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "bytes_rate[json]": 4 + }, + "signature": [ + "query", + "bytes_rate[json]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "count_over_time({app=\"api-gateway\"} | json | method=\"GET\" [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted', 'request_id_extracted']", + "query": "count_over_time({app=\"api-gateway\"} | json | status=\"200\" [5m])" + } + ], + "id": "d088f040", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time[json,label-filter]": 4 + }, + "signature": [ + "query", + "count_over_time[json,label-filter]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted']", + "query": "count_over_time({app=\"payment-service\",env=\"production\"} | logfmt | level!=\"debug\" [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted']", + "query": "count_over_time({app=\"payment-service\",env=\"production\"} | logfmt | level=\"error\"[5m])" + } + ], + "id": "150024f9", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "count_over_time[logfmt,label-filter]": 4 + }, + "signature": [ + "query", + "count_over_time[logfmt,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "first_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "first_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "fe307701", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "first_over_time[json,unwrap]": 4 + }, + "signature": [ + "query", + "first_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation first_over_time without unwrap | proxy first_over_time requires `| unwrap ` for range aggregation", + "query": "first_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation first_over_time without unwrap | proxy first_over_time requires `| unwrap ` for range aggregation", + "query": "first_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "60c7e720", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "first_over_time": 4 + }, + "signature": [ + "query", + "first_over_time", + "error-text", + "400 with different text: first_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "last_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "last_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "d41734a9", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "last_over_time[json,unwrap]": 4 + }, + "signature": [ + "query", + "last_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation last_over_time without unwrap | proxy last_over_time requires `| unwrap ` for range aggregation", + "query": "last_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation last_over_time without unwrap | proxy last_over_time requires `| unwrap ` for range aggregation", + "query": "last_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "83a0bbb4", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "last_over_time": 4 + }, + "signature": [ + "query", + "last_over_time", + "error-text", + "400 with different text: last_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "max_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "max_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "32f36208", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "max_over_time[json,unwrap]": 4 + }, + "signature": [ + "query", + "max_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation max_over_time without unwrap | proxy max_over_time requires `| unwrap ` for range aggregation", + "query": "max_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation max_over_time without unwrap | proxy max_over_time requires `| unwrap ` for range aggregation", + "query": "max_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "2fd5ec28", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "max_over_time": 4 + }, + "signature": [ + "query", + "max_over_time", + "error-text", + "400 with different text: max_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "min_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "min_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "422b5298", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "min_over_time[json,unwrap]": 4 + }, + "signature": [ + "query", + "min_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation min_over_time without unwrap | proxy min_over_time requires `| unwrap ` for range aggregation", + "query": "min_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation min_over_time without unwrap | proxy min_over_time requires `| unwrap ` for range aggregation", + "query": "min_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "cebdc20e", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "min_over_time": 4 + }, + "signature": [ + "query", + "min_over_time", + "error-text", + "400 with different text: min_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\",env=\"production\"} | json | method=\"GET\"[5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\"} | json | method=\"GET\" [1m])" + } + ], + "id": "cfff8628", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "rate[json,label-filter]": 4 + }, + "signature": [ + "query", + "rate[json,label-filter]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "rate({app=\"api-gateway\",env=\"production\"} | json | status>=400 [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "rate({app=\"api-gateway\",env=\"production\"} | json | status>=400 | status<500 [5m])" + } + ], + "id": "4ec90d6a", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "rate[json,label-filter]": 4 + }, + "signature": [ + "query", + "rate[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\"} |= \"GET\" | json [1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'error_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted']", + "query": "rate({app=\"api-gateway\"} |~ \"POST|PUT\" | json [1m])" + } + ], + "id": "fd91e575", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "rate[json,line-filter]": 4 + }, + "signature": [ + "query", + "rate[json,line-filter]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 25: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "rate({app=\"api-gateway\"})" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 42: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "rate({app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "7705bcbf", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "rate": 4 + }, + "signature": [ + "query", + "rate", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 25: syntax error: unexpected ) | proxy unsupported instant aggregation target", + "query": "sort({app=\"api-gateway\"})" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 42: syntax error: unexpected ) | proxy unsupported instant aggregation target", + "query": "sort({app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "ea5fa199", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sort": 4 + }, + "signature": [ + "query", + "sort", + "error-text", + "400 with different text: unsupported instant aggregation target" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 30: syntax error: unexpected ) | proxy unsupported instant aggregation target", + "query": "sort_desc({app=\"api-gateway\"})" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 47: syntax error: unexpected ) | proxy unsupported instant aggregation target", + "query": "sort_desc({app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "4d13f90a", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sort_desc": 4 + }, + "signature": [ + "query", + "sort_desc", + "error-text", + "400 with different text: unsupported instant aggregation target" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"app\": \"batch-etl\"} @1791224400.0: loki 11.081716625116119 proxy 911", + "query": "stddev by(app)(count_over_time({env=\"production\"}[5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"level\": \"info\"} @1791224400.0: loki 42.30685195358102 proxy 3343", + "query": "stddev by(level)(count_over_time({app=\"api-gateway\",env=\"production\"}[5m]))" + } + ], + "id": "4b67fb14", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "stddev": 4 + }, + "signature": [ + "query", + "stddev", + "values", + "values differ (proxy higher)" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "stddev_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "stddev_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "a88e7ea7", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "stddev_over_time[json,unwrap]": 4 + }, + "signature": [ + "query", + "stddev_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation stddev_over_time without unwrap | proxy stddev_over_time requires `| unwrap ` for range aggregation", + "query": "stddev_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation stddev_over_time without unwrap | proxy stddev_over_time requires `| unwrap ` for range aggregation", + "query": "stddev_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "fcbdc6b4", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "stddev_over_time": 4 + }, + "signature": [ + "query", + "stddev_over_time", + "error-text", + "400 with different text: stddev_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "stdvar_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "stdvar_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "7f750e4f", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "stdvar_over_time[json,unwrap]": 4 + }, + "signature": [ + "query", + "stdvar_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation stdvar_over_time without unwrap | proxy stdvar_over_time requires `| unwrap ` for range aggregation", + "query": "stdvar_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation stdvar_over_time without unwrap | proxy stdvar_over_time requires `| unwrap ` for range aggregation", + "query": "stdvar_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "00c1351d", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "stdvar_over_time": 4 + }, + "signature": [ + "query", + "stdvar_over_time", + "error-text", + "400 with different text: stdvar_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 24: syntax error: unexpected ) | proxy sum() requires a range metric inside (e.g. rate({...}[5m]), count_over_time({...}[5m]))", + "query": "sum({app=\"api-gateway\"})" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 32: syntax error: unexpected ) | proxy sum() requires a range metric inside (e.g. rate({...}[5m]), count_over_time({...}[5m]))", + "query": "sum by(job) ({namespace=\"prod\"})" + } + ], + "id": "d1da0216", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum": 4 + }, + "signature": [ + "query", + "sum", + "error-text", + "400 with different text: sum() requires a range metric inside (e.g. rate({...}[Nm]), count_over_time({..." + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "sum_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "sum_over_time({app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "93b5bb76", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum_over_time[json,unwrap]": 4 + }, + "signature": [ + "query", + "sum_over_time[json,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation sum_over_time without unwrap | proxy sum_over_time requires `| unwrap ` for range aggregation", + "query": "sum_over_time({app=\"api-gateway\"}[1m])" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation sum_over_time without unwrap | proxy sum_over_time requires `| unwrap ` for range aggregation", + "query": "sum_over_time({app=\"api-gateway\"}[5m])" + } + ], + "id": "0b43cabb", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum_over_time": 4 + }, + "signature": [ + "query", + "sum_over_time", + "error-text", + "400 with different text: sum_over_time requires Q for range aggregation" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 28: syntax error: unexpected ) | proxy unsupported instant aggregation target", + "query": "topk(5, {app=\"api-gateway\"})" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 45: syntax error: unexpected ) | proxy unsupported instant aggregation target", + "query": "topk(2, {app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "380bfbcd", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "topk": 4 + }, + "signature": [ + "query", + "topk", + "error-text", + "400 with different text: unsupported instant aggregation target" + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but ca...", + "query": "sum by (level) (rate({env=\"production\"} | json [5m]))" + }, + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but ca...", + "query": "sum by (status) (count_over_time({env=\"production\"} | json [5m]))" + } + ], + "id": "19bb4804", + "impact": "grafana-datasource", + "queries": 2, + "requests": 4, + "shapes": { + "sum[json]": 4 + }, + "signature": [ + "query", + "sum[json]", + "error-text", + "400 with different text: pipeline error: Q for series: Qt find closing Q symbol\", app=..." + ], + "sources": { + "repo": 4 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : grouping not allowed for absent_over_time aggregation | proxy -", + "query": "absent_over_time({namespace=\"prod\"}[1h]) by (foo)" + } + ], + "id": "7980881e", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time": 2 + }, + "signature": [ + "query", + "absent_over_time", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 34", + "query": "absent_over_time({service_name=\"api-gateway\"} | json [1m])" + } + ], + "id": "bd172c7d", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time[json]": 2 + }, + "signature": [ + "query", + "absent_over_time[json]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 0", + "query": "absent_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json [5m])" + } + ], + "id": "aeebcc0d", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time[json,line-filter]": 2 + }, + "signature": [ + "query", + "absent_over_time[json,line-filter]", + "series", + "proxy-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "absent_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms | unwrap latency | __error__!~\".*\" | foo >5[5m])" + } + ], + "id": "7b225b99", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query", + "absent_over_time[json,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably, the whole string m" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "avg( label_replace( count_over_time({namespace=\"prod\"}[1h]), \"bar\", \"$1$2\", \"foo\", \"(.*).(.*)\" ) ) by (bar,foo)" + } + ], + "id": "b3444d76", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "avg": 2 + }, + "signature": [ + "query", + "avg", + "status", + "loki 200 proxy 400: log queries are not supported as an instant query type, please change your query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "avg( rate( ( {namespace=\"prod\"} |= \"GET\" ) [10s] ) ) by (region)" + } + ], + "id": "26eda2af", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "avg[line-filter]": 2 + }, + "signature": [ + "query", + "avg[line-filter]", + "status", + "loki 200 proxy 400: log queries are not supported as an instant query type, please change your query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 164", + "query": "avg(rate({service_name=\"api-gateway\"} | json [5m]))" + } + ], + "id": "0d361b0c", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "avg[json]": 2 + }, + "signature": [ + "query", + "avg[json]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 2", + "query": "avg_over_time({namespace=\"prod\"} | unwrap bytes [5m]) by (cluster)" + } + ], + "id": "188a6f9f", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "avg_over_time[unwrap]": 2 + }, + "signature": [ + "query", + "avg_over_time[unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 9: syntax error: unexpected IDENTIFIER | proxy parse error : logql: bottomk requires a numeric k parameter", + "query": "bottomk(he,count_over_time({namespace=\"prod\"}[1h]))" + } + ], + "id": "7d93601c", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "bottomk": 2 + }, + "signature": [ + "query", + "bottomk", + "error-text", + "400 with different text: parse error : logql: bottomk requires a numeric k parameter" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "bytes_over_time(({namespace=\"prod\"} |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")[5m])" + } + ], + "id": "6813c1b2", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_over_time[line-filter]": 2 + }, + "signature": [ + "query", + "bytes_over_time[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1180 series, proxy 54", + "query": "bytes_over_time({service_name=\"payment-service\"} | logfmt [3m])" + } + ], + "id": "03f3b6e5", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_over_time[logfmt]": 2 + }, + "signature": [ + "query", + "bytes_over_time[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1180 series, proxy 54", + "query": "bytes_rate({service_name=\"payment-service\"} | logfmt [3m])" + } + ], + "id": "241fb900", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_rate[logfmt]": 2 + }, + "signature": [ + "query", + "bytes_rate[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 16: syntax error: unexpected RATE, expecting IDENTIFIER or ) | proxy parse error : logql: expected ), got ( (\"(\")", + "query": "count without (rate({namespace=\"prod\"}[15s]))" + } + ], + "id": "e1d8fb09", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count": 2 + }, + "signature": [ + "query", + "count", + "error-text", + "400 with different text: parse error : logql: expected ), got ( (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 164", + "query": "count(rate({service_name=\"api-gateway\"} | json | keep foo [5m]))" + } + ], + "id": "feb671c2", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count[json,keep]": 2 + }, + "signature": [ + "query", + "count[json,keep]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 164", + "query": "count(rate({service_name=\"api-gateway\"} | json | label_format foo=bar [5m]))" + } + ], + "id": "da345f70", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count[json,label_format]": 2 + }, + "signature": [ + "query", + "count[json,label_format]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : unexpected type for right leg of binary operation (>): *syntax.MatchersExpr | proxy parse error at line 1, col 1: unexpected expression for binary operation", + "query": "count_over_time({namespace=\"prod\"}[12m]) > {namespace=\"prod\"}" + } + ], + "id": "80e2ee76", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time": 2 + }, + "signature": [ + "query", + "count_over_time", + "error-text", + "400 with different text: parse error at line N, col N: unexpected expression for binary operation" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['layer7_something_specific']", + "query": "count_over_time({service_name=\"api-gateway\"} | json layer7_something_specific=\"layer7_something_specific\" [12m])" + } + ], + "id": "fcc4cda7", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json]": 2 + }, + "signature": [ + "query", + "count_over_time[json]", + "labels", + "loki-only " + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation count_over_time with unwrap | proxy -", + "query": "count_over_time({service_name=\"api-gateway\"} |= \"foo\" | json | unwrap foo [5m])" + } + ], + "id": "354d31ed", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json,unwrap,line-filter]": 2 + }, + "signature": [ + "query", + "count_over_time[json,unwrap,line-filter]", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "count_over_time({namespace=\"prod\"}[3s] offset -1s)" + } + ], + "id": "5c22832f", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time": 2 + }, + "signature": [ + "query", + "count_over_time", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 49: syntax error: unexpected $end, expecting STRING | proxy parse error : logql: expected STRING or RAWSTRING, got EOF (\"\")", + "query": "count_over_time({namespace=\"prod\"} | line_format" + } + ], + "id": "e6255407", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[line_format]": 2 + }, + "signature": [ + "query", + "count_over_time[line_format]", + "error-text", + "400 with different text: parse error : logql: expected string or rawstring, got eof (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid aggregation count_over_time with unwrap | proxy -", + "query": "count_over_time({namespace=\"prod\"} |~ \"\\\\w+\" | unwrap foo[5m])" + } + ], + "id": "b879c156", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[unwrap,line-filter]": 2 + }, + "signature": [ + "query", + "count_over_time[unwrap,line-filter]", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : stage '|~ \"error\\\\\"' : error parsing regexp: trailing backslash at end of expression: `` | proxy parse error : logql: expected ), got |~ (\"|~\")", + "query": "count_over_time({namespace=\"prod\"}[1h] |~ `error\\`)" + } + ], + "id": "1216c87b", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[line-filter]": 2 + }, + "signature": [ + "query", + "count_over_time[line-filter]", + "error-text", + "400 with different text: parse error : logql: expected ), got |~ (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "first_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s] offset -2s) by (a)" + } + ], + "id": "a5c93607", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "first_over_time[logfmt,unwrap]": 2 + }, + "signature": [ + "query", + "first_over_time[logfmt,unwrap]", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "first_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms | unwrap latency | __error__!~\".*\" | foo >5[5m])" + } + ], + "id": "163be53e", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "first_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query", + "first_over_time[json,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably, the whole string m" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 46: syntax error: unexpected ), expecting , | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "label_replace(rate({namespace=\"prod\"}[5m]),\"\")" + } + ], + "id": "1bce60c7", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "label_replace": 2 + }, + "signature": [ + "query", + "label_replace", + "error-text", + "400 with different text: log queries are not supported as an instant query type, please change your query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : invalid regex in label_replace: error parsing regexp: invalid escape sequence: `\\q` | proxy -", + "query": "label_replace(rate({namespace=\"prod\"}[5m]),\"foo\",\"$1\",\"bar\",\"^^^^x43\\\\q\")" + } + ], + "id": "b04a9c73", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "label_replace": 2 + }, + "signature": [ + "query", + "label_replace", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "last_over_time(({namespace=\"prod\"} |~\".+bar\" | unwrap foo)[5m])" + } + ], + "id": "c77525e2", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "last_over_time[unwrap,line-filter]": 2 + }, + "signature": [ + "query", + "last_over_time[unwrap,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "last_over_time({service_name=\"payment-service\"} | logfmt | unwrap value [1s] offset -2s) by (a)" + } + ], + "id": "27e68f5e", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "last_over_time[logfmt,unwrap]": 2 + }, + "signature": [ + "query", + "last_over_time[logfmt,unwrap]", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"last\"", + "query": "last_over_time({namespace=\"prod\"}|~\".+bar\"| unwrap foo[5m])" + } + ], + "id": "b91f5292", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "last_over_time[unwrap,line-filter]": 2 + }, + "signature": [ + "query", + "last_over_time[unwrap,line-filter]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "last_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms | unwrap latency | __error__!~\".*\" | foo >5[5m])" + } + ], + "id": "adb16001", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "last_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query", + "last_over_time[json,unwrap,label-filter,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably, the whole string m" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "max( sum(count_over_time({namespace=\"prod\"}[1h])) without (foo,bar) ) by (foo)" + } + ], + "id": "2137c2ba", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "max": 2 + }, + "signature": [ + "query", + "max", + "status", + "loki 200 proxy 400: log queries are not supported as an instant query type, please change your query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "max(count(rate({namespace=\"prod\"}[1s])))" + } + ], + "id": "229e4f30", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "max": 2 + }, + "signature": [ + "query", + "max", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 14.243333333333334 proxy 4273", + "query": "max(sum by (abc) (rate({service_name=\"api-gateway\"} | json | label_format bazz=buzz [5m])))" + } + ], + "id": "472453d0", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "max[json,label_format]": 2 + }, + "signature": [ + "query", + "max[json,label_format]", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "max(sum_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]))" + } + ], + "id": "fc1254b0", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "max[logfmt,unwrap]": 2 + }, + "signature": [ + "query", + "max[logfmt,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "1 samples, e.g. {} @1791224400.0: \"\"", + "query": "max_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]) by (baz)" + } + ], + "id": "0adb5d8c", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "max_over_time[logfmt,unwrap]": 2 + }, + "signature": [ + "query", + "max_over_time[logfmt,unwrap]", + "shape", + "proxy sample value is not a number" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "max_over_time({namespace=\"prod\"} | unwrap bar [5m] offset -5m) without (foo,bar)" + } + ], + "id": "d34c2dbd", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "max_over_time[unwrap]": 2 + }, + "signature": [ + "query", + "max_over_time[unwrap]", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "min(sum_over_time({service_name=\"payment-service\"} | logfmt | unwrap bar [3m]))" + } + ], + "id": "9ac3197a", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "min[logfmt,unwrap]": 2 + }, + "signature": [ + "query", + "min[logfmt,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 0, col 23: syntax error: unexpected RANGE | proxy parse error : logql: expected ), got [ (\"[\")", + "query": "min({namespace=\"prod\"}[5m])" + } + ], + "id": "82283849", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "min": 2 + }, + "signature": [ + "query", + "min", + "error-text", + "400 with different text: parse error : logql: expected ), got [ (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : parameter required for operation quantile_over_time | proxy invalid quantile query", + "query": "quantile_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms| unwrap latency [5m])" + } + ], + "id": "f22ad939", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "quantile_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query", + "quantile_over_time[json,unwrap,label-filter,line-filter]", + "error-text", + "400 with different text: invalid quantile query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['deployment_environment', 'k8s_cluster_name', 'k8s_namespace_name', 'k8s_pod_name', 'service_name_extracted', 'service_version']", + "query": "rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] )>-1" + } + ], + "id": "d32173c3", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[line-filter]": 2 + }, + "signature": [ + "query", + "rate[line-filter]", + "labels", + "loki-only *_extracted," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "rate(({namespace=\"prod\"} |~\".+bar\" | unwrap bar)[1m])" + } + ], + "id": "2731385f", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[unwrap,line-filter]": 2 + }, + "signature": [ + "query", + "rate[unwrap,line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 0, col 24: missing closing ']' in duration | proxy parse error : logql: expected duration: logql: expected DURATION, got NUMBER (\"5\")", + "query": "rate({namespace=\"prod\"}[5)" + } + ], + "id": "832022c8", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate": 2 + }, + "signature": [ + "query", + "rate", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got number (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected duration after offset: logql: expected DURATION, got - (\"-\")", + "query": "rate({namespace=\"prod\"}[3s] offset -1s)" + } + ], + "id": "02fa6772", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate": 2 + }, + "signature": [ + "query", + "rate", + "status", + "loki 200 proxy 400: parse error : logql: expected duration after offset: logql: expected duration, g" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "rate({service_name=\"api-gateway\"} | json | unwrap foo | latency >= 250ms or bytes > 42B or ( status_code < 500 and status_code > 200) or source = ip(\"\") and user = \"me\" [1m])" + } + ], + "id": "02494ca7", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json,unwrap,label-filter]": 2 + }, + "signature": [ + "query", + "rate[json,unwrap,label-filter]", + "status", + "loki 200 proxy 400: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1039", + "query": "rate({service_name=\"api-gateway\"} | json | unwrap foo | latency >= 250ms or bytes=~\".*\" [1m])" + } + ], + "id": "13cecc6e", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json,unwrap,label-filter]": 2 + }, + "signature": [ + "query", + "rate[json,unwrap,label-filter]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "rate({service_name=\"api-gateway\"} | json | label_format foo=bar [5m])" + } + ], + "id": "ac4a8c52", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json,label_format]": 2 + }, + "signature": [ + "query", + "rate[json,label_format]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : sort and sort_desc doesn't allow grouping by | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sort(sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) )) by (app)" + } + ], + "id": "87216ff6", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sort[line-filter]": 2 + }, + "signature": [ + "query", + "sort[line-filter]", + "error-text", + "400 with different text: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sort_desc(sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) ))" + } + ], + "id": "bc81e845", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sort_desc[line-filter]": 2 + }, + "signature": [ + "query", + "sort_desc[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : sort and sort_desc doesn't allow grouping by | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "sort_desc(sum by(a) (rate( ( {namespace=\"prod\"} |=\"error\" !=\"timeout\" ) [10s] ) )) by (app)" + } + ], + "id": "5d3d5b11", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sort_desc[line-filter]": 2 + }, + "signature": [ + "query", + "sort_desc[line-filter]", + "error-text", + "400 with different text: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: missing whitespace or ':' between \"\\\"\u2026\"\" and \"prod\"; probably, the whole string must be put into quotes", + "query": "stddev(count_over_time(({namespace=\"prod\"} |~\".+bar\")[1m]))" + } + ], + "id": "2fe5e780", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "stddev[line-filter]": 2 + }, + "signature": [ + "query", + "stddev[line-filter]", + "status", + "loki 200 proxy 400: cannot parse query arg [\u2026]: missing whitespace or Q between Q\u2026Q and Q; probably," + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 26: syntax error: unexpected ) | proxy unsupported instant aggregation target", + "query": "stddev({namespace=\"prod\"})" + } + ], + "id": "5b4749b3", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "stddev": 2 + }, + "signature": [ + "query", + "stddev", + "error-text", + "400 with different text: unsupported instant aggregation target" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "", + "query": "stdvar without (app) (count_over_time(({namespace=\"prod\"} |~\".+bar\")[1m]))" + } + ], + "id": "ce65447e", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "stdvar[line-filter]": 2 + }, + "signature": [ + "query", + "stdvar[line-filter]", + "result-type", + "vector vs None" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "sum by (container) (min_over_time({service_name=\"api-gateway\"} |= \"foo\" or \"bar\" | logfmt | unwrap duration [1m])) / sum by (container) (max_over_time({service_name=\"api-gateway\"} |~ \"(foo|bar)\" | json | unwrap duration [1m]))" + } + ], + "id": "c061fca0", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,logfmt,unwrap,line-filter]": 2 + }, + "signature": [ + "query", + "sum[json,logfmt,unwrap,line-filter]", + "status", + "loki 200 proxy 400: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 3248.5555264316354 proxy 33800", + "query": "sum by (job) ( count_over_time({namespace=\"prod\"} |= \"level=error\"[5m]) / count_over_time({namespace=\"prod\"}[5m]) ) * 100" + } + ], + "id": "6f105696", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[line-filter]": 2 + }, + "signature": [ + "query", + "sum[line-filter]", + "values", + "values differ (proxy higher)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 1; loki-only [] proxy-only [\"{}\"]", + "query": "sum by(name)(rate({service_name=\"api-gateway\"} | json | line_format \"something else\" | unwrap foo[5m]))" + } + ], + "id": "ddbd51ef", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,line_format,unwrap]": 2 + }, + "signature": [ + "query", + "sum[json,line_format,unwrap]", + "series", + "loki-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 107.78666666666666 proxy 53.89333333333333", + "query": "sum( rate( {namespace=\"prod\"} [5m] ) ) - sum( rate( {namespace=\"prod\"} [5m] ) ) % sum( rate( {namespace=\"prod\"} [5m] ) ) + sum( rate( {namespace=\"prod\"} [5m] ) )" + } + ], + "id": "9d1a4d17", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum": 2 + }, + "signature": [ + "query", + "sum", + "values", + "values differ (proxy lower)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : unsupported parameter for operation sum(3, | proxy parse error : logql: expected ), got , (\",\")", + "query": "sum(3 ,count_over_time({namespace=\"prod\"}[1h]))" + } + ], + "id": "4b30f46e", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum": 2 + }, + "signature": [ + "query", + "sum", + "error-text", + "400 with different text: parse error : logql: expected ), got , (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error : logql: expected ), got |= (\"|=\")", + "query": "sum(count_over_time({namespace=\"prod\"}[5m] |= \"baz\" |~ \"blip\" != \"flip\" !~ \"flap\")) by (foo)" + } + ], + "id": "dbd01e9a", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[line-filter]": 2 + }, + "signature": [ + "query", + "sum[line-filter]", + "status", + "loki 200 proxy 400: parse error : logql: expected ), got |= (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : unexpected type for right leg of binary operation (-): *syntax.MatchersExpr | proxy parse error at line 1, col 1: unexpected expression for binary operation", + "query": "sum(count_over_time({namespace=\"prod\"}[5m])) by (foo) - {namespace=\"prod\"}" + } + ], + "id": "4fb822da", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum": 2 + }, + "signature": [ + "query", + "sum", + "error-text", + "400 with different text: parse error at line N, col N: unexpected expression for binary operation" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "sum(count_over_time({service_name=\"payment-service\"} | logfmt --strict [5m] offset 10m))" + } + ], + "id": "75526081", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt]": 2 + }, + "signature": [ + "query", + "sum[logfmt]", + "status", + "loki 200 proxy 400: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy cannot parse query arg [\u2026]: cannot parse \"stats\"\u2026\"last\"", + "query": "sum(last_over_time({service_name=\"payment-service\"} | logfmt | unwrap total_count [1h]) by (foo)) or vector(0)" + } + ], + "id": "20680725", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,unwrap]": 2 + }, + "signature": [ + "query", + "sum[logfmt,unwrap]", + "status", + "loki 200 proxy 422: cannot parse query arg [\u2026]: cannot parse Q\u2026Q" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 164", + "query": "sum(max(rate({service_name=\"api-gateway\"} | json | label_format foo=bar [5m])))" + } + ], + "id": "bb04eea0", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,label_format]": 2 + }, + "signature": [ + "query", + "sum[json,label_format]", + "series", + "proxy-more" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 0; loki-only [\"{}\"] proxy-only []", + "query": "sum(rate({namespace=\"prod\"} |= \"foo\" != \"foo\"[1s]) or vector(1))" + } + ], + "id": "8b8db278", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[line-filter]": 2 + }, + "signature": [ + "query", + "sum[line-filter]", + "series", + "proxy-empty" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 36: syntax error: unexpected $end, expecting STRING | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "sum(rate({namespace=\"prod\"} | msg=~" + } + ], + "id": "893f45f2", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[label-filter]": 2 + }, + "signature": [ + "query", + "sum[label-filter]", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 40: syntax error: unexpected $end | proxy parse error : logql: expected ), got EOF (\"\")", + "query": "sum(rate({namespace=\"prod\"}[5m])-1 > 30" + } + ], + "id": "4a83670c", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum": 2 + }, + "signature": [ + "query", + "sum", + "error-text", + "400 with different text: parse error : logql: expected ), got eof (Q)" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : parameter 50 not supported for operation sum_over_time | proxy parse error at line 1, col 15: syntax error: unexpected NUMBER, expecting NUMBER or { or (", + "query": "sum_over_time(50,{service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms| unwrap latency [5m])" + } + ], + "id": "b1f0d548", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query", + "sum_over_time[json,unwrap,label-filter,line-filter]", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected number, expecting number " + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : grouping not allowed for sum_over_time aggregation | proxy backend returned 400: cannot parse query arg [\u2026]: cannot parse \"filter\"\u2026\"5\" and \",\"; probably, the whole string must be put into quotes", + "query": "sum_over_time({service_name=\"api-gateway\"} |= \"level=error\" | json |foo>=5,bar<25ms| unwrap latency [5m]) by (foo)" + } + ], + "id": "42cdded9", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum_over_time[json,unwrap,label-filter,line-filter]": 2 + }, + "signature": [ + "query", + "sum_over_time[json,unwrap,label-filter,line-filter]", + "error-text", + "400 with different text: backend returned N: cannot parse query arg [\u2026]: cannot parse Q\u2026Q and Q; probably" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy log queries are not supported as an instant query type, please change your query to a range query type", + "query": "topk by (name)(10,sum(rate({namespace=\"prod\"}[5m])))" + } + ], + "id": "4a1aab68", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "topk": 2 + }, + "signature": [ + "query", + "topk", + "status", + "loki 200 proxy 400: log queries are not supported as an instant query type, please change your query" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki - | proxy unsupported instant aggregation target", + "query": "topk( 10, sum by (ip) ( sum_over_time({service_name=\"api-gateway\"} | json | unwrap bytes(bytes)[1m]) ) )" + } + ], + "id": "9c93bc66", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "topk[json,unwrap]": 2 + }, + "signature": [ + "query", + "topk[json,unwrap]", + "status", + "loki 200 proxy 400: unsupported instant aggregation target" + ], + "sources": { + "loki-tests": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 898 series, proxy 898; loki-only [\"{\\\"duration_s\\\": \\\"14.0\\\"}\", \"{\\\"duration_s\\\": \\\"149.0\\\"}\"] proxy-only [\"{\\\"duration_s\\\": \\\"14\\\"}\", \"{\\\"duration_s\\\": \\\"149\\\"}\"]", + "query": "sum by (duration_s) (count_over_time({service_name=\"batch-etl\"} | json [5m]))" + } + ], + "id": "056ee345", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json]": 2 + }, + "signature": [ + "query", + "sum[json]", + "series", + "different series, same count" + ], + "sources": { + "seeded": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 898 series, proxy 898; loki-only [\"{\\\"duration_s\\\": \\\"14.0\\\"}\", \"{\\\"duration_s\\\": \\\"149.0\\\"}\"] proxy-only [\"{\\\"duration_s\\\": \\\"14\\\"}\", \"{\\\"duration_s\\\": \\\"149\\\"}\"]", + "query": "sum by (duration_s) (count_over_time({service_name=\"batch-etl\"} | json | duration_s!=\"\" [5m]))" + } + ], + "id": "7938c66e", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,label-filter]": 2 + }, + "signature": [ + "query", + "sum[json,label-filter]", + "series", + "different series, same count" + ], + "sources": { + "seeded": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 1 series, proxy 2; loki-only [] proxy-only [\"{\\\"level\\\": \\\"info\\\"}\"]", + "query": "sum by (level) (max_over_time({service_name=\"cache-redis\"} | logfmt | unwrap size_bytes | __error__=\"\" [5m]))" + } + ], + "id": "37898b71", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,unwrap,label-filter]": 2 + }, + "signature": [ + "query", + "sum[logfmt,unwrap,label-filter]", + "series", + "proxy-more" + ], + "sources": { + "seeded": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 37: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "absent_over_time({app=\"api-gateway\"})" + } + ], + "id": "85e26f96", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "absent_over_time": 2 + }, + "signature": [ + "query", + "absent_over_time", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 5 series, proxy 494", + "query": "avg by(app)(avg_over_time({env=\"production\"} | json | unwrap duration_ms [5m]))" + } + ], + "id": "2ec3f6c0", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "avg[json,unwrap]": 2 + }, + "signature": [ + "query", + "avg[json,unwrap]", + "series", + "proxy-more" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 36: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "bytes_over_time({app=\"api-gateway\"})" + } + ], + "id": "717fedc0", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_over_time": 2 + }, + "signature": [ + "query", + "bytes_over_time", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 5302 series, proxy 1120", + "query": "count without (cluster) (count_over_time({env=\"production\"}[5m]))" + } + ], + "id": "272583f4", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count": 2 + }, + "signature": [ + "query", + "count", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "count_over_time({app=\"api-gateway\",env=\"production\"} |= \"GET\" | json | status>=200 | status<400 [5m])" + } + ], + "id": "60488a50", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json,label-filter,line-filter]": 2 + }, + "signature": [ + "query", + "count_over_time[json,label-filter,line-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['duration_ms_extracted', 'ip_extracted', 'method_extracted', 'path_extracted', 'region_extracted', 'request_id_extracted']", + "query": "count_over_time({app=\"api-gateway\"} != \"error\" | json [1m])" + } + ], + "id": "9be22235", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json,line-filter]": 2 + }, + "signature": [ + "query", + "count_over_time[json,line-filter]", + "labels", + "proxy-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 0, col 44: not a valid duration string: \"WINDOW\" | proxy parse error : logql: expected duration: logql: expected DURATION, got IDENT (\"WINDOW\")", + "query": "count_over_time({app=\"api-gateway\"} | json [WINDOW])" + } + ], + "id": "e7cdda75", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json]": 2 + }, + "signature": [ + "query", + "count_over_time[json]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got ident (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 0, col 59: not a valid duration string: \"WINDOW\" | proxy parse error : logql: expected duration: logql: expected DURATION, got IDENT (\"WINDOW\")", + "query": "count_over_time({app=\"api-gateway\"} | json | method=\"GET\" [WINDOW])" + } + ], + "id": "acc8d4a9", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[json,label-filter]": 2 + }, + "signature": [ + "query", + "count_over_time[json,label-filter]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got ident (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : stage '|~ \"a{2,1}\"' : error parsing regexp: invalid repeat count: `{2,1}` | proxy cannot parse query arg [\u2026]: invalid regexp \"_msg\":\"a{\u2026}\": error parsing regexp: invalid repeat count: `{\u2026}`", + "query": "count_over_time({app=\"api-gateway\"} |~ \"a{2,1}\" [1m])" + } + ], + "id": "bf2a7f15", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[line-filter]": 2 + }, + "signature": [ + "query", + "count_over_time[line-filter]", + "error-text", + "400 with different text: cannot parse query arg [\u2026]: invalid regexp Q:Q: error parsing regexp: invalid re" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 36: syntax error: unexpected ) | proxy parse error at line 1, col 1: syntax error: unexpected ), expecting RANGE", + "query": "count_over_time({app=\"api-gateway\"})" + } + ], + "id": "b0921dfc", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time": 2 + }, + "signature": [ + "query", + "count_over_time", + "error-text", + "400 with different text: parse error at line N, col N: syntax error: unexpected ), expecting range" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 0, col 50: not a valid duration string: \"WINDOW\" | proxy parse error : logql: expected duration: logql: expected DURATION, got IDENT (\"WINDOW\")", + "query": "count_over_time({app=\"payment-service\"} | logfmt [WINDOW])" + } + ], + "id": "26a758d3", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[logfmt]": 2 + }, + "signature": [ + "query", + "count_over_time[logfmt]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got ident (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 0 series, proxy 930", + "query": "count_over_time({service_name=\"api-gateway\"} | unpack | status >= 400 [5m])" + } + ], + "id": "2b0fb55a", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[unpack,label-filter]": 2 + }, + "signature": [ + "query", + "count_over_time[unpack,label-filter]", + "series", + "loki-empty" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 3 series, proxy 164", + "query": "max by (level)(max_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms [5m]))" + } + ], + "id": "9259c6c6", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "max[json,unwrap]": 2 + }, + "signature": [ + "query", + "max[json,unwrap]", + "series", + "proxy-more" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"app\": \"pricing-svc\"} @1791224400.0: loki 599 proxy 593.9", + "query": "max by(app)(quantile_over_time(0.99, {env=\"production\"} | json | unwrap duration_ms [5m]))" + } + ], + "id": "e6579e4c", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "max[json,unwrap]": 2 + }, + "signature": [ + "query", + "max[json,unwrap]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "max_over_time({app=\"api-gateway\"} | json | method=\"GET\" | unwrap duration_ms [5m])" + } + ], + "id": "31d74267", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "max_over_time[json,unwrap,label-filter]": 2 + }, + "signature": [ + "query", + "max_over_time[json,unwrap,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 20: syntax error: unexpected -, expecting NUMBER or { or ( | proxy parse error at line 1, col 1: invalid parameter for quantile_over_time: expected range [0, 1] but got -0.1", + "query": "quantile_over_time(-0.1, {app=\"api-gateway\"} | json | unwrap duration_ms [5m])" + } + ], + "id": "24b236fd", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "quantile_over_time[json,unwrap]": 2 + }, + "signature": [ + "query", + "quantile_over_time[json,unwrap]", + "error-text", + "400 with different text: parse error at line N, col N: invalid parameter for quantile_over_time: expected" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 0, col 33: not a valid duration string: \"WINDOW\" | proxy parse error : logql: expected duration: logql: expected DURATION, got IDENT (\"WINDOW\")", + "query": "rate({app=\"api-gateway\"} | json [WINDOW])" + } + ], + "id": "291568c8", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json]": 2 + }, + "signature": [ + "query", + "rate[json]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got ident (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 387 series, proxy 305", + "query": "rate({namespace=\"prod\"} |= \"error\"[5m])" + } + ], + "id": "dbd817af", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[line-filter]": 2 + }, + "signature": [ + "query", + "rate[line-filter]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 446 series, proxy 18", + "query": "rate({app=\"payment-service\"} | logfmt [1m])" + } + ], + "id": "6bc7202e", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[logfmt]": 2 + }, + "signature": [ + "query", + "rate[logfmt]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted']", + "query": "rate({app=\"payment-service\"} | logfmt | drop msg [5m])" + } + ], + "id": "5f23b125", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[logfmt,drop]": 2 + }, + "signature": [ + "query", + "rate[logfmt,drop]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but ca...", + "query": "rate({env=\"production\"} | json | __error__!=\"\" [5m])" + } + ], + "id": "c44d6ae3", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "rate[json,label-filter]": 2 + }, + "signature": [ + "query", + "rate[json,label-filter]", + "error-text", + "400 with different text: pipeline error: Q for series: Qt find closing Q symbol\", app=..." + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"app\": \"batch-etl\"} @1791224400.0: loki 122.80444335937499 proxy 911", + "query": "stdvar by(app)(count_over_time({env=\"production\"}[5m]))" + } + ], + "id": "65210f05", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "stdvar": 2 + }, + "signature": [ + "query", + "stdvar", + "values", + "values differ (proxy higher)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but ca...", + "query": "sum by (level) (count_over_time({namespace=\"prod\"}|json|level!=\"\"[2m]))" + } + ], + "id": "1597db8f", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,label-filter]": 2 + }, + "signature": [ + "query", + "sum[json,label-filter]", + "error-text", + "400 with different text: pipeline error: Q for series: Qt find closing Q symbol\", app=..." + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 0, col 78: missing closing ']' in duration | proxy parse error : logql: expected duration: logql: expected DURATION, got EOF (\"\")", + "query": "sum by (level, detected_level) (count_over_time({app=~\".+\"} | drop __error__ [" + } + ], + "id": "235b2fd7", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[drop]": 2 + }, + "signature": [ + "query", + "sum[drop]", + "error-text", + "400 with different text: parse error : logql: expected duration: logql: expected duration, got eof (Q)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error : stage '|~ \"a{2,1}\"' : error parsing regexp: invalid repeat count: `{2,1}` | proxy cannot parse query arg [\u2026]: invalid regexp \"_msg\":\"a{\u2026}\": error parsing regexp: invalid repeat count: `{\u2026}`", + "query": "sum(count_over_time({app=\"api-gateway\"} |~ \"a{2,1}\" [1m]))" + } + ], + "id": "80af726a", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[line-filter]": 2 + }, + "signature": [ + "query", + "sum[line-filter]", + "error-text", + "400 with different text: cannot parse query arg [\u2026]: invalid regexp Q:Q: error parsing regexp: invalid re" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 82: syntax error: unexpected unwrap, expecting IDENTIFIER or ( | proxy parse error : syntax error: unexpected unwrap", + "query": "sum_over_time({app=\"api-gateway\",env=\"production\"} | json | unwrap duration_ms | unwrap status [5m])" + } + ], + "id": "236be44b", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum_over_time[json,unwrap]": 2 + }, + "signature": [ + "query", + "sum_over_time[json,unwrap]", + "error-text", + "400 with different text: parse error : syntax error: unexpected unwrap" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted']", + "query": "sum_over_time({app=\"payment-service\",env=\"production\"} | logfmt | unwrap latency_ms [5m])" + } + ], + "id": "99c8515b", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum_over_time[logfmt,unwrap]": 2 + }, + "signature": [ + "query", + "sum_over_time[logfmt,unwrap]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki pipeline error: 'JSONParserErr' for series: '{__error__=\"JSONParserErr\", __error_details__=\"Value looks like object, but can't find closing '}' symbol\", app=... | proxy -", + "query": "topk(3, rate({cluster=\"us-east-1\"} | json [5m]))" + } + ], + "id": "556649c4", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "topk[json]": 2 + }, + "signature": [ + "query", + "topk[json]", + "status", + "loki 400 proxy 200: -" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "bytes_over_time({app=\"api-gateway\",env=\"production\"} | json | status >= 500 [5m])" + } + ], + "id": "3f356b01", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_over_time[json,label-filter]": 2 + }, + "signature": [ + "query", + "bytes_over_time[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "label names ['level_extracted', 'service_name_extracted', 'version_extracted']", + "query": "bytes_rate({app=\"api-gateway\",env=\"production\"} | json | status >= 500 [5m])" + } + ], + "id": "9f075b30", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "bytes_rate[json,label-filter]": 2 + }, + "signature": [ + "query", + "bytes_rate[json,label-filter]", + "labels", + "loki-only *_extracted" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki parse error at line 1, col 48: syntax error: unexpected ) | proxy unsupported instant aggregation target", + "query": "bottomk(1, {app=\"api-gateway\",env=\"production\"})" + } + ], + "id": "702df3da", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "bottomk": 2 + }, + "signature": [ + "query", + "bottomk", + "error-text", + "400 with different text: unsupported instant aggregation target" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 4 series, proxy 7; loki-only [] proxy-only [\"{\\\"detected_level\\\": \\\"debug\\\", \\\"level\\\": \\\"info\\\"}\", \"{\\\"detected_level\\\": \\\"info\\\", \\\"level\\\": \\\"error\\\"}\"]", + "query": "sum by (level, detected_level) (count_over_time({env=\"production\"} | logfmt | drop __error__ [1m]))" + } + ], + "id": "e7da85bf", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,drop]": 2 + }, + "signature": [ + "query", + "sum[logfmt,drop]", + "series", + "proxy-more" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"level\": \"info\"} @1791224400.0: loki 4148 proxy 4138", + "query": "sum by (level) (count_over_time({env=\"production\"} | logfmt | level=`info` | drop __error__ [1m]))" + } + ], + "id": "08fb5da3", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,drop,label-filter]": 2 + }, + "signature": [ + "query", + "sum[logfmt,drop,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"detected_level\": \"info\", \"level\": \"info\"} @1791224400.0: loki 4148 proxy 4138", + "query": "sum by (level, detected_level) (count_over_time({env=\"production\"} | detected_level=\"info\" | json | drop __error__ [1m]))" + } + ], + "id": "5e798d62", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,drop,detected_level,label-filter]": 2 + }, + "signature": [ + "query", + "sum[json,drop,detected_level,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 446 series, proxy 378; loki-only [\"{\\\"job_id\\\": \\\"job_002519df\\\"}\", \"{\\\"job_id\\\": \\\"job_02d477c3\\\"}\"] proxy-only []", + "query": "sum by (job_id) (count_over_time({env=\"production\"} | detected_level=\"info\" | logfmt | job_id!=\"\" [1m]))" + } + ], + "id": "9016b6b0", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,detected_level,label-filter]": 2 + }, + "signature": [ + "query", + "sum[logfmt,detected_level,label-filter]", + "series", + "proxy-fewer" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{} @1791224400.0: loki 202050 proxy 201440", + "query": "sum(count_over_time({env=\"production\"} | detected_level=\"info\" | json | logfmt | drop __error__, __error_details__ [1h]))" + } + ], + "id": "f248d966", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[json,logfmt,drop,detected_level,label-filter]": 2 + }, + "signature": [ + "query", + "sum[json,logfmt,drop,detected_level,label-filter]", + "values", + "values differ (proxy lower)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "{\"lvl\": \"error\"} @1791224400.0: loki 207 proxy 240", + "query": "sum by (lvl) (count_over_time({env=\"production\"} | logfmt | label_format lvl=level [1m]))" + } + ], + "id": "47dddcfb", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "sum[logfmt,label_format]": 2 + }, + "signature": [ + "query", + "sum[logfmt,label_format]", + "values", + "values differ (same total)" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": null, + "examples": [ + { + "encoding": "categorize", + "endpoint": "query", + "excerpt": "loki 518 series, proxy 4273", + "query": "count_over_time({app=\"api-gateway\"} | regexp \"(?PGET|POST|PUT)\" [5m])" + } + ], + "id": "7ba35380", + "impact": "grafana-datasource", + "queries": 1, + "requests": 2, + "shapes": { + "count_over_time[regexp]": 2 + }, + "signature": [ + "query", + "count_over_time[regexp]", + "series", + "proxy-more" + ], + "sources": { + "repo": 2 + } + }, + { + "documented": "drilldown/detected-labels-sampled-cardinality", + "examples": [ + { + "encoding": "plain", + "endpoint": "detected_labels", + "excerpt": "component: loki 2 proxy 3", + "query": "{env=\"production\"}" + }, + { + "encoding": "plain", + "endpoint": "detected_labels", + "excerpt": "pod: loki 2990 proxy 164", + "query": "{service_name=\"api-gateway\"}" + } + ], + "id": "cbd9c560", + "impact": "drilldown-visible", + "queries": 16, + "requests": 16, + "shapes": { + "logs": 16 + }, + "signature": [ + "detected_labels", + "logs", + "cardinality", + "label cardinality differs [drilldown/detected-labels-sampled-cardinality]" + ], + "sources": { + "seeded": 16 + } + }, + { + "documented": "drilldown/detected-labels-sampled-cardinality", + "examples": [ + { + "encoding": "plain", + "endpoint": "detected_labels", + "excerpt": "pod: loki 800 proxy 43", + "query": "detected_labels?end=2026-10-05T18%3A20%3A00.000Z&query=%7Bservice_name%3D%22otel-collector%22%7D&start=2026-10-05T18%3A05%3A00.000Z" + }, + { + "encoding": "plain", + "endpoint": "detected_labels", + "excerpt": "pod: loki 800 proxy 43", + "query": "detected_labels?end=2026-10-05T18%3A20%3A00.000Z&query=%7Bservice_name%3D%22otel-collector%22%7D&start=2026-10-05T17%3A20%3A00.000Z" + } + ], + "id": "3550bbd8", + "impact": "drilldown-visible", + "queries": 4, + "requests": 4, + "shapes": { + "-": 4 + }, + "signature": [ + "detected_labels", + "-", + "cardinality", + "label cardinality differs [drilldown/detected-labels-sampled-cardinality]" + ], + "sources": { + "grafana": 4 + } + }, + { + "documented": "profiles/volume-bytes-accounting", + "examples": [ + { + "encoding": "plain", + "endpoint": "volume", + "excerpt": "{\"service_name\": \"api-gateway\"} @1791224400.0: loki 4329472 proxy 4236803", + "query": "index/volume?end=2026-10-05T18%3A20%3A00.000Z&limit=5000&query=%7Bservice_name%3D~%60.%2B%60%7D&start=2026-10-05T18%3A05%3A00.000Z" + }, + { + "encoding": "plain", + "endpoint": "volume", + "excerpt": "{\"service_name\": \"api-gateway\"} @1791224400.0: loki 17463296 proxy 17082777", + "query": "index/volume?end=2026-10-05T18%3A20%3A00.000Z&limit=5000&query=%7Bservice_name%3D~%60.%2B%60%7D&start=2026-10-05T17%3A20%3A00.000Z" + } + ], + "id": "08dc4434", + "impact": "drilldown-visible", + "queries": 2, + "requests": 2, + "shapes": { + "-": 2 + }, + "signature": [ + "volume", + "-", + "values", + "values differ (proxy lower) [profiles/volume-bytes-accounting]" + ], + "sources": { + "grafana": 2 + } + }, + { + "documented": "profiles/volume-bytes-accounting", + "examples": [ + { + "encoding": "plain", + "endpoint": "volume", + "excerpt": "{\"service_name\": \"api-gateway\"} @1791224400.0: loki 17463296 proxy 17082777", + "query": "{env=\"production\"}" + } + ], + "id": "4644306c", + "impact": "drilldown-visible", + "queries": 1, + "requests": 4, + "shapes": { + "logs": 4 + }, + "signature": [ + "volume", + "logs", + "values", + "values differ (proxy lower) [profiles/volume-bytes-accounting]" + ], + "sources": { + "seeded": 4 + } + }, + { + "documented": "profiles/volume-bytes-accounting", + "examples": [ + { + "encoding": "plain", + "endpoint": "volume_range", + "excerpt": "{\"service_name\": \"api-gateway\"} @1791221999.999: loki 286204 proxy 288795", + "query": "{env=\"production\"}" + } + ], + "id": "ae2b5d6d", + "impact": "drilldown-visible", + "queries": 1, + "requests": 4, + "shapes": { + "logs": 4 + }, + "signature": [ + "volume_range", + "logs", + "values", + "values differ (proxy lower) [profiles/volume-bytes-accounting]" + ], + "sources": { + "seeded": 4 + } + } + ], + "health": { + "lines_loki": 291425, + "lines_vl": 291425, + "loki_restarts": 0, + "loki_restarts_after": 0, + "not_reproducible": 12, + "ok": true, + "ok_after": true, + "services_in_loki_metric": 15, + "services_listed": 15, + "slices": 6, + "slices_differing": [], + "vl_restarts": 0, + "vl_restarts_after": 0 + }, + "passes": { + "pass1": { + "checked": [ + "2026-10-05T18:29:43Z", + "2026-10-05T19:01:11Z" + ], + "lines": 291425, + "ok": [ + true, + true + ], + "restarts_unchanged": true + }, + "repo": { + "checked": [ + "2026-10-05T19:54:45Z", + "2026-10-05T20:12:30Z" + ], + "lines": 291425, + "ok": [ + true, + true + ], + "restarts_unchanged": true + } + }, + "recorded": "2026-10-05T20:16:15Z", + "ref": "fcaccfcc", + "summary": { + "grafana": { + "blocked": 0, + "diff": 34, + "requests": 159, + "same": 125, + "vacuous": 0 + }, + "loki-tests": { + "blocked": 24, + "diff": 1850, + "requests": 3570, + "same": 448, + "vacuous": 1248 + }, + "repo": { + "blocked": 17, + "diff": 935, + "requests": 2124, + "same": 912, + "vacuous": 260 + }, + "seeded": { + "blocked": 13, + "diff": 2424, + "requests": 5033, + "same": 2492, + "vacuous": 104 + } + }, + "window": [ + 1791220800, + 1791224400 + ] +} diff --git a/conformance/registry/generated/wiring.json b/conformance/registry/generated/wiring.json index 61546bc2d..a6f3f5b8e 100644 --- a/conformance/registry/generated/wiring.json +++ b/conformance/registry/generated/wiring.json @@ -432,7 +432,7 @@ "test/e2e-compat/range_metric_offset_series_limit_test.go" ] }, - "registry_items": 213, + "registry_items": 274, "routes_without_registry_item": [], "test_to_items": { "cmd/proxy/main_test.go": [ @@ -844,6 +844,13 @@ "api_prom_tail", "data-density-and-chart-quality", "data-probing-and-freshness", + "drilldown/detected-field-values", + "drilldown/detected-fields-type-inference", + "drilldown/detected-labels-sampled-cardinality", + "drilldown/patterns-mined-differently", + "drilldown/plugin-legend-sync-race", + "limits/drilldown-7d-breakdown-response-limit", + "limits/drilldown-breakdown-keeps-busiest", "limits/series-cap-and-byte-budget-interact", "loki_api_v1_cache_generation_numbers", "loki_api_v1_delete", @@ -859,15 +866,69 @@ "loki_api_v1_rules_namespace", "loki_api_v1_rules_namespace_groupname", "loki_api_v1_status_buildinfo", + "metric-identity/structured-metadata-splits-series", "numeric-and-response-formatting", + "parsed-identity/bare-parser-metric-duplicates-extracted", + "parsed-identity/dynamic-key-parsers", "parsed-identity/regexp-partial-match-series-count", + "profiles/detected-fields-no-extracted-suffix", + "profiles/existence-filter-on-sanitized-key", + "profiles/grafana-stats-error-as-warning", + "profiles/json-extraction-list-exposes-only-listed", + "profiles/json-extraction-list-missing-key", "profiles/json-line-stored-without-line", + "profiles/json-number-label-text", + "profiles/keep-removes-derived-service-name", + "profiles/keep-structured-metadata-key", "profiles/label-endpoints-window-and-data", "profiles/label-filter-regexp-anchoring", + "profiles/metadata-default-lookback", + "profiles/multi-tenant-volume-merge", "profiles/structured-metadata-label-collision", + "profiles/volume-bytes-accounting", + "profiles/volume-detected-level-extension", "prometheus_api_v1_alerts", "prometheus_api_v1_rules", - "service-name-derivation" + "quality/detected-metadata-proxy-cache", + "quality/index-stats-bytes-and-chunks", + "quality/index-stats-loki-chunk-accounting", + "quality/index-stats-streams-differ", + "quality/label-cache-aged-labels-kept", + "quality/label-values-chunk-window", + "quality/stats-volume-freshness", + "semantics/absent-over-time-semantics", + "semantics/binary-ignoring-matching", + "semantics/count-values-unsupported", + "semantics/eager-pipeline-validation", + "semantics/extracted-suffix-collision", + "semantics/first-last-over-time-translation", + "semantics/instant-result-type-missing", + "semantics/ip-filter-precision", + "semantics/label-filter-expression-syntax", + "semantics/label-replace-new-label", + "semantics/line-filter-or-alternatives", + "semantics/log-entries-differ-same-count", + "semantics/logfmt-existence-filter-ungrouped-sum", + "semantics/non-sum-vector-aggregation-unreduced", + "semantics/parenthesized-log-range", + "semantics/parse-error-labels-on-log-lines", + "semantics/parse-error-text-parity", + "semantics/pattern-line-filter", + "semantics/proxy-accepts-invalid-logql", + "semantics/proxy-rejects-valid-logql", + "semantics/stddev-stdvar-grouped-values", + "semantics/tail-history-start-limit", + "semantics/topk-bottomk-range-series", + "semantics/unpack-non-string-values", + "semantics/unwrap-conversion-error", + "semantics/unwrap-range-last-point", + "semantics/unwrap-sample-validity", + "service-name-derivation", + "severity/detected-level-filter-json-level", + "severity/detected-level-stage-side-effects", + "versions/capabilities-floor-vs-matrix", + "versions/format-if-before-vl-1.35", + "versions/otlp-severity-before-vl-1.35" ], "wired_items": 89 } diff --git a/conformance/reports/gaps.md b/conformance/reports/gaps.md index fe9c14779..fe884d5e8 100644 --- a/conformance/reports/gaps.md +++ b/conformance/reports/gaps.md @@ -63,20 +63,20 @@ Semantics, severity, identity and data-quality behaviour the proxy must reproduc | limits | `backend-admission-and-heavy-query-queueing` — Heavy backend work is admitted, queued, then refused like Loki's scheduler | 15 | 7 | proven | | resource_control | `backend-deadlines-and-cancellation` — Work the client has given up on stops in the backend too | 6 | 2 | proven | | data_quality | `data-density-and-chart-quality` — Chart density, zero-fill and high-cardinality behaviour | 6 | 0 | gap | -| data_quality | `data-probing-and-freshness` — Probes the proxy runs, and their cost and staleness | 6 | 0 | gap | -| limits | `heavy-metric-fetch-bounds` — Metric evaluation reads bounded work from VictoriaLogs, or refuses early | 9 | 3 | partial | -| semantics | `loki-compatible-profile` — The Loki-compatible profile holds requests and responses to Loki's contract | 10 | 5 | partial | -| identity | `metric-series-identity` — A metric series is named by the stream, its metadata and its parsed labels | 4 | 2 | partial | +| data_quality | `data-probing-and-freshness` — Probes the proxy runs, and their cost and staleness | 9 | 0 | gap | +| limits | `heavy-metric-fetch-bounds` — Metric evaluation reads bounded work from VictoriaLogs, or refuses early | 10 | 3 | partial | +| semantics | `loki-compatible-profile` — The Loki-compatible profile holds requests and responses to Loki's contract | 13 | 5 | partial | +| identity | `metric-series-identity` — A metric series is named by the stream, its metadata and its parsed labels | 6 | 2 | partial | | semantics | `numeric-and-response-formatting` — Timestamps, number formatting and empty shapes | 5 | 0 | gap | | limits | `operator-configurable-limits` — Every bound on work is an operator flag, documented from one source | 13 | 7 | proven | -| identity | `parsed-label-series-identity` — Labels a parser extracts are part of a metric series | 4 | 4 | partial | -| semantics | `parser-error-and-label-collision` — Parser errors, __error__ and _extracted collisions | 14 | 3 | partial | +| identity | `parsed-label-series-identity` — Labels a parser extracts are part of a metric series | 6 | 4 | partial | +| semantics | `parser-error-and-label-collision` — Parser errors, __error__ and _extracted collisions | 18 | 3 | partial | | limits | `series-limits-and-partial-results` — Series limits: error, or partial result with a warning | 9 | 9 | partial | -| identity | `service-name-derivation` — service_name follows Loki's discovery order | 8 | 0 | partial | -| severity | `severity-detected-level-derivation` — detected_level is derived on the read path | 8 | 4 | partial | -| severity | `severity-exposure-surfaces` — Where detected_level and level must appear | 6 | 3 | partial | +| identity | `service-name-derivation` — service_name follows Loki's discovery order | 9 | 0 | partial | +| severity | `severity-detected-level-derivation` — detected_level is derived on the read path | 10 | 4 | partial | +| severity | `severity-exposure-surfaces` — Where detected_level and level must appear | 7 | 3 | partial | | limits | `tenant-query-limits` — Loki's per-tenant query limits, enforced as published | 13 | 3 | proven | -| semantics | `window-bounds-and-step-alignment` — Range windows, bucket edges and step alignment | 7 | 3 | partial | +| semantics | `window-bounds-and-step-alignment` — Range windows, bucket edges and step alignment | 8 | 3 | partial | ## LogQL surface (v3.7.7) diff --git a/conformance/reports/parity-gaps.md b/conformance/reports/parity-gaps.md new file mode 100644 index 000000000..83efe9748 --- /dev/null +++ b/conformance/reports/parity-gaps.md @@ -0,0 +1,87 @@ +# Proxy vs Loki: ranked differences + +Generated by `conformance/scripts/parity_gaps.py` from the registry cases that carry a `gap:` +block and the last differential run (`bench/parity`, recorded in +`conformance/registry/generated/parity-discovery.json`). Do not edit by hand. + +Rank: open first, then user impact (explore- and drilldown-visible before the datasource path, +API-only and edge cases), then the corpus queries that hit the gap's clusters. Fixed gaps are +listed apart until a new run no longer shows their clusters. + +Last run: 2026-10-05T20:16:15Z on `fcaccfcc`, window [1791220800, 1791224400]. + +| corpus source | requests | same | differ | both empty | blocked | +|---|---:|---:|---:|---:|---:| +| grafana | 159 | 125 | 34 | 0 | 0 | +| loki-tests | 3570 | 448 | 1850 | 1248 | 24 | +| repo | 2124 | 912 | 935 | 260 | 17 | +| seeded | 5033 | 2492 | 2424 | 104 | 13 | + +| # | case | status | impact | queries | clusters | effort | area | Loki reference | planned test | +|---:|---|---|---|---:|---:|---|---|---|---| +| 1 | `semantics/extracted-suffix-collision` — A parsed key named like a stream label is renamed with _extracted in query results (open; detected_fields decision unchanged) | open | explore-visible | 749 | 76 | M | internal/proxy stage-field exposure (parsed-label naming) | pkg/logql/log/parser.go:25 duplicateSuffix; :152 json, :342 logfmt, :696 json expression | TestCompat_ExtractedSuffixOnParsedCollision (test/e2e-compat, group semantics) | +| 2 | `semantics/unwrap-sample-validity` — unwrap emits samples Loki skips: a missing label gives "" values, a unit string is passed through (open) | open | explore-visible | 295 | 64 | M | internal/proxy range-metric unwrap evaluation (stats pushdown over a missing/non-numeric field) | pkg/logql/log/metrics_extraction.go:202-231 streamLabelSampleExtractor.Process | TestCompat_UnwrapMissingAndNonNumeric (test/e2e-compat, group semantics) | +| 3 | `profiles/structured-metadata-label-collision` — Structured metadata named like a stream label gets Loki's _extracted suffix (open gap) | open | explore-visible | 293 | 20 | M | internal/proxy structured-metadata naming in log responses (collision with derived service_name) | pkg/logql/log/labels.go LabelsBuilder (structured metadata renamed with _extracted on a stream-label collision) | TestCompat_StructuredMetadataLabelCollision (test/e2e-compat, group otel-edge) | +| 4 | `profiles/keep-removes-derived-service-name` — `\| keep` removes service_name the proxy derived for a stream without one (open) | open | explore-visible | 117 | 5 | S | internal/proxy service_name derivation after keep/drop | pkg/logql/log/keep_labels.go:15-44 KeepLabels.Process | TestCompat_KeepRemovesDerivedServiceName (test/e2e-compat, group semantics) | +| 5 | `semantics/non-sum-vector-aggregation-unreduced` — max, min, count and avg over a range function return one series per stream on query_range (open) | open | explore-visible | 92 | 24 | M | internal/translator seriesLevelOuterAggregation (range path) + internal/proxy range stats folding | pkg/logql/evaluator.go:400-529 newVectorAggEvaluator / VectorAggEvaluator.Next (fold per group per step) | TestCompat_NonSumVectorAggregationRange (test/e2e-compat, group semantics) over max/min/count/avg x by/without/none | +| 6 | `semantics/parenthesized-log-range` — A parenthesized log expression under a range function fails translation (open) | open | explore-visible | 77 | 41 | S | internal/logql parser / internal/translator (LogRangeExpr with parenthesized left side) | pkg/logql/syntax/syntax.y logRangeExpr: OPEN_PARENTHESIS logExpr CLOSE_PARENTHESIS RANGE | TestCompat_ParenthesizedLogRange (test/e2e-compat, group semantics) | +| 7 | `semantics/line-filter-or-alternatives` — Line filters with `or` alternatives are rejected (open) | open | explore-visible | 42 | 6 | S | internal/logql parser (line filter alternatives) + translator | pkg/logql/syntax/ast.go:473 newOrLineFilterExpr | TestCompat_LineFilterOrAlternatives (test/e2e-compat, group semantics) | +| 8 | `severity/detected-level-stage-side-effects` — keep/drop detected_level and a detected_level filter change other labels (open) | open | explore-visible | 37 | 3 | S | internal/proxy/detected_level.go (level/detected_level coupling in keep/drop/filter) | pkg/logql/log/keep_labels.go, drop_labels.go (detected_level is ordinary structured metadata) | TestCompat_DetectedLevelKeepDrop (test/e2e-compat, group drilldown) | +| 9 | `semantics/logfmt-existence-filter-ungrouped-sum` — An ungrouped sum over `\| logfmt \| key!=""` returns no series (open) | open | explore-visible | 36 | 1 | S | internal/proxy/ordered_json_metric.go setStatsPushdown / pushdownLabelFilters (logfmt, ungrouped) | pkg/logql/log/parser.go:398-409 logfmt label extraction; label filter on the parsed label | TestCompat_LogfmtExistenceFilterUngroupedSum (test/e2e-compat, group semantics), cases semantics/json-filter-pushdown-ungrouped-sum sibling | +| 10 | `parsed-identity/bare-parser-metric-duplicates-extracted` — A bare `\| json` metric labels each series with every parsed key twice, once with _extracted (open) | open | explore-visible | 34 | 14 | S | internal/proxy exact parser series identity (bare parser metric labels) | pkg/logql/log/parser.go:152 (suffix only on collision) | TestCompat_BareParserMetricLabels (test/e2e-compat, group semantics) | +| 11 | `semantics/topk-bottomk-range-series` — topk/bottomk on query_range return k series overall, not the per-step top k (open) | open | explore-visible | 29 | 7 | M | internal/proxy topk/bottomk range ranking (cases limits/top-n-ranking-not-silent) | pkg/logql/evaluator.go:443-494 VectorAggEvaluator topk/bottomk per step | TestCompat_TopkRangePerStep (test/e2e-compat, group semantics) | +| 12 | `profiles/keep-structured-metadata-key` — `\| keep` of a structured-metadata key drops it (open) | open | explore-visible | 24 | 1 | S | internal/proxy keep stage over structured metadata (Loki-compatible profile names) | pkg/logql/log/keep_labels.go:15-44 | TestCompat_KeepStructuredMetadataKey (test/e2e-compat, group otel-edge) | +| 13 | `semantics/label-filter-expression-syntax` — Comma-joined and parenthesized label-filter expressions are rejected (open) | open | explore-visible | 24 | 11 | M | internal/logql parser (label filter grammar) + translator filter emission | pkg/logql/syntax/syntax.y labelFilter: labelFilter COMMA labelFilter, OPEN_PARENTHESIS labelFilter CLOSE_PARENTHESIS | TestCompat_LabelFilterExpressionSyntax (test/e2e-compat, group semantics) | +| 14 | `profiles/json-extraction-list-exposes-only-listed` — Extraction lists (`\| json msg="msg"`, `\| logfmt op`) expose level from the line (open) | open | explore-visible | 18 | 1 | S | internal/proxy stage-field exposure (extraction lists) | pkg/logql/log/parser.go:671-700 JSONExpressionParser.Process (listed ids only) | TestCompat_JSONExtractionListExposesOnlyListed (test/e2e-compat, group semantics) | +| 15 | `semantics/first-last-over-time-translation` — first_over_time and last_over_time with offset or a line filter fail in VictoriaLogs (open) | open | explore-visible | 16 | 10 | S | internal/translator first/last_over_time emission (row_min/row_max with offset and filters) | pkg/logql/first_last_over_time.go | TestCompat_FirstLastOverTimeOffsetFilter (test/e2e-compat, group semantics) | +| 16 | `semantics/tail-history-start-limit` — Native tail ignores start and limit: Grafana Live shows no history where Loki shows its last 100 lines (open) | open | explore-visible | 15 | 1 | M | internal/proxy/tail.go openNativeTailStream (history replay before the live stream) | pkg/loghttp/tail.go:70-94 ParseTailQuery (limit, start); querier tailer sends history first | TestCompat_TailHistoryStartLimit (test/e2e-compat, group tail-multitenancy) + @explore-tail Playwright | +| 17 | `semantics/binary-ignoring-matching` — `+ ignoring(label)` between two sums joins the wrong series (open) | open | explore-visible | 10 | 2 | S | internal/proxy/vector_matching.go (ignoring with a label absent on both sides) | pkg/logql/evaluator.go binary vector matching (ignoring) | TestCompat_BinaryIgnoringAbsentLabel (test/e2e-compat, group semantics) | +| 18 | `semantics/parse-error-labels-on-log-lines` — A line a parser rejects carries __error__ and __error_details__ in Loki's log answer (open) | open | explore-visible | 10 | 2 | M | internal/proxy log-row assembly (stage-field exposure) + translator parse-error predicate | pkg/logql/log/parser.go:97 addErrLabel(errJSON, err) -> :777-787 SetErr/SetErrorDetails; pkg/logql/log/error.go:5 errJSON | TestCompat_ParseErrorLabelsOnLogLines (test/e2e-compat, group semantics) | +| 19 | `semantics/absent-over-time-semantics` — absent_over_time answers when data exists and stays empty when it is absent, after a parser (open) | open | explore-visible | 8 | 6 | S | internal/translator absent_over_time (parser pipelines) | pkg/logql/evaluator.go:647-664, :1320 absentLabels | TestCompat_AbsentOverTimeWithParser (test/e2e-compat, group semantics) | +| 20 | `semantics/stddev-stdvar-grouped-values` — stddev and stdvar by a label compute different values (open) | open | explore-visible | 7 | 5 | S | internal/translator stddev/stdvar outer aggregation (by-group fold) | pkg/logql/evaluator.go VectorAggEvaluator stddev/stdvar | TestCompat_StddevStdvarBy (test/e2e-compat, group semantics) | +| 21 | `semantics/label-replace-new-label` — label_replace does not add a new destination label (open) | open | explore-visible | 5 | 1 | S | internal/proxy label_replace post-processing | pkg/logql/evaluator.go:387, :1253 label_replace evaluator | TestCompat_LabelReplaceNewLabel (test/e2e-compat, group semantics) | +| 22 | `quality/label-values-chunk-window` — Loki lists label values of streams whose chunks overlap the window; the proxy only of streams with lines in it (open) | open | explore-visible | 2 | 2 | M | internal/proxy label values window (chunk-overlap semantics) | pkg/querier/querier.go Label (index lookups by chunk overlap) | TestCompat_LabelValuesWindowEdge (test/e2e-compat, group core) | +| 23 | `semantics/pattern-line-filter` — An empty pattern line filter (`\|> ""`, `!> ""`) selects the opposite lines (open) | open | explore-visible | 2 | 2 | S | internal/translator pattern line filter | pkg/logql/syntax/lex.go:26,29 (NPA, PIPE_PATTERN); pkg/logql/log/filter.go pattern filter | TestCompat_PatternLineFilter (test/e2e-compat, group semantics) | +| 24 | `semantics/log-entries-differ-same-count` — A log query returns the same number of lines with different entries (open, cause not isolated) | open | explore-visible | 1 | 1 | S | to be isolated (log query paging / numeric label filters) | pkg/logql/log/label_filter.go numeric filters; pkg/iter entry ordering | TestCompat_NumericLabelFilterLines (test/e2e-compat, group semantics) | +| 25 | `profiles/label-filter-regexp-anchoring` — A label-filter regexp after a parser is not anchored the way Loki's is (open) | open | explore-visible | — | — | S | internal/translator label-filter regexp emission (anchor ^(?:re)$); external PR #572 open | pkg/logql/log/label_filter.go (regexp matcher on the whole value) | TestCompat_LabelFilterRegexpAnchoring (test/e2e-compat, group semantics) | +| 26 | `quality/label-cache-aged-labels-kept` — Labels that age out of the window are never dropped from the merged label cache (open) | open | explore-visible | — | — | S | internal/proxy/label_metadata.go:1259 mergeLabelsIntoCache | pkg/querier/querier.go Label (window-bounded) | TestE2E_LabelsAgeOutOfWindow (test/e2e-compat, group core) | +| 27 | `semantics/unwrap-conversion-error` — An unwrap over non-numeric values fails in Loki with a 400 SampleExtractionErr; the proxy answers 200 (open) | open | explore-visible | — | — | M | internal/proxy range metric unwrap path (one-lookup probe like pipeline-error-text-parity) | pkg/logql/log/metrics_extraction.go SampleExtractionErr; logqlmodel.PipelineError | TestCompat_UnwrapConversionErrorParity (test/e2e-compat, group semantics) | +| 28 | `semantics/unwrap-range-last-point` — A grouped unwrap range metric misses the sample at `end` (open) | open | explore-visible | — | — | S | internal/proxy range-metric unwrap path (end bucket) | pkg/logql/range_vector.go step iteration start..end inclusive | TestCompat_UnwrapRangeEndSample (test/e2e-compat, group semantics) | +| 29 | `drilldown/detected-field-values` — detected_field values differ: none for service_name, other values for numeric and id fields (open) | open | drilldown-visible | 33 | 3 | M | internal/proxy detected_field values (sampling window and stream-label fields) | pkg/querier detected field values (v3.7.7): values of the sampled lines | TestCompat_DetectedFieldValues (test/e2e-compat, group drilldown) | +| 30 | `profiles/json-number-label-text` — JSON numbers become label values in a different text form ("14.0" vs "14") (open) | open | drilldown-visible | 12 | 6 | M | internal/proxy JSON number rendering of unpacked values (VictoriaLogs unpack_json normalises) | pkg/logql/log/parser.go readValue (raw number bytes) | TestCompat_JSONNumberLabelText (test/e2e-compat, group drilldown) | +| 31 | `severity/detected-level-filter-json-level` — `\| detected_level=` filters select different lines than Loki (JSON-level lines, stream level precedence) (open) | open | drilldown-visible | 12 | 8 | M | internal/translator/detected_level_logsql.go (filter predicate) vs internal/proxy/detected_level.go (response derivation) | pkg/distributor field detection (v3.7.7) stores detected_level as structured metadata; filter reads it | TestCompat_DetectedLevelFilterJSONLevel (test/e2e-compat, group drilldown) | +| 32 | `limits/drilldown-7d-breakdown-response-limit` — Metric queries fail on the 64 MiB buffered-response budget where Loki answers (open) | open | drilldown-visible | 8 | 4 | M | internal/proxy/range_metric_compat.go:767, execution_limits.go:11 (raw-row metric paths buffer rows; push down or stream) | pkg/logql engine series limit (max_query_series) with partial result for Drilldown | TestCompat_MetricBufferedBudget1h + TestE2E_Drilldown7dHighCardinalityBreakdown (test/e2e-compat, groups semantics and drilldown) | +| 33 | `quality/index-stats-streams-differ` — index/stats stream counts differ from Loki in both directions (open) | open | drilldown-visible | 6 | 2 | S | internal/proxy/label_handlers.go handleIndexStats (stream counting per window) | pkg/storage index stats (streams from the index for the window) | bench/parity rerun with the /series cross-check, then TestCompat_IndexStatsStreams (test/e2e-compat, group drilldown) | +| 34 | `drilldown/detected-fields-type-inference` — detected_fields infers a different type for some fields (open) | open | drilldown-visible | — | — | S | internal/proxy detected_fields type inference | pkg/querier detected fields type detection (v3.7.7): int before duration before string | TestCompat_DetectedFieldsTypes (test/e2e-compat, group drilldown) | +| 35 | `profiles/existence-filter-on-sanitized-key` — An existence filter on a sanitized key reads the stored dotted field, not the JSON line key (open) | open | drilldown-visible | — | — | M | internal/translator (label-filter key variants, parsed_key_variants.go); Drilldown single-field fast paths | pkg/logql/log/parser.go:141 sanitizeLabelKey on every extracted key; label filter reads the parsed label | TestCompat_ExistenceFilterSanitizedKey (test/e2e-compat, group semantics) | +| 36 | `quality/detected-metadata-proxy-cache` — detected_labels and detected_fields are served from proxy caches; Loki answers each request fresh (open) | open | drilldown-visible | — | — | S | internal/proxy/cache_keys.go:48,130 (detected_* cache TTL) + recent-tail freshness bypass | pkg/querier detected labels/fields: no results cache in v3.7.7 | TestE2E_DetectedMetadataFreshness (test/e2e-compat, group drilldown) | +| 37 | `quality/stats-volume-freshness` — index/stats and volume answers ending now are cached; Loki does not cache inside max_stats_cache_freshness (10m) (open) | open | drilldown-visible | — | — | S | internal/proxy/volume.go handleVolume, label_handlers.go handleIndexStats (cache freshness) | pkg/validation/limits.go:413-414 max_stats_cache_freshness default 10m | TestE2E_StatsVolumeFreshness (test/e2e-compat, group drilldown) | +| 38 | `semantics/parse-error-text-parity` — Invalid queries fail on both sides, with different error text or status (open) | open | grafana-datasource | 210 | 129 | L | internal/logql parser error messages (Loki's goyacc messages and positions) + VictoriaLogs error mapping | pkg/logql/syntax/parser.go ParseError (line, col) and syntax.y | TestCompat_ParseErrorText corpus (test/e2e-compat, bad_request_status_parity_test.go) | +| 39 | `semantics/instant-result-type-missing` — Some instant binary/label_replace answers carry no resultType (open) | open | grafana-datasource | 4 | 2 | S | internal/proxy instant response assembly (binary/label_replace/without paths) | pkg/logqlmodel result types; pkg/util/marshal query response | TestCompat_InstantResultTypeAlwaysSet (test/e2e-compat, group semantics) | +| 40 | `semantics/proxy-rejects-valid-logql` — The proxy rejects LogQL Loki accepts: comments, negative and compound offsets, filters after a range, logfmt flags, ip() label filters (o... | open | api-only | 79 | 48 | M | internal/logql parser (lexer: comments, signed durations, flags) + translator | pkg/logql/syntax/lex.go:149 comments, :90-91 OpStrict/OpKeepEmpty; syntax.y offsetExpr; pkg/logql/log/ip.go | TestCompat_LogQLSurfaceAccepted (test/e2e-compat, group semantics), one subtest per family | +| 41 | `semantics/proxy-accepts-invalid-logql` — Queries Loki rejects with 400 are answered by the proxy (open) | open | api-only | 18 | 14 | S | internal/logql parser validation | pkg/logql/syntax/ast.go:1446,1579 grouping not allowed; duration parsing | TestCompat_InvalidLogQLRejected (test/e2e-compat, bad_request_status_parity_test.go) | +| 42 | `parsed-identity/regexp-partial-match-series-count` — A partially-matching \| regexp returns about 3x Loki's series count | open | api-only | 2 | 2 | M | internal/proxy regexp capture series identity (non-matching lines) | pkg/logql/log/parser.go RegexpParser (no capture label for a non-matching line) | TestCompat_RegexpPartialMatchSeries (test/e2e-compat, group semantics) | +| 43 | `profiles/json-extraction-list-missing-key` — A json extraction-list key absent from the line is dropped; Loki keeps it in the metric series (open) | open | api-only | 2 | 2 | S | internal/proxy stage-field exposure (extraction lists, metric path) | pkg/logql/log/parser.go:671-720 JSONExpressionParser.Process | TestCompat_JSONExtractionListMissingKey (test/e2e-compat, group semantics) | +| 44 | `semantics/unpack-non-string-values` — `\| unpack` extracts non-string JSON values that Loki ignores (open) | open | api-only | 2 | 2 | S | internal/translator unpack (string values only) | pkg/logql/log/parser.go:789-820 UnpackParser.unpack | TestCompat_UnpackStringValuesOnly (test/e2e-compat, group semantics) | +| 45 | `versions/capabilities-floor-vs-matrix` — logsql.Capabilities treats VictoriaLogs below v1.40 as unsupported while the matrix claims v1.30 (open) | open | edge | — | — | M | internal/logsql/capabilities.go:47 + test/e2e-compat/compatibility-matrix.json | n/a (backend capability) | capabilities unit table per matrix version + compat-vl matrix run | +| 46 | `versions/format-if-before-vl-1.35` — `format if (...)` drops the destination field on VictoriaLogs before v1.35 (open, version-gated) | open | edge | — | — | S | internal/translator (format emission) + logsql.Capabilities gate | pkg/logql/log/fmt.go label_format/line_format | unit test for the capability fallback + one e2e group on v1.34 | +| 47 | `versions/otlp-severity-before-vl-1.35` — OTLP severity is not read as detected_level on VictoriaLogs v1.30-v1.34 (open, version-gated) | open | edge | — | — | S | internal/proxy/detected_level.go + logsql capabilities | detected_level from OTLP severity (distributor field detection, v3.7.7) | TestVLTrackScore version matrix row for OTLP severity (compat-vl workflow) | +| 48 | `metric-identity/structured-metadata-splits-series` — Loki splits a stream's metric series per structured-metadata set; the proxy keeps the stream (owner-accepted deviation) | owner-kept | explore-visible | 173 | 37 | L | internal/translator series identity (stats by stream); VictoriaLogs cannot group by a row's remaining fields | pkg/logql/log/metrics_extraction.go:102-120 builder.Add(StructuredMetadataLabel) -> GroupedLabels | differential run cluster count (bench/parity) | +| 49 | `parsed-identity/dynamic-key-parsers` — `\| json` and `\| logfmt` metric series keep the stream identity by default (owner-accepted deviation, opt-in flag) | owner-kept | explore-visible | 95 | 29 | L | internal/translator parser series identity; -exact-parser-series-identity | pkg/logql/log/parser.go label extraction into the sample labels | differential run cluster count (bench/parity) | +| 50 | `profiles/label-filter-on-line-field` — A label filter on a line key no earlier stage exposes matches like Loki | owner-kept | explore-visible | — | — | M | internal/proxy label-filter refill (-label-filter-refill-max-pages); metric queries on keys no stage exposes | pkg/logql/log/label_filter.go (filter reads only labels the pipeline set) | TestCompat_LabelFilterOnLineField (exists, profile tests) | +| 51 | `profiles/detected-fields-no-extracted-suffix` — detected_fields lists service / service.name without Loki's _extracted suffix (owner decision) | owner-kept | drilldown-visible | — | — | S | internal/proxy/label_handlers.go handleDetectedFields | pkg/logql/log/parser.go:25 duplicateSuffix | bench/parity diff rule (documented facet, service fields only) | +| 52 | `profiles/json-line-stored-without-line` — A JSON line pushed without _msg is stored without its line (documented) | documented | explore-visible | — | — | L | client push configuration (disable_message_parsing=1); docs/KNOWN_ISSUES.md | pkg/distributor push keeps the line verbatim | cases/profiles/stage-field-exposure with disable_message_parsing pushes | +| 53 | `drilldown/detected-labels-sampled-cardinality` — detected_labels cardinality is sampled from the window's last minutes; Loki counts every stream its ingesters hold (documented) | documented | drilldown-visible | 20 | 2 | M | internal/proxy/patterns.go handleDetectedLabels | pkg/ingester/instance.go LabelsWithValues | bench/parity diff rule (documented facet) | +| 54 | `profiles/volume-detected-level-extension` — index/volume answers targetLabels=detected_level; Loki returns no volume for it (documented proxy extension) | documented | drilldown-visible | 17 | 2 | S | internal/proxy/volume.go | pkg/storage volume index (stream labels only) | differential run row volume by detected_level | +| 55 | `drilldown/patterns-mined-differently` — Patterns come from different miners on each side (documented) | documented | drilldown-visible | 15 | 3 | L | internal/proxy/patterns.go | pkg/pattern (ingester-side mining) | bench/visual history-dependent comparison (exists) | +| 56 | `profiles/volume-bytes-accounting` — Volume values count line bytes; Loki counts chunk bytes with structured metadata (documented) | documented | drilldown-visible | 4 | 3 | L | internal/proxy/volume.go; docs/KNOWN_ISSUES.md | pkg/storage volume index (chunk bytes incl. structured metadata) | bench/parity diff rule (documented facet) | +| 57 | `limits/drilldown-breakdown-keeps-busiest` — Over max_query_series a Drilldown breakdown keeps the busiest series; Loki keeps the first it meets (documented) | documented | drilldown-visible | 1 | 1 | S | internal/proxy series limit ranking | pkg/logql engine series limit | cases/limits/drilldown-partial-with-warning (exists) | +| 58 | `quality/index-stats-bytes-and-chunks` — index/stats bytes and chunks have no VictoriaLogs equivalent (documented) | documented | drilldown-visible | — | — | L | internal/proxy/label_handlers.go handleIndexStats; docs/KNOWN_ISSUES.md | pkg/storage index stats from chunk refs (bytes, chunks) | bench/parity diff rule (documented facet) | +| 59 | `profiles/grafana-stats-error-as-warning` — A failed stats query from Grafana answers 200 with warnings; Loki returns the error (documented) | documented | grafana-datasource | — | — | S | internal/proxy Grafana-sourced stats error handling | pkg/querier/queryrange error propagation (no warnings conversion) | TestCompat_GrafanaStatsErrorStatus (test/e2e-compat, fault injection) | +| 60 | `profiles/metadata-default-lookback` — Label endpoints without start/end default to a different lookback than Loki's 1h (documented) | documented | api-only | — | — | S | internal/config flag default -metadata-default-lookback | pkg/loghttp/params.go defaultSince = 1h | TestCompat_MetadataDefaultLookback (test/e2e-compat, group core) | +| 61 | `profiles/multi-tenant-volume-merge` — Multi-tenant volume returns per-tenant series with __tenant_id__; Loki sums the tenants' volumes (documented) | documented | api-only | — | — | S | internal/proxy/volume.go multi-tenant merge | pkg/querier/queryrange volume merge | TestCompat_MultiTenantVolume (test/e2e-compat, group tail-multitenancy) | +| 62 | `semantics/count-values-unsupported` — count_values() is rejected with 400 where Loki answers (documented) | documented | api-only | — | — | M | internal/translator + proxy-side post-aggregation of a stats matrix | pkg/logql/syntax OpTypeCountValues; vector evaluator | TestCompat_CountValues (test/e2e-compat, group semantics) | +| 63 | `semantics/eager-pipeline-validation` — Invalid pipeline stages fail with 400 regardless of data; Loki answers 200 empty over a range with no data (documented) | documented | edge | — | — | S | internal/translator validation | pkg/logql/log/fmt.go template build at pipeline construction | TestCompat_EagerValidationEmptyRange (test/e2e-compat, group semantics) | +| 64 | `semantics/ip-filter-precision` — ip() filters are approximated with regular expressions (documented) | documented | edge | — | — | M | internal/proxy/postprocess.go:84 ip filtering | pkg/logql/log/ip.go | TestCompat_IPFilterForms (test/e2e-compat, group semantics) | +| 65 | `drilldown/plugin-legend-sync-race` — Logs Drilldown crashes a breakdown page when a query answers before the panel module loads (upstream, not the proxy) | upstream | drilldown-visible | — | — | S | none (grafana-lokiexplore-app) | n/a (client race) | bench/visual capture waits for the panel module (already) | +| 66 | `quality/index-stats-loki-chunk-accounting` — Loki's index/stats counts chunks it holds twice; the proxy's counts equal Loki's own count_over_time (upstream) | upstream | drilldown-visible | — | — | S | none (Loki chunk index accounting) | pkg/storage stats from chunk refs (flushed and retained chunks both counted) | bench/parity cross-check (run.py loki_counts_itself) | diff --git a/conformance/scripts/parity_gaps.py b/conformance/scripts/parity_gaps.py new file mode 100644 index 000000000..dd37bd6ff --- /dev/null +++ b/conformance/scripts/parity_gaps.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +"""Rank every known difference between the proxy and Loki. Writes conformance/reports/parity-gaps.md. + +Inputs: + * every registry case carrying a `gap:` block (cases/**.yaml): the difference, + its Loki reference, the proxy's behaviour, user impact, effort, code area, + the planned test and the differential-run clusters it accounts for; + * registry/generated/parity-discovery.json: the last differential run + (bench/parity/run.py) recorded by bench/parity/publish.py: request counts + per source and every gap signature with the corpus queries that hit it. + +A gap's status is one of: + open a difference to remove + owner-kept a difference the owner decided to keep (with the decision in the case or its behaviour's state) + documented a difference documented for users (docs/KNOWN_ISSUES.md) + upstream not the proxy's: Loki or a client behaves this way + fixed removed by the change named in `fixed_by:` (a PR number or commit); its clusters stay claimed + until the next recorded run no longer shows them, and the report lists it apart + +Rank: open first, then by user impact (explore- and drilldown-visible first), +then by the number of corpus queries that hit the gap's clusters. + +--check writes nothing and fails when: + * an undocumented cluster is claimed by no case, or by more than one; + * a case claims a cluster the discovery does not hold; + * a cluster the diff marked as a recorded deviation names a case id the + registry does not hold, or a case whose status is not documented, + owner-kept, upstream or fixed; + * a case has an unknown status or impact, or `status: fixed` without `fixed_by:`. +""" +import argparse +import os +import re +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from registry_io import load_json, read_text, write_text # noqa: E402 + +ROOT = "conformance/registry" +DISCOVERY = os.path.join(ROOT, "generated", "parity-discovery.json") +REPORT = "conformance/reports/parity-gaps.md" +IMPACT = ("explore-visible", "drilldown-visible", "grafana-datasource", "api-only", "edge") +STATUS = ("open", "owner-kept", "documented", "upstream", "fixed") +RECORDED = ("documented", "owner-kept", "upstream", "fixed") + + +def block(text, name): + """The indented lines under a top-level `name:` key.""" + found = re.search(rf"^{name}:\n((?:[ \t]+.*\n|\n)+)", text, re.M) + return found.group(1) if found else "" + + +def scalar(text, key, indent=" "): + found = re.search(rf"^{indent}{key}:\s*(.+)$", text, re.M) + if not found: + return "" + value = found.group(1).strip() + if len(value) > 1 and value[0] == value[-1] == "'": + return value[1:-1].replace("''", "'") + return value.strip('"') + + +def items(text, key, indent=" "): + found = re.search(rf"^{indent}{key}:\s*\[(.*)\]\s*$", text, re.M) + if found: + return [x.strip().strip('"') for x in found.group(1).split(",") if x.strip()] + found = re.search(rf"^{indent}{key}:\n((?:{indent} - .+\n)+)", text, re.M) + return re.findall(r"- (\S+)", found.group(1)) if found else [] + + +def cases(root=ROOT): + """Every case with a gap block, and the ids of all cases (with or without one).""" + out, ids = [], set() + base = os.path.join(root, "cases") + for current, _, files in os.walk(base): + for name in sorted(files): + if not name.endswith(".yaml"): + continue + identifier = os.path.relpath(os.path.join(current, name), base)[:-5] + ids.add(identifier) + text = read_text(os.path.join(current, name)) + gap = block(text, "gap") + if not gap: + continue + title = re.search(r"^title:\s*(.+)$", text, re.M) + out.append({"id": identifier, "title": title.group(1).strip() if title else identifier, + "status": scalar(gap, "status") or "open", "impact": scalar(gap, "impact") or "api-only", + "effort": scalar(gap, "effort") or "?", "area": scalar(gap, "area"), + "loki": scalar(gap, "loki"), "planned_test": scalar(gap, "planned_test"), + "fixed_by": scalar(gap, "fixed_by"), "clusters": items(gap, "clusters")}) + return out, ids + + +def analyse(root=ROOT, discovery_path=DISCOVERY): + """(cases ranked, discovery, problems).""" + found, ids = cases(root) + by_id = {c["id"]: c for c in found} + discovery = load_json(discovery_path) if os.path.exists(discovery_path) else {"clusters": [], "summary": {}} + clusters = {c["id"]: c for c in discovery.get("clusters", [])} + problems = [] + claimed = {} + for case in found: + if case["status"] not in STATUS: + problems.append(f"{case['id']}: status {case['status']} is not one of {', '.join(STATUS)}") + if case["impact"] not in IMPACT: + problems.append(f"{case['id']}: impact {case['impact']} is not one of {', '.join(IMPACT)}") + if case["status"] == "fixed" and not case["fixed_by"]: + problems.append(f"{case['id']}: status fixed needs fixed_by (the PR number or commit that fixed it)") + for cid in case["clusters"]: + claimed.setdefault(cid, []).append(case["id"]) + if cid not in clusters: + problems.append(f"{case['id']}: cluster {cid} is not in {discovery_path}") + for cid, c in clusters.items(): + rule = c.get("documented") + if rule: + if rule not in ids: + problems.append(f"cluster {cid} is marked as recorded deviation {rule}, which is no registry case") + elif rule not in by_id or by_id[rule]["status"] not in RECORDED: + problems.append(f"cluster {cid} is marked as recorded deviation {rule}, whose gap status is not one " + f"of {', '.join(RECORDED)}") + else: + claimed.setdefault(cid, []).append(rule) + owners = sorted(set(claimed.get(cid, []))) + if not owners: + problems.append(f"cluster {cid} ({' / '.join(c['signature'])}) is accounted for by no registry case") + elif len(owners) > 1: + problems.append(f"cluster {cid} is claimed by {len(owners)} cases ({', '.join(owners)}); one owns it") + for case in found: + mine = [clusters[c] for c in clusters if case["id"] in claimed.get(c, [])] + case["queries"] = sum(c["queries"] for c in mine) + case["requests"] = sum(c["requests"] for c in mine) + case["cluster_count"] = len(mine) + found.sort(key=lambda c: (STATUS.index(c["status"]) if c["status"] in STATUS else 9, + IMPACT.index(c["impact"]) if c["impact"] in IMPACT else 9, -c["queries"], c["id"])) + return found, discovery, problems + + +def render(found, discovery): + summary = discovery.get("summary", {}) + lines = ["# Proxy vs Loki: ranked differences", "", + "Generated by `conformance/scripts/parity_gaps.py` from the registry cases that carry a `gap:`", + "block and the last differential run (`bench/parity`, recorded in", + "`conformance/registry/generated/parity-discovery.json`). Do not edit by hand.", "", + "Rank: open first, then user impact (explore- and drilldown-visible before the datasource path,", + "API-only and edge cases), then the corpus queries that hit the gap's clusters. Fixed gaps are", + "listed apart until a new run no longer shows their clusters.", ""] + if summary: + lines += [f"Last run: {discovery.get('recorded', '?')} on `{discovery.get('ref', '?')}`, " + f"window {discovery.get('window', '?')}.", "", + "| corpus source | requests | same | differ | both empty | blocked |", "|---|---:|---:|---:|---:|---:|"] + for source, s in sorted(summary.items()): + lines.append(f"| {source} | {s['requests']} | {s['same']} | {s['diff']} | {s['vacuous']} | {s['blocked']} |") + lines.append("") + else: + lines += ["No differential run is recorded.", ""] + current = [c for c in found if c["status"] != "fixed"] + lines += ["| # | case | status | impact | queries | clusters | effort | area | Loki reference | planned test |", + "|---:|---|---|---|---:|---:|---|---|---|---|"] + for rank, case in enumerate(current, 1): + lines.append(f"| {rank} | `{case['id']}` — {cell(case['title'])} | {case['status']} | {case['impact']} | " + f"{case['queries'] or '—'} | {case['cluster_count'] or '—'} | {case['effort']} | " + f"{cell(case['area'])} | {cell(case['loki'])} | {cell(case['planned_test'])} |") + fixed = [c for c in found if c["status"] == "fixed"] + if fixed: + lines += ["", "## Fixed, awaiting the next run", "", "| case | fixed by | clusters still recorded | queries |", + "|---|---|---:|---:|"] + for case in fixed: + lines.append(f"| `{case['id']}` — {cell(case['title'])} | {cell(case['fixed_by'])} | " + f"{case['cluster_count']} | {case['queries']} |") + lines.append("") + return "\n".join(lines) + + +def cell(text, limit=140): + text = str(text or "").replace("|", "\\|").replace("\n", " ") + return text if len(text) <= limit else text[: limit - 3] + "..." + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--check", action="store_true") + a = ap.parse_args() + found, discovery, problems = analyse() + if not a.check: # --check only validates; the report generator run writes (and the gate diffs) the report + write_text(REPORT, render(found, discovery)) + print(f"{REPORT}: {len(found)} registered differences, {len(discovery.get('clusters', []))} discovered clusters, " + f"{len(problems)} problem(s)") + for problem in problems: + print(" " + problem) + return 1 if a.check and problems else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/ci/check_conformance.py b/scripts/ci/check_conformance.py index 42b4cd550..409836d51 100644 --- a/scripts/ci/check_conformance.py +++ b/scripts/ci/check_conformance.py @@ -13,7 +13,9 @@ set) covers nothing that names code, so no change could select it for the per-PR visual smoke (bench/visual/plan.py); * a generated report is stale (performance, coverage map, gaps, translation map, roadmap, - compatibility matrix), + compatibility matrix, ranked parity gaps), + * a cluster of the last differential run (bench/parity) is accounted for by + no registry case, or a case names a cluster the run does not hold, * a flag is missing from the places that list every flag, * a score or an item's state regressed against the committed baseline, or a waiver expired, @@ -40,6 +42,7 @@ ("translation_map.py", "conformance/reports/translation-map.md", None), ("roadmap.py", "conformance/reports/roadmap.md", None), ("matrix_link.py", "conformance/reports/compatibility-matrix.md", None), + ("parity_gaps.py", "conformance/reports/parity-gaps.md", None), ) @@ -77,6 +80,12 @@ def main(): if visual_plan.returncode: failures.append("visual-smoke entry selection") + # Every difference the last differential run found is registered. + parity = run("parity_gaps.py", "--check") + print(parity.stdout.strip()) + if parity.returncode: + failures.append("parity gap registration") + flags = run("flag_docs.py", "--check") print(flags.stdout.strip()) if flags.returncode: