@intentius/chant-lexicon-otel 0.124.46 → 0.124.48

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
@@ -0,0 +1,223 @@
1
+ /**
2
+ * The ClickHouse tables a `clickhouse` exporter writes (#4397), read from its
3
+ * config the way the exporter reads it at `COLLECTOR_PIN` (contrib v0.130.0).
4
+ * OTEL129 and the otel lexicon's `workspaceLinks` hook share this, so the
5
+ * check in one build root and the link across workspace members agree on
6
+ * which tables and columns an exporter needs.
7
+ *
8
+ * Sources in opentelemetry-collector-contrib at v0.130.0,
9
+ * `exporter/clickhouseexporter/`:
10
+ *
11
+ * - Default names: `createDefaultConfig` and `buildMetricTableNames` in
12
+ * `config.go`. The exponential histogram table is
13
+ * `otel_metrics_exponential_histogram` there; the README says
14
+ * `otel_metrics_exp_histogram`, and the code is what runs.
15
+ * - Database: `Config.database()` in `config.go`. `database` wins unless it
16
+ * is `default`, then the endpoint DSN's database, then `default`.
17
+ * - Columns: the column lists of the insert statements in
18
+ * `internal/sqltemplates/*_insert.sql`. A `Nested` field such as
19
+ * `Events.Timestamp` is listed under its column, `Events`.
20
+ * - Which tables a signal touches: each signal's exporter writes only its own
21
+ * tables (`exporter_logs.go`, `exporter_traces.go`, `exporter_metrics.go`),
22
+ * and only when a pipeline of that signal uses the exporter.
23
+ * - `create_schema` defaults to true and makes the exporter run
24
+ * `CREATE TABLE IF NOT EXISTS` for its tables at start.
25
+ *
26
+ * The trace-id lookup table and its materialized view, which the traces
27
+ * exporter creates beside the traces table when `create_schema` is true, are
28
+ * not written by the exporter: the view fills the table from the traces
29
+ * table's inserts. They are not part of what an exporter needs here.
30
+ */
31
+
32
+ import type { CollectorConfig } from "./model";
33
+ import { parseComponentId, pipelineSignal } from "./model";
34
+
35
+ /** One table an exporter writes. */
36
+ export interface ClickHouseExporterTable {
37
+ /** The signal whose pipeline writes it. */
38
+ signal: "logs" | "traces" | "metrics";
39
+ /** The config key that names it: `logs_table_name`, `metrics_tables.gauge.name`. */
40
+ key: string;
41
+ /** The table, as the exporter resolves it. */
42
+ name: string;
43
+ /** The columns its insert statement names. */
44
+ columns: readonly string[];
45
+ }
46
+
47
+ /** What one `clickhouse` exporter writes. */
48
+ export interface ClickHouseExporterTargets {
49
+ /** The exporter's id: `clickhouse` or `clickhouse/<name>`. */
50
+ id: string;
51
+ database: string;
52
+ /** Whether the exporter creates its database and tables at start (`create_schema`, default true). */
53
+ createSchema: boolean;
54
+ tables: ClickHouseExporterTable[];
55
+ /** Values chant can't read without the environment, such as `${env:CH_DB}`: the tables they name are left out. */
56
+ unread: string[];
57
+ }
58
+
59
+ const METRIC_HEAD = [
60
+ "ResourceAttributes",
61
+ "ResourceSchemaUrl",
62
+ "ScopeName",
63
+ "ScopeVersion",
64
+ "ScopeAttributes",
65
+ "ScopeDroppedAttrCount",
66
+ "ScopeSchemaUrl",
67
+ "ServiceName",
68
+ "MetricName",
69
+ "MetricDescription",
70
+ "MetricUnit",
71
+ "Attributes",
72
+ "StartTimeUnix",
73
+ "TimeUnix",
74
+ ] as const;
75
+
76
+ /** The columns each insert statement names, at v0.130.0. */
77
+ export const CLICKHOUSE_EXPORTER_COLUMNS = {
78
+ logs: [
79
+ "Timestamp",
80
+ "TraceId",
81
+ "SpanId",
82
+ "TraceFlags",
83
+ "SeverityText",
84
+ "SeverityNumber",
85
+ "ServiceName",
86
+ "Body",
87
+ "ResourceSchemaUrl",
88
+ "ResourceAttributes",
89
+ "ScopeSchemaUrl",
90
+ "ScopeName",
91
+ "ScopeVersion",
92
+ "ScopeAttributes",
93
+ "LogAttributes",
94
+ ],
95
+ traces: [
96
+ "Timestamp",
97
+ "TraceId",
98
+ "SpanId",
99
+ "ParentSpanId",
100
+ "TraceState",
101
+ "SpanName",
102
+ "SpanKind",
103
+ "ServiceName",
104
+ "ResourceAttributes",
105
+ "ScopeName",
106
+ "ScopeVersion",
107
+ "SpanAttributes",
108
+ "Duration",
109
+ "StatusCode",
110
+ "StatusMessage",
111
+ "Events",
112
+ "Links",
113
+ ],
114
+ gauge: [...METRIC_HEAD, "Value", "Flags", "Exemplars"],
115
+ sum: [...METRIC_HEAD, "Value", "Flags", "Exemplars", "AggregationTemporality", "IsMonotonic"],
116
+ summary: [...METRIC_HEAD, "Count", "Sum", "ValueAtQuantiles", "Flags"],
117
+ histogram: [...METRIC_HEAD, "Count", "Sum", "BucketCounts", "ExplicitBounds", "Exemplars", "Flags", "Min", "Max", "AggregationTemporality"],
118
+ exponential_histogram: [
119
+ ...METRIC_HEAD,
120
+ "Count",
121
+ "Sum",
122
+ "Scale",
123
+ "ZeroCount",
124
+ "PositiveOffset",
125
+ "PositiveBucketCounts",
126
+ "NegativeOffset",
127
+ "NegativeBucketCounts",
128
+ "Exemplars",
129
+ "Flags",
130
+ "Min",
131
+ "Max",
132
+ "AggregationTemporality",
133
+ ],
134
+ } as const satisfies Record<string, readonly string[]>;
135
+
136
+ /** The metric types in `metrics_tables`, with the suffix each default name takes. */
137
+ const METRIC_TYPES = [
138
+ ["gauge", "_gauge"],
139
+ ["sum", "_sum"],
140
+ ["summary", "_summary"],
141
+ ["histogram", "_histogram"],
142
+ ["exponential_histogram", "_exponential_histogram"],
143
+ ] as const;
144
+
145
+ const DEFAULT_DATABASE = "default";
146
+ const DEFAULT_METRICS_PREFIX = "otel_metrics";
147
+
148
+ const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === "object" && v !== null && !Array.isArray(v);
149
+ const str = (v: unknown): string | undefined => (typeof v === "string" && v !== "" ? v : undefined);
150
+ /** A value the collector expands from the environment or another config source. */
151
+ const expanded = (v: string) => v.includes("${");
152
+
153
+ /** The database in the endpoint DSN's path, or undefined when it has none or can't be read. */
154
+ function dsnDatabase(endpoint: string | undefined): string | undefined {
155
+ if (!endpoint || expanded(endpoint)) return undefined;
156
+ try {
157
+ const path = new URL(endpoint).pathname.replace(/^\/+/, "");
158
+ return path === "" ? undefined : decodeURIComponent(path);
159
+ } catch {
160
+ return undefined;
161
+ }
162
+ }
163
+
164
+ /** The signals whose pipelines list exporter `id`. */
165
+ export function exporterSignals(config: CollectorConfig, id: string): Set<string> {
166
+ const out = new Set<string>();
167
+ for (const [pid, p] of Object.entries(config.service?.pipelines ?? {})) {
168
+ const exporters = isRecord(p) && Array.isArray(p.exporters) ? p.exporters : [];
169
+ if (exporters.includes(id)) out.add(pipelineSignal(pid));
170
+ }
171
+ return out;
172
+ }
173
+
174
+ /**
175
+ * The tables one `clickhouse` exporter writes, for the signals whose
176
+ * pipelines use it. `raw` is its config block.
177
+ */
178
+ export function clickhouseExporterTargets(id: string, raw: unknown, signals: ReadonlySet<string>): ClickHouseExporterTargets {
179
+ const c = isRecord(raw) ? raw : {};
180
+ const unread: string[] = [];
181
+
182
+ let database = DEFAULT_DATABASE;
183
+ const configured = str(c.database);
184
+ if (configured && expanded(configured)) unread.push(`database ${configured}`);
185
+ else if (configured && configured !== DEFAULT_DATABASE) database = configured;
186
+ else {
187
+ const endpoint = str(c.endpoint);
188
+ if (endpoint && expanded(endpoint)) unread.push(`endpoint ${endpoint}, whose path may name the database`);
189
+ database = dsnDatabase(endpoint) ?? DEFAULT_DATABASE;
190
+ }
191
+ if (unread.length > 0) return { id, database, createSchema: c.create_schema !== false, tables: [], unread };
192
+
193
+ const tables: ClickHouseExporterTable[] = [];
194
+ const add = (signal: ClickHouseExporterTable["signal"], key: string, name: string, columns: readonly string[]) => {
195
+ if (expanded(name)) unread.push(`${key} ${name}`);
196
+ else tables.push({ signal, key, name, columns });
197
+ };
198
+ if (signals.has("logs")) add("logs", "logs_table_name", str(c.logs_table_name) ?? "otel_logs", CLICKHOUSE_EXPORTER_COLUMNS.logs);
199
+ if (signals.has("traces")) add("traces", "traces_table_name", str(c.traces_table_name) ?? "otel_traces", CLICKHOUSE_EXPORTER_COLUMNS.traces);
200
+ if (signals.has("metrics")) {
201
+ const mt = isRecord(c.metrics_tables) ? c.metrics_tables : {};
202
+ const nameOf = (type: string) => (isRecord(mt[type]) ? str((mt[type] as Record<string, unknown>).name) : undefined);
203
+ // The deprecated metrics_table_name is a prefix, used only when no metrics_tables name is set.
204
+ const anySet = METRIC_TYPES.some(([type]) => nameOf(type) !== undefined);
205
+ const prefix = !anySet ? (str(c.metrics_table_name) ?? DEFAULT_METRICS_PREFIX) : DEFAULT_METRICS_PREFIX;
206
+ for (const [type, suffix] of METRIC_TYPES) {
207
+ add("metrics", `metrics_tables.${type}.name`, nameOf(type) ?? `${prefix}${suffix}`, CLICKHOUSE_EXPORTER_COLUMNS[type]);
208
+ }
209
+ }
210
+ return { id, database, createSchema: c.create_schema !== false, tables, unread };
211
+ }
212
+
213
+ /** Every `clickhouse` exporter a pipeline uses in `config`, with what it writes. */
214
+ export function clickhouseExportersOf(config: CollectorConfig): ClickHouseExporterTargets[] {
215
+ const out: ClickHouseExporterTargets[] = [];
216
+ for (const [id, raw] of Object.entries(config.exporters ?? {})) {
217
+ if (parseComponentId(id)?.type !== "clickhouse") continue;
218
+ const signals = exporterSignals(config, id);
219
+ if (signals.size === 0) continue;
220
+ out.push(clickhouseExporterTargets(id, raw, signals));
221
+ }
222
+ return out;
223
+ }
@@ -59,8 +59,10 @@ on the same address (OTEL117), and a literal credential in source (OTEL002)
59
59
  or in the config (OTEL120). Others report deprecated fields (OTEL119),
