@intentius/chant-lexicon-otel 0.102.0 → 0.104.1
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 +1 -1
- package/dist/attribution.d.ts +69 -0
- package/dist/attribution.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/components/processors.d.ts +1 -1
- package/dist/components/processors.d.ts.map +1 -1
- package/dist/components/sampling.d.ts +3 -0
- package/dist/components/sampling.d.ts.map +1 -1
- package/dist/config-hygiene.d.ts +33 -0
- package/dist/config-hygiene.d.ts.map +1 -0
- package/dist/define.d.ts +18 -2
- package/dist/define.d.ts.map +1 -1
- package/dist/import/generator.d.ts +5 -1
- package/dist/import/generator.d.ts.map +1 -1
- package/dist/import/parser.d.ts +1 -1
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +15 -5
- package/dist/lint/audit-catalog.d.ts +3 -2
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/otel-helpers.d.ts +2 -2
- package/dist/lint/post-synth/otel-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/otel118.d.ts +8 -0
- package/dist/lint/post-synth/otel118.d.ts.map +1 -0
- package/dist/lint/post-synth/otel119.d.ts +8 -0
- package/dist/lint/post-synth/otel119.d.ts.map +1 -0
- package/dist/lint/post-synth/otel120.d.ts +8 -0
- package/dist/lint/post-synth/otel120.d.ts.map +1 -0
- package/dist/lint/post-synth/otel121.d.ts +8 -0
- package/dist/lint/post-synth/otel121.d.ts.map +1 -0
- package/dist/lint/post-synth/otel122.d.ts +8 -0
- package/dist/lint/post-synth/otel122.d.ts.map +1 -0
- package/dist/lint/post-synth/otel123.d.ts +8 -0
- package/dist/lint/post-synth/otel123.d.ts.map +1 -0
- package/dist/lint/post-synth/otel124.d.ts +8 -0
- package/dist/lint/post-synth/otel124.d.ts.map +1 -0
- package/dist/lint/post-synth/otel125.d.ts +8 -0
- package/dist/lint/post-synth/otel125.d.ts.map +1 -0
- package/dist/lint/post-synth/otel126.d.ts +8 -0
- package/dist/lint/post-synth/otel126.d.ts.map +1 -0
- package/dist/lint/post-synth/otel127.d.ts +8 -0
- package/dist/lint/post-synth/otel127.d.ts.map +1 -0
- package/dist/lint/rules/literal-credential.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/metric-names.d.ts +29 -0
- package/dist/metric-names.d.ts.map +1 -1
- package/dist/model.d.ts +24 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/okf/index.md +10 -0
- package/dist/okf/rules/OTEL118.md +11 -0
- package/dist/okf/rules/OTEL119.md +11 -0
- package/dist/okf/rules/OTEL120.md +11 -0
- package/dist/okf/rules/OTEL121.md +11 -0
- package/dist/okf/rules/OTEL122.md +11 -0
- package/dist/okf/rules/OTEL123.md +11 -0
- package/dist/okf/rules/OTEL124.md +11 -0
- package/dist/okf/rules/OTEL125.md +11 -0
- package/dist/okf/rules/OTEL126.md +11 -0
- package/dist/okf/rules/OTEL127.md +11 -0
- package/dist/platform.d.ts +11 -8
- package/dist/platform.d.ts.map +1 -1
- package/dist/rules/literal-credential.ts +2 -3
- package/dist/rules/otel-helpers.ts +15 -4
- package/dist/rules/otel118.ts +17 -0
- package/dist/rules/otel119.ts +17 -0
- package/dist/rules/otel120.ts +17 -0
- package/dist/rules/otel121.ts +17 -0
- package/dist/rules/otel122.ts +17 -0
- package/dist/rules/otel123.ts +17 -0
- package/dist/rules/otel124.ts +17 -0
- package/dist/rules/otel125.ts +17 -0
- package/dist/rules/otel126.ts +17 -0
- package/dist/rules/otel127.ts +17 -0
- package/dist/semconv.d.ts +2 -1
- package/dist/semconv.d.ts.map +1 -1
- package/dist/skills/chant-otel.md +6 -2
- package/dist/validate-config.d.ts +27 -9
- package/dist/validate-config.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/attribution.ts +385 -0
- package/src/codegen/docs.ts +8 -1
- package/src/components/k8s-receivers.test.ts +5 -2
- package/src/components/processors.ts +1 -1
- package/src/components/sampling.ts +3 -0
- package/src/composites/composites.test.ts +16 -0
- package/src/config-hygiene.ts +470 -0
- package/src/connectors.test.ts +3 -0
- package/src/define.ts +25 -4
- package/src/import/generator.test.ts +42 -4
- package/src/import/generator.ts +7 -2
- package/src/import/parser.test.ts +11 -0
- package/src/import/parser.ts +16 -4
- package/src/import/roundtrip.test.ts +90 -14
- package/src/index.ts +7 -0
- package/src/lint/audit-catalog.ts +87 -2
- package/src/lint/post-synth/hygiene.test.ts +334 -0
- package/src/lint/post-synth/index.ts +20 -0
- package/src/lint/post-synth/otel-helpers.ts +15 -4
- package/src/lint/post-synth/otel118.test.ts +308 -0
- package/src/lint/post-synth/otel118.ts +17 -0
- package/src/lint/post-synth/otel119.ts +17 -0
- package/src/lint/post-synth/otel120.ts +17 -0
- package/src/lint/post-synth/otel121.ts +17 -0
- package/src/lint/post-synth/otel122.ts +17 -0
- package/src/lint/post-synth/otel123.ts +17 -0
- package/src/lint/post-synth/otel124.ts +17 -0
- package/src/lint/post-synth/otel125.ts +17 -0
- package/src/lint/post-synth/otel126.ts +17 -0
- package/src/lint/post-synth/otel127.ts +17 -0
- package/src/lint/post-synth/post-synth.test.ts +18 -0
- package/src/lint/rules/literal-credential.ts +2 -3
- package/src/metric-names.test.ts +24 -2
- package/src/metric-names.ts +66 -1
- package/src/model.ts +48 -0
- package/src/platform.test.ts +35 -0
- package/src/platform.ts +28 -19
- package/src/plugin.test.ts +10 -0
- package/src/semconv.ts +6 -2
- package/src/skills/chant-otel.md +6 -2
- package/src/validate-config.ts +81 -16
|
@@ -15,6 +15,16 @@ import { otel114 } from "./otel114";
|
|
|
15
15
|
import { otel115 } from "./otel115";
|
|
16
16
|
import { otel116 } from "./otel116";
|
|
17
17
|
import { otel117 } from "./otel117";
|
|
18
|
+
import { otel118 } from "./otel118";
|
|
19
|
+
import { otel119 } from "./otel119";
|
|
20
|
+
import { otel120 } from "./otel120";
|
|
21
|
+
import { otel121 } from "./otel121";
|
|
22
|
+
import { otel122 } from "./otel122";
|
|
23
|
+
import { otel123 } from "./otel123";
|
|
24
|
+
import { otel124 } from "./otel124";
|
|
25
|
+
import { otel125 } from "./otel125";
|
|
26
|
+
import { otel126 } from "./otel126";
|
|
27
|
+
import { otel127 } from "./otel127";
|
|
18
28
|
|
|
19
29
|
export const postSynthChecks: PostSynthCheck[] = [
|
|
20
30
|
otel101,
|
|
@@ -32,4 +42,14 @@ export const postSynthChecks: PostSynthCheck[] = [
|
|
|
32
42
|
otel115,
|
|
33
43
|
otel116,
|
|
34
44
|
otel117,
|
|
45
|
+
otel118,
|
|
46
|
+
otel119,
|
|
47
|
+
otel120,
|
|
48
|
+
otel121,
|
|
49
|
+
otel122,
|
|
50
|
+
otel123,
|
|
51
|
+
otel124,
|
|
52
|
+
otel125,
|
|
53
|
+
otel126,
|
|
54
|
+
otel127,
|
|
35
55
|
];
|
|
@@ -10,6 +10,7 @@ import type { Declarable } from "@intentius/chant/declarable";
|
|
|
10
10
|
import { looksLikeCollectorConfig, type CollectorConfig } from "../../model";
|
|
11
11
|
import { configMapCollectorConfigs, describeConfigMapConfig } from "../../configmap";
|
|
12
12
|
import { validateCollectorConfig, validateCollectorEntities, type CollectorIssueCode, type CollectorIssue } from "../../validate-config";
|
|
13
|
+
import { attributionIssues } from "../../attribution";
|
|
13
14
|
|
|
14
15
|
/** Where a collector config was found in the build output. */
|
|
15
16
|
export interface FoundCollectorConfig {
|
|
@@ -74,20 +75,30 @@ function toDiagnostic(issue: CollectorIssue, found?: Omit<FoundCollectorConfig,
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
/**
|
|
77
|
-
*
|
|
78
|
+
* The config-level issues for one config. OTEL118 joins them only when the
|
|
79
|
+
* build stamps the telemetry attribution (`ctx.telemetry`), so a level-0
|
|
80
|
+
* project never sees it.
|
|
81
|
+
*/
|
|
82
|
+
function configIssues(ctx: PostSynthContext, config: CollectorConfig): CollectorIssue[] {
|
|
83
|
+
const issues = validateCollectorConfig(config);
|
|
84
|
+
return ctx.telemetry ? [...issues, ...attributionIssues(config)] : issues;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Every config-level diagnostic (OTEL101-OTEL106, OTEL112-OTEL127 and any later
|
|
78
89
|
* config check) over the collector configs in the output. `configMapsOnly`
|
|
79
90
|
* keeps the configs held in ConfigMaps, which is what WK8604 reads.
|
|
80
91
|
*/
|
|
81
92
|
export function collectorConfigDiagnostics(ctx: PostSynthContext, opts: { configMapsOnly?: boolean } = {}): PostSynthDiagnostic[] {
|
|
82
93
|
return collectorConfigs(ctx)
|
|
83
94
|
.filter((found) => !opts.configMapsOnly || found.configMap)
|
|
84
|
-
.flatMap(({ config, ...found }) =>
|
|
95
|
+
.flatMap(({ config, ...found }) => configIssues(ctx, config).map((i) => toDiagnostic(i, found)));
|
|
85
96
|
}
|
|
86
97
|
|
|
87
|
-
/** Diagnostics for one config-level code (OTEL101-OTEL106, OTEL112-
|
|
98
|
+
/** Diagnostics for one config-level code (OTEL101-OTEL106, OTEL112-OTEL127) across every collector config in the output. */
|
|
88
99
|
export function configDiagnostics(ctx: PostSynthContext, code: CollectorIssueCode): PostSynthDiagnostic[] {
|
|
89
100
|
return collectorConfigs(ctx).flatMap(({ config, ...found }) =>
|
|
90
|
-
|
|
101
|
+
configIssues(ctx, config)
|
|
91
102
|
.filter((i) => i.code === code)
|
|
92
103
|
.map((i) => toDiagnostic(i, found)),
|
|
93
104
|
);
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { dump } from "js-yaml";
|
|
3
|
+
import { makePostSynthCtx } from "@intentius/chant-test-utils";
|
|
4
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
5
|
+
import type { TelemetryAttribution } from "@intentius/chant/telemetry-attribution";
|
|
6
|
+
import { otel118 } from "./otel118";
|
|
7
|
+
import { collectorConfigDiagnostics } from "./otel-helpers";
|
|
8
|
+
import { isProtectedResourceAttribute, PROTECTED_RESOURCE_ATTRIBUTES } from "../../attribution";
|
|
9
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
10
|
+
import { NodeAgent } from "../../composites";
|
|
11
|
+
import { OtlpExporter } from "../../components/exporters";
|
|
12
|
+
import { collectorYaml } from "../../collector";
|
|
13
|
+
import { genAiPipeline } from "../../genai";
|
|
14
|
+
|
|
15
|
+
/** A collector with one processor in a traces pipeline. */
|
|
16
|
+
function withProcessor(id: string, body: unknown): Record<string, unknown> {
|
|
17
|
+
return {
|
|
18
|
+
receivers: { otlp: { protocols: { grpc: {} } } },
|
|
19
|
+
processors: { [id]: body },
|
|
20
|
+
exporters: { debug: {} },
|
|
21
|
+
service: { pipelines: { traces: { receivers: ["otlp"], processors: [id], exporters: ["debug"] } } },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ctxOf(config: unknown, telemetry: TelemetryAttribution | null = { workspace: "acme" }, lexicon = "otel"): PostSynthContext {
|
|
26
|
+
const ctx = makePostSynthCtx(lexicon, dump(config, { lineWidth: -1 }));
|
|
27
|
+
return telemetry ? { ...ctx, telemetry } : ctx;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const run = (config: unknown, telemetry?: TelemetryAttribution | null) => otel118.check(ctxOf(config, telemetry));
|
|
31
|
+
const messages = (config: unknown) => run(config).map((d) => d.message);
|
|
32
|
+
|
|
33
|
+
describe("OTEL118 protected keys", () => {
|
|
34
|
+
test("come from TELEMETRY_ATTRIBUTES, plus any chant.* key", () => {
|
|
35
|
+
expect(PROTECTED_RESOURCE_ATTRIBUTES).toEqual([
|
|
36
|
+
"service.name",
|
|
37
|
+
"service.version",
|
|
38
|
+
"deployment.environment.name",
|
|
39
|
+
"vcs.ref.head.revision",
|
|
40
|
+
"chant.workspace",
|
|
41
|
+
"chant.member",
|
|
42
|
+
"chant.decl",
|
|
43
|
+
]);
|
|
44
|
+
expect(isProtectedResourceAttribute("chant.team")).toBe(true);
|
|
45
|
+
expect(isProtectedResourceAttribute("service.namespace")).toBe(false);
|
|
46
|
+
expect(isProtectedResourceAttribute("chantx")).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("OTEL118 gate", () => {
|
|
51
|
+
const upsert = withProcessor("resource", { attributes: [{ key: "service.name", value: "x", action: "upsert" }] });
|
|
52
|
+
|
|
53
|
+
test("a build outside a workspace without telemetry.attribution gets no diagnostics", () => {
|
|
54
|
+
expect(otel118.check(ctxOf(upsert, null))).toEqual([]);
|
|
55
|
+
expect(collectorConfigDiagnostics(ctxOf(upsert, null))).toEqual([]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("a build with telemetry.attribution: true outside a workspace is checked", () => {
|
|
59
|
+
expect(run(upsert, {})).toHaveLength(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("reports a warning naming the processor and its pipelines", () => {
|
|
63
|
+
const [d] = run(upsert);
|
|
64
|
+
expect(d).toMatchObject({ checkId: "OTEL118", severity: "warning", entity: "resource", lexicon: "otel" });
|
|
65
|
+
expect(d.message).toMatch(/^processor "resource" \(pipelines traces\): its upsert action replaces "service\.name"\. /);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("a processor no pipeline lists is not checked", () => {
|
|
69
|
+
const config = { ...upsert, service: { pipelines: { traces: { receivers: ["otlp"], exporters: ["debug"] } } } };
|
|
70
|
+
expect(run(config)).toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("WK8604's entry point reports it for a config in a ConfigMap", () => {
|
|
74
|
+
const configMap = { apiVersion: "v1", kind: "ConfigMap", metadata: { name: "otel-agent-config", namespace: "observability" }, data: { "config.yaml": dump(upsert) } };
|
|
75
|
+
const diags = collectorConfigDiagnostics(ctxOf(configMap, { workspace: "acme" }, "k8s"), { configMapsOnly: true }).filter((d) => d.checkId === "OTEL118");
|
|
76
|
+
expect(diags).toHaveLength(1);
|
|
77
|
+
expect(diags[0].message).toMatch(/^ConfigMap observability\/otel-agent-config, key config\.yaml: processor "resource"/);
|
|
78
|
+
expect(collectorConfigDiagnostics(ctxOf(configMap, null, "k8s"), { configMapsOnly: true })).toEqual([]);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("OTEL118 resource processor", () => {
|
|
83
|
+
test.each(["delete", "update", "upsert", "hash"])("%s on a protected key is reported", (action) => {
|
|
84
|
+
expect(messages(withProcessor("resource/x", { attributes: [{ key: "deployment.environment.name", value: "prod", action }] }))).toHaveLength(1);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("a chant.* key is protected", () => {
|
|
88
|
+
expect(messages(withProcessor("resource", { attributes: [{ key: "chant.team", action: "delete" }] }))[0]).toContain('its delete action removes "chant.team"');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("a delete pattern that matches a protected key is reported", () => {
|
|
92
|
+
expect(messages(withProcessor("resource", { attributes: [{ pattern: "^chant\\..*", action: "delete" }] }))[0]).toContain(
|
|
93
|
+
'pattern "^chant\\..*" matches "chant.workspace", "chant.member", "chant.decl"',
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("insert, extract and unrelated keys pass", () => {
|
|
98
|
+
expect(
|
|
99
|
+
run(
|
|
100
|
+
withProcessor("resource", {
|
|
101
|
+
attributes: [
|
|
102
|
+
{ key: "service.name", value: "x", action: "insert" },
|
|
103
|
+
{ key: "service.name", pattern: "^(?P<svc>.*)$", action: "extract" },
|
|
104
|
+
{ key: "k8s.cluster.name", value: "prod", action: "upsert" },
|
|
105
|
+
{ key: "service.namespace", action: "delete" },
|
|
106
|
+
{ pattern: "^k8s\\.", action: "delete" },
|
|
107
|
+
],
|
|
108
|
+
}),
|
|
109
|
+
),
|
|
110
|
+
).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe("OTEL118 transform processor", () => {
|
|
115
|
+
const transform = (trace_statements: unknown[]) => withProcessor("transform/attrs", { trace_statements });
|
|
116
|
+
|
|
117
|
+
test("set on a protected key in the resource context, and on resource.attributes from another", () => {
|
|
118
|
+
const msgs = messages(
|
|
119
|
+
transform([
|
|
120
|
+
{ context: "resource", statements: ['set(attributes["service.version"], "1")'] },
|
|
121
|
+
{ context: "span", statements: ['set(resource.attributes["vcs.ref.head.revision"], "abc")'] },
|
|
122
|
+
]),
|
|
123
|
+
);
|
|
124
|
+
expect(msgs).toHaveLength(2);
|
|
125
|
+
expect(msgs[0]).toContain('the OTTL text `set(attributes["service.version"], "1")` matches a call where set replaces "service.version"');
|
|
126
|
+
expect(msgs[1]).toContain('set replaces "vcs.ref.head.revision"');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("delete_key and delete_matching_keys", () => {
|
|
130
|
+
const msgs = messages(
|
|
131
|
+
transform([
|
|
132
|
+
'delete_key(resource.attributes, "chant.decl")',
|
|
133
|
+
{ context: "resource", statements: ['delete_matching_keys(attributes, "^service\\\\..*")'] },
|
|
134
|
+
]),
|
|
135
|
+
);
|
|
136
|
+
expect(msgs).toHaveLength(2);
|
|
137
|
+
expect(msgs[0]).toContain('delete_key removes "chant.decl"');
|
|
138
|
+
expect(msgs[1]).toContain('removes "service.name", "service.version"');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("keep_keys and keep_matching_keys that leave a protected key out", () => {
|
|
142
|
+
const msgs = messages(
|
|
143
|
+
transform([
|
|
144
|
+
{ context: "resource", statements: ['keep_keys(attributes, ["service.name", "k8s.pod.name"])'] },
|
|
145
|
+
'keep_matching_keys(resource.attributes, "^(service|deployment)\\\\.")',
|
|
146
|
+
]),
|
|
147
|
+
);
|
|
148
|
+
expect(msgs).toHaveLength(2);
|
|
149
|
+
expect(msgs[0]).toContain('keep_keys drops "service.version", "deployment.environment.name", "vcs.ref.head.revision", "chant.workspace", "chant.member", "chant.decl"');
|
|
150
|
+
expect(msgs[1]).toContain('drops "vcs.ref.head.revision", "chant.workspace", "chant.member", "chant.decl"');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("keep_keys that keeps every protected key passes", () => {
|
|
154
|
+
const keep = `keep_keys(resource.attributes, [${PROTECTED_RESOURCE_ATTRIBUTES.map((k) => `"${k}"`).join(", ")}])`;
|
|
155
|
+
expect(run(transform([keep]))).toEqual([]);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("span attributes, unrelated keys and other functions pass", () => {
|
|
159
|
+
expect(
|
|
160
|
+
run(
|
|
161
|
+
transform([
|
|
162
|
+
'set(attributes["service.name"], "x")',
|
|
163
|
+
{ context: "span", statements: ['delete_key(attributes, "service.name")', 'set(attributes["chant.decl"], "x")'] },
|
|
164
|
+
{ context: "resource", statements: ['set(attributes["k8s.cluster.name"], "prod")', 'delete_key(attributes, "host.name")', 'replace_pattern(attributes["service.name"], "a", "b")'] },
|
|
165
|
+
]),
|
|
166
|
+
),
|
|
167
|
+
).toEqual([]);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe("OTEL118 resourcedetection processor", () => {
|
|
172
|
+
test("override defaults to true, and detectors to [env], at contrib v0.130.0", () => {
|
|
173
|
+
expect(messages(withProcessor("resourcedetection", {}))[0]).toContain("with override on by default, the env detector writes any key in the collector's own OTEL_RESOURCE_ATTRIBUTES");
|
|
174
|
+
expect(messages(withProcessor("resourcedetection", { detectors: ["env", "system"] }))).toHaveLength(1);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("override: true with a detector that writes a protected key", () => {
|
|
178
|
+
const msgs = messages(withProcessor("resourcedetection", { detectors: ["env", "heroku", "elastic_beanstalk", "gcp"], override: true }));
|
|
179
|
+
expect(msgs).toHaveLength(3);
|
|
180
|
+
expect(msgs[1]).toContain('with override: true, the heroku detector writes "service.name" and "service.version"');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("the renamed type resource_detection is read as resourcedetection", () => {
|
|
184
|
+
expect(messages(withProcessor("resource_detection/env", { detectors: ["env"] }))[0]).toMatch(/^processor "resource_detection\/env" .*the env detector/);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("override: false, or detectors that write no protected key, pass", () => {
|
|
188
|
+
expect(run(withProcessor("resourcedetection", { detectors: ["env"], override: false }))).toEqual([]);
|
|
189
|
+
expect(run(withProcessor("resourcedetection", { detectors: ["system", "gcp", "k8snode"] }))).toEqual([]);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe("OTEL118 groupbyattrs processor", () => {
|
|
194
|
+
test("a protected key in keys is reported; other keys pass", () => {
|
|
195
|
+
expect(messages(withProcessor("groupbyattrs", { keys: ["host.name", "service.name"] }))[0]).toContain(
|
|
196
|
+
'it groups by "service.name", so a record attribute with that key is copied over the resource\'s value',
|
|
197
|
+
);
|
|
198
|
+
expect(run(withProcessor("groupbyattrs", { keys: ["host.name"] }))).toEqual([]);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe("OTEL118 redaction processor", () => {
|
|
203
|
+
test("allow_all_keys off, its default, deletes protected keys not allowed or ignored", () => {
|
|
204
|
+
const msgs = messages(withProcessor("redaction", { allowed_keys: ["service.name", "service.version"], ignored_keys: ["chant.decl"] }));
|
|
205
|
+
expect(msgs).toHaveLength(1);
|
|
206
|
+
expect(msgs[0]).toContain(
|
|
207
|
+
'with allow_all_keys off by default, it deletes resource keys missing from allowed_keys and ignored_keys: "deployment.environment.name", "vcs.ref.head.revision", "chant.workspace", "chant.member"',
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("a blocked key pattern matching a protected key masks it unless ignored", () => {
|
|
212
|
+
const msgs = messages(withProcessor("redaction", { allow_all_keys: true, blocked_key_patterns: ["^service\\."], ignored_keys: ["service.version"] }));
|
|
213
|
+
expect(msgs).toEqual([expect.stringContaining('its blocked_key_patterns entry "^service\\." masks the value of "service.name"')]);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("allow_all_keys with unrelated patterns passes", () => {
|
|
217
|
+
expect(run(withProcessor("redaction", { allow_all_keys: true, blocked_key_patterns: ["^gen_ai\\.input"], blocked_values: ["\\d{16}"] }))).toEqual([]);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("OTEL118 sumologic processor", () => {
|
|
222
|
+
test("translate_attributes is on by default and renames service.name", () => {
|
|
223
|
+
expect(messages(withProcessor("sumologic", {}))[0]).toContain('translate_attributes (on by default) renames "service.name" to "service"');
|
|
224
|
+
expect(run(withProcessor("sumologic", { translate_attributes: false }))).toEqual([]);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("nest_attributes and aggregate_attributes over protected keys", () => {
|
|
228
|
+
const msgs = messages(
|
|
229
|
+
withProcessor("sumologic", {
|
|
230
|
+
translate_attributes: false,
|
|
231
|
+
nest_attributes: { enabled: true, include: ["chant."], exclude: ["chant.decl"] },
|
|
232
|
+
aggregate_attributes: [{ attribute: "svc", prefixes: ["service."] }, { attribute: "k8s", prefixes: ["k8s.pod."] }],
|
|
233
|
+
}),
|
|
234
|
+
);
|
|
235
|
+
expect(msgs).toHaveLength(2);
|
|
236
|
+
expect(msgs[0]).toContain('nest_attributes turns "chant.workspace", "chant.member" into nested maps');
|
|
237
|
+
expect(msgs[1]).toContain('move "service.name", "service.version"');
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe("OTEL118 schema processor", () => {
|
|
242
|
+
test("a target below 1.29.0 reverts vcs.ref.head.revision, below 1.27.0 deployment.environment.name too", () => {
|
|
243
|
+
expect(messages(withProcessor("schema", { targets: ["https://opentelemetry.io/schemas/1.28.0"] }))).toEqual([
|
|
244
|
+
expect.stringContaining('renames "vcs.ref.head.revision" back to "vcs.repository.ref.revision"'),
|
|
245
|
+
]);
|
|
246
|
+
expect(messages(withProcessor("schema", { targets: ["https://opentelemetry.io/schemas/1.26.0"] }))).toHaveLength(2);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("a target at 1.29.0 or later, or another family, passes", () => {
|
|
250
|
+
expect(run(withProcessor("schema", { targets: ["https://opentelemetry.io/schemas/1.29.0", "https://example.com/schemas/1.0.0"] }))).toEqual([]);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe("OTEL118 metricstransform processor", () => {
|
|
255
|
+
test("group_resource_labels setting a protected key is reported", () => {
|
|
256
|
+
const transforms = [
|
|
257
|
+
{ include: "^x", match_type: "regexp", action: "group", group_resource_labels: { "deployment.environment.name": "prod", team: "a" } },
|
|
258
|
+
{ include: "y", action: "update", new_name: "z" },
|
|
259
|
+
];
|
|
260
|
+
expect(messages(withProcessor("metricstransform", { transforms }))[0]).toContain('group_resource_labels sets "deployment.environment.name"');
|
|
261
|
+
expect(run(withProcessor("metricstransform", { transforms: [{ include: "^x", action: "group", group_resource_labels: { team: "a" } }] }))).toEqual([]);
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
describe("OTEL118 logstransform processor", () => {
|
|
266
|
+
test("operators on protected resource fields, a narrowing retain, and parse_to: resource", () => {
|
|
267
|
+
const msgs = messages(
|
|
268
|
+
withProcessor("logstransform", {
|
|
269
|
+
operators: [
|
|
270
|
+
{ type: "remove", field: 'resource["chant.member"]' },
|
|
271
|
+
{ type: "move", from: 'attributes["svc"]', to: 'resource["service.name"]' },
|
|
272
|
+
{ type: "retain", fields: ['resource["service.name"]'] },
|
|
273
|
+
{ type: "json_parser", parse_to: "resource" },
|
|
274
|
+
],
|
|
275
|
+
}),
|
|
276
|
+
);
|
|
277
|
+
expect(msgs).toHaveLength(4);
|
|
278
|
+
expect(msgs[0]).toContain('its remove operator writes or removes "chant.member"');
|
|
279
|
+
expect(msgs[2]).toContain("its retain operator keeps only the resource fields it lists");
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("operators on other fields pass", () => {
|
|
283
|
+
const operators = [
|
|
284
|
+
{ type: "add", field: 'resource["team"]', value: "a" },
|
|
285
|
+
{ type: "retain", fields: ['attributes["x"]'] },
|
|
286
|
+
{ type: "json_parser", parse_to: "attributes" },
|
|
287
|
+
];
|
|
288
|
+
expect(run(withProcessor("logstransform", { operators }))).toEqual([]);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("OTEL118 processors that don't replace resource keys", () => {
|
|
293
|
+
test("attributes acts on span, log and metric attributes", () => {
|
|
294
|
+
expect(run(withProcessor("attributes", { actions: [{ key: "service.name", action: "delete" }] }))).toEqual([]);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test("k8sattributes only fills service.name and service.version where they are absent", () => {
|
|
298
|
+
expect(run(withProcessor("k8sattributes", { extract: { metadata: ["service.name", "service.version"], otel_annotations: true } }))).toEqual([]);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("NodeAgent, with every option on, and genAiPipeline report nothing", () => {
|
|
302
|
+
const gateway = new OtlpExporter({ name: "gateway", endpoint: "gw:4317", tls: { insecure: true } });
|
|
303
|
+
const agent = NodeAgent({ exporters: [gateway], clusterName: "prod", kubeletStats: true });
|
|
304
|
+
for (const yaml of [collectorYaml(Object.values(agent.members) as Declarable[]), collectorYaml(genAiPipeline())]) {
|
|
305
|
+
expect(otel118.check({ ...makePostSynthCtx("otel", yaml), telemetry: { workspace: "acme" } })).toEqual([]);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL118: A pipeline processor can remove or replace a resource attribute chant's telemetry attribution stamps
|
|
3
|
+
*
|
|
4
|
+
* A workload built in a workspace, or with `telemetry.attribution: true`, carries `service.name`, `service.version`, `deployment.environment.name`, `vcs.ref.head.revision` and `chant.*` resource attributes. A `resource` processor that deletes, updates, upserts or hashes one, a `transform` statement that sets, deletes or fails to keep one, or a `resourcedetection` processor with `override` on and a detector that writes one undoes that. The `transform` case matches OTTL text. The check runs only when the build stamps the attribution, so a level-0 project sees nothing.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel118: PostSynthCheck = {
|
|
11
|
+
id: "OTEL118",
|
|
12
|
+
description: "A pipeline processor can remove or replace a resource attribute chant's telemetry attribution stamps",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL118");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL119: A collector config sets a field deprecated at or before the pinned release
|
|
3
|
+
*
|
|
4
|
+
* `invert_match: true` in a `tail_sampling` policy (inverted decisions are deprecated in collector-contrib v0.126.0 for a `drop` policy), `service.telemetry.metrics.address` (deprecated in collector v0.111.0 for `readers`), and `dimensions_cache_size` on `spanmetrics` (deprecated in collector-contrib v0.125.0 for `aggregation_cardinality_limit`). The collector still reads each at v0.130.0, and a later release drops it.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel119: PostSynthCheck = {
|
|
11
|
+
id: "OTEL119",
|
|
12
|
+
description: "A collector config sets a field deprecated at or before the pinned release",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL119");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL120: A component in the collector config holds a literal credential
|
|
3
|
+
*
|
|
4
|
+
* OTEL002's check over the emitted or imported config, with the same key pattern (`authorization`, `api-key`, `api_key`, `token`, `password`, `secret`, `key_pem`, `x-honeycomb-team`). A value containing `${` is a reference the collector expands at start-up, and a key ending in `_file` names a path. This catches what OTEL002 can't see: a config brought in by `chant import`, or one inside a ConfigMap.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel120: PostSynthCheck = {
|
|
11
|
+
id: "OTEL120",
|
|
12
|
+
description: "A component in the collector config holds a literal credential",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL120");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL121: An exporter sends a credential over plaintext
|
|
3
|
+
*
|
|
4
|
+
* A started exporter sends a credential header (`authorization`, `api-key` and the other OTEL002 keys) or a credential key such as `api_key` or `token` to a non-loopback endpoint that is `http://`, or that has `tls.insecure: true` and isn't `https://`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel121: PostSynthCheck = {
|
|
11
|
+
id: "OTEL121",
|
|
12
|
+
description: "An exporter sends a credential over plaintext",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL121");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL122: zpages or pprof listens on a non-loopback address
|
|
3
|
+
*
|
|
4
|
+
* zpages serves span samples and pipeline internals, and pprof serves profiles, with no authentication. A started one bound to anything but loopback (`0.0.0.0`, a pod IP, an `${env:...}` host) is reachable by whoever reaches that address. `health_check` is not reported: it serves only a status, and a kubelet probe needs it on the pod IP.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel122: PostSynthCheck = {
|
|
11
|
+
id: "OTEL122",
|
|
12
|
+
description: "zpages or pprof listens on a non-loopback address",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL122");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL123: A debug exporter at verbosity detailed shares a pipeline with a real exporter
|
|
3
|
+
*
|
|
4
|
+
* `verbosity: detailed` writes every record, attributes and bodies included, to the collector's log. In a pipeline that also sends to a backend, that copies the backend's data, sensitive values included, to wherever the collector's logs go.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel123: PostSynthCheck = {
|
|
11
|
+
id: "OTEL123",
|
|
12
|
+
description: "A debug exporter at verbosity detailed shares a pipeline with a real exporter",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL123");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL124: A remote exporter has its sending queue or retries turned off
|
|
3
|
+
*
|
|
4
|
+
* A started exporter with a non-loopback endpoint and `sending_queue.enabled: false` blocks the pipeline when the backend is slow; with `retry_on_failure.enabled: false` it drops a failed request's data.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel124: PostSynthCheck = {
|
|
11
|
+
id: "OTEL124",
|
|
12
|
+
description: "A remote exporter has its sending queue or retries turned off",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL124");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL125: A pipeline sends to a remote otlp or otlphttp exporter without batching
|
|
3
|
+
*
|
|
4
|
+
* No `batch` processor in the pipeline, and the `otlp` or `otlphttp` exporter (non-loopback endpoint) sets no `sending_queue.batch`. At v0.130.0 neither exporter batches by default, so each small request goes out on its own. Other exporters may batch themselves and are not checked.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel125: PostSynthCheck = {
|
|
11
|
+
id: "OTEL125",
|
|
12
|
+
description: "A pipeline sends to a remote otlp or otlphttp exporter without batching",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL125");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL126: k8sattributes extracts a metadata field it does not support
|
|
3
|
+
*
|
|
4
|
+
* Each name in a `k8sattributes` processor's `extract.metadata` must be one `Config.Validate` accepts at collector-contrib v0.130.0 (`K8S_ATTRIBUTES_METADATA`); the collector refuses any other.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel126: PostSynthCheck = {
|
|
11
|
+
id: "OTEL126",
|
|
12
|
+
description: "k8sattributes extracts a metadata field it does not support",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL126");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL127: resourcedetection lists a detector that does not exist
|
|
3
|
+
*
|
|
4
|
+
* Each name in a started `resourcedetection` processor's `detectors` must be one the processor registers at collector-contrib v0.130.0 (`RESOURCE_DETECTORS`); any other fails the processor's build and the collector exits.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel127: PostSynthCheck = {
|
|
11
|
+
id: "OTEL127",
|
|
12
|
+
description: "resourcedetection lists a detector that does not exist",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL127");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -275,6 +275,13 @@ describe("OTEL116 high-cardinality GenAI attributes as metric attributes", () =>
|
|
|
275
275
|
];
|
|
276
276
|
const run = (config: object, lexicon = "otel") => otel116.check(makePostSynthCtx(lexicon, dump(config, { lineWidth: -1 })));
|
|
277
277
|
|
|
278
|
+
test("reads span_metrics, service_graph and signal_to_metrics, the collector's newer names, as the built-ins", () => {
|
|
279
|
+
const rename = (s: string) => s.replace(/spanmetrics/g, "span_metrics").replace(/servicegraph/g, "service_graph").replace(/signaltometrics/g, "signal_to_metrics");
|
|
280
|
+
const key = GENAI_HIGH_CARDINALITY_ATTRIBUTES[0];
|
|
281
|
+
const config = JSON.parse(rename(JSON.stringify(connectorsSplitBy(key))));
|
|
282
|
+
expect(run(config).map((d) => [d.entity, d.message.match(/\((.+)\);/)?.[1]])).toEqual(FIELDS.map(([id, field]) => [rename(id), field]));
|
|
283
|
+
});
|
|
284
|
+
|
|
278
285
|
test.each(GENAI_HIGH_CARDINALITY_ATTRIBUTES.map((k) => [k]))("reports %s in every connector field", (key) => {
|
|
279
286
|
const diags = run(connectorsSplitBy(key));
|
|
280
287
|
expect(diags.map((d) => [d.entity, d.message.match(/\((.+)\);/)?.[1]])).toEqual(FIELDS);
|
|
@@ -349,6 +356,17 @@ describe("OTEL117 two started components on one address", () => {
|
|
|
349
356
|
]);
|
|
350
357
|
});
|
|
351
358
|
|
|
359
|
+
test("knows the contrib push receivers' defaults: carbon and wavefront both take localhost:2003 (#3122)", () => {
|
|
360
|
+
const diags = run({
|
|
361
|
+
receivers: { carbon: {}, wavefront: {}, "carbon/udp": { transport: "udp" } },
|
|
362
|
+
exporters: { debug: {} },
|
|
363
|
+
service: { pipelines: { metrics: { receivers: ["carbon", "wavefront", "carbon/udp"], exporters: ["debug"] } } },
|
|
364
|
+
});
|
|
365
|
+
expect(diags.map((d) => d.message)).toEqual([
|
|
366
|
+
expect.stringMatching(/^receiver "carbon" \(endpoint\) listens on localhost:2003 and receiver "wavefront" \(endpoint\) on localhost:2003/),
|
|
367
|
+
]);
|
|
368
|
+
});
|
|
369
|
+
|
|
352
370
|
test("a wildcard host overlaps a specific host on the same port, and an extension counts", () => {
|
|
353
371
|
const diags = run({
|
|
354
372
|
receivers: { zipkin: { endpoint: "127.0.0.1:13133" } },
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as ts from "typescript";
|
|
2
2
|
import type { LintRule, LintDiagnostic, LintContext } from "@intentius/chant/lint/rule";
|
|
3
3
|
import { calleeName, COMPONENT_CLASS, literalText, position, propertyName } from "./otel-ast";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const SECRET_KEY = /(authorization|api[-_]?key|password|passwd|secret|token|key_pem|x-honeycomb-team)/i;
|
|
4
|
+
// Shared with OTEL120, which applies the same check to the emitted YAML.
|
|
5
|
+
import { SECRET_KEY } from "../../config-hygiene";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* OTEL002: a credential written as a literal in a component's config.
|