Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/compat-loki.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ jobs:
raise SystemExit(1)
print(f"Loki compatibility score: {passed}/{total} ({pct_val:.1f}%)")
PY
- name: Run Loki query semantics matrix
run: |
set -euo pipefail
go test -v -tags=e2e -run '^TestQuerySemantics' ./test/e2e-compat/
- name: Tear down pinned stack
if: always()
working-directory: test/e2e-compat
Expand Down Expand Up @@ -144,6 +148,12 @@ jobs:
raise SystemExit(1)
print(f"Loki compatibility score: {passed}/{total} ({pct_val:.1f}%)")
PY
- name: Run Loki query semantics matrix
env:
LOKI_IMAGE: grafana/loki:${{ matrix.loki_version }}
run: |
set -euo pipefail
go test -v -tags=e2e -run '^TestQuerySemantics' ./test/e2e-compat/
- name: Tear down matrix stack
if: always()
working-directory: test/e2e-compat
Expand Down
11 changes: 4 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.10.3] - 2026-04-20
### Bug Fixes

- compat/loki: preserve Loki semantics for bare parser-derived metric queries and `absent_over_time(...)` on the direct `query` and `query_range` paths so valid Loki operations keep their parser-derived label cardinality, unwrap behavior, and empty-series semantics instead of collapsing into proxy-specific aggregated fallback results.

### Changed

- release/metadata: synchronized release metadata for v1.10.2.

### Bug Fixes

- drilldown/metadata: retry relaxed metadata candidates after successful-empty strict scans for native streams, native field names, detected fields, and detected labels so Drilldown labels/fields stay populated when strict parser filters over-constrain the sample.
- translator/patterns: translate `pattern`/`extract` stages without named captures into line filters instead of emitting invalid VictoriaLogs `extract` pipes such as `extract "Metrics"`.

### Tests

- drilldown/translator: update fallback coverage for empty strict scans and add regression cases for literal `extract`/`pattern` stage translation.
- compat/loki: make the query-semantics matrix and operation inventory required in CI, expand positive and negative Loki operation coverage across parser pipelines, unwrap range functions, boolean/set operators, and invalid log/metric combinations, and add unit coverage for the bare parser metric and `absent_over_time(...)` compatibility handlers plus cache-tier coverage for the current mainline helper cache paths.