60
60
  credentials sent in plaintext (OTEL121), zpages or pprof off loopback
61
61
  (OTEL122), detailed debug output beside a backend (OTEL123), delivery and
62
- batching settings (OTEL124, OTEL125), and k8sattributes fields or
63
- resourcedetection detectors the collector doesn't know (OTEL126, OTEL127). In
62
+ batching settings (OTEL124, OTEL125), k8sattributes fields or
63
+ resourcedetection detectors the collector doesn't know (OTEL126, OTEL127),
64
+ and, when the project declares a \`Distribution\`, a component the config uses
65
+ that the distribution doesn't build (OTEL128). In
64
66
  a build that stamps telemetry attribution, OTEL118 reports a processor that can
65
67
  drop or overwrite one of the attribution's resource attributes.
66
68
  `;
@@ -1,50 +1,126 @@
1
1
  /**
2
- * The otel lexicon's "generation" step.
2
+ * The otel lexicon's generation step (#3361).
3
3
  *
4
- * There is no upstream schema to fetch: the collector defines each
5
- * component's config as a Go struct, and publishes no machine-readable schema
6
- * for the set as a whole. The built-in types are written by hand against the
7
- * collector release named in `COLLECTOR_PIN` (src/define.ts), and this step
8
- * derives the registry from them, so the packaged `dist/meta.json` can never
9
- * disagree with the classes the package exports.
4
+ * The built-in components' config types are generated from upstream
5
+ * `config.schema.yaml`, the documents `schemagen` writes next to each
6
+ * component's `config.go`, at the collector-contrib release `COLLECTOR_PIN`
7
+ * names and scoped by the `otelcol-contrib` distribution manifest. Generation
8
+ * reads the committed snapshot (`src/spec/otel-schemas.snapshot.json`, refreshed
9
+ * by `just snapshot`), refuses one that does not match `OTEL_SPEC_PIN`, and
10
+ * writes, into the gitignored `src/generated/`:
11
+ *
12
+ * - `index.ts`: a type alias per schema definition the built-ins reach, and
13
+ * `<ClassName>SchemaConfig` for each built-in that has a schema. The
14
+ * hand-written `defineBuiltin` files build their config types on these,
15
+ * overlaying only what a schema lacks (see `src/components/overlay.ts`);
16
+ * - `properties.json`: each built-in's property tree with upstream's
17
+ * descriptions, for LSP completion and hover;
18
+ * - `lexicon-otel.json`: the registry, derived from the exported classes.
19
+ *
20
+ * A built-in with no upstream schema (`forward`, which has no config) keeps its
21
+ * hand-written type.
10
22
  */
