@primafuture/telemetry-stack 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -195,10 +195,24 @@ curl -sS -X POST http://localhost:4318/v1/logs \
195
195
  \"timeUnixNano\": \"${TS}\",
196
196
  \"severityText\": \"INFO\",
197
197
  \"body\": {\"stringValue\": \"hello from telemetry-stack smoke test\"},
198
- \"attributes\": [{
199
- \"key\": \"log_type\",
200
- \"value\": {\"stringValue\": \"smoke_test\"}
201
- }]
198
+ \"attributes\": [
199
+ {
200
+ \"key\": \"log_type\",
201
+ \"value\": {\"stringValue\": \"smoke_test\"}
202
+ },
203
+ {
204
+ \"key\": \"route\",
205
+ \"value\": {\"stringValue\": \"/smoke\"}
206
+ },
207
+ {
208
+ \"key\": \"count\",
209
+ \"value\": {\"intValue\": \"3\"}
210
+ },
211
+ {
212
+ \"key\": \"parallel\",
213
+ \"value\": {\"boolValue\": true}
214
+ }
215
+ ]
202
216
  }]
203
217
  }]
204
218
  }]
@@ -208,8 +222,75 @@ curl -sS -X POST http://localhost:4318/v1/logs \
208
222
  Query it from Loki:
209
223
 
210
224
  ```bash
211
- curl -G 'http://localhost:3100/loki/api/v1/query' \
212
- --data-urlencode 'query={service_name="telemetry-smoke-test"}'
225
+ curl -G 'http://localhost:3100/loki/api/v1/query_range' \
226
+ --data-urlencode 'query={loki_service_name="telemetry-smoke-test"}' \
227
+ --data-urlencode 'limit=5'
228
+ ```
229
+
230
+ For Grafana trace-to-logs compatibility, the same log is also indexed by the
231
+ legacy `service_name` label:
232
+
233
+ ```bash
234
+ curl -G 'http://localhost:3100/loki/api/v1/query_range' \
235
+ --data-urlencode 'query={service_name="telemetry-smoke-test"}' \
236
+ --data-urlencode 'limit=5'
237
+ ```
238
+
239
+ The returned log should expose application metadata under `app_*`, for example
240
+ `app_log_type`, `app_route`, `app_count`, and `app_parallel`. OpenTelemetry
241
+ metadata should be under `otel_*`, for example `otel_resource_service_name` and
242
+ `otel_scope_name`.
243
+
244
+ ## Log Metadata in Loki
245
+
246
+ Applications can log plain metadata such as `count`, `parallel`, or `route`.
247
+ Alloy normalizes those fields before Loki receives them, so application code does
248
+ not need to add prefixes itself.
249
+
250
+ The stack uses these names:
251
+
252
+ ```text
253
+ app_* application log attributes
254
+ otel_resource_* OpenTelemetry resource attributes
255
+ otel_scope_* OpenTelemetry instrumentation scope attributes
256
+ otel_log_* OpenTelemetry log record fields
257
+ loki_* explicit Loki index labels
258
+ service_name legacy compatibility label for Grafana trace-to-logs
259
+ ```
260
+
261
+ Examples:
262
+
263
+ ```text
264
+ count -> app_count
265
+ route -> app_route
266
+ service.name -> otel_resource_service_name
267
+ instrumentation scope -> otel_scope_name
268
+ trace id -> otel_log_trace_id
269
+ ```
270
+
271
+ Loki indexes only the low-cardinality resource labels configured in
272
+ `loki.yaml`, such as `loki_service_name` and the compatibility `service_name`.
273
+ Application and OpenTelemetry detail fields are stored as structured metadata,
274
+ not as index labels. Query them after selecting a stream:
275
+
276
+ ```logql
277
+ {loki_service_name="telemetry-dice-roller-example"}
278
+ | trace_id="0242ac120002"
279
+ | app_route="/roll"
280
+ ```
281
+
282
+ Current Loki versions return structured metadata merged into the `stream` labels
283
+ map in `query_range` responses. This is a Loki API behavior: the fields are still
284
+ not index labels. Use `/loki/api/v1/labels` or `/loki/api/v1/series` to see the
285
+ real indexed labels.
286
+
287
+ If a query response is too noisy, reduce returned fields at query time without
288
+ changing stored data:
289
+
290
+ ```logql
291
+ {service_name="telemetry-dice-roller-example"}
292
+ | trace_id="0242ac120002"
293
+ | keep service_name, loki_service_name, trace_id, span_id
213
294
  ```
