@intentius/chant-lexicon-otel 0.124.30 → 0.124.32

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.
Files changed (96) hide show
  1. package/dist/attribution.js +391 -0
  2. package/dist/catalog.js +42 -0
  3. package/dist/codegen/docs-cli.js +3 -0
  4. package/dist/codegen/docs.js +101 -0
  5. package/dist/codegen/generate-cli.js +7 -0
  6. package/dist/codegen/generate.js +43 -0
  7. package/dist/codegen/package.js +28 -0
  8. package/dist/collector.js +150 -0
  9. package/dist/components/common.js +50 -0
  10. package/dist/components/connectors.js +242 -0
  11. package/dist/components/exporters.js +57 -0
  12. package/dist/components/extensions.js +58 -0
  13. package/dist/components/filtering.js +106 -0
  14. package/dist/components/index.js +9 -0
  15. package/dist/components/k8s-receivers.js +36 -0
  16. package/dist/components/processors.js +103 -0
  17. package/dist/components/receivers.js +61 -0
  18. package/dist/components/sampling.js +257 -0
  19. package/dist/composites/catalog.js +368 -0
  20. package/dist/composites/genai-pipeline.js +28 -0
  21. package/dist/composites/index.js +8 -0
  22. package/dist/composites/node-agent.js +156 -0
  23. package/dist/composites/otlp-collector.js +57 -0
  24. package/dist/composites/red-metrics.js +95 -0
  25. package/dist/composites/tail-sampling-tier.js +132 -0
  26. package/dist/config-hygiene.js +464 -0
  27. package/dist/configmap.js +86 -0
  28. package/dist/define.js +204 -0
  29. package/dist/detect.js +22 -0
  30. package/dist/genai.js +569 -0
  31. package/dist/import/embedded.js +57 -0
  32. package/dist/import/generator.js +459 -0
  33. package/dist/import/parser.js +199 -0
  34. package/dist/index.js +34 -0
  35. package/dist/init-templates.js +119 -0
  36. package/dist/integrity.json +2 -2
  37. package/dist/lint/audit-catalog.js +49 -0
  38. package/dist/lint/post-synth/index.js +52 -0
  39. package/dist/lint/post-synth/otel-helpers.js +103 -0
  40. package/dist/lint/post-synth/otel101.js +13 -0
  41. package/dist/lint/post-synth/otel102.js +13 -0
  42. package/dist/lint/post-synth/otel103.js +13 -0
  43. package/dist/lint/post-synth/otel104.js +13 -0
  44. package/dist/lint/post-synth/otel105.js +13 -0
  45. package/dist/lint/post-synth/otel106.js +13 -0
  46. package/dist/lint/post-synth/otel107.js +13 -0
  47. package/dist/lint/post-synth/otel108.js +13 -0
  48. package/dist/lint/post-synth/otel109.js +13 -0
  49. package/dist/lint/post-synth/otel112.js +13 -0
  50. package/dist/lint/post-synth/otel113.js +13 -0
  51. package/dist/lint/post-synth/otel114.js +13 -0
  52. package/dist/lint/post-synth/otel115.js +13 -0
  53. package/dist/lint/post-synth/otel116.js +13 -0
  54. package/dist/lint/post-synth/otel117.js +13 -0
  55. package/dist/lint/post-synth/otel118.js +13 -0
  56. package/dist/lint/post-synth/otel119.js +13 -0
  57. package/dist/lint/post-synth/otel120.js +13 -0
  58. package/dist/lint/post-synth/otel121.js +13 -0
  59. package/dist/lint/post-synth/otel122.js +13 -0
  60. package/dist/lint/post-synth/otel123.js +13 -0
  61. package/dist/lint/post-synth/otel124.js +13 -0
  62. package/dist/lint/post-synth/otel125.js +13 -0
  63. package/dist/lint/post-synth/otel126.js +13 -0
  64. package/dist/lint/post-synth/otel127.js +13 -0
  65. package/dist/lint/rules/component-id-syntax.js +66 -0
  66. package/dist/lint/rules/index.js +6 -0
  67. package/dist/lint/rules/literal-credential.js +91 -0
  68. package/dist/lint/rules/otel-ast.js +30 -0
  69. package/dist/lsp/completions.js +11 -0
  70. package/dist/lsp/hover.js +23 -0
  71. package/dist/manifest.json +1 -1
  72. package/dist/metric-names.js +244 -0
  73. package/dist/model.js +90 -0
  74. package/dist/op/activities/collector-audit.js +213 -0
  75. package/dist/op/activities/collector-health.js +176 -0
  76. package/dist/op/activities/exec.js +16 -0
  77. package/dist/op/activities/github.js +110 -0
  78. package/dist/op/activities/index.js +16 -0
  79. package/dist/op/activities/otelcol.js +173 -0
  80. package/dist/op/activity-contracts.js +64 -0
  81. package/dist/op/audit-op.js +33 -0
  82. package/dist/op/builders.js +38 -0
  83. package/dist/package-cli.js +14 -0
  84. package/dist/pipeline.js +31 -0
  85. package/dist/platform.js +144 -0
  86. package/dist/plugin.js +126 -0
  87. package/dist/semconv.js +50 -0
  88. package/dist/serializer.js +26 -0
  89. package/dist/skill-defs.js +34 -0
  90. package/dist/topology.js +104 -0
  91. package/dist/validate-cli.js +7 -0
  92. package/dist/validate-config.js +706 -0
  93. package/dist/validate.js +85 -0
  94. package/dist/yaml.js +157 -0
  95. package/package.json +8 -8
  96. package/src/codegen/package.ts +2 -2