11
23
 
12
24
  import { dirname, join } from "path";
13
25
  import { fileURLToPath } from "url";
14
26
  import { mkdirSync, writeFileSync } from "fs";
15
27
  import type { GenerateResult } from "@intentius/chant/codegen/generate";
16
- import { BUILTIN_CATALOG, lexiconRegistry } from "../catalog";
28
+ import { loadSnapshot } from "../spec/snapshot";
29
+ import { assertPinnedSnapshot, assertSnapshotVersions } from "../spec/pin";
30
+ import { generateTypes, propertyDocs, type PropertyDoc } from "../spec/parse";
31
+ import { BUILTIN_SPECS } from "../spec/builtins";
32
+ import type { OtelSpecSnapshot } from "../spec/fetch";
17
33
 
18
34
  export type { GenerateResult };
19
35
 
20
36
  /** The packaged types entry re-exports the compiled declarations, which carry every config type. */
21
- const TYPES_DTS = `// The otel lexicon's entity classes and config types are hand-written in
22
- // src/ and compiled to dist/index.d.ts; this entry re-exports them.
37
+ const TYPES_DTS = `// The otel lexicon's entity classes and config types are compiled to
38
+ // dist/index.d.ts (config types generated from upstream config.schema.yaml);
39
+ // this entry re-exports them.
23
40
  export * from "../index";
