@intentius/chant-lexicon-otel 0.81.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 +49 -0
- package/dist/catalog.d.ts +18 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/codegen/docs-cli.d.ts +3 -0
- package/dist/codegen/docs-cli.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts +8 -0
- package/dist/codegen/docs.d.ts.map +1 -0
- package/dist/codegen/generate-cli.d.ts +3 -0
- package/dist/codegen/generate-cli.d.ts.map +1 -0
- package/dist/codegen/generate.d.ts +19 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +7 -0
- package/dist/codegen/package.d.ts.map +1 -0
- package/dist/collector.d.ts +29 -0
- package/dist/collector.d.ts.map +1 -0
- package/dist/components/common.d.ts +144 -0
- package/dist/components/common.d.ts.map +1 -0
- package/dist/components/exporters.d.ts +86 -0
- package/dist/components/exporters.d.ts.map +1 -0
- package/dist/components/extensions.d.ts +32 -0
- package/dist/components/extensions.d.ts.map +1 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/processors.d.ts +118 -0
- package/dist/components/processors.d.ts.map +1 -0
- package/dist/components/receivers.d.ts +118 -0
- package/dist/components/receivers.d.ts.map +1 -0
- package/dist/define.d.ts +136 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/detect.d.ts +2 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/integrity.json +25 -0
- package/dist/lint/audit-catalog.d.ts +13 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts +3 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -0
- package/dist/lint/post-synth/otel-helpers.d.ts +22 -0
- package/dist/lint/post-synth/otel-helpers.d.ts.map +1 -0
- package/dist/lint/post-synth/otel101.d.ts +8 -0
- package/dist/lint/post-synth/otel101.d.ts.map +1 -0
- package/dist/lint/post-synth/otel102.d.ts +8 -0
- package/dist/lint/post-synth/otel102.d.ts.map +1 -0
- package/dist/lint/post-synth/otel103.d.ts +8 -0
- package/dist/lint/post-synth/otel103.d.ts.map +1 -0
- package/dist/lint/post-synth/otel104.d.ts +8 -0
- package/dist/lint/post-synth/otel104.d.ts.map +1 -0
- package/dist/lint/post-synth/otel105.d.ts +8 -0
- package/dist/lint/post-synth/otel105.d.ts.map +1 -0
- package/dist/lint/post-synth/otel106.d.ts +8 -0
- package/dist/lint/post-synth/otel106.d.ts.map +1 -0
- package/dist/lint/post-synth/otel107.d.ts +8 -0
- package/dist/lint/post-synth/otel107.d.ts.map +1 -0
- package/dist/lint/post-synth/otel108.d.ts +8 -0
- package/dist/lint/post-synth/otel108.d.ts.map +1 -0
- package/dist/lint/post-synth/otel109.d.ts +8 -0
- package/dist/lint/post-synth/otel109.d.ts.map +1 -0
- package/dist/lint/rules/component-id-syntax.d.ts +14 -0
- package/dist/lint/rules/component-id-syntax.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +6 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lint/rules/literal-credential.d.ts +13 -0
- package/dist/lint/rules/literal-credential.d.ts.map +1 -0
- package/dist/lint/rules/otel-ast.d.ts +14 -0
- package/dist/lint/rules/otel-ast.d.ts.map +1 -0
- package/dist/lsp/completions.d.ts +4 -0
- package/dist/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/hover.d.ts +4 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/manifest.json +8 -0
- package/dist/meta.json +102 -0
- package/dist/model.d.ts +53 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/okf/index.md +40 -0
- package/dist/okf/rules/OTEL001.md +15 -0
- package/dist/okf/rules/OTEL002.md +11 -0
- package/dist/okf/rules/OTEL101.md +11 -0
- package/dist/okf/rules/OTEL102.md +11 -0
- package/dist/okf/rules/OTEL103.md +11 -0
- package/dist/okf/rules/OTEL104.md +11 -0
- package/dist/okf/rules/OTEL105.md +11 -0
- package/dist/okf/rules/OTEL106.md +11 -0
- package/dist/okf/rules/OTEL107.md +11 -0
- package/dist/okf/rules/OTEL108.md +11 -0
- package/dist/okf/rules/OTEL109.md +11 -0
- package/dist/okf/types/AttributesProcessor.md +9 -0
- package/dist/okf/types/BatchProcessor.md +9 -0
- package/dist/okf/types/DebugExporter.md +9 -0
- package/dist/okf/types/FileLogReceiver.md +9 -0
- package/dist/okf/types/GoogleCloudExporter.md +9 -0
- package/dist/okf/types/HealthCheckExtension.md +9 -0
- package/dist/okf/types/HostMetricsReceiver.md +9 -0
- package/dist/okf/types/K8sAttributesProcessor.md +9 -0
- package/dist/okf/types/MemoryLimiterProcessor.md +9 -0
- package/dist/okf/types/OtlpExporter.md +9 -0
- package/dist/okf/types/OtlpHttpExporter.md +9 -0
- package/dist/okf/types/OtlpReceiver.md +9 -0
- package/dist/okf/types/Pipeline.md +13 -0
- package/dist/okf/types/PprofExtension.md +9 -0
- package/dist/okf/types/PrometheusExporter.md +9 -0
- package/dist/okf/types/PrometheusReceiver.md +9 -0
- package/dist/okf/types/ResourceDetectionProcessor.md +9 -0
- package/dist/okf/types/ResourceProcessor.md +9 -0
- package/dist/okf/types/Service.md +9 -0
- package/dist/okf/types/ZPagesExtension.md +9 -0
- package/dist/package-cli.d.ts +3 -0
- package/dist/package-cli.d.ts.map +1 -0
- package/dist/pipeline.d.ts +66 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/platform.d.ts +57 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/plugin.d.ts +11 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/rules/component-id-syntax.ts +68 -0
- package/dist/rules/literal-credential.ts +62 -0
- package/dist/rules/otel-ast.ts +30 -0
- package/dist/rules/otel-helpers.ts +70 -0
- package/dist/rules/otel101.ts +17 -0
- package/dist/rules/otel102.ts +17 -0
- package/dist/rules/otel103.ts +17 -0
- package/dist/rules/otel104.ts +17 -0
- package/dist/rules/otel105.ts +17 -0
- package/dist/rules/otel106.ts +17 -0
- package/dist/rules/otel107.ts +17 -0
- package/dist/rules/otel108.ts +17 -0
- package/dist/rules/otel109.ts +17 -0
- package/dist/serializer.d.ts +16 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/skill-defs.d.ts +3 -0
- package/dist/skill-defs.d.ts.map +1 -0
- package/dist/skills/chant-otel-custom-components.md +51 -0
- package/dist/skills/chant-otel-platforms.md +34 -0
- package/dist/skills/chant-otel.md +60 -0
- package/dist/topology.d.ts +56 -0
- package/dist/topology.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/validate-cli.d.ts +3 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate-config.d.ts +30 -0
- package/dist/validate-config.d.ts.map +1 -0
- package/dist/validate.d.ts +11 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/yaml.d.ts +27 -0
- package/dist/yaml.d.ts.map +1 -0
- package/package.json +74 -0
- package/src/catalog.ts +56 -0
- package/src/codegen/docs-cli.ts +4 -0
- package/src/codegen/docs.ts +90 -0
- package/src/codegen/generate-cli.ts +8 -0
- package/src/codegen/generate.ts +50 -0
- package/src/codegen/package.ts +41 -0
- package/src/collector.ts +147 -0
- package/src/components/common.ts +151 -0
- package/src/components/exporters.ts +141 -0
- package/src/components/extensions.ts +51 -0
- package/src/components/index.ts +5 -0
- package/src/components/processors.ts +208 -0
- package/src/components/receivers.ts +177 -0
- package/src/define.test.ts +131 -0
- package/src/define.ts +249 -0
- package/src/detect.ts +11 -0
- package/src/generated/lexicon-otel.json +102 -0
- package/src/index.ts +73 -0
- package/src/lint/audit-catalog.ts +102 -0
- package/src/lint/post-synth/index.ts +23 -0
- package/src/lint/post-synth/otel-helpers.ts +70 -0
- package/src/lint/post-synth/otel101.ts +17 -0
- package/src/lint/post-synth/otel102.ts +17 -0
- package/src/lint/post-synth/otel103.ts +17 -0
- package/src/lint/post-synth/otel104.ts +17 -0
- package/src/lint/post-synth/otel105.ts +17 -0
- package/src/lint/post-synth/otel106.ts +17 -0
- package/src/lint/post-synth/otel107.ts +17 -0
- package/src/lint/post-synth/otel108.ts +17 -0
- package/src/lint/post-synth/otel109.ts +17 -0
- package/src/lint/post-synth/post-synth.test.ts +184 -0
- package/src/lint/rules/component-id-syntax.ts +68 -0
- package/src/lint/rules/index.ts +9 -0
- package/src/lint/rules/literal-credential.ts +62 -0
- package/src/lint/rules/otel-ast.ts +30 -0
- package/src/lint/rules/rules.test.ts +70 -0
- package/src/lsp/completions.test.ts +23 -0
- package/src/lsp/completions.ts +15 -0
- package/src/lsp/hover.test.ts +20 -0
- package/src/lsp/hover.ts +30 -0
- package/src/model.ts +88 -0
- package/src/package-cli.ts +17 -0
- package/src/pipeline.ts +89 -0
- package/src/platform.test.ts +79 -0
- package/src/platform.ts +143 -0
- package/src/plugin.test.ts +55 -0
- package/src/plugin.ts +111 -0
- package/src/serializer.test.ts +187 -0
- package/src/serializer.ts +31 -0
- package/src/skill-defs.ts +36 -0
- package/src/skills/chant-otel-custom-components.md +51 -0
- package/src/skills/chant-otel-platforms.md +34 -0
- package/src/skills/chant-otel.md +60 -0
- package/src/topology.test.ts +80 -0
- package/src/topology.ts +122 -0
- package/src/validate-cli.ts +7 -0
- package/src/validate-config.ts +232 -0
- package/src/validate.ts +82 -0
- package/src/yaml.test.ts +56 -0
- package/src/yaml.ts +145 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// OpenTelemetry Collector lexicon.
|
|
2
|
+
|
|
3
|
+
// Plugin and serializer
|
|
4
|
+
export { otelPlugin } from "./plugin";
|
|
5
|
+
export { otelSerializer } from "./serializer";
|
|
6
|
+
|
|
7
|
+
// Built-in components and their config types
|
|
8
|
+
export * from "./components";
|
|
9
|
+
|
|
10
|
+
// Pipelines and the service block
|
|
11
|
+
export {
|
|
12
|
+
Pipeline,
|
|
13
|
+
Service,
|
|
14
|
+
PIPELINE_TYPE,
|
|
15
|
+
SERVICE_TYPE,
|
|
16
|
+
isPipelineEntity,
|
|
17
|
+
isServiceEntity,
|
|
18
|
+
type ComponentRef,
|
|
19
|
+
type PipelineProps,
|
|
20
|
+
type PipelineEntity,
|
|
21
|
+
type ServiceProps,
|
|
22
|
+
type ServiceEntity,
|
|
23
|
+
type ServiceTelemetry,
|
|
24
|
+
} from "./pipeline";
|
|
25
|
+
|
|
26
|
+
// The extension point: components chant doesn't ship
|
|
27
|
+
export {
|
|
28
|
+
defineComponent,
|
|
29
|
+
definitionFor,
|
|
30
|
+
definitionOf,
|
|
31
|
+
registeredDefinitions,
|
|
32
|
+
componentEntityType,
|
|
33
|
+
isOTelComponent,
|
|
34
|
+
COLLECTOR_PIN,
|
|
35
|
+
type SchemaPin,
|
|
36
|
+
type SafeParseSchema,
|
|
37
|
+
type ConfigValidator,
|
|
38
|
+
type ComponentDefinition,
|
|
39
|
+
type CustomComponentOptions,
|
|
40
|
+
type ComponentProps,
|
|
41
|
+
type ComponentClass,
|
|
42
|
+
type OTelComponent,
|
|
43
|
+
} from "./define";
|
|
44
|
+
|
|
45
|
+
// The plain-data model, YAML, checks and topology
|
|
46
|
+
export * from "./model";
|
|
47
|
+
export { buildCollectorConfig, collectorYaml, componentConfig, type BuiltCollector } from "./collector";
|
|
48
|
+
export { emitCollectorYaml, type EmitOptions } from "./yaml";
|
|
49
|
+
export {
|
|
50
|
+
validateCollectorConfig,
|
|
51
|
+
validateCollectorEntities,
|
|
52
|
+
type CollectorIssue,
|
|
53
|
+
type CollectorIssueCode,
|
|
54
|
+
} from "./validate-config";
|
|
55
|
+
export {
|
|
56
|
+
collectorTopology,
|
|
57
|
+
collectorTopologyOf,
|
|
58
|
+
type CollectorTopology,
|
|
59
|
+
type TopologyPipeline,
|
|
60
|
+
type TopologyComponent,
|
|
61
|
+
type TopologyExporter,
|
|
62
|
+
} from "./topology";
|
|
63
|
+
|
|
64
|
+
// What the platform collector composites (docker, k8s, fly) share
|
|
65
|
+
export {
|
|
66
|
+
otlpCollector,
|
|
67
|
+
collectorEndpoints,
|
|
68
|
+
COLLECTOR_IMAGE,
|
|
69
|
+
COLLECTOR_CONFIG_PATH,
|
|
70
|
+
type OtlpCollectorOptions,
|
|
71
|
+
type CollectorPort,
|
|
72
|
+
type CollectorEndpoints,
|
|
73
|
+
} from "./platform";
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The otel lexicon's chant audit catalog, contributed via
|
|
3
|
+
* `otelPlugin.auditCatalog()` (#687, #1346).
|
|
4
|
+
*
|
|
5
|
+
* OTEL101-OTEL106 read the emitted collector YAML, so they are `yamlBased`.
|
|
6
|
+
* OTEL107-OTEL109 read the declared entities (a component's definition, its
|
|
7
|
+
* schema pin), which a standalone YAML file does not carry, so they are
|
|
8
|
+
* constructed with `yamlBased: false`. The two source-level lint rules are
|
|
9
|
+
* listed too, for a reader who meets them in a lint report.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { auditRule, type RuleMeta } from "@intentius/chant/audit/catalog";
|
|
13
|
+
|
|
14
|
+
function entityRule(
|
|
15
|
+
id: string,
|
|
16
|
+
category: RuleMeta["category"],
|
|
17
|
+
title: string,
|
|
18
|
+
remediation: string,
|
|
19
|
+
): RuleMeta {
|
|
20
|
+
return { id, tier: "merge-worthy", fixKind: "guidance", category, title, remediation, yamlBased: false };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const otelAuditCatalog: Record<string, RuleMeta> = {
|
|
24
|
+
OTEL001: entityRule(
|
|
25
|
+
"OTEL001",
|
|
26
|
+
"correctness",
|
|
27
|
+
"Collector component id or instance name is not valid syntax",
|
|
28
|
+
'Write component ids as `type` or `type/name` (e.g. "otlp/backend") and give instances a non-empty name with no whitespace.',
|
|
29
|
+
),
|
|
30
|
+
OTEL002: entityRule(
|
|
31
|
+
"OTEL002",
|
|
32
|
+
"security",
|
|
33
|
+
"Collector credential declared as a literal",
|
|
34
|
+
'Replace the value with `${env:NAME}` or `${file:/path}` so the collector reads it at start-up.',
|
|
35
|
+
),
|
|
36
|
+
OTEL101: auditRule(
|
|
37
|
+
"OTEL101",
|
|
38
|
+
"merge-worthy",
|
|
39
|
+
"guidance",
|
|
40
|
+
"Pipeline uses an undeclared component",
|
|
41
|
+
"Declare the receiver, processor or exporter under its section, or reference the declared entity instead of an id string.",
|
|
42
|
+
{ category: "correctness" },
|
|
43
|
+
),
|
|
44
|
+
OTEL102: auditRule(
|
|
45
|
+
"OTEL102",
|
|
46
|
+
"merge-worthy",
|
|
47
|
+
"guidance",
|
|
48
|
+
"Pipeline has no receivers or no exporters",
|
|
49
|
+
"Give every pipeline at least one receiver and at least one exporter, or remove it.",
|
|
50
|
+
{ category: "correctness" },
|
|
51
|
+
),
|
|
52
|
+
OTEL103: auditRule(
|
|
53
|
+
"OTEL103",
|
|
54
|
+
"report-only",
|
|
55
|
+
"guidance",
|
|
56
|
+
"Declared collector component is never used",
|
|
57
|
+
"List the component in a pipeline (or the extension in service.extensions), or remove its declaration.",
|
|
58
|
+
{ category: "best-practice" },
|
|
59
|
+
),
|
|
60
|
+
OTEL104: auditRule(
|
|
61
|
+
"OTEL104",
|
|
62
|
+
"merge-worthy",
|
|
63
|
+
"guidance",
|
|
64
|
+
"service.extensions enables an undeclared extension",
|
|
65
|
+
"Declare the extension under extensions, or drop it from service.extensions.",
|
|
66
|
+
{ category: "correctness" },
|
|
67
|
+
),
|
|
68
|
+
OTEL105: auditRule(
|
|
69
|
+
"OTEL105",
|
|
70
|
+
"report-only",
|
|
71
|
+
"guidance",
|
|
72
|
+
"memory_limiter is not the first processor",
|
|
73
|
+
"Move memory_limiter to the front of the pipeline's processors list.",
|
|
74
|
+
{ category: "best-practice" },
|
|
75
|
+
),
|
|
76
|
+
OTEL106: auditRule(
|
|
77
|
+
"OTEL106",
|
|
78
|
+
"merge-worthy",
|
|
79
|
+
"guidance",
|
|
80
|
+
"Pipeline id or component reference is not valid collector syntax",
|
|
81
|
+
"Name pipelines traces, metrics or logs (optionally /name) and reference components as type or type/name.",
|
|
82
|
+
{ category: "correctness" },
|
|
83
|
+
),
|
|
84
|
+
OTEL107: entityRule(
|
|
85
|
+
"OTEL107",
|
|
86
|
+
"correctness",
|
|
87
|
+
"Collector component config breaks its definition's rules",
|
|
88
|
+
"Fix the config the message names; for a custom component the rule comes from its defineComponent validate.",
|
|
89
|
+
),
|
|
90
|
+
OTEL108: entityRule(
|
|
91
|
+
"OTEL108",
|
|
92
|
+
"correctness",
|
|
93
|
+
"Two collector components or pipelines declare the same id",
|
|
94
|
+
"Give each instance its own name so their ids (type/name) differ.",
|
|
95
|
+
),
|
|
96
|
+
OTEL109: entityRule(
|
|
97
|
+
"OTEL109",
|
|
98
|
+
"correctness",
|
|
99
|
+
"Custom collector component has no schema pin",
|
|
100
|
+
"Pass defineComponent a pin with the source and version the component's config type follows.",
|
|
101
|
+
),
|
|
102
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Code generated by chant generate. DO NOT EDIT.
|
|
2
|
+
import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import { otel101 } from "./otel101";
|
|
4
|
+
import { otel102 } from "./otel102";
|
|
5
|
+
import { otel103 } from "./otel103";
|
|
6
|
+
import { otel104 } from "./otel104";
|
|
7
|
+
import { otel105 } from "./otel105";
|
|
8
|
+
import { otel106 } from "./otel106";
|
|
9
|
+
import { otel107 } from "./otel107";
|
|
10
|
+
import { otel108 } from "./otel108";
|
|
11
|
+
import { otel109 } from "./otel109";
|
|
12
|
+
|
|
13
|
+
export const postSynthChecks: PostSynthCheck[] = [
|
|
14
|
+
otel101,
|
|
15
|
+
otel102,
|
|
16
|
+
otel103,
|
|
17
|
+
otel104,
|
|
18
|
+
otel105,
|
|
19
|
+
otel106,
|
|
20
|
+
otel107,
|
|
21
|
+
otel108,
|
|
22
|
+
otel109,
|
|
23
|
+
];
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared plumbing for the otel post-synth checks: find the collector configs
|
|
3
|
+
* in a build's output, and run the plain-function checks over them.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
7
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
8
|
+
import { loadAll } from "js-yaml";
|
|
9
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
10
|
+
import { looksLikeCollectorConfig, type CollectorConfig } from "../../model";
|
|
11
|
+
import { validateCollectorConfig, validateCollectorEntities, type CollectorIssueCode, type CollectorIssue } from "../../validate-config";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Every collector config in the output: the otel lexicon's own, and any YAML
|
|
15
|
+
* document shaped like one. Parsed with js-yaml rather than `ctx.docs`,
|
|
16
|
+
* because collector configs are written with flow lists (`[otlp, batch]`)
|
|
17
|
+
* that core's small YAML reader keeps as strings.
|
|
18
|
+
*/
|
|
19
|
+
export function collectorConfigs(ctx: PostSynthContext): Array<{ source: string; config: CollectorConfig }> {
|
|
20
|
+
const out: Array<{ source: string; config: CollectorConfig }> = [];
|
|
21
|
+
for (const [lexicon, output] of ctx.outputs) {
|
|
22
|
+
const texts: Array<[string, string]> =
|
|
23
|
+
typeof output === "string"
|
|
24
|
+
? [[lexicon, output]]
|
|
25
|
+
: [[lexicon, (output as SerializerResult).primary], ...Object.entries((output as SerializerResult).files ?? {})];
|
|
26
|
+
for (const [source, text] of texts) {
|
|
27
|
+
if (!text) continue;
|
|
28
|
+
let docs: unknown[];
|
|
29
|
+
try {
|
|
30
|
+
docs = loadAll(text);
|
|
31
|
+
} catch {
|
|
32
|
+
continue; // not YAML, or not ours
|
|
33
|
+
}
|
|
34
|
+
for (const doc of docs) {
|
|
35
|
+
if (typeof doc !== "object" || doc === null || Array.isArray(doc)) continue;
|
|
36
|
+
if ((lexicon === "otel" && source === lexicon) || looksLikeCollectorConfig(doc)) {
|
|
37
|
+
out.push({ source, config: doc as CollectorConfig });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function toDiagnostic(issue: CollectorIssue, source?: string): PostSynthDiagnostic {
|
|
46
|
+
return {
|
|
47
|
+
checkId: issue.code,
|
|
48
|
+
severity: issue.severity,
|
|
49
|
+
message: source && source !== "otel" ? `${source}: ${issue.message}` : issue.message,
|
|
50
|
+
...(issue.component ? { entity: issue.component } : issue.pipeline ? { entity: issue.pipeline } : {}),
|
|
51
|
+
lexicon: "otel",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Diagnostics for one config-level code (OTEL101-OTEL106) across every collector config in the output. */
|
|
56
|
+
export function configDiagnostics(ctx: PostSynthContext, code: CollectorIssueCode): PostSynthDiagnostic[] {
|
|
57
|
+
return collectorConfigs(ctx).flatMap(({ source, config }) =>
|
|
58
|
+
validateCollectorConfig(config)
|
|
59
|
+
.filter((i) => i.code === code)
|
|
60
|
+
.map((i) => toDiagnostic(i, source)),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Diagnostics for one entity-level code (OTEL107-OTEL109) over the build's otel entities. */
|
|
65
|
+
export function entityDiagnostics(ctx: PostSynthContext, code: CollectorIssueCode): PostSynthDiagnostic[] {
|
|
66
|
+
const otel: Declarable[] = [...(ctx.entities?.values() ?? [])].filter((e) => e?.lexicon === "otel");
|
|
67
|
+
return validateCollectorEntities(otel)
|
|
68
|
+
.filter((i) => i.code === code)
|
|
69
|
+
.map((i) => toDiagnostic(i));
|
|
70
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL101: A pipeline uses a receiver, processor or exporter that is not declared
|
|
3
|
+
*
|
|
4
|
+
* The collector refuses to start on this. Typed entity references cannot go wrong this way, so it mostly catches id strings (`"otlp/backend"`) and hand-edited or parsed configs. A connector id is accepted in receivers and exporters.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel101: PostSynthCheck = {
|
|
11
|
+
id: "OTEL101",
|
|
12
|
+
description: "A pipeline uses a receiver, processor or exporter that is not declared",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL101");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL102: A pipeline has no receivers or no exporters
|
|
3
|
+
*
|
|
4
|
+
* A pipeline with no receivers takes in nothing, and one with no exporters sends what it takes in nowhere. The collector rejects both at start.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel102: PostSynthCheck = {
|
|
11
|
+
id: "OTEL102",
|
|
12
|
+
description: "A pipeline has no receivers or no exporters",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL102");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL103: A declared component is never used
|
|
3
|
+
*
|
|
4
|
+
* A receiver, processor or exporter no pipeline lists, or an extension missing from service.extensions, is ignored by the collector. Usually it means a pipeline was meant to list it. A warning, since the config still runs.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel103: PostSynthCheck = {
|
|
11
|
+
id: "OTEL103",
|
|
12
|
+
description: "A declared component is never used",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL103");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL104: service.extensions enables an extension that is not declared
|
|
3
|
+
*
|
|
4
|
+
* The collector refuses to start when service.extensions names an id with no entry under extensions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel104: PostSynthCheck = {
|
|
11
|
+
id: "OTEL104",
|
|
12
|
+
description: "service.extensions enables an extension that is not declared",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL104");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL105: memory_limiter is not the first processor in a pipeline
|
|
3
|
+
*
|
|
4
|
+
* The collector's own guidance puts memory_limiter first, so it can refuse data before any other processor buffers it. Later in the chain it limits too late to protect the process.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel105: PostSynthCheck = {
|
|
11
|
+
id: "OTEL105",
|
|
12
|
+
description: "memory_limiter is not the first processor in a pipeline",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL105");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL106: A pipeline id or component reference is not valid collector syntax
|
|
3
|
+
*
|
|
4
|
+
* A pipeline id is a signal (traces, metrics, logs) optionally followed by /name, and every reference is type or type/name. Anything else is a config error at collector start.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { configDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel106: PostSynthCheck = {
|
|
11
|
+
id: "OTEL106",
|
|
12
|
+
description: "A pipeline id or component reference is not valid collector syntax",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return configDiagnostics(ctx, "OTEL106");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL107: A component's config breaks its definition's rules
|
|
3
|
+
*
|
|
4
|
+
* Each component definition, built-in or custom, can check its own config beyond what the TypeScript type says: memory_limiter needs a limit, filelog needs an include, and so on. A custom component supplies its checks through defineComponent's validate.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { entityDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel107: PostSynthCheck = {
|
|
11
|
+
id: "OTEL107",
|
|
12
|
+
description: "A component's config breaks its definition's rules",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return entityDiagnostics(ctx, "OTEL107");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL108: Two components or two pipelines declare the same id
|
|
3
|
+
*
|
|
4
|
+
* The collector config is keyed by id, so the second declaration would silently replace the first. The serializer keeps the first and this check fails the build.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { entityDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel108: PostSynthCheck = {
|
|
11
|
+
id: "OTEL108",
|
|
12
|
+
description: "Two components or two pipelines declare the same id",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return entityDiagnostics(ctx, "OTEL108");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTEL109: A custom component has no schema pin
|
|
3
|
+
*
|
|
4
|
+
* defineComponent requires a pin naming the source and version its config type follows, so a reader can tell which schema the component was checked against. This check fails a build whose custom component lost its pin, for instance through an untyped call.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
8
|
+
import { entityDiagnostics } from "./otel-helpers";
|
|
9
|
+
|
|
10
|
+
export const otel109: PostSynthCheck = {
|
|
11
|
+
id: "OTEL109",
|
|
12
|
+
description: "A custom component has no schema pin",
|
|
13
|
+
|
|
14
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
15
|
+
return entityDiagnostics(ctx, "OTEL109");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { makePostSynthCtx } from "@intentius/chant-test-utils";
|
|
3
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
4
|
+
import { otel101 } from "./otel101";
|
|
5
|
+
import { otel102 } from "./otel102";
|
|
6
|
+
import { otel103 } from "./otel103";
|
|
7
|
+
import { otel104 } from "./otel104";
|
|
8
|
+
import { otel105 } from "./otel105";
|
|
9
|
+
import { otel106 } from "./otel106";
|
|
10
|
+
import { otel107 } from "./otel107";
|
|
11
|
+
import { otel108 } from "./otel108";
|
|
12
|
+
import { otel109 } from "./otel109";
|
|
13
|
+
import { BatchProcessor, DebugExporter, FileLogReceiver, MemoryLimiterProcessor, OtlpReceiver } from "../../components";
|
|
14
|
+
import { defineComponent } from "../../define";
|
|
15
|
+
import { Pipeline } from "../../pipeline";
|
|
16
|
+
|
|
17
|
+
const GOOD = `receivers:
|
|
18
|
+
otlp:
|
|
19
|
+
protocols:
|
|
20
|
+
grpc: {}
|
|
21
|
+
|
|
22
|
+
processors:
|
|
23
|
+
memory_limiter:
|
|
24
|
+
check_interval: 1s
|
|
25
|
+
limit_mib: 400
|
|
26
|
+
batch: {}
|
|
27
|
+
|
|
28
|
+
exporters:
|
|
29
|
+
debug: {}
|
|
30
|
+
|
|
31
|
+
extensions:
|
|
32
|
+
health_check: {}
|
|
33
|
+
|
|
34
|
+
service:
|
|
35
|
+
extensions: [health_check]
|
|
36
|
+
pipelines:
|
|
37
|
+
traces:
|
|
38
|
+
receivers: [otlp]
|
|
39
|
+
processors: [memory_limiter, batch]
|
|
40
|
+
exporters: [debug]
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const configChecks = [otel101, otel102, otel103, otel104, otel105, otel106];
|
|
44
|
+
|
|
45
|
+
function entities(list: unknown[]): Map<string, Declarable> {
|
|
46
|
+
return new Map(list.map((e, i) => [`e${i}`, e as Declarable]));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe("config-level checks on a clean config", () => {
|
|
50
|
+
test.each(configChecks.map((c) => [c.id, c] as const))("%s finds nothing", (_id, check) => {
|
|
51
|
+
expect(check.check(makePostSynthCtx("otel", GOOD))).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("OTEL101 undeclared component", () => {
|
|
56
|
+
test("flags an exporter and a processor nobody declares", () => {
|
|
57
|
+
const yaml = GOOD.replace("exporters: [debug]", "exporters: [debug, otlp/backend]").replace(
|
|
58
|
+
"processors: [memory_limiter, batch]",
|
|
59
|
+
"processors: [memory_limiter, batch, attributes/redact]",
|
|
60
|
+
);
|
|
61
|
+
const diags = otel101.check(makePostSynthCtx("otel", yaml));
|
|
62
|
+
expect(diags.map((d) => d.entity)).toEqual(["attributes/redact", "otlp/backend"]);
|
|
63
|
+
expect(diags[0].severity).toBe("error");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("accepts a connector id as a receiver or exporter", () => {
|
|
67
|
+
const yaml = `connectors:
|
|
68
|
+
forward: {}
|
|
69
|
+
receivers:
|
|
70
|
+
otlp: {protocols: {grpc: {}}}
|
|
71
|
+
exporters:
|
|
72
|
+
debug: {}
|
|
73
|
+
service:
|
|
74
|
+
pipelines:
|
|
75
|
+
traces:
|
|
76
|
+
receivers: [otlp]
|
|
77
|
+
exporters: [forward]
|
|
78
|
+
traces/2:
|
|
79
|
+
receivers: [forward]
|
|
80
|
+
exporters: [debug]
|
|
81
|
+
`;
|
|
82
|
+
expect(otel101.check(makePostSynthCtx("otel", yaml))).toEqual([]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("finds a collector config in another lexicon's output by its shape", () => {
|
|
86
|
+
const yaml = GOOD.replace("exporters: [debug]", "exporters: [nope]");
|
|
87
|
+
expect(otel101.check(makePostSynthCtx("docker", yaml))).toHaveLength(1);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("OTEL102 empty ends", () => {
|
|
92
|
+
test("flags a pipeline with no receivers and one with no exporters", () => {
|
|
93
|
+
const yaml = `receivers:
|
|
94
|
+
otlp: {protocols: {grpc: {}}}
|
|
95
|
+
exporters:
|
|
96
|
+
debug: {}
|
|
97
|
+
service:
|
|
98
|
+
pipelines:
|
|
99
|
+
traces:
|
|
100
|
+
receivers: []
|
|
101
|
+
exporters: [debug]
|
|
102
|
+
logs:
|
|
103
|
+
receivers: [otlp]
|
|
104
|
+
`;
|
|
105
|
+
const diags = otel102.check(makePostSynthCtx("otel", yaml));
|
|
106
|
+
expect(diags.map((d) => d.message)).toEqual([
|
|
107
|
+
'pipeline "traces" has no receivers, so nothing enters it',
|
|
108
|
+
'pipeline "logs" has no exporters, so what enters it goes nowhere',
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("OTEL103 unused", () => {
|
|
114
|
+
test("warns about an unused exporter and an extension never enabled", () => {
|
|
115
|
+
const yaml = GOOD.replace(" debug: {}\n", " debug: {}\n otlp/spare:\n endpoint: x:4317\n").replace(
|
|
116
|
+
" health_check: {}\n",
|
|
117
|
+
" health_check: {}\n zpages: {}\n",
|
|
118
|
+
);
|
|
119
|
+
const diags = otel103.check(makePostSynthCtx("otel", yaml));
|
|
120
|
+
expect(diags.map((d) => [d.entity, d.severity])).toEqual([
|
|
121
|
+
["otlp/spare", "warning"],
|
|
122
|
+
["zpages", "warning"],
|
|
123
|
+
]);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("OTEL104 undeclared extension", () => {
|
|
128
|
+
test("flags service.extensions naming an extension nobody declares", () => {
|
|
129
|
+
const yaml = GOOD.replace("extensions: [health_check]", "extensions: [health_check, pprof]");
|
|
130
|
+
expect(otel104.check(makePostSynthCtx("otel", yaml)).map((d) => d.entity)).toEqual(["pprof"]);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("OTEL105 memory_limiter position", () => {
|
|
135
|
+
test("warns when memory_limiter is not first", () => {
|
|
136
|
+
const yaml = GOOD.replace("processors: [memory_limiter, batch]", "processors: [batch, memory_limiter]");
|
|
137
|
+
const diags = otel105.check(makePostSynthCtx("otel", yaml));
|
|
138
|
+
expect(diags).toHaveLength(1);
|
|
139
|
+
expect(diags[0].severity).toBe("warning");
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("OTEL106 syntax", () => {
|
|
144
|
+
test("flags a pipeline id that names no signal", () => {
|
|
145
|
+
const yaml = GOOD.replace(" traces:\n", " trace:\n");
|
|
146
|
+
expect(otel106.check(makePostSynthCtx("otel", yaml)).map((d) => d.entity)).toEqual(["trace"]);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("entity-level checks", () => {
|
|
151
|
+
test("OTEL107 reports a built-in's own config rules", () => {
|
|
152
|
+
const limiter = new MemoryLimiterProcessor({ check_interval: "1s" });
|
|
153
|
+
const logs = new FileLogReceiver({ include: [] });
|
|
154
|
+
const diags = otel107.check(makePostSynthCtx("otel", "", entities([limiter, logs])));
|
|
155
|
+
expect(diags.map((d) => d.entity)).toEqual(["memory_limiter", "filelog"]);
|
|
156
|
+
expect(diags[0].message).toContain("limit_mib or limit_percentage");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("OTEL107 is quiet for valid config", () => {
|
|
160
|
+
const limiter = new MemoryLimiterProcessor({ check_interval: "1s", limit_percentage: 80 });
|
|
161
|
+
const otlp = new OtlpReceiver({ protocols: { grpc: {} } });
|
|
162
|
+
expect(otel107.check(makePostSynthCtx("otel", "", entities([limiter, otlp, new BatchProcessor({})])))).toEqual([]);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("OTEL108 flags duplicate component and pipeline ids", () => {
|
|
166
|
+
const a = new DebugExporter({});
|
|
167
|
+
const b = new DebugExporter({ verbosity: "detailed" });
|
|
168
|
+
const otlp = new OtlpReceiver({ protocols: { grpc: {} } });
|
|
169
|
+
const p1 = new Pipeline({ signal: "traces", receivers: [otlp], exporters: [a] });
|
|
170
|
+
const p2 = new Pipeline({ signal: "traces", receivers: [otlp], exporters: [b] });
|
|
171
|
+
const diags = otel108.check(makePostSynthCtx("otel", "", entities([a, b, otlp, p1, p2])));
|
|
172
|
+
expect(diags.map((d) => d.entity)).toEqual(["debug", "traces"]);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("OTEL109 flags a custom component whose pin is unusable", () => {
|
|
176
|
+
const Loose = defineComponent<{ endpoint?: string }>()({
|
|
177
|
+
kind: "exporter",
|
|
178
|
+
type: "loosepin2559",
|
|
179
|
+
pin: { source: "", version: "" },
|
|
180
|
+
});
|
|
181
|
+
const diags = otel109.check(makePostSynthCtx("otel", "", entities([new Loose({})])));
|
|
182
|
+
expect(diags.map((d) => d.checkId)).toEqual(["OTEL109"]);
|
|
183
|
+
});
|
|
184
|
+
});
|