214
295
 
215
296
  ## Useful URLs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primafuture/telemetry-stack",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Reusable PrimaFuture telemetry stack launcher powered by Docker Compose.",
6
6
  "author": "PrimaFuture.cz s.r.o. <dev@primafuture.cz>",
@@ -1,13 +1,49 @@
1
1
  // Main Alloy pipeline for application telemetry.
2
2
  // It receives OTLP traces, metrics, and logs from apps and routes them to the stack.
3
3
 
4
- // Copy selected log attributes into resource attributes so Loki can index them as labels.
4
+ // Normalize log metadata before Loki stores it as labels/structured metadata.
5
+ // App-provided log attributes stay queryable under app.* and OTel/system data under otel.*.
5
6
  otelcol.processor.transform "loki_labels" {
7
+ error_mode = "ignore"
8
+
6
9
  log_statements {
7
10
  context = "log"
8
11
  statements = [
9
- `set(resource.attributes["log_type"], attributes["log_type"]) where attributes["log_type"] != nil`,
10
- `set(resource.attributes["log.file.path"], attributes["log.file.path"]) where attributes["log.file.path"] != nil`,
12
+ // Copy application log attributes into app.* without requiring apps to prefix anything themselves.
13
+ `merge_maps(log.cache, log.attributes, "upsert")`,
14
+ `flatten(log.cache, "app", resolveConflicts=true)`,
15
+ `merge_maps(log.attributes, log.cache, "upsert")`,
16
+ `delete_matching_keys(log.cache, ".*")`,
17
+
18
+ // Copy original resource attributes into otel.resource.* before resource labels are rewritten for Loki.
19
+ `merge_maps(log.cache, resource.attributes, "upsert")`,
20
+ `flatten(log.cache, "otel.resource", resolveConflicts=true)`,
21
+ `merge_maps(log.attributes, log.cache, "upsert")`,
22
+ `delete_matching_keys(log.cache, ".*")`,
23
+
24
+ // Copy instrumentation scope data into otel.scope.*.
25
+ `merge_maps(log.cache, instrumentation_scope.attributes, "upsert")`,
26
+ `flatten(log.cache, "otel.scope.attributes", resolveConflicts=true)`,
27
+ `merge_maps(log.attributes, log.cache, "upsert")`,
28
+ `delete_matching_keys(log.cache, ".*")`,
29
+ `set(log.attributes["otel.scope.name"], instrumentation_scope.name) where instrumentation_scope.name != ""`,
30
+ `set(log.attributes["otel.scope.version"], instrumentation_scope.version) where instrumentation_scope.version != ""`,
31
+
32
+ // Duplicate intrinsic log fields into otel.log.* while leaving native trace/span fields intact for Grafana links.
33
+ `set(log.attributes["otel.log.severity_text"], log.severity_text) where log.severity_text != ""`,
34
+ `set(log.attributes["otel.log.severity_number"], log.severity_number)`,
35
+ `set(log.attributes["otel.log.trace_id"], log.trace_id.string) where log.trace_id.string != ""`,
36
+ `set(log.attributes["otel.log.span_id"], log.span_id.string) where log.span_id.string != ""`,
37
+ `set(log.attributes["otel.log.flags"], log.flags)`,
38
+
39
+ // Build explicit Loki labels from original values. Loki will normalize dots to underscores.
40
+ `set(resource.attributes["loki.service.name"], resource.attributes["service.name"]) where resource.attributes["service.name"] != nil`,
41
+ `set(resource.attributes["loki.log.type"], log.attributes["log_type"]) where log.attributes["log_type"] != nil`,
42
+ `set(resource.attributes["loki.log.file.path"], log.attributes["log.file.path"]) where log.attributes["log.file.path"] != nil`,
43
+
44
+ // Keep only namespaced structured metadata, explicit Loki label resource attributes, and the legacy service.name label.
45
+ `keep_matching_keys(log.attributes, "^(app|otel)[.]")`,
46
+ `keep_matching_keys(resource.attributes, "^(loki[.].*|service[.]name)$")`,
11
47
  ]
12
48
  }
13
49
  output {
@@ -12,7 +12,7 @@ datasources:
12
12
  url: http://prometheus:9090
13
13
  basicAuth: false
14
14
  isDefault: false
15
- version: 1
15
+ version: 2
16
16
  editable: true
17
17
  jsonData:
18
18
  httpMethod: GET
@@ -28,7 +28,7 @@ datasources:
28
28
  url: http://mimir:9009/prometheus
29
29
  basicAuth: false
30
30
  isDefault: false
31
- version: 1
31
+ version: 2
32
32
  editable: true
33
33
  jsonData:
34
34
  httpMethod: GET
@@ -41,7 +41,7 @@ datasources:
41
41
  url: http://loki:3100
42
42
  basicAuth: false
43
43
  isDefault: false
44
- version: 1
44
+ version: 2
45
45
  editable: true
46
46
  jsonData:
47
47
  httpMethod: GET
@@ -66,7 +66,7 @@ datasources:
66
66
  url: http://tempo:3200
67
67
  basicAuth: false
68
68
  isDefault: true
69
- version: 1
69
+ version: 2
70
70
  editable: true
71
71
  apiVersion: 1
72
72
  uid: tempo-streaming-enabled
@@ -77,6 +77,16 @@ datasources:
77
77
  streamingEnabled:
78
78
  search: true
79
79
  metrics: true
80
+ tracesToLogsV2:
81
+ customQuery: false
82
+ datasourceUid: loki
83
+ filterBySpanID: true
84
+ filterByTraceID: true
85
+ spanEndTimeShift: 10s
86
+ spanStartTimeShift: "-10s"
87
+ tags:
88
+ - key: service.name
89
+ value: loki_service_name
80
90
  # This Tempo datasource keeps streaming disabled for links that need stable TraceQL behavior.
81
91
  - name: Tempo (no streaming)
82
92
  type: tempo
@@ -85,7 +95,7 @@ datasources:
85
95
  url: http://tempo:3200
86
96
  basicAuth: false
87
97
  isDefault: false
88
- version: 1
98
+ version: 2
89
99
  editable: true
90
100
  apiVersion: 1
91
101
  uid: tempo-streaming-disabled
@@ -102,4 +112,4 @@ datasources:
102
112
  spanStartTimeShift: "-10s"
103
113
  tags:
104
114
  - key: service.name
105
- value: service_name
115
+ value: loki_service_name
@@ -52,12 +52,16 @@ compactor:
52
52
  # Keep demo logs for 7 days.
53
53
  limits_config:
54
54
  retention_period: 168h
55
+ # Keep synthetic Loki metadata out of the way; Alloy already namespaces these values.
56
+ discover_service_name: []
57
+ discover_log_levels: false
55
58
 
56
59
  # OTLP logs arrive with resource attributes. This list chooses which attributes
57
60
  # Loki indexes as labels; dots are converted to underscores in Loki/Grafana.
58
61
  distributor:
59
62
  otlp_config:
60
63
  default_resource_attributes_as_index_labels:
64
+ - loki.service.name
61
65
  - service.name
62
- - log_type
63
- - log.file.path
66
+ - loki.log.type
67
+ - loki.log.file.path