24
41
  `;
25
42
 
26
- export async function generate(opts?: { verbose?: boolean; force?: boolean }): Promise<GenerateResult> {
43
+ /** The upstream schema document behind each built-in, by class name, from the contrib index. */
44
+ export function builtinSchemaDocs(snapshot: OtelSpecSnapshot): Map<string, string> {
45
+ const out = new Map<string, string>();
46
+ const index = snapshot.distributions["otelcol-contrib"];
47
+ for (const b of BUILTIN_SPECS) {
48
+ const hit = index.find((c) => c.kind === b.kind && (c.type === b.type || c.deprecatedType === b.type));
49
+ if (hit?.schema && snapshot.schemas[hit.schema]) out.set(b.className, hit.schema);
50
+ }
51
+ return out;
52
+ }
53
+
54
+ export interface OtelGenerated extends GenerateResult {
55
+ /** Built-in class name → its property tree. */
56
+ propertiesJSON: string;
57
+ }
58
+
59
+ export async function generate(opts?: { verbose?: boolean; force?: boolean }): Promise<OtelGenerated> {
60
+ // The registry is derived from the classes, which import the generated
61
+ // types; types are erased at runtime, so this import does not need them.
62
+ const { BUILTIN_CATALOG, lexiconRegistry } = await import("../catalog");
63
+ const snapshot = loadSnapshot();
64
+ assertSnapshotVersions(snapshot.versions);
65
+ assertPinnedSnapshot(snapshot);
66
+
67
+ const roots = builtinSchemaDocs(snapshot);
68
+ const generated = generateTypes(snapshot.schemas, [...new Set(roots.values())]);
69
+ const aliases = [...roots]
70
+ .sort(([a], [b]) => a.localeCompare(b))
71
+ .map(([className, doc]) => `/** \`${className}\`'s config, from \`${doc}\`. */\nexport type ${className}SchemaConfig = ${generated.roots.get(doc)};\n`)
72
+ .join("\n");
73
+
74
+ const unresolved = [...generated.unresolved.keys()].sort();
75
+ const indexTS = [
76
+ "// Generated by `npm run generate` from src/spec/otel-schemas.snapshot.json. Do not edit.",
77
+ `// Versions: ${Object.entries(snapshot.versions).map(([k, v]) => `${k} ${v}`).join(", ")}.`,
78
+ ...(unresolved.length > 0
79
+ ? ["// Upstream publishes no schema for these $refs, so they are typed `unknown`:", ...unresolved.map((u) => `// ${u}`)]
80
+ : []),
81
+ "",
82
+ 'import type { Duration } from "../components/common";',
83
+ "",
84
+ generated.declarations,
85
+ "// ── Built-in config types ──────────────────────────────────────────",
86
+ "",
87
+ aliases,
88
+ ].join("\n");
89
+
90
+ const properties: Record<string, Record<string, PropertyDoc>> = {};
91
+ let propertyCount = 0;
92
+ for (const [className, doc] of [...roots].sort(([a], [b]) => a.localeCompare(b))) {
93
+ properties[className] = propertyDocs(snapshot.schemas, doc);
94
+ propertyCount += Object.keys(properties[className]).length;
95
+ }
96
+
27
97
  const registry = lexiconRegistry();
