@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.
- package/dist/attribution.js +391 -0
- package/dist/catalog.js +42 -0
- package/dist/codegen/docs-cli.js +3 -0
- package/dist/codegen/docs.js +101 -0
- package/dist/codegen/generate-cli.js +7 -0
- package/dist/codegen/generate.js +43 -0
- package/dist/codegen/package.js +28 -0
- package/dist/collector.js +150 -0
- package/dist/components/common.js +50 -0
- package/dist/components/connectors.js +242 -0
- package/dist/components/exporters.js +57 -0
- package/dist/components/extensions.js +58 -0
- package/dist/components/filtering.js +106 -0
- package/dist/components/index.js +9 -0
- package/dist/components/k8s-receivers.js +36 -0
- package/dist/components/processors.js +103 -0
- package/dist/components/receivers.js +61 -0
- package/dist/components/sampling.js +257 -0
- package/dist/composites/catalog.js +368 -0
- package/dist/composites/genai-pipeline.js +28 -0
- package/dist/composites/index.js +8 -0
- package/dist/composites/node-agent.js +156 -0
- package/dist/composites/otlp-collector.js +57 -0
- package/dist/composites/red-metrics.js +95 -0
- package/dist/composites/tail-sampling-tier.js +132 -0
- package/dist/config-hygiene.js +464 -0
- package/dist/configmap.js +86 -0
- package/dist/define.js +204 -0
- package/dist/detect.js +22 -0
- package/dist/genai.js +569 -0
- package/dist/import/embedded.js +57 -0
- package/dist/import/generator.js +459 -0
- package/dist/import/parser.js +199 -0
- package/dist/index.js +34 -0
- package/dist/init-templates.js +119 -0
- package/dist/integrity.json +2 -2
- package/dist/lint/audit-catalog.js +49 -0
- package/dist/lint/post-synth/index.js +52 -0
- package/dist/lint/post-synth/otel-helpers.js +103 -0
- package/dist/lint/post-synth/otel101.js +13 -0
- package/dist/lint/post-synth/otel102.js +13 -0
- package/dist/lint/post-synth/otel103.js +13 -0
- package/dist/lint/post-synth/otel104.js +13 -0
- package/dist/lint/post-synth/otel105.js +13 -0
- package/dist/lint/post-synth/otel106.js +13 -0
- package/dist/lint/post-synth/otel107.js +13 -0
- package/dist/lint/post-synth/otel108.js +13 -0
- package/dist/lint/post-synth/otel109.js +13 -0
- package/dist/lint/post-synth/otel112.js +13 -0
- package/dist/lint/post-synth/otel113.js +13 -0
- package/dist/lint/post-synth/otel114.js +13 -0
- package/dist/lint/post-synth/otel115.js +13 -0
- package/dist/lint/post-synth/otel116.js +13 -0
- package/dist/lint/post-synth/otel117.js +13 -0
- package/dist/lint/post-synth/otel118.js +13 -0
- package/dist/lint/post-synth/otel119.js +13 -0
- package/dist/lint/post-synth/otel120.js +13 -0
- package/dist/lint/post-synth/otel121.js +13 -0
- package/dist/lint/post-synth/otel122.js +13 -0
- package/dist/lint/post-synth/otel123.js +13 -0
- package/dist/lint/post-synth/otel124.js +13 -0
- package/dist/lint/post-synth/otel125.js +13 -0
- package/dist/lint/post-synth/otel126.js +13 -0
- package/dist/lint/post-synth/otel127.js +13 -0
- package/dist/lint/rules/component-id-syntax.js +66 -0
- package/dist/lint/rules/index.js +6 -0
- package/dist/lint/rules/literal-credential.js +91 -0
- package/dist/lint/rules/otel-ast.js +30 -0
- package/dist/lsp/completions.js +11 -0
- package/dist/lsp/hover.js +23 -0
- package/dist/manifest.json +1 -1
- package/dist/metric-names.js +244 -0
- package/dist/model.js +90 -0
- package/dist/op/activities/collector-audit.js +213 -0
- package/dist/op/activities/collector-health.js +176 -0
- package/dist/op/activities/exec.js +16 -0
- package/dist/op/activities/github.js +110 -0
- package/dist/op/activities/index.js +16 -0
- package/dist/op/activities/otelcol.js +173 -0
- package/dist/op/activity-contracts.js +64 -0
- package/dist/op/audit-op.js +33 -0
- package/dist/op/builders.js +38 -0
- package/dist/package-cli.js +14 -0
- package/dist/pipeline.js +31 -0
- package/dist/platform.js +144 -0
- package/dist/plugin.js +126 -0
- package/dist/semconv.js +50 -0
- package/dist/serializer.js +26 -0
- package/dist/skill-defs.js +34 -0
- package/dist/topology.js +104 -0
- package/dist/validate-cli.js +7 -0
- package/dist/validate-config.js +706 -0
- package/dist/validate.js +85 -0
- package/dist/yaml.js +157 -0
- package/package.json +8 -8
- package/src/codegen/package.ts +2 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RedMetrics`: rate, errors and duration (RED) metrics from traces, served
|
|
3
|
+
* for Prometheus to scrape.
|
|
4
|
+
*
|
|
5
|
+
* Spans come in on an `otlp` receiver (or the receivers given) and go
|
|
6
|
+
* through `memory_limiter` and `batch` to a `spanmetrics` connector and, by
|
|
7
|
+
* default, a `servicegraph` connector. Both feed a `metrics` pipeline that
|
|
8
|
+
* ends at a `prometheus` exporter on 0.0.0.0:8889. Traces can also go on to
|
|
9
|
+
* a backend through `traceExporters`.
|
|
10
|
+
*
|
|
11
|
+
* The metric names follow from the declaration: `redMetricsNames(red)` reads
|
|
12
|
+
* them back with `spanMetricsNames()` and `serviceGraphNames()`, and the
|
|
13
|
+
* grafana lexicon's `RedDashboard` and the prometheus lexicon's `RedAlerts`
|
|
14
|
+
* take the connector and the exporter (`red.spanMetrics`, `red.exporter`),
|
|
15
|
+
* so a renamed namespace moves every query built from them.
|
|
16
|
+
*/
|
|
17
|
+
import { Composite } from "@intentius/chant/composite";
|
|
18
|
+
import { OtlpReceiver } from "../components/receivers.js";
|
|
19
|
+
import { BatchProcessor, MemoryLimiterProcessor } from "../components/processors.js";
|
|
20
|
+
import { ServiceGraphConnector, SpanMetricsConnector, } from "../components/connectors.js";
|
|
21
|
+
import { PrometheusExporter } from "../components/exporters.js";
|
|
22
|
+
import { Pipeline } from "../pipeline.js";
|
|
23
|
+
import { serviceGraphNames, spanMetricsNames } from "../metric-names.js";
|
|
24
|
+
const NAME = /^[A-Za-z0-9_.-]+$/;
|
|
25
|
+
/** Why a set of `RedMetrics` props can't build, or undefined when they can. */
|
|
26
|
+
export function redMetricsPropsProblem(props = {}) {
|
|
27
|
+
if (props.receivers !== undefined && props.receivers.length === 0)
|
|
28
|
+
return "receivers must name at least one receiver when set";
|
|
29
|
+
if (props.memoryLimitMib !== undefined && !(Number.isInteger(props.memoryLimitMib) && props.memoryLimitMib > 0)) {
|
|
30
|
+
return "memoryLimitMib must be a positive whole number";
|
|
31
|
+
}
|
|
32
|
+
if (props.name !== undefined && !NAME.test(props.name))
|
|
33
|
+
return `name "${props.name}" must be letters, digits, '.', '_' or '-'`;
|
|
34
|
+
if (props.exporter !== undefined && props.exporter.componentType !== "prometheus") {
|
|
35
|
+
return `exporter must be a prometheus exporter, got ${props.exporter.componentType}`;
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* RED metrics from traces: `spanmetrics` and `servicegraph` connectors
|
|
41
|
+
* feeding a `prometheus` exporter.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { RedMetrics, OtlpExporter } from "@intentius/chant-lexicon-otel";
|
|
46
|
+
*
|
|
47
|
+
* const tempo = new OtlpExporter({ name: "tempo", endpoint: "tempo:4317", tls: { insecure: true } });
|
|
48
|
+
* export const red = RedMetrics({ traceExporters: [tempo], spanMetrics: { namespace: "shop", histogram: { unit: "s" } } });
|
|
49
|
+
* // RedDashboard({ spanMetrics: red.spanMetrics, exporter: red.exporter, datasource }) in the grafana lexicon
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export const RedMetrics = Composite((input) => {
|
|
53
|
+
const props = input ?? {};
|
|
54
|
+
const problem = redMetricsPropsProblem(props);
|
|
55
|
+
if (problem)
|
|
56
|
+
throw new Error(`RedMetrics: ${problem}`);
|
|
57
|
+
const name = props.name ?? "red";
|
|
58
|
+
const otlp = props.receivers
|
|
59
|
+
? undefined
|
|
60
|
+
: new OtlpReceiver({ protocols: { grpc: { endpoint: "0.0.0.0:4317" }, http: { endpoint: "0.0.0.0:4318" } } });
|
|
61
|
+
const receivers = props.receivers ?? [otlp];
|
|
62
|
+
const memoryLimiter = new MemoryLimiterProcessor(props.memoryLimitMib !== undefined
|
|
63
|
+
? { check_interval: "1s", limit_mib: props.memoryLimitMib, spike_limit_mib: Math.ceil(props.memoryLimitMib / 4) }
|
|
64
|
+
: { check_interval: "1s", limit_percentage: 80, spike_limit_percentage: 20 });
|
|
65
|
+
const batch = new BatchProcessor({});
|
|
66
|
+
const spanMetrics = new SpanMetricsConnector({ name, ...(props.spanMetrics ?? {}) });
|
|
67
|
+
const graphConfig = props.serviceGraph === undefined || props.serviceGraph === true ? {} : props.serviceGraph === false ? undefined : props.serviceGraph;
|
|
68
|
+
const serviceGraph = graphConfig ? new ServiceGraphConnector({ name, ...graphConfig }) : undefined;
|
|
69
|
+
const exporter = props.exporter ?? new PrometheusExporter({ name, endpoint: "0.0.0.0:8889" });
|
|
70
|
+
const connectors = [spanMetrics, ...(serviceGraph ? [serviceGraph] : [])];
|
|
71
|
+
const traces = new Pipeline({
|
|
72
|
+
signal: "traces",
|
|
73
|
+
name,
|
|
74
|
+
receivers,
|
|
75
|
+
processors: [memoryLimiter, batch],
|
|
76
|
+
exporters: [...connectors, ...(props.traceExporters ?? [])],
|
|
77
|
+
});
|
|
78
|
+
const metrics = new Pipeline({ signal: "metrics", name, receivers: connectors, processors: [batch], exporters: [exporter] });
|
|
79
|
+
// A composite member must be a declarable, so the components that are off are left out rather than undefined.
|
|
80
|
+
const members = { memoryLimiter, batch, spanMetrics, exporter, traces, metrics };
|
|
81
|
+
if (otlp)
|
|
82
|
+
members.otlp = otlp;
|
|
83
|
+
if (serviceGraph)
|
|
84
|
+
members.serviceGraph = serviceGraph;
|
|
85
|
+
return members;
|
|
86
|
+
}, "RedMetrics");
|
|
87
|
+
/** The metric names a `RedMetrics(...)` result serves, read from its connectors and exporter. */
|
|
88
|
+
export function redMetricsNames(red) {
|
|
89
|
+
if (!red?.spanMetrics || !red.exporter)
|
|
90
|
+
throw new Error("redMetricsNames: pass a RedMetrics(...) result");
|
|
91
|
+
return {
|
|
92
|
+
spans: spanMetricsNames(red.spanMetrics, red.exporter),
|
|
93
|
+
...(red.serviceGraph ? { serviceGraph: serviceGraphNames(red.serviceGraph, red.exporter) } : {}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `TailSamplingTier`: the collector config of a tail sampling tier on any
|
|
3
|
+
* host (VMs, Docker, Fly, ECS), for where the k8s lexicon's
|
|
4
|
+
* `OtelCollectorGateway` does not run.
|
|
5
|
+
*
|
|
6
|
+
* Tail sampling needs every span of a trace in one collector. The agents in
|
|
7
|
+
* front send to this tier through a `loadbalancing` exporter that routes by
|
|
8
|
+
* trace id (`tailSamplingLoadBalancer()` declares it), and each replica of
|
|
9
|
+
* the tier holds a trace for `decisionWait`, then keeps it when a policy
|
|
10
|
+
* says so:
|
|
11
|
+
*
|
|
12
|
+
* - `errors`: any span ended in error (default: on);
|
|
13
|
+
* - `slowerThanMs`: the trace took at least this long (default 1000 ms);
|
|
14
|
+
* - `percentage`: a baseline share of everything else (default 10%);
|
|
15
|
+
* - `policies`: more `tail_sampling` policies of your own, after those.
|
|
16
|
+
*
|
|
17
|
+
* The pipeline is `otlp` in, `memory_limiter`, `tail_sampling`, `batch`, the
|
|
18
|
+
* given exporters out. Compute span metrics before this tier (the agents, or
|
|
19
|
+
* `RedMetrics`), or they count only the traces kept.
|
|
20
|
+
*/
|
|
21
|
+
import { Composite } from "@intentius/chant/composite";
|
|
22
|
+
import { goDurationMs } from "../components/common.js";
|
|
23
|
+
import { OtlpReceiver } from "../components/receivers.js";
|
|
24
|
+
import { BatchProcessor, MemoryLimiterProcessor } from "../components/processors.js";
|
|
25
|
+
import { DebugExporter } from "../components/exporters.js";
|
|
26
|
+
import { HealthCheckExtension } from "../components/extensions.js";
|
|
27
|
+
import { LoadBalancingExporter, TailSamplingProcessor, } from "../components/sampling.js";
|
|
28
|
+
import { Pipeline } from "../pipeline.js";
|
|
29
|
+
/** Why a set of `TailSamplingTier` props can't build, or undefined when they can. */
|
|
30
|
+
export function tailSamplingTierPropsProblem(props = {}) {
|
|
31
|
+
if (props.exporters !== undefined && props.exporters.length === 0)
|
|
32
|
+
return "exporters must name at least one exporter when set";
|
|
33
|
+
if (props.receivers !== undefined && props.receivers.length === 0)
|
|
34
|
+
return "receivers must name at least one receiver when set";
|
|
35
|
+
if (props.decisionWait !== undefined) {
|
|
36
|
+
const ms = goDurationMs(props.decisionWait, 0);
|
|
37
|
+
if (ms === undefined || ms <= 0)
|
|
38
|
+
return `decisionWait ${JSON.stringify(props.decisionWait)} is not a positive duration`;
|
|
39
|
+
}
|
|
40
|
+
const s = props.slowerThanMs;
|
|
41
|
+
if (s !== undefined && s !== false && !(Number.isInteger(s) && s > 0))
|
|
42
|
+
return "slowerThanMs must be a positive whole number of milliseconds, or false";
|
|
43
|
+
const pc = props.percentage;
|
|
44
|
+
if (pc !== undefined && pc !== false && !(typeof pc === "number" && pc > 0 && pc <= 100))
|
|
45
|
+
return "percentage must be above 0 and at most 100, or false";
|
|
46
|
+
if (props.numTraces !== undefined && !(Number.isInteger(props.numTraces) && props.numTraces > 0))
|
|
47
|
+
return "numTraces must be a positive whole number";
|
|
48
|
+
if (props.memoryLimitMib !== undefined && !(Number.isInteger(props.memoryLimitMib) && props.memoryLimitMib > 0)) {
|
|
49
|
+
return "memoryLimitMib must be a positive whole number";
|
|
50
|
+
}
|
|
51
|
+
if (tierPolicies(props).length === 0)
|
|
52
|
+
return "every policy is off, so no trace would ever be kept";
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
function tierPolicies(props) {
|
|
56
|
+
const out = [];
|
|
57
|
+
if (props.errors !== false)
|
|
58
|
+
out.push({ name: "errors", type: "status_code", status_code: { status_codes: ["ERROR"] } });
|
|
59
|
+
const slow = props.slowerThanMs ?? 1000;
|
|
60
|
+
if (slow !== false)
|
|
61
|
+
out.push({ name: "slow", type: "latency", latency: { threshold_ms: slow } });
|
|
62
|
+
const pc = props.percentage ?? 10;
|
|
63
|
+
if (pc !== false)
|
|
64
|
+
out.push({ name: "baseline", type: "probabilistic", probabilistic: { sampling_percentage: pc } });
|
|
65
|
+
return [...out, ...(props.policies ?? [])];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The collector config of a tail sampling tier: whole traces in, the ones
|
|
69
|
+
* a policy keeps out.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { TailSamplingTier, OtlpExporter } from "@intentius/chant-lexicon-otel";
|
|
74
|
+
*
|
|
75
|
+
* const tempo = new OtlpExporter({ name: "tempo", endpoint: "tempo:4317", tls: { insecure: true } });
|
|
76
|
+
* export const sampler = TailSamplingTier({ exporters: [tempo], slowerThanMs: 500, percentage: 5 });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export const TailSamplingTier = Composite((input) => {
|
|
80
|
+
const props = input ?? {};
|
|
81
|
+
const problem = tailSamplingTierPropsProblem(props);
|
|
82
|
+
if (problem)
|
|
83
|
+
throw new Error(`TailSamplingTier: ${problem}`);
|
|
84
|
+
const otlp = props.receivers
|
|
85
|
+
? undefined
|
|
86
|
+
: new OtlpReceiver({ protocols: { grpc: { endpoint: "0.0.0.0:4317" }, http: { endpoint: "0.0.0.0:4318" } } });
|
|
87
|
+
const memoryLimiter = new MemoryLimiterProcessor(props.memoryLimitMib !== undefined
|
|
88
|
+
? { check_interval: "1s", limit_mib: props.memoryLimitMib, spike_limit_mib: Math.ceil(props.memoryLimitMib / 4) }
|
|
89
|
+
: { check_interval: "1s", limit_percentage: 80, spike_limit_percentage: 20 });
|
|
90
|
+
const tailSampling = new TailSamplingProcessor({
|
|
91
|
+
decision_wait: props.decisionWait ?? "10s",
|
|
92
|
+
...(props.numTraces !== undefined ? { num_traces: props.numTraces } : {}),
|
|
93
|
+
policies: tierPolicies(props),
|
|
94
|
+
});
|
|
95
|
+
const batch = new BatchProcessor({});
|
|
96
|
+
const debug = props.exporters ? undefined : new DebugExporter({ verbosity: "basic" });
|
|
97
|
+
const traces = new Pipeline({
|
|
98
|
+
signal: "traces",
|
|
99
|
+
receivers: props.receivers ?? [otlp],
|
|
100
|
+
processors: [memoryLimiter, tailSampling, batch],
|
|
101
|
+
exporters: props.exporters ?? [debug],
|
|
102
|
+
});
|
|
103
|
+
const health = props.healthCheck === false ? undefined : new HealthCheckExtension({ endpoint: "0.0.0.0:13133" });
|
|
104
|
+
// A composite member must be a declarable, so the parts that are off are left out rather than undefined.
|
|
105
|
+
const members = { memoryLimiter, tailSampling, batch, traces };
|
|
106
|
+
if (otlp)
|
|
107
|
+
members.otlp = otlp;
|
|
108
|
+
if (debug)
|
|
109
|
+
members.debug = debug;
|
|
110
|
+
if (health)
|
|
111
|
+
members.health = health;
|
|
112
|
+
return members;
|
|
113
|
+
}, "TailSamplingTier");
|
|
114
|
+
/**
|
|
115
|
+
* The exporter the agents in front of a `TailSamplingTier` send traces with:
|
|
116
|
+
* a `loadbalancing` exporter that routes every span of a trace to the same
|
|
117
|
+
* replica of the tier, by trace id.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* // TLS by default; pass `{ otlp: { tls: { insecure: true } } }` as `protocol` on a private network.
|
|
122
|
+
* const toSampler = tailSamplingLoadBalancer({ dns: { hostname: "sampler.internal" } }, "sampler");
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export function tailSamplingLoadBalancer(resolver, name, protocol) {
|
|
126
|
+
return new LoadBalancingExporter({
|
|
127
|
+
...(name !== undefined ? { name } : {}),
|
|
128
|
+
routing_key: "traceID",
|
|
129
|
+
...(protocol !== undefined ? { protocol } : {}),
|
|
130
|
+
resolver,
|
|
131
|
+
});
|
|
132
|
+
}
|