@@ -0,0 +1,368 @@
1
+ export const compositeCatalog = [
2
+ {
3
+ "name": "GenAiPipeline",
4
+ "lexicon": "otel",
5
+ "description": "The collector config of an OTLP collector for GenAI workloads, from the GenAI preset.",
6
+ "bundles": [
7
+ "BatchProcessor",
8
+ "DebugExporter",
9
+ "DeltaToCumulativeProcessor",
10
+ "FilterProcessor",
11
+ "ForwardConnector",
12
+ "HealthCheckExtension",
13
+ "MemoryLimiterProcessor",
14
+ "OtlpReceiver",
15
+ "Pipeline",
16
+ "RedactionProcessor",
17
+ "SignalToMetricsConnector",
18
+ "SpanMetricsConnector",
19
+ "SumConnector",
20
+ "TransformProcessor"
21
+ ],
22
+ "params": [
23
+ {
24
+ "name": "traceExporters",
25
+ "type": "Exporter[]",
26
+ "required": false,
27
+ "description": "Where traces go."
28
+ },
29
+ {
30
+ "name": "metricExporters",
31
+ "type": "Exporter[]",
32
+ "required": false,
33
+ "description": "Where the span and token metrics go, and with `clientMetrics` the SDK's metrics too."
34
+ },
35
+ {
36
+ "name": "logExporters",
37
+ "type": "Exporter[]",
38
+ "required": false,
39
+ "description": "Where logs (and events sent as logs) go."
40
+ },
41
+ {
42
+ "name": "logs",
43
+ "type": "boolean",
44
+ "required": false,
45
+ "description": "Give logs a pipeline, so event-based content is removed too."
46
+ },
47
+ {
48
+ "name": "sampling",
49
+ "type": "Processor[]",
50
+ "required": false,
51
+ "description": "Processors that thin exported traces, such as `tail_sampling`."
52
+ },
53
+ {
54
+ "name": "healthCheck",
55
+ "type": "boolean",
56
+ "required": false,
57
+ "description": "Serve `health_check` on 0.0.0.0:13133."
58
+ },
59
+ {
60
+ "name": "deltaToCumulative",
61
+ "type": "boolean | \"auto\"",
62
+ "required": false,
63
+ "description": "Put a `deltatocumulative/genai` processor in front of `batch` on `metrics/genai`, so the delta token sums of the `sum` connector, and with `clientMetrics."
64
+ },
65
+ {
66
+ "name": "keepContent",
67
+ "type": "boolean",
68
+ "required": false,
69
+ "description": "Keep content attributes and bodies."
70
+ },
71
+ {
72
+ "name": "contentAttributes",
73
+ "type": "string[]",
74
+ "required": false,
75
+ "description": "More attribute keys that hold content, deleted along with the convention ones."
76
+ },
77
+ {
78
+ "name": "maskValues",
79
+ "type": "string[]",
80
+ "required": false,
81
+ "description": "RE2 patterns masked in every attribute value (redaction `blocked_values`), whether content is kept or not."
82
+ },
83
+ {
84
+ "name": "hashFunction",
85
+ "type": "RedactionProcessorConfig[\"hash_function\"]",
86
+ "required": false,
87
+ "description": "Hash masked values with this function instead of writing `****`."
88
+ },
89
+ {
90
+ "name": "buckets",
91
+ "type": "Duration[]",
92
+ "required": false,
93
+ "description": "Duration histogram buckets."
94
+ },
95
+ {
96
+ "name": "metricsFlushInterval",
97
+ "type": "Duration",
98
+ "required": false,
99
+ "description": "How often span and token metrics are flushed."
100
+ },
101
+ {
102
+ "name": "namespace",
103
+ "type": "string",
104
+ "required": false,
105
+ "description": "Prefix of the emitted metric names."
106
+ },
107
+ {
108
+ "name": "dimensions",
109
+ "type": "SpanMetricsDimension[]",
110
+ "required": false,
111
+ "description": "Dimensions added to the span metrics beyond the GenAI ones."
112
+ },
113
+ {
114
+ "name": "providerDimensions",
115
+ "type": "boolean",
116
+ "required": false,
117
+ "description": "Add `gen_ai.provider.name` and `gen_ai.response.model` to `genai.calls` and `genai.duration`."
118
+ },
119
+ {
120
+ "name": "clientMetrics",
121
+ "type": "GenAiClientMetricsSource",
122
+ "required": false,
123
+ "description": "Also produce the conventions' client metrics, `gen_ai.client.operation.duration` and `gen_ai.client.token.usage`."
124
+ }
125
+ ]
126
+ },
127
+ {
128
+ "name": "NodeAgent",
129
+ "lexicon": "otel",
130
+ "description": "The collector config of a per-node Kubernetes agent.",
131
+ "bundles": [
132
+ "BatchProcessor",
133
+ "FileLogReceiver",
134
+ "HealthCheckExtension",
135
+ "HostMetricsReceiver",
136
+ "K8sAttributesProcessor",
137
+ "KubeletStatsReceiver",
138
+ "MemoryLimiterProcessor",
139
+ "OtlpReceiver",
140
+ "Pipeline",
141
+ "ResourceDetectionProcessor",
142
+ "ResourceProcessor"
143
+ ],
144
+ "params": [
145
+ {
146
+ "name": "exporters",
147
+ "type": "Exporter[]",
148
+ "required": true,
149
+ "description": "Where traces and logs go: usually the gateway (the k8s lexicon's `gatewayExporter()`), or an OTLP backend."
150
+ },
151
+ {
152
+ "name": "metricExporters",
153
+ "type": "Exporter[]",
154
+ "required": false,
155
+ "description": "Where metrics go, e.g. a `prometheus` exporter for a scrape on each node."
156
+ },
157
+ {
158
+ "name": "clusterName",
159
+ "type": "string",
160
+ "required": false,
161
+ "description": "Sets `k8s.cluster.name` on everything the agent sends."
162
+ },
163
+ {
164
+ "name": "nodeNameEnv",
165
+ "type": "string",
166
+ "required": false,
167
+ "description": "The environment variable holding this node's name, set from `spec.nodeName` (default `K8S_NODE_NAME`)."
168
+ },
169
+ {
170
+ "name": "hostMetrics",
171
+ "type": "boolean | { interval?: Duration; rootPath?: string }",
172
+ "required": false,
173
+ "description": "Node CPU, memory, load, filesystem and network from the host root at `/hostfs` (default: on, every 30s)."
174
+ },
175
+ {
176
+ "name": "containerLogs",
177
+ "type": "boolean | { selfContainer?: string }",
178
+ "required": false,
179
+ "description": "Container logs from `/var/log/pods` (default: on)."
180
+ },
181
+ {
182
+ "name": "kubeletStats",
183
+ "type": "boolean | { interval?: Duration }",
184
+ "required": false,
185
+ "description": "Pod and container resource metrics from this node's kubelet (default: off)."
186
+ },
187
+ {
188
+ "name": "memoryLimitMib",
189
+ "type": "number",
190
+ "required": false,
191
+ "description": "`memory_limiter`'s hard limit in MiB (default 400; the spike limit is a quarter of it)."
192
+ },
193
+ {
194
+ "name": "healthCheck",
195
+ "type": "boolean",
196
+ "required": false,
197
+ "description": "Serve `health_check` on 0.0.0.0:13133 (default: on)."
198
+ }
199
+ ]
200
+ },
201
+ {
202
+ "name": "OtlpCollector",
203
+ "lexicon": "otel",
204
+ "description": "A small OTLP collector config: OTLP in, `memory_limiter` and `batch`, the given exporters out, for each signal.",
205
+ "bundles": [
206
+ "BatchProcessor",
207
+ "DebugExporter",
208
+ "HealthCheckExtension",
209
+ "MemoryLimiterProcessor",
210
+ "OtlpReceiver",
211
+ "Pipeline"
212
+ ],
213
+ "params": [
214
+ {
215
+ "name": "exporters",
216
+ "type": "OTelComponent<\"exporter\", string, any>[]",
217
+ "required": false,
218
+ "description": "Where telemetry goes."
219
+ },
220
+ {
221
+ "name": "signals",
222
+ "type": "Signal[]",
223
+ "required": false,
224
+ "description": "Which signals get a pipeline."
225
+ },
226
+ {
227
+ "name": "healthCheck",
228
+ "type": "boolean",
229
+ "required": false,
230
+ "description": "Serve `health_check` on 0.0.0.0:13133."
231
+ }
232
+ ]
233
+ },
234
+ {
235
+ "name": "RedMetrics",
236
+ "lexicon": "otel",
237
+ "description": "RED metrics from traces: `spanmetrics` and `servicegraph` connectors feeding a `prometheus` exporter.",
238
+ "bundles": [
239
+ "BatchProcessor",
240
+ "MemoryLimiterProcessor",
241
+ "OtlpReceiver",
242
+ "Pipeline",
243
+ "PrometheusExporter",
244
+ "ServiceGraphConnector",
245
+ "SpanMetricsConnector"
246
+ ],
247
+ "params": [
248
+ {
249
+ "name": "receivers",
250
+ "type": "TraceSource[]",
251
+ "required": false,
252
+ "description": "Where spans come from: receivers, or a connector from another traces pipeline."
253
+ },
254
+ {
255
+ "name": "traceExporters",
256
+ "type": "Exporter[]",
257
+ "required": false,
258
+ "description": "Where traces go besides the connectors, e.g. a tracing backend."
259
+ },
260
+ {
261
+ "name": "exporter",
262
+ "type": "OTelComponent<\"exporter\", \"prometheus\", PrometheusExporterConfig>",
263
+ "required": false,
264
+ "description": "The `prometheus` exporter the metrics are served on."
265
+ },
266
+ {
267
+ "name": "spanMetrics",
268
+ "type": "SpanMetricsConnectorConfig",
269
+ "required": false,
270
+ "description": "The `spanmetrics` connector's config: namespace, dimensions, histogram unit and buckets (default: the connector's defaults)."
271
+ },
272
+ {
273
+ "name": "serviceGraph",
274
+ "type": "boolean | ServiceGraphConnectorConfig",
275
+ "required": false,
276
+ "description": "Service-to-service edge metrics from a `servicegraph` connector (default: on)."
277
+ },
278
+ {
279
+ "name": "memoryLimitMib",
280
+ "type": "number",
281
+ "required": false,
282
+ "description": "`memory_limiter`'s hard limit in MiB (default: 80% of the container's memory, with a 20% spike limit)."
283
+ },
284
+ {
285
+ "name": "name",
286
+ "type": "string",
287
+ "required": false,
288
+ "description": "The instance name of the connectors, the exporter and the two pipelines (default `red`)."
289
+ }
290
+ ]
291
+ },
292
+ {
293
+ "name": "TailSamplingTier",
294
+ "lexicon": "otel",
295
+ "description": "The collector config of a tail sampling tier: whole traces in, the ones a policy keeps out.",
296
+ "bundles": [
297
+ "BatchProcessor",
298
+ "DebugExporter",
299
+ "HealthCheckExtension",
300
+ "MemoryLimiterProcessor",
301
+ "OtlpReceiver",
302
+ "Pipeline",
303
+ "TailSamplingProcessor"
304
+ ],
305
+ "params": [
306
+ {
307
+ "name": "exporters",
308
+ "type": "Exporter[]",
309
+ "required": false,
310
+ "description": "Where the kept traces go."
311
+ },
312
+ {
313
+ "name": "receivers",
314
+ "type": "TraceSource[]",
315
+ "required": false,
316
+ "description": "Where spans come from."
317
+ },
318
+ {
319
+ "name": "decisionWait",
320
+ "type": "Duration",
321
+ "required": false,
322
+ "description": "How long after a trace's first span the decision is made (default `10s`)."
323
+ },
324
+ {
325
+ "name": "errors",
326
+ "type": "boolean",
327
+ "required": false,
328
+ "description": "Keep every trace with a span ended in error (default: on)."
329
+ },
330
+ {
331
+ "name": "slowerThanMs",
332
+ "type": "number | false",
333
+ "required": false,
334
+ "description": "Keep every trace that took at least this many milliseconds (default 1000; `false` for none)."
335
+ },
336
+ {
337
+ "name": "percentage",
338
+ "type": "number | false",
339
+ "required": false,
340
+ "description": "Keep this percentage of all other traces, by trace id hash (default 10; `false` for none)."
341
+ },
342
+ {
343
+ "name": "policies",
344
+ "type": "TailSamplingPolicy[]",
345
+ "required": false,
346
+ "description": "More policies, evaluated after the ones above."
347
+ },
348
+ {
349
+ "name": "numTraces",
350
+ "type": "number",
351
+ "required": false,
352
+ "description": "How many traces to hold in memory at once (default: the collector's 50000)."
353
+ },
354
+ {
355
+ "name": "memoryLimitMib",
356
+ "type": "number",
357
+ "required": false,
358
+ "description": "`memory_limiter`'s hard limit in MiB (default: 80% of the container's memory, with a 20% spike limit)."
359
+ },
360
+ {
361
+ "name": "healthCheck",
362
+ "type": "boolean",
363
+ "required": false,
364
+ "description": "Serve `health_check` on 0.0.0.0:13133 (default: on)."
365
+ }
366
+ ]
367
+ }
368
+ ];
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `GenAiPipeline`: `genAiPipeline()` as a composite.
3
+ *
4
+ * The same collector config `genAiPipeline(options)` returns as a list (an
5
+ * `otlp` receiver, content removal on traces and logs, a `traces/genai`
6
+ * branch that turns GenAI spans into metrics before any sampling, and a
7
+ * `metrics/genai` pipeline that exports them), with each entity under a
8
+ * member name, so a project exports one declaration and reads the parts by
9
+ * name. The options are `genAiPipeline()`'s; every one has its default.
10
+ *
11
+ * The prometheus lexicon's `GenAiRules` reads the metric names from
12
+ * `genAiMetrics(options)` with the same options.
13
+ */
14
+ import { Composite } from "@intentius/chant/composite";
15
+ import { genAiPipelineParts } from "../genai.js";
16
+ /**
17
+ * The collector config of an OTLP collector for GenAI workloads, from the
18
+ * GenAI preset.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * import { GenAiPipeline, PrometheusExporter } from "@intentius/chant-lexicon-otel";
23
+ *
24
+ * const scrape = new PrometheusExporter({ endpoint: "0.0.0.0:8889" });
25
+ * export const genai = GenAiPipeline({ metricExporters: [scrape], clientMetrics: "derive" });
26
+ * ```
27
+ */
28
+ export const GenAiPipeline = Composite((props) => genAiPipelineParts(props ?? {}), "GenAiPipeline");
@@ -0,0 +1,8 @@
1
+ /**
2
+ * otel lexicon composites: declarations that expand to a collector config's components and pipelines.
3
+ */
4
+ export { NodeAgent, nodeAgentPropsProblem } from "./node-agent.js";
5
+ export { RedMetrics, redMetricsNames, redMetricsPropsProblem } from "./red-metrics.js";
6
+ export { GenAiPipeline } from "./genai-pipeline.js";
7
+ export { OtlpCollector, otlpCollectorPropsProblem } from "./otlp-collector.js";
8
+ export { TailSamplingTier, tailSamplingTierPropsProblem, tailSamplingLoadBalancer } from "./tail-sampling-tier.js";
@@ -0,0 +1,156 @@
1
+ /**
2
+ * `NodeAgent`: the collector config a per-node agent runs on Kubernetes.
3
+ *
4
+ * One collector per node (a DaemonSet) takes OTLP from the pods on its node,
5
+ * reads the node's own telemetry, puts Kubernetes metadata on all of it, and
6
+ * hands it on, usually to a gateway tier. This composite declares that
7
+ * config:
8
+ *
9
+ * - receivers: `otlp` (gRPC on 4317, HTTP on 4318), and by default
10
+ * `hostmetrics` (CPU, memory, load, filesystem, network of the node, read
11
+ * under `/hostfs`) and `filelog` (container logs under `/var/log/pods`,
12
+ * parsed by the `container` operator). `kubeletstats` (pod and container
13
+ * resource metrics from the local kubelet) is opt-in.
14
+ * - processors, in order: `memory_limiter`; `k8sattributes`, filtered to
15
+ * pods on this node and matching by pod IP, pod UID or the connection;
16
+ * `resourcedetection` (`env`, `system`); a `resource` processor setting
17
+ * `k8s.cluster.name` when `clusterName` is given; `batch`.
18
+ * - a `traces`, a `metrics` and a `logs` pipeline, each through those
19
+ * processors to the given exporters.
20
+ * - a `health_check` extension on 0.0.0.0:13133.
21
+ *
22
+ * Tail sampling is left out on purpose: an agent sees only its node's
23
+ * spans of a trace, so a sampling decision there is made on partial traces.
24
+ * Sample on the gateway.
25
+ *
26
+ * What the pod has to provide: the node name in an environment variable
27
+ * (`K8S_NODE_NAME` by default, from `spec.nodeName`), the host root mounted
28
+ * read-only at `/hostfs` for `hostmetrics`, `/var/log/pods` mounted
29
+ * read-only for `filelog`, and a service account that may get, list and
30
+ * watch pods, namespaces and nodes (and replicasets, for
31
+ * `k8s.deployment.name`). The k8s lexicon's `OtelCollector` takes this
32
+ * composite's entities as its `config` and derives all of that from them:
33
+ * the variable, the read-only host mounts, group 0 for the root-owned log
34
+ * files, the RBAC and the ports.
35
+ */
36
+ import { Composite } from "@intentius/chant/composite";
37
+ import { OtlpReceiver, HostMetricsReceiver, FileLogReceiver } from "../components/receivers.js";
38
+ import { KubeletStatsReceiver } from "../components/k8s-receivers.js";
39
+ import { BatchProcessor, K8sAttributesProcessor, MemoryLimiterProcessor, ResourceDetectionProcessor, ResourceProcessor, } from "../components/processors.js";
40
+ import { HealthCheckExtension } from "../components/extensions.js";
41
+ import { Pipeline } from "../pipeline.js";
42
+ const HOST_SCRAPERS = { cpu: {}, memory: {}, load: {}, filesystem: {}, network: {} };
43
+ /** Pod metadata for every signal. `k8s.pod.uid` is what the `container` log parser sets on log records. */
44
+ const POD_METADATA = {
45
+ metadata: ["k8s.namespace.name", "k8s.pod.name", "k8s.pod.uid", "k8s.deployment.name", "k8s.node.name"],
46
+ };
47
+ const POD_ASSOCIATION = [
48
+ { sources: [{ from: "resource_attribute", name: "k8s.pod.ip" }] },
49
+ { sources: [{ from: "resource_attribute", name: "k8s.pod.uid" }] },
50
+ { sources: [{ from: "connection" }] },
51
+ ];
52
+ function option(value, onByDefault) {
53
+ if (value === undefined)
54
+ return onByDefault ? {} : undefined;
55
+ if (value === false)
56
+ return undefined;
57
+ return value === true ? {} : value;
58
+ }
59
+ /** Why a set of `NodeAgent` props can't build, or undefined when they can. */
60
+ export function nodeAgentPropsProblem(props) {
61
+ if (!Array.isArray(props.exporters) || props.exporters.length === 0)
62
+ return "exporters must name at least one exporter";
63
+ if (props.metricExporters !== undefined && props.metricExporters.length === 0)
64
+ return "metricExporters must name at least one exporter when set";
65
+ if (props.memoryLimitMib !== undefined && !(Number.isInteger(props.memoryLimitMib) && props.memoryLimitMib > 0)) {
66
+ return "memoryLimitMib must be a positive whole number";
67
+ }
68
+ if (props.nodeNameEnv !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(props.nodeNameEnv)) {
69
+ return `nodeNameEnv "${props.nodeNameEnv}" is not an environment variable name`;
70
+ }
71
+ return undefined;
72
+ }
73
+ /**
74
+ * The collector config of a per-node Kubernetes agent: OTLP from the node's
75
+ * pods, host metrics and container logs from the node itself, Kubernetes
76
+ * metadata on everything, sent on to the given exporters.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import { NodeAgent, OtlpExporter } from "@intentius/chant-lexicon-otel";
81
+ *
82
+ * const gateway = new OtlpExporter({ name: "gateway", endpoint: "otel-gateway.observability.svc:4317", tls: { insecure: true } });
83
+ * export const agent = NodeAgent({ exporters: [gateway], clusterName: "prod-eu-1" });
84
+ * ```
85
+ */
86
+ export const NodeAgent = Composite((props) => {
87
+ const problem = nodeAgentPropsProblem(props);
88
+ if (problem)
89
+ throw new Error(`NodeAgent: ${problem}`);
90
+ const nodeNameEnv = props.nodeNameEnv ?? "K8S_NODE_NAME";
91
+ const limit = props.memoryLimitMib ?? 400;
92
+ const host = option(props.hostMetrics, true);
93
+ const logFiles = option(props.containerLogs, true);
94
+ const kubelet = option(props.kubeletStats, false);
95
+ const otlp = new OtlpReceiver({
96
+ protocols: { grpc: { endpoint: "0.0.0.0:4317" }, http: { endpoint: "0.0.0.0:4318" } },
97
+ });
98
+ const hostMetrics = host
99
+ ? new HostMetricsReceiver({ collection_interval: host.interval ?? "30s", root_path: host.rootPath ?? "/hostfs", scrapers: HOST_SCRAPERS })
100
+ : undefined;
101
+ const containerLogs = logFiles
102
+ ? new FileLogReceiver({
103
+ include: ["/var/log/pods/*/*/*.log"],
104
+ exclude: [`/var/log/pods/*/${logFiles.selfContainer ?? "otel-collector"}/*.log`],
105
+ start_at: "end",
106
+ include_file_path: true,
107
+ operators: [{ type: "container", id: "container-parser" }],
108
+ })
109
+ : undefined;
110
+ const kubeletStats = kubelet
111
+ ? new KubeletStatsReceiver({
112
+ collection_interval: kubelet.interval ?? "30s",
113
+ auth_type: "serviceAccount",
114
+ endpoint: `https://\${env:${nodeNameEnv}}:10250`,
115
+ insecure_skip_verify: true,
116
+ node: `\${env:${nodeNameEnv}}`,
117
+ })
118
+ : undefined;
119
+ const memoryLimiter = new MemoryLimiterProcessor({ check_interval: "1s", limit_mib: limit, spike_limit_mib: Math.ceil(limit / 4) });
120
+ const k8sAttributes = new K8sAttributesProcessor({
121
+ auth_type: "serviceAccount",
122
+ filter: { node_from_env_var: nodeNameEnv },
123
+ extract: POD_METADATA,
124
+ pod_association: POD_ASSOCIATION,
125
+ });
126
+ const resourceDetection = new ResourceDetectionProcessor({ detectors: ["env", "system"], timeout: "5s", override: false });
127
+ const cluster = props.clusterName
128
+ ? new ResourceProcessor({ attributes: [{ key: "k8s.cluster.name", value: props.clusterName, action: "upsert" }] })
129
+ : undefined;
130
+ const batch = new BatchProcessor({ timeout: "10s" });
131
+ const processors = [memoryLimiter, k8sAttributes, resourceDetection, ...(cluster ? [cluster] : []), batch];
132
+ const exporters = props.exporters;
133
+ const metricExporters = props.metricExporters ?? exporters;
134
+ const traces = new Pipeline({ signal: "traces", receivers: [otlp], processors, exporters });
135
+ const metrics = new Pipeline({
136
+ signal: "metrics",
137
+ receivers: [otlp, ...(hostMetrics ? [hostMetrics] : []), ...(kubeletStats ? [kubeletStats] : [])],
138
+ processors,
139
+ exporters: metricExporters,
140
+ });
141
+ const logs = new Pipeline({ signal: "logs", receivers: [...(containerLogs ? [containerLogs] : []), otlp], processors, exporters });
142
+ const health = props.healthCheck === false ? undefined : new HealthCheckExtension({ endpoint: "0.0.0.0:13133" });
143
+ // A composite member must be a declarable, so the components that are off are left out rather than undefined.
144
+ const members = { otlp, memoryLimiter, k8sAttributes, resourceDetection, batch, traces, metrics, logs };
145
+ if (hostMetrics)
146
+ members.hostMetrics = hostMetrics;
147
+ if (containerLogs)
148
+ members.containerLogs = containerLogs;
149
+ if (kubeletStats)
150
+ members.kubeletStats = kubeletStats;
151
+ if (cluster)
152
+ members.cluster = cluster;
153
+ if (health)
154
+ members.health = health;
155
+ return members;
156
+ }, "NodeAgent");
@@ -0,0 +1,57 @@
1
+ /**
2
+ * `OtlpCollector`: `otlpCollector()` as a composite.
3
+ *
4
+ * The small OTLP collector the docker, k8s and fly platform composites start
5
+ * from: an `otlp` receiver on 4317 (gRPC) and 4318 (HTTP), `memory_limiter`
6
+ * then `batch`, the given exporters (default: one `debug` exporter), one
7
+ * pipeline per signal, and a `health_check` extension on 0.0.0.0:13133.
8
+ * Each part is a member, so a project exports one declaration and hands
9
+ * the parts on by name.
10
+ */
11
+ import { Composite } from "@intentius/chant/composite";
12
+ import { otlpCollectorParts } from "../platform.js";
13
+ /** Why a set of `OtlpCollector` props can't build, or undefined when they can. */
14
+ export function otlpCollectorPropsProblem(props = {}) {
15
+ if (props.exporters !== undefined && props.exporters.length === 0)
16
+ return "exporters must name at least one exporter when set";
17
+ if (props.signals !== undefined) {
18
+ if (props.signals.length === 0)
19
+ return "signals must name at least one signal when set";
20
+ const bad = props.signals.find((s) => s !== "traces" && s !== "metrics" && s !== "logs");
21
+ if (bad !== undefined)
22
+ return `signals: "${String(bad)}" is not traces, metrics or logs`;
23
+ }
24
+ return undefined;
25
+ }
26
+ /**
27
+ * A small OTLP collector config: OTLP in, `memory_limiter` and `batch`, the
28
+ * given exporters out, for each signal.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { OtlpCollector, OtlpExporter } from "@intentius/chant-lexicon-otel";
33
+ *
34
+ * const backend = new OtlpExporter({ name: "backend", endpoint: "collector.example.com:4317" });
35
+ * export const collector = OtlpCollector({ exporters: [backend], signals: ["traces", "logs"] });
36
+ * ```
37
+ */
38
+ export const OtlpCollector = Composite((input) => {
39
+ const props = input ?? {};
40
+ const problem = otlpCollectorPropsProblem(props);
41
+ if (problem)
42
+ throw new Error(`OtlpCollector: ${problem}`);
43
+ const p = otlpCollectorParts(props);
44
+ // A composite member must be a declarable, so the parts that are off are left out rather than undefined.
45
+ const members = { otlp: p.otlp, memoryLimiter: p.memoryLimiter, batch: p.batch };
46
+ if (props.exporters === undefined)
47
+ members.debug = p.exporters[0];
48
+ if (p.health)
49
+ members.health = p.health;
50
+ if (p.pipelines.traces)
51
+ members.traces = p.pipelines.traces;
52
+ if (p.pipelines.metrics)
53
+ members.metrics = p.pipelines.metrics;
54
+ if (p.pipelines.logs)
55
+ members.logs = p.pipelines.logs;
56
+ return members;
57
+ }, "OtlpCollector");