Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Loki: Add backend functionality to parse structured metadata from Loki #77361

Merged
merged 15 commits into from
Nov 16, 2023

Conversation

svennergr
Copy link
Contributor

What is this feature?

Copy of #76350.

Loki has recently added functionality for structured metadata. This PR adds the correct parsing of structured metadata to Grafana's data source.

When the lokiStructuredMetadata feature flag is set, the datasource will add a X-Loki-Response-Encoding-Flags HTTP header with the value of categorize-labels. That will tell Loki to respond with structured metadata attached to each logline. Grafana's parser will parse the response into a new DataFrame field called labelTypes. That field stores a JSON object for each log line containing the label name and a single character to distinguish between label types:

  • I will be indexed labels
  • P will be parsed labels
  • S will be structured metadata

Why do we need this feature?

To support structured metadata.

Special notes for your reviewer:

  1. You need to checkout this branch from github/loki: Flag categorize labels on streams response loki#10419
  2. Compile local Loki: go build ./cmd/loki
  3. Start local Loki pointing to the loki-config from our devenv: ./loki -config.file=../grafana/devenv/docker/blocks/loki/loki-config.yaml
  4. Push data into the local Loki from the grafana directory: node devenv/docker/blocks/loki/data/data.js http://localhost:3100
  5. Start Grafana with the lokiStructuredMetadata feature flag enabled.
  6. Query Loki in Explore and see a similar dataframe (for length 1):
[
  {
    "schema": {
      "refId": "A",
      "meta": {
        "typeVersion": [
          0,
          0
        ],
        "custom": {
          "frameType": "LabeledTimeValues",
          "lokiQueryStatKey": "Summary: total bytes processed"
        },
        "stats": [
          {
            "displayName": "Summary: bytes processed per second",
            "unit": "Bps",
            "value": 493885
          },
          {
            "displayName": "Summary: lines processed per second",
            "value": 2602
          },
          {
            "displayName": "Summary: total bytes processed",
            "unit": "decbytes",
            "value": 1708
          },
          {
            "displayName": "Summary: total lines processed",
            "value": 9
          },
          {
            "displayName": "Summary: exec time",
            "unit": "s",
            "value": 0.003458
          },
          {
            "displayName": "Ingester: total reached",
            "value": 1
          },
          {
            "displayName": "Ingester: total chunks matched",
            "value": 5
          },
          {
            "displayName": "Ingester: total batches",
            "value": 1
          },
          {
            "displayName": "Ingester: total lines sent",
            "value": 1
          },
          {
            "displayName": "Ingester: head chunk bytes",
            "unit": "decbytes",
            "value": 0
          },
          {
            "displayName": "Ingester: head chunk lines",
            "value": 0
          },
          {
            "displayName": "Ingester: decompressed bytes",
            "unit": "decbytes",
            "value": 0
          },
          {
            "displayName": "Ingester: decompressed lines",
            "value": 0
          },
          {
            "displayName": "Ingester: compressed bytes",
            "unit": "decbytes",
            "value": 0
          },
          {
            "displayName": "Ingester: total duplicates",
            "value": 0
          }
        ],
        "executedQueryString": "Expr: {age=\"new\", place=\"moon\"}",
        "preferredVisualisationType": "logs",
        "limit": 1,
        "searchWords": []
      },
      "fields": [
        {
          "name": "labels",
          "type": "other",
          "typeInfo": {
            "frame": "json.RawMessage"
          },
          "config": {}
        },
        {
          "name": "Time",
          "type": "time",
          "typeInfo": {
            "frame": "time.Time"
          },
          "config": {}
        },
        {
          "name": "Line",
          "type": "string",
          "typeInfo": {
            "frame": "string"
          },
          "config": {}
        },
        {
          "name": "tsNs",
          "type": "string",
          "typeInfo": {
            "frame": "string"
          },
          "config": {}
        },
        {
          "name": "labelTypes",
          "type": "other",
          "typeInfo": {
            "frame": "json.RawMessage"
          },
          "config": {
            "custom": {
              "hidden": true
            }
          }
        },
        {
          "name": "id",
          "type": "string",
          "typeInfo": {
            "frame": "string"
          },
          "config": {}
        },
        {
          "name": "test",
          "type": "string",
          "config": {
            "links": [
              {
                "title": "test",
                "url": "http://test.com"
              }
            ]
          }
        }
      ]
    },
    "data": {
      "values": [
        [
          {
            "source": "data",
            "nonIndexed": "value",
            "age": "new",
            "instance": "server\\1",
            "job": "\"grafana/data\"",
            "place": "moon",
            "re": "one.two$three^four"
          }
        ],
        [
          1697027135016
        ],
        [
          "{\"_entry\":\"log text with ANSI \\u001b[31mpart of the text\\u001b[0m [272230661]\",\"counter\":\"10034\",\"float\":\"73.589\",\"wave\":-0.9510565162952045,\"label\":\"val2\",\"level\":\"error\"}"
        ],
        [
          "1697027135016798346"
        ],
        [
          {
            "nonIndexed": "S",
            "place": "I",
            "re": "I",
            "source": "I",
            "age": "I",
            "instance": "I",
            "job": "I"
          }
        ],
        [
          "1697027135016798346_daa2f713"
        ],
        [
          "73.589"
        ]
      ],
      "nanos": [
        null,
        [
          798346
        ],
        null,
        null,
        null,
        null,
        null
      ]
    }
  }
]

@github-actions
Copy link
Contributor

Backend code coverage report for PR #77361

Plugin Main PR Difference
loki 65.4% 64.8% -.6%

@github-actions
Copy link
Contributor

Frontend code coverage report for PR #77361
No changes

@svennergr svennergr added the no-backport Skip backport of PR label Oct 30, 2023
@ivanahuckova ivanahuckova mentioned this pull request Nov 2, 2023
15 tasks
Copy link
Contributor

@matyax matyax left a comment

Choose a reason for hiding this comment

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

Very nice! Added some comments but nothing major.

@@ -30,6 +30,8 @@ type LokiAPI struct {
url string
log log.Logger
tracer tracing.Tracer

Copy link
Contributor

@matyax matyax Nov 2, 2023

Choose a reason for hiding this comment

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

Does this new line has any particular purpose?

}

func makeDataRequest(ctx context.Context, lokiDsUrl string, query lokiQuery) (*http.Request, error) {
func makeDataRequest(ctx context.Context, lokiDsUrl string, query lokiQuery, requestStructuredMetadata bool) (*http.Request, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to change anything, but given the flag we set calls to "categorize labels", maybe this could have been called categorizeLabels too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call!

return rspErr(err)
}

plabelsMap, clabelsMap, err := readCategorizedStreamField(iter)
Copy link
Contributor

Choose a reason for hiding this comment

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

plabel and clabel are just too bad to read. Could these names be improved so they are easier to understand?

@svennergr svennergr merged commit a01f8c5 into main Nov 16, 2023
20 checks passed
@svennergr svennergr deleted the svennergr/structured-metadata-backend-2 branch November 16, 2023 16:06
@aangelisc aangelisc modified the milestones: 10.3.x, 10.2.3 Dec 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants