@intentius/chant-lexicon-otel 0.124.46 → 0.124.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/README.md +2 -2
  2. package/dist/build/capability-plugin.d.ts +10 -0
  3. package/dist/build/capability-plugin.d.ts.map +1 -0
  4. package/dist/build/capability-plugin.js +19 -0
  5. package/dist/build/ocb-build.d.ts +38 -0
  6. package/dist/build/ocb-build.d.ts.map +1 -0
  7. package/dist/build/ocb-build.js +49 -0
  8. package/dist/clickhouse-tables.d.ts +73 -0
  9. package/dist/clickhouse-tables.d.ts.map +1 -0
  10. package/dist/clickhouse-tables.js +199 -0
  11. package/dist/codegen/docs.d.ts.map +1 -1
  12. package/dist/codegen/docs.js +4 -2
  13. package/dist/codegen/generate.d.ts +29 -10
  14. package/dist/codegen/generate.d.ts.map +1 -1
  15. package/dist/codegen/generate.js +82 -17
  16. package/dist/components/common.d.ts +3 -1
  17. package/dist/components/common.d.ts.map +1 -1
  18. package/dist/components/common.js +2 -0
  19. package/dist/components/connectors.d.ts +25 -90
  20. package/dist/components/connectors.d.ts.map +1 -1
  21. package/dist/components/connectors.js +3 -0
  22. package/dist/components/exporters.d.ts +78 -34
  23. package/dist/components/exporters.d.ts.map +1 -1
  24. package/dist/components/exporters.js +26 -1
  25. package/dist/components/extensions.d.ts +16 -33
  26. package/dist/components/extensions.d.ts.map +1 -1
  27. package/dist/components/filtering.d.ts +13 -60
  28. package/dist/components/filtering.d.ts.map +1 -1
  29. package/dist/components/k8s-receivers.d.ts +5 -53
  30. package/dist/components/k8s-receivers.d.ts.map +1 -1
  31. package/dist/components/overlay.d.ts +25 -0
  32. package/dist/components/overlay.d.ts.map +1 -0
  33. package/dist/components/overlay.js +19 -0
  34. package/dist/components/processors.d.ts +18 -79
  35. package/dist/components/processors.d.ts.map +1 -1
  36. package/dist/components/receivers.d.ts +17 -71
  37. package/dist/components/receivers.d.ts.map +1 -1
  38. package/dist/components/receivers.js +3 -1
  39. package/dist/components/sampling.d.ts +19 -35
  40. package/dist/components/sampling.d.ts.map +1 -1
  41. package/dist/config-hygiene.d.ts +10 -8
  42. package/dist/config-hygiene.d.ts.map +1 -1
  43. package/dist/config-hygiene.js +36 -13
  44. package/dist/coverage.d.ts +50 -0
  45. package/dist/coverage.d.ts.map +1 -0
  46. package/dist/coverage.js +92 -0
  47. package/dist/define.d.ts +8 -5
  48. package/dist/define.d.ts.map +1 -1
  49. package/dist/define.js +10 -6
  50. package/dist/distribution.d.ts +125 -0
  51. package/dist/distribution.d.ts.map +1 -0
  52. package/dist/distribution.js +227 -0
  53. package/dist/generated/index.d.ts +4234 -0
  54. package/dist/generated/index.d.ts.map +1 -0
  55. package/dist/generated/index.js +23 -0
  56. package/dist/index.d.ts +7 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +9 -1
  59. package/dist/init-templates.js +2 -2
  60. package/dist/integrity.json +11 -9
  61. package/dist/lint/audit-catalog.d.ts +3 -0
  62. package/dist/lint/audit-catalog.d.ts.map +1 -1
  63. package/dist/lint/audit-catalog.js +5 -0
  64. package/dist/lint/post-synth/index.d.ts.map +1 -1
  65. package/dist/lint/post-synth/index.js +4 -0
  66. package/dist/lint/post-synth/otel-helpers.d.ts +2 -1
  67. package/dist/lint/post-synth/otel-helpers.d.ts.map +1 -1
  68. package/dist/lint/post-synth/otel-helpers.js +1 -1
  69. package/dist/lint/post-synth/otel119.d.ts +1 -1
  70. package/dist/lint/post-synth/otel119.js +1 -1
  71. package/dist/lint/post-synth/otel125.d.ts +1 -1
  72. package/dist/lint/post-synth/otel125.js +1 -1
  73. package/dist/lint/post-synth/otel126.d.ts +1 -1
  74. package/dist/lint/post-synth/otel126.js +1 -1
  75. package/dist/lint/post-synth/otel127.d.ts +1 -1
  76. package/dist/lint/post-synth/otel127.js +1 -1
  77. package/dist/lint/post-synth/otel128.d.ts +8 -0
  78. package/dist/lint/post-synth/otel128.d.ts.map +1 -0
  79. package/dist/lint/post-synth/otel128.js +17 -0
  80. package/dist/lint/post-synth/otel129.d.ts +15 -0
  81. package/dist/lint/post-synth/otel129.d.ts.map +1 -0
  82. package/dist/lint/post-synth/otel129.js +92 -0
  83. package/dist/lsp/completions.d.ts +10 -1
  84. package/dist/lsp/completions.d.ts.map +1 -1
  85. package/dist/lsp/completions.js +29 -1
  86. package/dist/lsp/hover.d.ts +5 -1
  87. package/dist/lsp/hover.d.ts.map +1 -1
  88. package/dist/lsp/hover.js +16 -3
  89. package/dist/lsp/properties.d.ts +34 -0
  90. package/dist/lsp/properties.d.ts.map +1 -0
  91. package/dist/lsp/properties.js +135 -0
  92. package/dist/manifest.json +1 -1
  93. package/dist/meta.json +5 -0
  94. package/dist/metric-names.d.ts +36 -8
  95. package/dist/metric-names.d.ts.map +1 -1
  96. package/dist/metric-names.js +42 -9
  97. package/dist/model.d.ts +1 -1
  98. package/dist/model.js +1 -1
  99. package/dist/okf/index.md +3 -0
  100. package/dist/okf/rules/OTEL128.md +11 -0
  101. package/dist/okf/rules/OTEL129.md +11 -0
  102. package/dist/okf/types/ClickHouseExporter.md +9 -0
  103. package/dist/op/activities/collector-audit.d.ts +18 -6
  104. package/dist/op/activities/collector-audit.d.ts.map +1 -1
  105. package/dist/op/activities/collector-audit.js +37 -21
  106. package/dist/plugin.d.ts.map +1 -1
  107. package/dist/plugin.js +51 -8
  108. package/dist/rules/otel-helpers.ts +1 -1
  109. package/dist/rules/otel119.ts +1 -1
  110. package/dist/rules/otel125.ts +1 -1
  111. package/dist/rules/otel126.ts +1 -1
  112. package/dist/rules/otel127.ts +1 -1
  113. package/dist/rules/otel128.ts +22 -0
  114. package/dist/rules/otel129.ts +99 -0
  115. package/dist/serializer.d.ts +3 -0
  116. package/dist/serializer.d.ts.map +1 -1
  117. package/dist/serializer.js +12 -2
  118. package/dist/spec/builtins.d.ts +17 -0
  119. package/dist/spec/builtins.d.ts.map +1 -0
  120. package/dist/spec/builtins.js +45 -0
  121. package/dist/spec/fetch.d.ts +120 -0
  122. package/dist/spec/fetch.d.ts.map +1 -0
  123. package/dist/spec/fetch.js +282 -0
  124. package/dist/spec/parse.d.ts +77 -0
  125. package/dist/spec/parse.d.ts.map +1 -0
  126. package/dist/spec/parse.js +337 -0
  127. package/dist/spec/pin.d.ts +82 -0
  128. package/dist/spec/pin.d.ts.map +1 -0
  129. package/dist/spec/pin.js +126 -0
  130. package/dist/spec/snapshot-cli.d.ts +13 -0
  131. package/dist/spec/snapshot-cli.d.ts.map +1 -0
  132. package/dist/spec/snapshot-cli.js +31 -0
  133. package/dist/spec/snapshot.d.ts +9 -0
  134. package/dist/spec/snapshot.d.ts.map +1 -0
  135. package/dist/spec/snapshot.js +14 -0
  136. package/dist/types/index.d.ts +3 -2
  137. package/dist/validate-config.d.ts +1 -1
  138. package/dist/validate-config.d.ts.map +1 -1
  139. package/dist/validate.d.ts.map +1 -1
  140. package/dist/validate.js +1 -0
  141. package/dist/workspace-links.d.ts +25 -0
  142. package/dist/workspace-links.d.ts.map +1 -0
  143. package/dist/workspace-links.js +49 -0
  144. package/package.json +3 -2
  145. package/src/build/capability-plugin.ts +30 -0
  146. package/src/build/ocb-build.ts +73 -0
  147. package/src/clickhouse-exporter.e2e.test.ts +208 -0
  148. package/src/clickhouse-tables.test.ts +216 -0
  149. package/src/clickhouse-tables.ts +223 -0
  150. package/src/codegen/docs.ts +4 -2
  151. package/src/codegen/generate.ts +95 -19
  152. package/src/components/common.ts +4 -2
  153. package/src/components/connectors.ts +30 -77
  154. package/src/components/exporters.ts +128 -61
  155. package/src/components/extensions.ts +22 -36
  156. package/src/components/filtering.ts +17 -58
  157. package/src/components/k8s-receivers.test.ts +1 -1
  158. package/src/components/k8s-receivers.ts +3 -45
  159. package/src/components/overlay.ts +24 -0
  160. package/src/components/processors.ts +18 -63
  161. package/src/components/receivers.ts +20 -60
  162. package/src/components/sampling.ts +24 -37
  163. package/src/config-hygiene.ts +36 -13
  164. package/src/coverage.test.ts +37 -0
  165. package/src/coverage.ts +121 -0
  166. package/src/define.test.ts +2 -0
  167. package/src/define.ts +10 -6
  168. package/src/distribution.test.ts +250 -0
  169. package/src/distribution.ts +305 -0
  170. package/src/generated/index.ts +4576 -0
  171. package/src/generated/lexicon-otel.json +5 -0
  172. package/src/generated/properties.json +1 -0
  173. package/src/import/roundtrip.test.ts +16 -0
  174. package/src/import/testdata/clickhouse.yaml +37 -0
  175. package/src/import/testdata/fixtures.ts +17 -2
  176. package/src/import/testdata/upstream/clickhouse-exporter.yaml +60 -0
  177. package/src/index.ts +69 -0
  178. package/src/init-templates.ts +2 -2
  179. package/src/lint/audit-catalog.ts +15 -0
  180. package/src/lint/post-synth/index.ts +4 -0
  181. package/src/lint/post-synth/otel-helpers.ts +1 -1
  182. package/src/lint/post-synth/otel119.ts +1 -1
  183. package/src/lint/post-synth/otel125.ts +1 -1
  184. package/src/lint/post-synth/otel126.ts +1 -1
  185. package/src/lint/post-synth/otel127.ts +1 -1
  186. package/src/lint/post-synth/otel128.ts +22 -0
  187. package/src/lint/post-synth/otel129.ts +99 -0
  188. package/src/lsp/completions.ts +30 -1
  189. package/src/lsp/hover.ts +14 -3
  190. package/src/lsp/properties.test.ts +72 -0
  191. package/src/lsp/properties.ts +141 -0
  192. package/src/metric-names.test.ts +11 -0
  193. package/src/metric-names.ts +64 -16
  194. package/src/model.ts +1 -1
  195. package/src/op/activities/collector-audit.test.ts +29 -22
  196. package/src/op/activities/collector-audit.ts +38 -18
  197. package/src/plugin.test.ts +2 -0
  198. package/src/plugin.ts +57 -6
  199. package/src/serializer.ts +11 -2
  200. package/src/spec/builtins.ts +56 -0
  201. package/src/spec/fetch.ts +332 -0
  202. package/src/spec/otel-schemas.snapshot.json +13932 -0
  203. package/src/spec/parse.ts +364 -0
  204. package/src/spec/pin.ts +155 -0
  205. package/src/spec/snapshot-cli.ts +32 -0
  206. package/src/spec/snapshot.ts +19 -0
  207. package/src/spec/spec.test.ts +144 -0
  208. package/src/validate-config.ts +4 -3
  209. package/src/validate.ts +1 -0
  210. package/src/workspace-links.e2e.test.ts +91 -0
  211. package/src/workspace-links.ts +50 -0
