@primafuture/telemetry-stack 0.1.0 → 0.3.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 +106 -6
- package/package.json +1 -1
- package/stack/configs/config.alloy +45 -3
- package/stack/configs/grafana-datasources.yaml +16 -6
- package/stack/templates/loki.yaml +6 -2
package/README.md
CHANGED
|
@@ -195,10 +195,32 @@ 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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
198
|
+
\"attributes\": [
|
|
199
|
+
{
|
|
200
|
+
\"key\": \"app.value.log_type\",
|
|
201
|
+
\"value\": {\"stringValue\": \"smoke_test\"}
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
\"key\": \"app.value.route\",
|
|
205
|
+
\"value\": {\"stringValue\": \"/smoke\"}
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
\"key\": \"app.value.count\",
|
|
209
|
+
\"value\": {\"intValue\": \"3\"}
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
\"key\": \"app.value.parallel\",
|
|
213
|
+
\"value\": {\"boolValue\": true}
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
\"key\": \"app.type.deletedAt\",
|
|
217
|
+
\"value\": {\"stringValue\": \"null\"}
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
\"key\": \"app.meta.json\",
|
|
221
|
+
\"value\": {\"stringValue\": \"{\\\"log_type\\\":\\\"smoke_test\\\",\\\"route\\\":\\\"/smoke\\\",\\\"count\\\":3,\\\"parallel\\\":true,\\\"deletedAt\\\":null}\"}
|
|
222
|
+
}
|
|
223
|
+
]
|
|
202
224
|
}]
|
|
203
225
|
}]
|
|
204
226
|
}]
|
|
@@ -208,8 +230,86 @@ curl -sS -X POST http://localhost:4318/v1/logs \
|
|
|
208
230
|
Query it from Loki:
|
|
209
231
|
|
|
210
232
|
```bash
|
|
211
|
-
curl -G 'http://localhost:3100/loki/api/v1/
|
|
212
|
-
--data-urlencode 'query={
|
|
233
|
+
curl -G 'http://localhost:3100/loki/api/v1/query_range' \
|
|
234
|
+
--data-urlencode 'query={loki_service_name="telemetry-smoke-test"}' \
|
|
235
|
+
--data-urlencode 'limit=5'
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
For Grafana trace-to-logs compatibility, the same log is also indexed by the
|
|
239
|
+
legacy `service_name` label:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
curl -G 'http://localhost:3100/loki/api/v1/query_range' \
|
|
243
|
+
--data-urlencode 'query={service_name="telemetry-smoke-test"}' \
|
|
244
|
+
--data-urlencode 'limit=5'
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The returned log should expose application metadata under structured names such
|
|
248
|
+
as `app_value_log_type`, `app_value_route`, `app_value_count`,
|
|
249
|
+
`app_value_parallel`, `app_type_deletedAt`, and `app_meta_json`.
|
|
250
|
+
OpenTelemetry metadata should be under `otel_*`, for example
|
|
251
|
+
`otel_resource_service_name` and `otel_scope_name`.
|
|
252
|
+
|
|
253
|
+
## Log Metadata in Loki
|
|
254
|
+
|
|
255
|
+
PrimaFuture telemetry libraries encode application metadata before export. The
|
|
256
|
+
query-friendly view uses `app.value.*` for values, `app.type.*` for type markers
|
|
257
|
+
such as `null` or `undefined`, and `app.meta.json` for the JSON representation.
|
|
258
|
+
Loki displays those dotted names as underscore names such as `app_value_route`.
|
|
259
|
+
|
|
260
|
+
Legacy applications can still log plain metadata such as `count`, `parallel`, or
|
|
261
|
+
`route`. Alloy wraps only those unprefixed legacy fields into `app.*`; it leaves
|
|
262
|
+
already namespaced `app.*` metadata untouched.
|
|
263
|
+
|
|
264
|
+
The stack uses these names:
|
|
265
|
+
|
|
266
|
+
```text
|
|
267
|
+
app_value_* query-friendly application metadata values
|
|
268
|
+
app_type_* application metadata type markers
|
|
269
|
+
app_meta_json JSON application metadata representation
|
|
270
|
+
app_* legacy application log attributes wrapped by Alloy
|
|
271
|
+
otel_resource_* OpenTelemetry resource attributes
|
|
272
|
+
otel_scope_* OpenTelemetry instrumentation scope attributes
|
|
273
|
+
otel_log_* OpenTelemetry log record fields
|
|
274
|
+
loki_* explicit Loki index labels
|
|
275
|
+
service_name legacy compatibility label for Grafana trace-to-logs
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Examples:
|
|
279
|
+
|
|
280
|
+
```text
|
|
281
|
+
count -> app_value_count
|
|
282
|
+
route -> app_value_route
|
|
283
|
+
deletedAt: null -> app_type_deletedAt = "null"
|
|
284
|
+
metadata JSON -> app_meta_json
|
|
285
|
+
service.name -> otel_resource_service_name
|
|
286
|
+
instrumentation scope -> otel_scope_name
|
|
287
|
+
trace id -> otel_log_trace_id
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Loki indexes only the low-cardinality resource labels configured in
|
|
291
|
+
`loki.yaml`, such as `loki_service_name` and the compatibility `service_name`.
|
|
292
|
+
Application and OpenTelemetry detail fields are stored as structured metadata,
|
|
293
|
+
not as index labels. Query them after selecting a stream:
|
|
294
|
+
|
|
295
|
+
```logql
|
|
296
|
+
{loki_service_name="telemetry-dice-roller-example"}
|
|
297
|
+
| trace_id="0242ac120002"
|
|
298
|
+
| app_value_route="/roll"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Current Loki versions return structured metadata merged into the `stream` labels
|
|
302
|
+
map in `query_range` responses. This is a Loki API behavior: the fields are still
|
|
303
|
+
not index labels. Use `/loki/api/v1/labels` or `/loki/api/v1/series` to see the
|
|
304
|
+
real indexed labels.
|
|
305
|
+
|
|
306
|
+
If a query response is too noisy, reduce returned fields at query time without
|
|
307
|
+
changing stored data:
|
|
308
|
+
|
|
309
|
+
```logql
|
|
310
|
+
{service_name="telemetry-dice-roller-example"}
|
|
311
|
+
| trace_id="0242ac120002"
|
|
312
|
+
| keep service_name, loki_service_name, trace_id, span_id
|
|
213
313
|
```
|
|
214
314
|
|
|
215
315
|
## Useful URLs
|
package/package.json
CHANGED
|
@@ -1,13 +1,55 @@
|
|
|
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
|
-
//
|
|
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
|
-
|
|
10
|
-
`
|
|
12
|
+
// Copy legacy application log attributes into app.* without wrapping already namespaced app.* fields again.
|
|
13
|
+
`merge_maps(log.cache, log.attributes, "upsert")`,
|
|
14
|
+
`delete_matching_keys(log.cache, "^app[.].*")`,
|
|
15
|
+
`delete_matching_keys(log.cache, "^otel[.].*")`,
|
|
16
|
+
`flatten(log.cache, "app", resolveConflicts=true)`,
|
|
17
|
+
`merge_maps(log.attributes, log.cache, "upsert")`,
|
|
18
|
+
`delete_matching_keys(log.cache, ".*")`,
|
|
19
|
+
|
|
20
|
+
// Copy original resource attributes into otel.resource.* before resource labels are rewritten for Loki.
|
|
21
|
+
`merge_maps(log.cache, resource.attributes, "upsert")`,
|
|
22
|
+
`flatten(log.cache, "otel.resource", resolveConflicts=true)`,
|
|
23
|
+
`merge_maps(log.attributes, log.cache, "upsert")`,
|
|
24
|
+
`delete_matching_keys(log.cache, ".*")`,
|
|
25
|
+
|
|
26
|
+
// Copy instrumentation scope data into otel.scope.*.
|
|
27
|
+
`merge_maps(log.cache, instrumentation_scope.attributes, "upsert")`,
|
|
28
|
+
`flatten(log.cache, "otel.scope.attributes", resolveConflicts=true)`,
|
|
29
|
+
`merge_maps(log.attributes, log.cache, "upsert")`,
|
|
30
|
+
`delete_matching_keys(log.cache, ".*")`,
|
|
31
|
+
`set(log.attributes["otel.scope.name"], instrumentation_scope.name) where instrumentation_scope.name != ""`,
|
|
32
|
+
`set(log.attributes["otel.scope.version"], instrumentation_scope.version) where instrumentation_scope.version != ""`,
|
|
33
|
+
|
|
34
|
+
// Duplicate intrinsic log fields into otel.log.* while leaving native trace/span fields intact for Grafana links.
|
|
35
|
+
`set(log.attributes["otel.log.severity_text"], log.severity_text) where log.severity_text != ""`,
|
|
36
|
+
`set(log.attributes["otel.log.severity_number"], log.severity_number)`,
|
|
37
|
+
`set(log.attributes["otel.log.trace_id"], log.trace_id.string) where log.trace_id.string != ""`,
|
|
38
|
+
`set(log.attributes["otel.log.span_id"], log.span_id.string) where log.span_id.string != ""`,
|
|
39
|
+
`set(log.attributes["otel.log.flags"], log.flags)`,
|
|
40
|
+
|
|
41
|
+
// Build explicit Loki labels from original values. Loki will normalize dots to underscores.
|
|
42
|
+
`set(resource.attributes["loki.service.name"], resource.attributes["service.name"]) where resource.attributes["service.name"] != nil`,
|
|
43
|
+
`set(resource.attributes["loki.log.type"], log.attributes["log_type"]) where log.attributes["log_type"] != nil`,
|
|
44
|
+
`set(resource.attributes["loki.log.type"], log.attributes["value.log_type"]) where resource.attributes["loki.log.type"] == nil and log.attributes["value.log_type"] != nil`,
|
|
45
|
+
`set(resource.attributes["loki.log.type"], log.attributes["app.value.log_type"]) where resource.attributes["loki.log.type"] == nil and log.attributes["app.value.log_type"] != nil`,
|
|
46
|
+
`set(resource.attributes["loki.log.file.path"], log.attributes["log.file.path"]) where log.attributes["log.file.path"] != nil`,
|
|
47
|
+
`set(resource.attributes["loki.log.file.path"], log.attributes["value.log.file.path"]) where resource.attributes["loki.log.file.path"] == nil and log.attributes["value.log.file.path"] != nil`,
|
|
48
|
+
`set(resource.attributes["loki.log.file.path"], log.attributes["app.value.log.file.path"]) where resource.attributes["loki.log.file.path"] == nil and log.attributes["app.value.log.file.path"] != nil`,
|
|
49
|
+
|
|
50
|
+
// Keep only namespaced structured metadata, explicit Loki label resource attributes, and the legacy service.name label.
|
|
51
|
+
`keep_matching_keys(log.attributes, "^(app|otel)[.]")`,
|
|
52
|
+
`keep_matching_keys(resource.attributes, "^(loki[.].*|service[.]name)$")`,
|
|
11
53
|
]
|
|
12
54
|
}
|
|
13
55
|
output {
|
|
@@ -12,7 +12,7 @@ datasources:
|
|
|
12
12
|
url: http://prometheus:9090
|
|
13
13
|
basicAuth: false
|
|
14
14
|
isDefault: false
|
|
15
|
-
version:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
-
|
|
63
|
-
- log.file.path
|
|
66
|
+
- loki.log.type
|
|
67
|
+
- loki.log.file.path
|