28
- const lexiconJSON = `${JSON.stringify(registry, null, 2)}\n`;
29
- const components = BUILTIN_CATALOG.filter((c) => c.type !== undefined).length;
30
98
  if (opts?.verbose) {
31
- console.error(`otel: ${components} built-in components, plus Pipeline and Service (${BUILTIN_CATALOG.length} entities)`);
99
+ const components = BUILTIN_CATALOG.filter((c) => c.type !== undefined).length;
100
+ console.error(
101
+ `otel: ${components} built-in components, ${roots.size} typed from upstream config.schema.yaml ` +
102
+ `(${snapshot.versions["otelcol-contrib"]}), plus Pipeline and Service (${BUILTIN_CATALOG.length} entities)`,
103
+ );
104
+ if (unresolved.length > 0) console.error(`otel: ${unresolved.length} upstream $refs have no schema and are typed unknown`);
32
105
  }
33
106
  return {
34
- lexiconJSON,
107
+ lexiconJSON: `${JSON.stringify(registry, null, 2)}\n`,
35
108
  typesDTS: TYPES_DTS,
36
- indexTS: "",
109
+ indexTS,
110
+ propertiesJSON: `${JSON.stringify(properties)}\n`,
37
111
  resources: BUILTIN_CATALOG.length,
38
- properties: 0,
112
+ properties: propertyCount,
39
113
  enums: 0,
40
114
  warnings: [],
41
115
  };