@@ -217,6 +217,22 @@ describe("YAML -> TypeScript -> YAML", () => {
217
217
  expect(out.warnings).toEqual([]);
218
218
  });
219
219
 
220
+ test("a clickhouse exporter imports to the typed class (#4397)", async () => {
221
+ const out = await expectRoundTrip(read("clickhouse.yaml"));
222
+ expect(out.source).toContain("new ClickHouseExporter(");
223
+ expect(out.source).not.toContain("defineComponent");
224
+ expect(out.source).toContain("create_schema: false");
225
+ expect(out.warnings).toEqual([]);
226
+ });
227
+
228
+ test("collector-contrib v0.130.0: the clickhouse exporter's own example (#4397)", async () => {
229
+ // fluentforward is not a built-in, so it alone goes through defineComponent; the example's literal password is reported.
230
+ const out = await expectRoundTrip(read("upstream", "clickhouse-exporter.yaml"), { lintRules: ["OTEL002"] });
231
+ expect(out.source).toContain("new ClickHouseExporter(");
232
+ expect(out.source).toMatch(/type: "fluentforward"/);
233
+ expect(out.source).not.toMatch(/type: "clickhouse"/);
234
+ });
235
+
220
236
  test("the collector configs of examples/agent-observability", async () => {
221
237
  for (const file of ["agent-observability-agent.yaml", "agent-observability-gateway.yaml"]) {
222
238
  await expectRoundTrip(read(file));
@@ -0,0 +1,37 @@
1
+ # A collector writing every signal to ClickHouse tables the sql lexicon manages (#4397).
2
+ receivers:
3
+ otlp:
4
+ protocols:
5
+ grpc:
6
+ endpoint: 0.0.0.0:4317
7
+ processors:
8
+ batch: {}
9
+ exporters:
10
+ clickhouse:
11
+ endpoint: tcp://clickhouse:9000
12
+ database: otel
13
+ username: ${env:CLICKHOUSE_USER}
14
+ password: ${env:CLICKHOUSE_PASSWORD}
15
+ create_schema: false
16
+ logs_table_name: app_logs
17
+ metrics_tables:
18
+ exponential_histogram:
19
+ name: app_exp_histogram
20
+ sending_queue:
21
+ queue_size: 1000
22
+ retry_on_failure:
23
+ enabled: true
24
+ service:
25
+ pipelines:
26
+ logs:
27
+ receivers: [otlp]
28
+ processors: [batch]
29
+ exporters: [clickhouse]
30
+ traces:
31
+ receivers: [otlp]
32
+ processors: [batch]
33
+ exporters: [clickhouse]
34
+ metrics:
35
+ receivers: [otlp]
36
+ processors: [batch]
37
+ exporters: [clickhouse]
@@ -183,6 +183,20 @@ export function everyBuiltin(): Declarable[] {
183
183
  trace: { attribute_mappings: [{ key: "http.route", replacement: "/http/route" }] },
184
184
  log: { default_log_name: "otel" },
185
185
  });
186
+ const clickhouse = new c.ClickHouseExporter({
187
+ endpoint: "tcp://clickhouse:9000",
188
+ database: "otel",
189
+ username: "${env:CLICKHOUSE_USER}",
190
+ password: "${env:CLICKHOUSE_PASSWORD}",
191
+ create_schema: false,
192
+ ttl: "72h",
193
+ logs_table_name: "app_logs",
194
+ metrics_tables: { gauge: { name: "app_gauge" }, exponential_histogram: { name: "app_exp_histogram" } },
195
+ async_insert: true,
196
+ compress: "zstd",
197
+ timeout: "10s",
198
+ sending_queue: { queue_size: 1000 },
199
+ });
186
200
  const loadbalancing = new c.LoadBalancingExporter({
187
201
  routing_key: "service",
188
202
  protocol: { otlp: { timeout: "1s", tls: { insecure: true } } },
@@ -266,6 +280,7 @@ export function everyBuiltin(): Declarable[] {
266
280
  debug,
267
281
  prometheusOut,
268
282
  googlecloud,
283
+ clickhouse,
269
284
  loadbalancing,
270
285
  spanmetrics,
271
286
  servicegraph,
@@ -290,13 +305,13 @@ export function everyBuiltin(): Declarable[] {
290
305
  signal: "metrics",
291
306
  receivers: [otlp, prometheusIn, hostmetrics, cluster, kubelet, spanmetrics, servicegraph, count, sum, signalToMetrics],
292
307
  processors: [memoryLimiter, filter, deltaToCumulative, batch],
293
- exporters: [prometheusOut, otlphttp],
308
+ exporters: [prometheusOut, otlphttp, clickhouse],
294
309
  }),
295
310
  new Pipeline({
296
311
  signal: "logs",
297
312
  receivers: [otlp, filelog],
298
313
  processors: [memoryLimiter, redaction, batch],
299
- exporters: [otlphttp, debug, signalToMetrics],
314
+ exporters: [otlphttp, debug, signalToMetrics, clickhouse],
300
315
  }),
301
316
  new Service({
302
317
  extensions: [health, zpages, pprof, leaderElector],
@@ -0,0 +1,60 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/exporter/clickhouseexporter/example/otel-collector-config.yml
3
+
4
+ receivers:
5
+ fluentforward:
6
+ endpoint: 0.0.0.0:24224
7
+ otlp:
8
+ protocols:
9
+ grpc:
10
+ endpoint: 0.0.0.0:4317
11
+ processors:
12
+ batch:
13
+ send_batch_size: 100000
14
+ timeout: 5s
15
+ memory_limiter:
16
+ check_interval: 2s
17
+ limit_mib: 1800
18
+ spike_limit_mib: 500
19
+ resourcedetection/system:
20
+ detectors: [ "system" ]
21
+ system:
22
+ hostname_sources: [ "os" ]
23
+ resource:
24
+ attributes:
25
+ - key: service.name
26
+ value: "serviceName"
27
+ action: upsert
28
+ exporters:
29
+ clickhouse:
30
+ endpoint: tcp://clickhouse:9000
31
+ database: otel
32
+ logs_table_name: otel_logs
33
+ traces_table_name: otel_traces
34
+ ttl: 12h
35
+ timeout: 10s
36
+ sending_queue:
37
+ queue_size: 100
38
+ retry_on_failure:
39
+ enabled: true
40
+ initial_interval: 5s
41
+ max_interval: 30s
42
+ max_elapsed_time: 300s
43
+ username: default
44
+ password: default
45
+ extensions:
46
+ health_check:
47
+ pprof:
48
+ zpages:
49
+
50
+ service:
51
+ extensions: [ pprof, zpages, health_check ]
52
+ pipelines:
53
+ logs:
54
+ receivers: [ fluentforward, otlp ]
55
+ processors: [ memory_limiter, resourcedetection/system, resource, batch ]
56
+ exporters: [ clickhouse ]
57
+ traces:
58
+ receivers: [ otlp ]
59
+ processors: [ memory_limiter, resourcedetection/system, resource, batch ]
60
+ exporters: [ clickhouse ]
package/src/index.ts CHANGED
@@ -7,6 +7,48 @@ export { otelSerializer } from "./serializer";
7
7
  // Built-in components and their config types
8
8
  export * from "./components";
9
9
 
10
+ // The config types generated from upstream config.schema.yaml at COLLECTOR_PIN
11
+ // (#3361), before any hand-written overlay. Each built-in's `<Class>Config`
12
+ // builds on its `<Class>SchemaConfig`.
13
+ export type {
14
+ AttributesProcessorSchemaConfig,
15
+ BatchProcessorSchemaConfig,
16
+ CountConnectorSchemaConfig,
17
+ DebugExporterSchemaConfig,
18
+ DeltaToCumulativeProcessorSchemaConfig,
19
+ FileLogReceiverSchemaConfig,
20
+ FilterProcessorSchemaConfig,
21
+ GoogleCloudExporterSchemaConfig,
22
+ HealthCheckExtensionSchemaConfig,
23
+ HostMetricsReceiverSchemaConfig,
24
+ K8sAttributesProcessorSchemaConfig,
25
+ K8sClusterReceiverSchemaConfig,
26
+ K8sLeaderElectorExtensionSchemaConfig,
27
+ KubeletStatsReceiverSchemaConfig,
28
+ LoadBalancingExporterSchemaConfig,
29
+ MemoryLimiterProcessorSchemaConfig,
30
+ OtlpExporterSchemaConfig,
31
+ OtlpHttpExporterSchemaConfig,
32
+ OtlpReceiverSchemaConfig,
33
+ PprofExtensionSchemaConfig,
34
+ ProbabilisticSamplerProcessorSchemaConfig,
35
+ PrometheusExporterSchemaConfig,
36
+ PrometheusReceiverSchemaConfig,
37
+ RedactionProcessorSchemaConfig,
38
+ ResourceDetectionProcessorSchemaConfig,
39
+ ResourceProcessorSchemaConfig,
40
+ RoutingConnectorSchemaConfig,
41
+ ServiceGraphConnectorSchemaConfig,
42
+ SignalToMetricsConnectorSchemaConfig,
43
+ SpanMetricsConnectorSchemaConfig,
44
+ SumConnectorSchemaConfig,
45
+ TailSamplingProcessorSchemaConfig,
46
+ TransformProcessorSchemaConfig,
47
+ ZPagesExtensionSchemaConfig,
48
+ } from "./generated/index";
49
+ export { coverageReport, formatCoverage, type OtelCoverageReport, type DistributionCoverage } from "./coverage";
50
+ export { OTELCOL_CONTRIB_VERSION, OTELCOL_K8S_VERSION, OTEL_SPEC_PIN, type OtelSpecPin } from "./spec/pin";
51
+
10
52
  // Pipelines and the service block
11
53
  export {
12
54
  Pipeline,
@@ -168,6 +210,8 @@ export {
168
210
  type SpanMetricsNames,
169
211
  type SpanMetricsNamingConfig,
170
212
  type PrometheusNaming,
213
+ type PrometheusTranslationStrategy,
214
+ exporterNaming,
171
215
  promSelector,
172
216
  promSumRate,
173
217
  promErrorRatio,
@@ -188,3 +232,28 @@ export {
188
232
  // `@intentius/chant-lexicon-otel/op/activities`.
189
233
  export { otelcolValidate, otelcolComponents, collectorHealthObserve, collectorAudit } from "./op/builders";
190
234
  export { CollectorAuditOp, type CollectorAuditOpConfig, type CollectorAuditOpResources } from "./op/audit-op";
235
+
236
+ // A custom collector built with ocb (#3374): the builder manifest as an
237
+ // entity, the `ocb-build` build verb, and the check that a config uses only
238
+ // what the distribution builds (OTEL128).
239
+ export {
240
+ Distribution,
241
+ DISTRIBUTION_TYPE,
242
+ DISTRIBUTION_MANIFEST_FILE,
243
+ BUILTIN_MODULES,
244
+ isDistributionEntity,
245
+ distributionManifest,
246
+ distributionYaml,
247
+ distributionComponents,
248
+ distributionIssues,
249
+ componentModule,
250
+ moduleComponentType,
251
+ type DistributionProps,
252
+ type DistributionDist,
253
+ type DistributionModule,
254
+ type DistributionComponent,
255
+ type DistributionEntity,
256
+ type DistributionComponents,
257
+ } from "./distribution";
258
+ export { otelCapabilityPlugin, OTEL_VERB_FAMILIES, ocbBuild } from "./build/capability-plugin";
259
+ export { createOcbBuildCapability, ocbBuildCapability, ocbOutputPath, type OcbBuildInput, type OcbBuildOutput } from "./build/ocb-build";
@@ -78,8 +78,8 @@ const retry: RetrySettings = { enabled: true, max_elapsed_time: "300s" };
78
78
  const gateway = new OtlpExporter({ name: "gateway", endpoint: "otel-gateway.observability.svc:4317", tls: inCluster, retry_on_failure: retry });
79
79
 
80
80
  // Resource attributes (k8s.namespace.name, k8s.pod.name, ...) become metric labels.
81
- const asLabels = { enabled: true };
82
- const scrape = new PrometheusExporter({ endpoint: "0.0.0.0:8889", resource_to_telemetry_conversion: asLabels });
81
+ const asLabels = { included: ["*"] };
82
+ const scrape = new PrometheusExporter({ endpoint: "0.0.0.0:8889", resource_constant_labels: asLabels });
83
83
 
84
84
  export { gateway, scrape };
85
85
  `;
@@ -5,6 +5,9 @@
5
5
  * OTEL101-OTEL106, OTEL112-OTEL117 and OTEL119-OTEL127 read the emitted collector YAML, so they are
6
6
  * `yamlBased`. OTEL118 reads it too, but only in a build that stamps
7
7
  * telemetry attribution, so it is not.
8
+ * OTEL128 reads both: the emitted config and the declared `Distribution`.
9
+ * OTEL129 reads both too: the emitted config and the sql lexicon's declared
10
+ * ClickHouse tables.
8
11
  * OTEL107-OTEL109 read the declared entities (a component's definition, its
9
12
  * schema pin), which a standalone YAML file does not carry, so they are
10
13
  * constructed with `yamlBased: false`. The two source-level lint rules are
@@ -233,4 +236,16 @@ export const otelAuditCatalog: Record<string, RuleMeta> = {
233
236
  "Name detectors as the processor registers them (elastic_beanstalk, not elasticbeanstalk), or remove the entry.",
234
237
  { category: "correctness" },
235
238
  ),
239
+ OTEL128: entityRule(
240
+ "OTEL128",
241
+ "correctness",
242
+ "Collector config uses a component the declared Distribution does not build",
243
+ "Add the component to the Distribution's list for its kind, or remove it from the collector config.",
244
+ ),
245
+ OTEL129: entityRule(
246
+ "OTEL129",
247
+ "correctness",
248
+ "A clickhouse exporter writes a table or column the sql build in the same root does not declare",
249
+ "Declare the exporter's tables with OtelTables from the sql lexicon, or point the exporter's table names at declared tables, and set create_schema: false.",
250
+ ),
236
251
  };
@@ -25,6 +25,8 @@ import { otel124 } from "./otel124";
25
25
  import { otel125 } from "./otel125";
26
26
  import { otel126 } from "./otel126";
27
27
  import { otel127 } from "./otel127";
28
+ import { otel128 } from "./otel128";
29
+ import { otel129 } from "./otel129";
28
30
 
29
31
  export const postSynthChecks: PostSynthCheck[] = [
30
32
  otel101,
@@ -52,4 +54,6 @@ export const postSynthChecks: PostSynthCheck[] = [
52
54
  otel125,
53
55
  otel126,
54
56
  otel127,
57
+ otel128,
58
+ otel129,
55
59
  ];
@@ -68,7 +68,7 @@ export function collectorConfigs(ctx: PostSynthContext): FoundCollectorConfig[]
68
68
  return out;
69
69
  }
70
70
 
71
- function toDiagnostic(issue: CollectorIssue, found?: Omit<FoundCollectorConfig, "config">): PostSynthDiagnostic {
71
+ export function toDiagnostic(issue: CollectorIssue, found?: Omit<FoundCollectorConfig, "config">): PostSynthDiagnostic {
72
72
  const where = found?.configMap
73
73
  ? describeConfigMapConfig(found.configMap)
74
74
  : found?.operator
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * OTEL119: A collector config sets a field deprecated at or before the pinned release
3
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.
4
+ * `invert_match: true` in a `tail_sampling` policy (inverted decisions are deprecated in collector-contrib v0.126.0 for a `drop` policy, and v0.155.0 removed the gate that kept them), `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 parses each at v0.162.0, and a later release drops it.
5
5
  */
6
6
 
7
7
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * OTEL125: A pipeline sends to a remote otlp or otlphttp exporter without batching
3
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.
4
+ * No `batch` processor in the pipeline, and the `otlp` or `otlphttp` exporter (non-loopback endpoint) sets no `sending_queue.batch`. At v0.162.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
5
  */
6
6
 
7
7
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * OTEL126: k8sattributes extracts a metadata field it does not support
3
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.
4
+ * Each name in a `k8sattributes` processor's `extract.metadata` must be one `Config.Validate` accepts at collector-contrib v0.162.0 (`K8S_ATTRIBUTES_METADATA`); the collector refuses any other.
5
5
  */
6
6
 
7
7
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * OTEL127: resourcedetection lists a detector that does not exist
3
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.
4
+ * Each name in a started `resourcedetection` processor's `detectors` must be one the processor registers at collector-contrib v0.162.0 (`RESOURCE_DETECTORS`); any other fails the processor's build and the collector exits.
5
5
  */
6
6
 
7
7
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * OTEL128: The collector config uses a component the declared Distribution does not build
3
+ *
4
+ * When a project declares a `Distribution` (an ocb builder manifest), every component a collector config declares must be one the distribution builds; a collector built from it fails at start with "unknown type" otherwise. A project without a `Distribution` is not checked.
5
+ */
6
+
7
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
8
+ import { distributionIssues, isDistributionEntity } from "../../distribution";
9
+ import { collectorConfigs, toDiagnostic } from "./otel-helpers";
10
+
11
+ export const otel128: PostSynthCheck = {
12
+ id: "OTEL128",
13
+ description: "The collector config uses a component the declared Distribution does not build",
14
+
15
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
16
+ const dist = [...(ctx.entities?.values() ?? [])].find(isDistributionEntity);
17
+ if (!dist) return [];
18
+ return collectorConfigs(ctx).flatMap(({ config, ...found }) =>
19
+ distributionIssues(config, dist).map((i) => toDiagnostic(i, found)),
20
+ );
21
+ },
22
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * OTEL129: A clickhouse exporter writes a table the sql build in the same root does not declare
3
+ *
4
+ * When the build root also declares ClickHouse tables with the sql lexicon, every table a `clickhouse` exporter writes (for the signals whose pipelines use it) must be one of them, in the database the exporter writes to, with every column the exporter's insert statement names. A renamed or dropped table fails here instead of at the exporter's first insert. When `create_schema` is true (the exporter's default) for tables the build declares, it warns: the exporter would run its own `CREATE TABLE IF NOT EXISTS` beside the sql lexicon's plan. A build root with no sql ClickHouse tables is not checked. Sees one build root at a time (#1939); across workspace members the otel lexicon's workspace links check the same tables (#3931).
5
+ */
6
+
7
+ import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
8
+ import type { Declarable } from "@intentius/chant/declarable";
9
+ import { clickhouseExportersOf } from "../../clickhouse-tables";
10
+ import { collectorConfigs } from "./otel-helpers";
11
+
12
+ /** The sql lexicon's ClickHouse table entity type, matched by name so otel needs no dependency on the sql lexicon. */
13
+ const CLICKHOUSE_TABLE = "ClickHouse::Table";
14
+
15
+ interface DeclaredTable {
16
+ columns: Set<string>;
17
+ }
18
+
19
+ /** Every ClickHouse table the build declares, by `database.name`. */
20
+ export function declaredClickHouseTables(entities: Iterable<Declarable>): Map<string, DeclaredTable> {
21
+ const out = new Map<string, DeclaredTable>();
22
+ for (const e of entities) {
23
+ if (e.entityType !== CLICKHOUSE_TABLE) continue;
24
+ const props = (e as unknown as { props?: { name?: unknown; database?: unknown; columns?: unknown } }).props;
25
+ if (typeof props?.name !== "string") continue;
26
+ const database = typeof props.database === "string" && props.database !== "" ? props.database : "default";
27
+ const columns = new Set<string>();
28
+ for (const c of Array.isArray(props.columns) ? props.columns : []) {
29
+ const name = (c as { name?: unknown })?.name;
30
+ if (typeof name === "string") columns.add(name);
31
+ }
32
+ out.set(`${database}.${props.name}`, { columns });
33
+ }
34
+ return out;
35
+ }
36
+
37
+ /** A column the insert names is declared as itself, or as the flattened fields of a Nested column (`Events.Timestamp`). */
38
+ function hasColumn(declared: Set<string>, column: string): boolean {
39
+ if (declared.has(column)) return true;
40
+ for (const c of declared) if (c.startsWith(`${column}.`)) return true;
41
+ return false;
42
+ }
43
+
44
+ export const otel129: PostSynthCheck = {
45
+ id: "OTEL129",
46
+ description: "A clickhouse exporter writes a table the sql build in the same root does not declare",
47
+
48
+ check(ctx: PostSynthContext): PostSynthDiagnostic[] {
49
+ const declared = declaredClickHouseTables(ctx.entities?.values() ?? []);
50
+ if (declared.size === 0) return [];
51
+ const known = [...declared.keys()].sort();
52
+ const diagnostics: PostSynthDiagnostic[] = [];
53
+ for (const { config } of collectorConfigs(ctx)) {
54
+ for (const exp of clickhouseExportersOf(config)) {
55
+ const createsDeclared: string[] = [];
56
+ for (const t of exp.tables) {
57
+ const qualified = `${exp.database}.${t.name}`;
58
+ const table = declared.get(qualified);
59
+ if (!table) {
60
+ diagnostics.push({
61
+ checkId: "OTEL129",
62
+ severity: "error",
63
+ message:
64
+ `exporter ${exp.id} writes ${t.signal} to ${qualified} (${t.key}), which the sql build does not declare. ` +
65
+ `Declare it (OtelTables from @intentius/chant-lexicon-sql/clickhouse declares the exporter's tables) or point ${t.key} at a declared table. ` +
66
+ `Declared ClickHouse tables: ${known.join(", ")}`,
67
+ entity: exp.id,
68
+ lexicon: "otel",
69
+ });
70
+ continue;
71
+ }
72
+ const missing = t.columns.filter((c) => !hasColumn(table.columns, c));
73
+ if (missing.length > 0) {
74
+ diagnostics.push({
75
+ checkId: "OTEL129",
76
+ severity: "error",
77
+ message: `exporter ${exp.id} inserts ${t.signal} into ${qualified} with column${missing.length === 1 ? "" : "s"} ${missing.join(", ")}, which the declared table does not have; the insert would fail`,
78
+ entity: exp.id,
79
+ lexicon: "otel",
80
+ });
81
+ }
82
+ createsDeclared.push(qualified);
83
+ }
84
+ if (exp.createSchema && createsDeclared.length > 0) {
85
+ diagnostics.push({
86
+ checkId: "OTEL129",
87
+ severity: "warning",
88
+ message:
89
+ `exporter ${exp.id} has create_schema true (the default), so at start it runs CREATE TABLE IF NOT EXISTS for ${createsDeclared.join(", ")}, which the sql build declares. ` +
90
+ `Set create_schema: false so the sql lexicon alone manages them`,
91
+ entity: exp.id,
92
+ lexicon: "otel",
93
+ });
94
+ }
95
+ }
96
+ }
97
+ return diagnostics;
98
+ },
99
+ };
@@ -1,6 +1,7 @@
1
1
  import type { CompletionContext, CompletionItem } from "@intentius/chant/lsp/types";
2
2
  import { LexiconIndex, lexiconCompletions } from "@intentius/chant/lsp/lexicon-providers";
3
3
  import { lexiconRegistry } from "../catalog";
4
+ import { propertiesAt, propertyMarkdown, propertyPosition } from "./properties";
4
5
 
5
6
  let cachedIndex: LexiconIndex | null = null;
6
7
 
@@ -9,7 +10,35 @@ function getIndex(): LexiconIndex {
9
10
  return cachedIndex;
10
11
  }
11
12
 
12
- /** Completions for the otel entity classes: the built-in components, Pipeline and Service. */
13
+ /** The text of `content` before the cursor. */
14
+ export function textBefore(content: string, position: { line: number; character: number }): string {
15
+ const lines = content.split("\n");
16
+ return [...lines.slice(0, position.line), (lines[position.line] ?? "").slice(0, position.character)].join("\n");
17
+ }
18
+
19
+ /**
20
+ * Completions for the otel entity classes after `new`, and inside a built-in's
21
+ * constructor, the config properties at the cursor's depth, with upstream's
22
+ * descriptions (#3361).
23
+ */
13
24
  export function completions(ctx: CompletionContext): CompletionItem[] {
25
+ if (ctx?.content && ctx.position && !/\bnew\s+\w*$/.test(ctx.linePrefix ?? "")) {
26
+ const position = propertyPosition(textBefore(ctx.content, ctx.position));
27
+ const props = position && propertiesAt(position);
28
+ if (position && props) {
29
+ const prefix = (ctx.wordAtCursor ?? "").toLowerCase();
30
+ return Object.entries(props)
31
+ .filter(([name]) => name.toLowerCase().startsWith(prefix))
32
+ .sort(([a], [b]) => a.localeCompare(b))
33
+ .map(([name, doc]) => ({
34
+ label: name,
35
+ insertText: name,
36
+ kind: "property" as const,
37
+ detail: doc.type,
38
+ documentation: propertyMarkdown(name, doc, position.className),
39
+ ...(doc.deprecated ? { deprecated: true } : {}),
40
+ }));
41
+ }
42
+ }
14
43
  return lexiconCompletions(ctx, getIndex(), "OpenTelemetry Collector entity");
15
44
  }
package/src/lsp/hover.ts CHANGED
@@ -2,6 +2,8 @@ import type { HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
2
2
  import { LexiconIndex, lexiconHover, type LexiconEntry } from "@intentius/chant/lsp/lexicon-providers";
3
3
  import { BUILTIN_CATALOG, lexiconRegistry } from "../catalog";
4
4
  import { COLLECTOR_PIN } from "../define";
5
+ import { propertiesAt, propertyMarkdown, propertyPosition } from "./properties";
6
+ import { textBefore } from "./completions";
5
7
 
6
8
  let cachedIndex: LexiconIndex | null = null;
7
9
 
@@ -10,9 +12,18 @@ function getIndex(): LexiconIndex {
10
12
  return cachedIndex;
11
13
  }
12
14
 
13
- /** Hover for otel entity classes: what the component does and its collector id. */
15
+ /**
16
+ * Hover for otel entity classes (what the component does and its collector
17
+ * id), and for a config property inside a built-in's constructor (upstream's
18
+ * description, #3361).
19
+ */
14
20
  export function hover(ctx: HoverContext): HoverInfo | undefined {
15
- return lexiconHover(ctx, getIndex(), resourceHover);
21
+ const entity = lexiconHover(ctx, getIndex(), resourceHover);
22
+ if (entity) return entity;
23
+ if (!ctx?.content || !ctx.position || !ctx.word) return undefined;
24
+ const position = propertyPosition(textBefore(ctx.content, ctx.position));
25
+ const doc = position && propertiesAt(position)?.[ctx.word];
26
+ return position && doc ? { contents: propertyMarkdown(ctx.word, doc, position.className) } : undefined;
16
27
  }
17
28
 
18
29
  function resourceHover(className: string, entry: LexiconEntry): HoverInfo | undefined {
@@ -23,7 +34,7 @@ function resourceHover(className: string, entry: LexiconEntry): HoverInfo | unde
23
34
  lines.push(
24
35
  "",
25
36
  `Collector id \`${cat.type}\`, or \`${cat.type}/<name>\` with \`name\` set. ` +
26
- `Config typed against ${COLLECTOR_PIN.source} ${COLLECTOR_PIN.version}.`,
37
+ `Config typed from upstream config.schema.yaml at ${COLLECTOR_PIN.source} ${COLLECTOR_PIN.version}.`,
27
38
  );
28
39
  }
29
40
  return { contents: lines.join("\n") };
@@ -0,0 +1,72 @@
1
+ import { afterEach, describe, expect, test } from "vitest";
2
+ import { propertiesAt, propertyPosition, setPropertyTrees } from "./properties";
3
+ import { completions } from "./completions";
4
+ import { hover } from "./hover";
5
+
6
+ const TREES = {
7
+ OtlpReceiver: {
8
+ protocols: {
9
+ type: "object",
10
+ description: "Protocols is the configuration for the supported protocols.",
11
+ properties: {
12
+ grpc: { type: "object", properties: { endpoint: { type: "string", description: "Endpoint to listen on." } } },
13
+ http: { type: "object" },
14
+ },
15
+ },
16
+ },
17
+ TailSamplingProcessor: {
18
+ decision_wait: { type: "duration" },
19
+ policies: { type: "object[]", properties: { name: { type: "string" }, latency: { type: "object", properties: { threshold_ms: { type: "number" } } } } },
20
+ num_traces: { type: "number", deprecated: true },
21
+ },
22
+ };
23
+
24
+ afterEach(() => setPropertyTrees(undefined));
25
+
26
+ describe("property positions", () => {
27
+ test("the keys of the objects around the cursor, through lists", () => {
28
+ expect(propertyPosition("new OtlpReceiver({ ")).toEqual({ className: "OtlpReceiver", path: [] });
29
+ expect(propertyPosition("new OtlpReceiver({ protocols: { grpc: { ")).toEqual({ className: "OtlpReceiver", path: ["protocols", "grpc"] });
30
+ expect(propertyPosition('new OtlpReceiver({ protocols: { http: {}, "grpc": { ')).toEqual({ className: "OtlpReceiver", path: ["protocols", "grpc"] });
31
+ expect(propertyPosition('new TailSamplingProcessor({ policies: [{ name: "a" }, { latency: { ')).toEqual({
32
+ className: "TailSamplingProcessor",
33
+ path: ["policies", "latency"],
34
+ });
35
+ expect(propertyPosition("new OtlpReceiver({ protocols: {} });\nconst x = { ")).toBeUndefined();
36
+ expect(propertyPosition("const x = { ")).toBeUndefined();
37
+ });
38
+
39
+ test("the properties at a path", () => {
40
+ setPropertyTrees(TREES);
41
+ expect(Object.keys(propertiesAt({ className: "OtlpReceiver", path: ["protocols"] })!)).toEqual(["grpc", "http"]);
42
+ expect(propertiesAt({ className: "OtlpReceiver", path: ["nope"] })).toBeUndefined();
43
+ });
44
+ });
45
+
46
+ describe("completion and hover", () => {
47
+ const at = (content: string, word = "") => {
48
+ const lines = content.split("\n");
49
+ return { uri: "file:///c.ts", content, position: { line: lines.length - 1, character: lines[lines.length - 1].length }, wordAtCursor: word, linePrefix: lines[lines.length - 1] };
50
+ };
51
+
52
+ test("offers the properties at the cursor's depth, with upstream's description", () => {
53
+ setPropertyTrees(TREES);
54
+ const items = completions(at("const r = new OtlpReceiver({ protocols: { grpc: { "));
55
+ expect(items.map((i) => i.label)).toEqual(["endpoint"]);
56
+ expect(items[0].documentation).toContain("Endpoint to listen on.");
57
+ const top = completions(at("new TailSamplingProcessor({ num", "num"));
58
+ expect(top).toEqual([expect.objectContaining({ label: "num_traces", detail: "number", deprecated: true })]);
59
+ });
60
+
61
+ test("still offers the classes after new", () => {
62
+ setPropertyTrees(TREES);
63
+ expect(completions(at("const r = new Otlp", "Otlp")).map((i) => i.label)).toContain("OtlpReceiver");
64
+ });
65
+
66
+ test("hovers a property with its description", () => {
67
+ setPropertyTrees(TREES);
68
+ const content = "new OtlpReceiver({ protocols";
69
+ const info = hover({ uri: "file:///c.ts", content, position: { line: 0, character: 19 }, word: "protocols", lineText: content });
70
+ expect(info?.contents).toContain("Protocols is the configuration for the supported protocols.");
71
+ });
72
+ });