## [1.10.2] - 2026-04-20

Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
[![VictoriaLogs Compatibility](https://github.com/ReliablyObserve/Loki-VL-proxy/actions/workflows/compat-vl.yaml/badge.svg?branch=main&event=push)](https://github.com/ReliablyObserve/Loki-VL-proxy/actions/workflows/compat-vl.yaml)
[![Go Version](https://img.shields.io/github/go-mod/go-version/ReliablyObserve/Loki-VL-proxy)](https://go.dev/)
[![Release](https://img.shields.io/github/v/release/ReliablyObserve/Loki-VL-proxy)](https://github.com/ReliablyObserve/Loki-VL-proxy/releases)
[![Lines of Code](https://img.shields.io/badge/go%20loc-79.3k-blue)](https://github.com/ReliablyObserve/Loki-VL-proxy)
[![Tests](https://img.shields.io/badge/tests-1811%20passed-brightgreen)](#tests)
[![Coverage](https://img.shields.io/badge/coverage-89.1%25-green)](#tests)
[![Lines of Code](https://img.shields.io/badge/go%20loc-79.2k-blue)](https://github.com/ReliablyObserve/Loki-VL-proxy)
[![Tests](https://img.shields.io/badge/tests-1809%20passed-brightgreen)](#tests)
[![Coverage](https://img.shields.io/badge/coverage-89.2%25-green)](#tests)
[![LogQL Coverage](https://img.shields.io/badge/LogQL%20coverage-100%25-brightgreen)](#logql-compatibility)
[![License](https://img.shields.io/github/license/ReliablyObserve/Loki-VL-proxy)](LICENSE)
[![CodeQL](https://github.com/ReliablyObserve/Loki-VL-proxy/actions/workflows/codeql.yaml/badge.svg?branch=main&event=push)](https://github.com/ReliablyObserve/Loki-VL-proxy/actions/workflows/codeql.yaml)
Expand Down Expand Up @@ -44,6 +44,9 @@ Related docs: [Architecture](docs/architecture.md), [Compatibility Matrix](docs/

- Loki-compatible read API for Grafana datasource, Explore, Drilldown, and API clients.
- Strict tuple contracts: default 2-tuple, explicit 3-tuple only via `categorize-labels`.
- Manifest-driven Loki query semantics parity tests against a real Loki oracle for valid and invalid LogQL combinations, backed by an explicit Loki operation inventory that is machine-checked in CI.
- Required coverage now includes parser pipelines, bare `unwrap` range functions, `absent_over_time`, scalar `bool` comparisons, vector set operators, and invalid LogQL forms that must fail the same way as Loki.
- Required PR compatibility gate for Loki-facing query semantics through the pinned `compat-loki` workflow.
- Grafana Logs Drilldown patterns support through `/loki/api/v1/patterns`, with explicit `-patterns-enabled` control when deployments need it disabled.
- Loki-compatible patterns endpoint with optional restart-safe persistence.
- Automatic pattern autodetection from successful `query`/`query_range` responses (`-patterns-autodetect-from-queries`) to keep Drilldown patterns warm behind the scenes.
Expand Down Expand Up @@ -80,7 +83,7 @@ Related docs: [Compatibility Matrix](docs/compatibility-matrix.md), [Patterns](d
- Layered CI security gates: `gitleaks`, `gosec`, `Trivy`, `actionlint`, `hadolint`, `OpenSSF Scorecard`, custom runtime regressions, OWASP ZAP, and curated `Nuclei`.
- Proxy-specific security coverage for tenant isolation, cache boundaries, browser-origin enforcement on `/tail`, forwarded auth/header handling, and debug/admin exposure.

Related docs: [Security](docs/security.md), [Security Policy](SECURITY.md), [Testing](docs/testing.md)
Related docs: [Security](docs/security.md), [Security Policy](SECURITY.md), [Testing](docs/testing.md), [Compatibility Matrix](docs/compatibility-matrix.md)

## UI Gallery

Expand Down
4 changes: 2 additions & 2 deletions charts/loki-vl-proxy/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ apiVersion: v2
name: loki-vl-proxy
description: HTTP proxy translating Loki API to VictoriaLogs
type: application
version: 1.10.3
appVersion: "1.10.3"
version: 1.10.2
appVersion: "1.10.2"
home: https://github.com/ReliablyObserve/Loki-VL-proxy
sources:
- https://github.com/ReliablyObserve/Loki-VL-proxy
Expand Down
86 changes: 86 additions & 0 deletions docs/compatibility-loki.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ This track measures how closely the proxy behaves like Loki on Loki-facing APIs
- `/labels`, `/label/<name>/values`, `/series`, `/query`, `/query_range`
- LogQL parser, filter, metric, and OTel label compatibility
- Synthetic compatibility labels the proxy must expose to Loki clients, such as `service_name`
- Manifest-driven query semantics parity against real Loki in [query-semantics-matrix.json](../test/e2e-compat/query-semantics-matrix.json)
- Tracked operation inventory in [query-semantics-operations.json](../test/e2e-compat/query-semantics-operations.json)

## CI And Score

- Workflow: `compat-loki.yaml`
- Score test: `TestLokiTrackScore`
- Required PR gate: `loki-pinned`, which runs the `TestQuerySemantics*` inventory + matrix suite
- Runtime matrix: real Loki images
- Support window: current Loki minor family plus one minor family behind

Expand Down Expand Up @@ -41,3 +44,86 @@ The Loki matrix is a moving window. When a new Loki minor becomes current, the m
- `detected_level` grouped metric queries used by Grafana log volume panels
- OTel dotted and underscore label parity through the underscore proxy
- Series and label-value parity for labels synthesized by the proxy

## Query Families In The Loki Semantics Matrix

The Loki semantics matrix focuses on query combinations where the proxy should match Loki as closely as possible on:

- HTTP status
- payload `status`
- `errorType`
- `resultType`
- line-count parity for log streams
- series-count parity for vectors and matrices
- exact metric-label-set parity for label-sensitive metric families such as bare parser metrics

The tracked operation inventory in [query-semantics-operations.json](../test/e2e-compat/query-semantics-operations.json) is machine-checked in CI. Every matrix case must belong to at least one inventory operation, and every inventory operation must reference live matrix cases.

Covered valid families:

| Family | Representative cases |
|---|---|
| Stream selectors | exact match, multi-label match, regex, negative match |
| Line filters | `|=`, `!=`, `|~`, `!~`, chained filter pipelines, negative-regex exclusions |
| Parser pipelines | `json`, `logfmt`, `regexp`, `pattern`, parser plus exact/regex/numeric field filter, `label_format` |
| Metric range queries | `count_over_time`, `rate`, `bytes_over_time`, `bytes_rate`, `absent_over_time`, grouped range aggregations, parser-inside-range filters, bare unwrap range functions |
| Aggregations | `sum by(...)`, `without(...)`, `topk(...)`, `bottomk(...)`, `sort(...)`, `sort_desc(...)` |
| Binary operations | scalar comparisons/math, `bool` comparisons, and vector-to-vector operations such as `/`, `and`, `or`, and `unless` over valid metric expressions |

Detailed operation inventory:

| Category | Operations currently enforced in CI |
|---|---|
| Selectors | exact selectors, multi-label regex selectors, negative-regex selectors |
| Line filters | `|=`, `!=`, `|~`, `!~`, mixed chained filters |
| Parsers | `json`, `logfmt`, `regexp`, `pattern` plus parsed/extracted field filters |
| Formatting | `line_format`, `label_format`, `keep`, `drop` |
| Metric functions | `count_over_time`, `rate`, `bytes_over_time`, `bytes_rate`, `absent_over_time`, `sum/avg/max/min/first/last/stddev/stdvar_over_time` with `unwrap`, `quantile_over_time` with `unwrap` |
| Aggregations | `sum by(...)`, `sum without(...)`, `topk`, `bottomk`, `sort`, `sort_desc` |
| Binary operators | scalar math/comparison, scalar `bool` comparison, vector arithmetic, `on(...)`, `group_left(...)`, logical `and`, `or`, `unless` |
| Invalid shapes | log-query aggregation misuse, missing metric range, malformed selector/parser syntax, invalid log-query binary ops |

Explicit invalid families:

| Family | Representative cases |
|---|---|
| Metric aggregation over a log query | `sum by(job) ({selector})` |
| Post-aggregation over a log query | `topk(2, {selector})`, `sort({selector})` |
| Missing range on a metric function | `rate({selector})` |
| Malformed selector / syntax | broken braces, parser syntax errors |
| Invalid binary shape | log-query to scalar/vector binary expressions |

These are intentionally called out because they are easy to regress while changing translation, shaping, or query planning.

## What Stays Outside Loki Parity

Some important compatibility behavior is still tested, but it is not part of the strict Loki parity matrix:

- synthetic `service_name` recovery when the backend only has structured metadata
- Drilldown helper endpoints like detected labels, detected fields, field values, volume, and patterns
- stale-on-error helper behavior under VictoriaLogs failures

Those cases live in the proxy contract suite because Loki itself is not the source of truth for them.

## Parity Rule

Valid Loki behavior is not an allowed exclusion category.

If a query shape works in real Loki and the proxy does not match it, that is treated as a parity bug and should be fixed or tracked with an explicit regression case. The bare parser-metric and bare `unwrap` metric shapes now live inside the required matrix for that reason.

## Detailed Edge Cases Now Gated

The required matrix is intentionally not limited to happy-path selectors. It now includes:

- parser-derived metric labelsets, not just result counts
- bare `unwrap` range functions where Loki keeps parsed labels but not the unwrap target field itself
- `pattern` parser extraction semantics
- set-style binary operators such as `or` and `unless`
- `bool` comparison semantics on metric expressions
- invalid log/metric shape rejections that must fail with the same class of error as Loki

When a new LogQL family is implemented or fixed in the proxy, the expectation is to add:

1. a runtime matrix case in `query-semantics-matrix.json`
2. an inventory entry in `query-semantics-operations.json`
3. a local or unit regression if the fix needed proxy-side translation or shaping changes
51 changes: 51 additions & 0 deletions docs/compatibility-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,57 @@ The GitHub Actions compatibility workflows read their matrix lists directly from
VictoriaLogs capability profiles (used by runtime version sensing in proxy code) are tracked in the same manifest under `stack.victorialogs.capability_profiles`.
Grafana runtime and client-surface capability profiles are tracked under `stack.grafana_loki_datasource_contract.capability_profiles` and `stack.logs_drilldown_contract.capability_profiles`.

## Query Semantics Matrix

Version coverage and query semantics are tracked separately on purpose.

- [compatibility-matrix.json](../test/e2e-compat/compatibility-matrix.json) answers which runtime families we support in CI.
- [query-semantics-matrix.json](../test/e2e-compat/query-semantics-matrix.json) answers which Loki-facing query combinations must behave the same as real Loki.
- [query-semantics-operations.json](../test/e2e-compat/query-semantics-operations.json) answers which Loki operations are explicitly tracked by the required semantics gate.

The query semantics matrix is manifest-driven and runs against a live Docker Compose stack with:

- real Loki as the oracle
- Loki-VL-proxy against VictoriaLogs as the system under test
- the required `compat-loki` GitHub Actions workflow as the enforcement gate on pull requests
- the `loki-pinned` PR job as the stable required runtime check, with wider Loki-family coverage in the scheduled matrix

Each case declares:

- query family such as selector, parser pipeline, metric aggregation, binary op, or invalid syntax
- endpoint shape: `query` or `query_range`
- expected outcome: `success`, `client_error`, or `server_error`
- expected `resultType` when the query is valid
- comparison mode such as exact line-count parity, exact series-count parity, or exact metric-label-set parity

The operation inventory is machine-checked alongside the matrix so the repo does not quietly drift into “tested cases” without a visible coverage model.

Representative covered combinations now include:

- selector + regex / negative-regex combinations
- chained line filters including exclusion cases
- parser pipelines with exact, numeric, regex, and pattern field filters
- parser-inside-range metric queries and bare `unwrap` range functions
- `absent_over_time(...)` semantics for missing selectors
- scalar `bool` comparisons and vector set operators like `or` / `unless`
- instant post-aggregations like `topk`, `bottomk`, `sort`, and `sort_desc`
- invalid log-query aggregations that must fail the same way as Loki

This keeps the repo explicit about what must match Loki exactly versus what is a proxy-only Grafana contract.

## Proxy-Only Contract Matrix

Some important behaviors are intentionally not judged against Loki because they only exist in the proxy compatibility layer.

Examples:

- synthetic label translation such as `service_name`
- Grafana Drilldown helper endpoints like detected labels, detected fields, field values, volume, and patterns
- stale-on-error helper behavior
- bounded fallback and recovery behavior when VictoriaLogs discovery endpoints fail

Those cases belong in the proxy contract suite, not the strict Loki semantics parity matrix.

## Support Window Policy

The matrix is intentionally not open-ended. For every upstream we support a moving window that advances as new releases land:
Expand Down
Loading
Loading