42
116
  }
43
117
 
44
- /** Write the registry to src/generated/lexicon-otel.json, for tools that read it from there. */
45
- export function writeGeneratedFiles(result: GenerateResult): void {
118
+ /** Write the generated files to src/generated/. */
119
+ export function writeGeneratedFiles(result: OtelGenerated): void {
46
120
  const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
47
121
  const dir = join(pkgDir, "src", "generated");
48
122
  mkdirSync(dir, { recursive: true });
49
123
  writeFileSync(join(dir, "lexicon-otel.json"), result.lexiconJSON);
124
+ writeFileSync(join(dir, "index.ts"), result.indexTS);
125
+ writeFileSync(join(dir, "properties.json"), result.propertiesJSON);
50
126
  }
@@ -176,8 +176,10 @@ export interface QueueSettings {
176
176
  }
177
177
 
178
178
  /** Problems in the `sending_queue` of an exporter's `exporterhelper` settings. */
179
- export function queueIssues(c: ExporterHelperSettings): string[] {
180
- const q = c.sending_queue;
179
+ export function queueIssues(c: { sending_queue?: unknown }): string[] {
180
+ // `blocking` is not an upstream field; reading it through the hand-written
181
+ // shape is how a config that sets it anyway gets told so.
182
+ const q = c.sending_queue as QueueSettings | null | undefined;
181
183
  if (!q) return [];
182
184
  const issues: string[] = [];
183
185
  if (q.blocking !== undefined) {
@@ -9,8 +9,9 @@
9
9
  */
10
10
 
11
11
  import { defineBuiltin } from "../define";
12
+ import type { Overlay, Require } from "./overlay";
13
+ import type { CountConnectorSchemaConfig, RoutingConnectorSchemaConfig, ServiceGraphConnectorSchemaConfig, SignalToMetricsConnectorSchemaConfig, SpanMetricsConnectorSchemaConfig, SumConnectorSchemaConfig } from "../generated/index";
12
14
  import type { ConnectorSignalPair } from "../model";
13
- import type { Duration } from "./common";
14
15
 
15
16
  const SAME_SIGNAL: ConnectorSignalPair[] = [
16
17
  { from: "traces", to: "traces" },
@@ -27,39 +28,7 @@ export interface SpanMetricsDimension {
27
28
  default?: string;
28
29
  }
29
30
 
30
- export interface SpanMetricsConnectorConfig {
31
- /** Extra dimensions on every metric, beyond service.name, span.name, span.kind and status.code. */
32
- dimensions?: SpanMetricsDimension[];
33
- /** Extra dimensions on the calls metric only. */
34
- calls_dimensions?: SpanMetricsDimension[];
35
- /** Default dimensions to leave out. */
36
- exclude_dimensions?: string[];
37
- resource_metrics_cache_size?: number;
38
- /** Resource attributes that key the metrics' resource, so a restart doesn't start a new series. */
39
- resource_metrics_key_attributes?: string[];
40
- aggregation_temporality?: "AGGREGATION_TEMPORALITY_CUMULATIVE" | "AGGREGATION_TEMPORALITY_DELTA";
41
- histogram?: {
42
- disable?: boolean;
43
- unit?: "ms" | "s";
44
- /** Explicit bucket boundaries. Set this or `exponential`, not both. */
45
- explicit?: { buckets?: Duration[] };
46
- exponential?: { max_size?: number };
47
- /** Extra dimensions on the duration histogram only. */
48
- dimensions?: SpanMetricsDimension[];
49
- };
50
- /** How often metrics are emitted (default 60s). */
51
- metrics_flush_interval?: Duration;
52
- /** Drop a series not updated for this long. */
53
- metrics_expiration?: Duration;
54
- metric_timestamp_cache_size?: number;
55
- /** Prefix for the emitted metric names (default `traces.span.metrics`). */
56
- namespace?: string;
57
- exemplars?: { enabled?: boolean; max_per_data_point?: number };
58
- /** Also count span events. Needs `dimensions` when enabled. */
59
- events?: { enabled?: boolean; dimensions?: SpanMetricsDimension[] };
60
- include_instrumentation_scope?: string[];
61
- aggregation_cardinality_limit?: number;
62
- }
31
+ export type SpanMetricsConnectorConfig = SpanMetricsConnectorSchemaConfig;
63
32
 
64
33
  /** Turns spans into request, error and duration (RED) metrics. */
65
34
  export const SpanMetricsConnector = defineBuiltin<SpanMetricsConnectorConfig, "connector", "spanmetrics">({
@@ -77,6 +46,8 @@ export const SpanMetricsConnector = defineBuiltin<SpanMetricsConnectorConfig, "c
77
46
  }
78
47
  const reserved = new Set(["service.name", "span.name", "span.kind", "status.code"]);
79
48
  for (const d of c.dimensions ?? []) {
49
+ // A dimension names one attribute or matches several by `glob`.
50
+ if (d.name === undefined) continue;
80
51
  if (reserved.has(d.name)) problems.push(`dimensions: "${d.name}" is already a default dimension`);
81
52
  reserved.add(d.name);
82
53
  }
@@ -86,21 +57,7 @@ export const SpanMetricsConnector = defineBuiltin<SpanMetricsConnectorConfig, "c
86
57
 
87
58
  // ── servicegraph ─────────────────────────────────────────────────────
88
59
 
89
- export interface ServiceGraphConnectorConfig {
90
- latency_histogram_buckets?: Duration[];
91
- /** Span attributes added as dimensions on the edge metrics. */
92
- dimensions?: string[];
93
- /** How long an unpaired client or server span waits for its other half. */
94
- store?: { ttl?: Duration; max_items?: number };
95
- cache_loop?: Duration;
96
- store_expiration_loop?: Duration;
97
- /** Attributes that name the peer when only one side of a call is instrumented. */
98
- virtual_node_peer_attributes?: string[];
99
- virtual_node_extra_label?: boolean;
100
- metrics_flush_interval?: Duration;
101
- /** Attributes that name the database for a database call edge. */
102
- database_name_attributes?: string[];
103
- }
60
+ export type ServiceGraphConnectorConfig = ServiceGraphConnectorSchemaConfig;
104
61
 
105
62
  /** Builds service-to-service edge metrics from paired client and server spans. */
106
63
  export const ServiceGraphConnector = defineBuiltin<ServiceGraphConnectorConfig, "connector", "servicegraph">({
@@ -113,7 +70,12 @@ export const ServiceGraphConnector = defineBuiltin<ServiceGraphConnectorConfig,
113
70
  // ── routing ──────────────────────────────────────────────────────────
114
71
 
115
72
  export interface RoutingTableItem {
116
- /** The OTTL context the condition runs in. `request` routes on request metadata such as headers. */
73
+ /**
74
+ * The OTTL context the condition runs in. `request` routes on request
75
+ * metadata such as headers; it is deprecated since collector-contrib
76
+ * v0.156.0 for `otelcol.client.metadata[...]` and `otelcol.grpc.metadata[...]`
77
+ * paths, which work in every context.
78
+ */
117
79
  context?: "resource" | "span" | "metric" | "datapoint" | "log" | "request";
118
80
  /** An OTTL `route() where ...` statement. Set this or `condition`, not both. */
119
81
  statement?: string;
@@ -123,12 +85,8 @@ export interface RoutingTableItem {
123
85
  pipelines: string[];
124
86
  }
125
87
 
126
- export interface RoutingConnectorConfig {
127
- table: RoutingTableItem[];
128
- /** Pipelines for data no route matches. */
129
- default_pipelines?: string[];
130
- error_mode?: "ignore" | "silent" | "propagate";
131
- }
88
+ /** The connector refuses to start with no routing table. */
89
+ export type RoutingConnectorConfig = Require<RoutingConnectorSchemaConfig, "table">;
132
90
 
133
91
  /** Sends each item to the pipelines whose route matches it. */
134
92
  export const RoutingConnector = defineBuiltin<RoutingConnectorConfig, "connector", "routing">({
@@ -172,14 +130,7 @@ export interface CountMetricInfo {
172
130
  }
173
131
 
174
132
  /** Each key is the name of a metric to emit. */
175
- export interface CountConnectorConfig {
176
- spans?: Record<string, CountMetricInfo>;
177
- spanevents?: Record<string, CountMetricInfo>;
178
- metrics?: Record<string, CountMetricInfo>;
179
- datapoints?: Record<string, CountMetricInfo>;
180
- logs?: Record<string, CountMetricInfo>;
181
- profiles?: Record<string, CountMetricInfo>;
182
- }
133
+ export type CountConnectorConfig = CountConnectorSchemaConfig;
183
134
 
184
135
  /** Counts spans, span events, metrics, data points or log records, as metrics. */
185
136
  export const CountConnector = defineBuiltin<CountConnectorConfig, "connector", "count">({
@@ -229,13 +180,7 @@ export interface SumMetricInfo {
229
180
  * while an exporter that needs cumulative input wants a `deltatocumulative`
230
181
  * processor in front of it.
231
182
  */
232
- export interface SumConnectorConfig {
233
- spans?: Record<string, SumMetricInfo>;
234
- spanevents?: Record<string, SumMetricInfo>;
235
- metrics?: Record<string, SumMetricInfo>;
236
- datapoints?: Record<string, SumMetricInfo>;
237
- logs?: Record<string, SumMetricInfo>;
238
- }
183
+ export type SumConnectorConfig = SumConnectorSchemaConfig;
239
184
 
240
185
  /** Sums a numeric attribute of spans, span events, data points or log records, as metrics. */
241
186
  export const SumConnector = defineBuiltin<SumConnectorConfig, "connector", "sum">({
@@ -383,12 +328,20 @@ export type SignalToMetricsMetric = SignalToMetricsMetricBase &
383
328
  * exporter that needs cumulative input (Prometheus remote write) wants a
384
329
  * `deltatocumulative` processor in front of it.
385
330
  */
386
- export interface SignalToMetricsConnectorConfig {
387
- spans?: SignalToMetricsMetric[];
388
- datapoints?: SignalToMetricsMetric[];
389
- logs?: SignalToMetricsMetric[];
390
- profiles?: SignalToMetricsMetric[];
391
- }
331
+ /**
332
+ * Upstream writes a metric as one struct with `sum`, `gauge`, `histogram` and
333
+ * `exponential_histogram` all optional; the connector takes exactly one, so
334
+ * here each entry is a union with one metric type set.
335
+ */
336
+ export type SignalToMetricsConnectorConfig = Overlay<
337
+ SignalToMetricsConnectorSchemaConfig,
338
+ {
339
+ spans?: SignalToMetricsMetric[];
340
+ datapoints?: SignalToMetricsMetric[];
341
+ logs?: SignalToMetricsMetric[];
342
+ profiles?: SignalToMetricsMetric[];
343
+ }
344
+ >;
392
345
 
393
346
  /** One entry of a `signaltometrics` config, with where it sits. */
394
347
  export interface SignalToMetricsEntry {