@intentius/chant-lexicon-otel 0.97.0 → 0.98.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 +5 -0
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/import/generator.d.ts +39 -0
- package/dist/import/generator.d.ts.map +1 -0
- package/dist/import/parser.d.ts +61 -0
- package/dist/import/parser.d.ts.map +1 -0
- package/dist/integrity.json +4 -4
- package/dist/lint/rules/literal-credential.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/literal-credential.ts +46 -17
- package/dist/skills/chant-otel.md +11 -0
- package/package.json +2 -2
- package/src/codegen/docs.ts +2 -1
- package/src/import/generated-types.e2e.test.ts +75 -0
- package/src/import/generator.test.ts +165 -0
- package/src/import/generator.ts +509 -0
- package/src/import/parser.test.ts +174 -0
- package/src/import/parser.ts +236 -0
- package/src/import/roundtrip.test.ts +365 -0
- package/src/import/testdata/agent-observability-agent.yaml +54 -0
- package/src/import/testdata/agent-observability-gateway.yaml +128 -0
- package/src/import/testdata/fixtures.ts +274 -0
- package/src/import/testdata/gateway.yaml +153 -0
- package/src/import/testdata/upstream/couchbase.yaml +150 -0
- package/src/import/testdata/upstream/fault-tolerant-logs.yaml +27 -0
- package/src/import/testdata/upstream/kubernetes-filelog.yaml +86 -0
- package/src/import/testdata/upstream/loadbalancing-agent.yaml +38 -0
- package/src/import/testdata/upstream/loadbalancing-backend.yaml +27 -0
- package/src/import/testdata/upstream/logline-filter-in.yaml +20 -0
- package/src/import/testdata/upstream/logline-filter-out.yaml +20 -0
- package/src/import/testdata/upstream/secure-tracing.yaml +26 -0
- package/src/import/testdata/upstream/servicegraph-nop.yaml +28 -0
- package/src/lint/rules/literal-credential.ts +46 -17
- package/src/lint/rules/rules.test.ts +16 -0
- package/src/plugin.ts +10 -0
- package/src/skills/chant-otel.md +11 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# The otel-gateway-config ConfigMap's config.yaml from examples/agent-observability,
|
|
2
|
+
# as that example builds it. Regenerate from examples/agent-observability/test/built.ts (gatewayConfigYaml).
|
|
3
|
+
|
|
4
|
+
# chant: semconv gen_ai github.com/open-telemetry/semantic-conventions@v1.41.1 (transform/genai_content, redaction/genai_content, filter/genai_spans, spanmetrics/genai, sum/genai_tokens)
|
|
5
|
+
|
|
6
|
+
receivers:
|
|
7
|
+
otlp:
|
|
8
|
+
protocols:
|
|
9
|
+
grpc:
|
|
10
|
+
endpoint: 0.0.0.0:4317
|
|
11
|
+
|
|
12
|
+
processors:
|
|
13
|
+
memory_limiter:
|
|
14
|
+
check_interval: 1s
|
|
15
|
+
limit_percentage: 80
|
|
16
|
+
spike_limit_percentage: 20
|
|
17
|
+
transform/genai_content:
|
|
18
|
+
error_mode: ignore
|
|
19
|
+
trace_statements:
|
|
20
|
+
- context: span
|
|
21
|
+
statements: ["delete_key(span.attributes, \"gen_ai.system_instructions\")", "delete_key(span.attributes, \"gen_ai.input.messages\")", "delete_key(span.attributes, \"gen_ai.output.messages\")", "delete_key(span.attributes, \"gen_ai.tool.call.arguments\")", "delete_key(span.attributes, \"gen_ai.tool.call.result\")", "delete_key(span.attributes, \"gen_ai.retrieval.query.text\")", "delete_key(span.attributes, \"gen_ai.retrieval.documents\")", "delete_key(span.attributes, \"gen_ai.prompt\")", "delete_key(span.attributes, \"gen_ai.completion\")", "delete_matching_keys(span.attributes, \"^gen_ai\\\\.(prompt|completion)\\\\.[0-9]+\\\\..+$\")"]
|
|
22
|
+
- context: spanevent
|
|
23
|
+
statements: ["delete_key(spanevent.attributes, \"gen_ai.system_instructions\")", "delete_key(spanevent.attributes, \"gen_ai.input.messages\")", "delete_key(spanevent.attributes, \"gen_ai.output.messages\")", "delete_key(spanevent.attributes, \"gen_ai.tool.call.arguments\")", "delete_key(spanevent.attributes, \"gen_ai.tool.call.result\")", "delete_key(spanevent.attributes, \"gen_ai.retrieval.query.text\")", "delete_key(spanevent.attributes, \"gen_ai.retrieval.documents\")", "delete_key(spanevent.attributes, \"gen_ai.prompt\")", "delete_key(spanevent.attributes, \"gen_ai.completion\")", "delete_matching_keys(spanevent.attributes, \"^gen_ai\\\\.(prompt|completion)\\\\.[0-9]+\\\\..+$\")"]
|
|
24
|
+
log_statements:
|
|
25
|
+
- context: log
|
|
26
|
+
statements: ["delete_key(log.attributes, \"gen_ai.system_instructions\")", "delete_key(log.attributes, \"gen_ai.input.messages\")", "delete_key(log.attributes, \"gen_ai.output.messages\")", "delete_key(log.attributes, \"gen_ai.tool.call.arguments\")", "delete_key(log.attributes, \"gen_ai.tool.call.result\")", "delete_key(log.attributes, \"gen_ai.retrieval.query.text\")", "delete_key(log.attributes, \"gen_ai.retrieval.documents\")", "delete_key(log.attributes, \"gen_ai.prompt\")", "delete_key(log.attributes, \"gen_ai.completion\")", "delete_matching_keys(log.attributes, \"^gen_ai\\\\.(prompt|completion)\\\\.[0-9]+\\\\..+$\")", "delete_matching_keys(log.body, \"^(content|message|tool_calls)$\") where IsMap(log.body) and (IsMatch(log.event_name, \"^(gen_ai\\\\.system\\\\.message|gen_ai\\\\.user\\\\.message|gen_ai\\\\.assistant\\\\.message|gen_ai\\\\.tool\\\\.message|gen_ai\\\\.choice)$\") or IsMatch(log.attributes[\"event.name\"], \"^(gen_ai\\\\.system\\\\.message|gen_ai\\\\.user\\\\.message|gen_ai\\\\.assistant\\\\.message|gen_ai\\\\.tool\\\\.message|gen_ai\\\\.choice)$\"))", "set(log.body, \"\") where IsString(log.body) and (IsMatch(log.event_name, \"^(gen_ai\\\\.system\\\\.message|gen_ai\\\\.user\\\\.message|gen_ai\\\\.assistant\\\\.message|gen_ai\\\\.tool\\\\.message|gen_ai\\\\.choice)$\") or IsMatch(log.attributes[\"event.name\"], \"^(gen_ai\\\\.system\\\\.message|gen_ai\\\\.user\\\\.message|gen_ai\\\\.assistant\\\\.message|gen_ai\\\\.tool\\\\.message|gen_ai\\\\.choice)$\"))"]
|
|
27
|
+
redaction/genai_content:
|
|
28
|
+
allow_all_keys: true
|
|
29
|
+
blocked_key_patterns: [^(gen_ai\.system_instructions|gen_ai\.input\.messages|gen_ai\.output\.messages|gen_ai\.tool\.call\.arguments|gen_ai\.tool\.call\.result|gen_ai\.retrieval\.query\.text|gen_ai\.retrieval\.documents|gen_ai\.prompt|gen_ai\.completion)$, "^gen_ai\\.(prompt|completion)\\.[0-9]+\\..+$"]
|
|
30
|
+
tail_sampling:
|
|
31
|
+
decision_wait: 5s
|
|
32
|
+
num_traces: 50000
|
|
33
|
+
policies:
|
|
34
|
+
- name: errors
|
|
35
|
+
type: status_code
|
|
36
|
+
status_code:
|
|
37
|
+
status_codes: [ERROR]
|
|
38
|
+
- name: slow
|
|
39
|
+
type: latency
|
|
40
|
+
latency:
|
|
41
|
+
threshold_ms: 2000
|
|
42
|
+
- name: baseline
|
|
43
|
+
type: probabilistic
|
|
44
|
+
probabilistic:
|
|
45
|
+
sampling_percentage: 10
|
|
46
|
+
batch:
|
|
47
|
+
timeout: 5s
|
|
48
|
+
filter/genai_spans:
|
|
49
|
+
error_mode: ignore
|
|
50
|
+
traces:
|
|
51
|
+
span: ["attributes[\"gen_ai.operation.name\"] == nil"]
|
|
52
|
+
|
|
53
|
+
exporters:
|
|
54
|
+
otlp/tempo:
|
|
55
|
+
endpoint: tempo:4317
|
|
56
|
+
tls:
|
|
57
|
+
insecure: true
|
|
58
|
+
prometheus:
|
|
59
|
+
endpoint: 0.0.0.0:8889
|
|
60
|
+
metric_expiration: 10m
|
|
61
|
+
otlphttp/loki:
|
|
62
|
+
endpoint: http://loki:3100/otlp
|
|
63
|
+
|
|
64
|
+
connectors:
|
|
65
|
+
spanmetrics:
|
|
66
|
+
histogram:
|
|
67
|
+
unit: ms
|
|
68
|
+
explicit:
|
|
69
|
+
buckets: [50ms, 100ms, 250ms, 500ms, 1s, 2s, 5s, 10s]
|
|
70
|
+
metrics_flush_interval: 15s
|
|
71
|
+
forward/genai: {}
|
|
72
|
+
forward/sampled: {}
|
|
73
|
+
spanmetrics/genai:
|
|
74
|
+
namespace: genai
|
|
75
|
+
dimensions:
|
|
76
|
+
- name: gen_ai.operation.name
|
|
77
|
+
- name: gen_ai.request.model
|
|
78
|
+
- name: gen_ai.tool.name
|
|
79
|
+
- name: error.type
|
|
80
|
+
histogram:
|
|
81
|
+
unit: s
|
|
82
|
+
explicit:
|
|
83
|
+
buckets: [100ms, 250ms, 500ms, 1s, 2s, 5s, 10s, 20s, 40s, 80s]
|
|
84
|
+
metrics_flush_interval: 15s
|
|
85
|
+
sum/genai_tokens:
|
|
86
|
+
spans:
|
|
87
|
+
genai.tokens.input:
|
|
88
|
+
source_attribute: gen_ai.usage.input_tokens
|
|
89
|
+
description: Input tokens used by GenAI operations
|
|
90
|
+
conditions: ["attributes[\"gen_ai.usage.input_tokens\"] != nil and not (kind == SPAN_KIND_INTERNAL and (attributes[\"gen_ai.operation.name\"] == \"invoke_agent\" or attributes[\"gen_ai.operation.name\"] == \"invoke_workflow\"))"]
|
|
91
|
+
attributes:
|
|
92
|
+
- key: gen_ai.request.model
|
|
93
|
+
default_value: unknown
|
|
94
|
+
genai.tokens.output:
|
|
95
|
+
source_attribute: gen_ai.usage.output_tokens
|
|
96
|
+
description: Output tokens used by GenAI operations
|
|
97
|
+
conditions: ["attributes[\"gen_ai.usage.output_tokens\"] != nil and not (kind == SPAN_KIND_INTERNAL and (attributes[\"gen_ai.operation.name\"] == \"invoke_agent\" or attributes[\"gen_ai.operation.name\"] == \"invoke_workflow\"))"]
|
|
98
|
+
attributes:
|
|
99
|
+
- key: gen_ai.request.model
|
|
100
|
+
default_value: unknown
|
|
101
|
+
|
|
102
|
+
extensions:
|
|
103
|
+
health_check:
|
|
104
|
+
endpoint: 0.0.0.0:13133
|
|
105
|
+
|
|
106
|
+
service:
|
|
107
|
+
extensions: [health_check]
|
|
108
|
+
pipelines:
|
|
109
|
+
traces:
|
|
110
|
+
receivers: [otlp]
|
|
111
|
+
processors: [memory_limiter, transform/genai_content, redaction/genai_content]
|
|
112
|
+
exporters: [spanmetrics, forward/genai, forward/sampled]
|
|
113
|
+
traces/sampled:
|
|
114
|
+
receivers: [forward/sampled]
|
|
115
|
+
processors: [tail_sampling, batch]
|
|
116
|
+
exporters: [otlp/tempo]
|
|
117
|
+
traces/genai:
|
|
118
|
+
receivers: [forward/genai]
|
|
119
|
+
processors: [filter/genai_spans]
|
|
120
|
+
exporters: [spanmetrics/genai, sum/genai_tokens]
|
|
121
|
+
metrics:
|
|
122
|
+
receivers: [otlp, spanmetrics, spanmetrics/genai, sum/genai_tokens]
|
|
123
|
+
processors: [memory_limiter, batch]
|
|
124
|
+
exporters: [prometheus]
|
|
125
|
+
logs:
|
|
126
|
+
receivers: [otlp]
|
|
127
|
+
processors: [memory_limiter, transform/genai_content, redaction/genai_content, batch]
|
|
128
|
+
exporters: [otlphttp/loki]
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixtures shared by the import round-trip tests (roundtrip.test.ts) and the
|
|
3
|
+
* type-check of the generated source (generated-types.e2e.test.ts).
|
|
4
|
+
*/
|
|
5
|
+
import { readdirSync, readFileSync, statSync } from "fs";
|
|
6
|
+
import { join, resolve } from "path";
|
|
7
|
+
import { build } from "@intentius/chant/build";
|
|
8
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
9
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
10
|
+
import { otelSerializer } from "../../serializer";
|
|
11
|
+
import { Pipeline, Service } from "../../pipeline";
|
|
12
|
+
import * as c from "../../components";
|
|
13
|
+
|
|
14
|
+
export const pkgDir = resolve(import.meta.dirname, "../../..");
|
|
15
|
+
export const repoRoot = resolve(pkgDir, "../..");
|
|
16
|
+
export const read = (...p: string[]) => readFileSync(join(import.meta.dirname, ...p), "utf-8");
|
|
17
|
+
|
|
18
|
+
export function primary(out: string | SerializerResult | undefined): string {
|
|
19
|
+
if (out === undefined) return "";
|
|
20
|
+
return typeof out === "string" ? out : out.primary;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Each otel example's `chant build` output. */
|
|
24
|
+
export async function exampleOutputs(): Promise<Array<[string, string]>> {
|
|
25
|
+
const examplesDir = join(pkgDir, "examples");
|
|
26
|
+
const out: Array<[string, string]> = [];
|
|
27
|
+
for (const name of readdirSync(examplesDir).sort()) {
|
|
28
|
+
const srcDir = join(examplesDir, name, "src");
|
|
29
|
+
try {
|
|
30
|
+
if (!statSync(srcDir).isDirectory()) continue;
|
|
31
|
+
} catch {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const result = await build(srcDir, [otelSerializer]);
|
|
35
|
+
if (result.errors.length > 0) throw new Error(`${name}: ${result.errors.map(String).join("; ")}`);
|
|
36
|
+
out.push([name, primary(result.outputs.get("otel"))]);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const UPSTREAM_BUILTIN_ONLY = [
|
|
42
|
+
"kubernetes-filelog.yaml",
|
|
43
|
+
"logline-filter-in.yaml",
|
|
44
|
+
"logline-filter-out.yaml",
|
|
45
|
+
"secure-tracing.yaml",
|
|
46
|
+
"loadbalancing-backend.yaml",
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/** One instance of every built-in, each using its typed fields, wired into pipelines the connectors accept. */
|
|
50
|
+
export function everyBuiltin(): Declarable[] {
|
|
51
|
+
const otlp = new c.OtlpReceiver({
|
|
52
|
+
protocols: {
|
|
53
|
+
grpc: { endpoint: "0.0.0.0:4317", max_recv_msg_size_mib: 16, keepalive: { server_parameters: { time: "30s" } } },
|
|
54
|
+
http: { endpoint: "0.0.0.0:4318", cors: { allowed_origins: ["https://*.example.com"] }, traces_url_path: "/v1/traces" },
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const prometheusIn = new c.PrometheusReceiver({
|
|
58
|
+
name: "self",
|
|
59
|
+
config: {
|
|
60
|
+
global: { scrape_interval: "30s" },
|
|
61
|
+
scrape_configs: [{ job_name: "collector", scrape_interval: "10s", static_configs: [{ targets: ["localhost:8888"], labels: { tier: "gw" } }] }],
|
|
62
|
+
},
|
|
63
|
+
trim_metric_suffixes: true,
|
|
64
|
+
});
|
|
65
|
+
const hostmetrics = new c.HostMetricsReceiver({
|
|
66
|
+
collection_interval: "30s",
|
|
67
|
+
root_path: "/hostfs",
|
|
68
|
+
scrapers: { cpu: {}, memory: {}, filesystem: { exclude_mount_points: { match_type: "regexp", mount_points: ["/dev/.*"] } } },
|
|
69
|
+
});
|
|
70
|
+
const filelog = new c.FileLogReceiver({
|
|
71
|
+
include: ["/var/log/pods/*/*/*.log"],
|
|
72
|
+
exclude: ["/var/log/pods/*/otel-collector/*.log"],
|
|
73
|
+
start_at: "end",
|
|
74
|
+
include_file_path: true,
|
|
75
|
+
storage: "file_storage",
|
|
76
|
+
operators: [{ type: "container", id: "container-parser" }],
|
|
77
|
+
retry_on_failure: { enabled: true, initial_interval: "1s" },
|
|
78
|
+
});
|
|
79
|
+
const cluster = new c.K8sClusterReceiver({
|
|
80
|
+
auth_type: "serviceAccount",
|
|
81
|
+
collection_interval: "30s",
|
|
82
|
+
node_conditions_to_report: ["Ready", "MemoryPressure"],
|
|
83
|
+
allocatable_types_to_report: ["cpu", "memory"],
|
|
84
|
+
metrics: { "k8s.pod.phase": { enabled: false } },
|
|
85
|
+
});
|
|
86
|
+
const kubelet = new c.KubeletStatsReceiver({
|
|
87
|
+
auth_type: "serviceAccount",
|
|
88
|
+
endpoint: "https://${env:K8S_NODE_NAME}:10250",
|
|
89
|
+
insecure_skip_verify: true,
|
|
90
|
+
metric_groups: ["node", "pod", "container"],
|
|
91
|
+
extra_metadata_labels: ["container.id"],
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const batch = new c.BatchProcessor({ timeout: "5s", send_batch_size: 1024, send_batch_max_size: 2048, metadata_keys: ["tenant"] });
|
|
95
|
+
const memoryLimiter = new c.MemoryLimiterProcessor({ check_interval: "1s", limit_mib: 1024, spike_limit_mib: 256 });
|
|
96
|
+
const resource = new c.ResourceProcessor({
|
|
97
|
+
attributes: [
|
|
98
|
+
{ key: "deployment.environment", value: "${env:DEPLOY_ENV}", action: "upsert" },
|
|
99
|
+
{ key: "host.id", action: "delete" },
|
|
100
|
+
],
|
|
101
|
+
});
|
|
102
|
+
const attributes = new c.AttributesProcessor({
|
|
103
|
+
name: "scrub",
|
|
104
|
+
actions: [
|
|
105
|
+
{ key: "user.email", action: "hash" },
|
|
106
|
+
{ key: "http.status_code", action: "convert", converted_type: "int" },
|
|
107
|
+
],
|
|
108
|
+
include: { match_type: "strict", services: ["checkout"] },
|
|
109
|
+
});
|
|
110
|
+
const k8sattributes = new c.K8sAttributesProcessor({
|
|
111
|
+
auth_type: "serviceAccount",
|
|
112
|
+
passthrough: false,
|
|
113
|
+
filter: { node_from_env_var: "K8S_NODE_NAME" },
|
|
114
|
+
extract: { metadata: ["k8s.pod.name", "k8s.namespace.name"], labels: [{ tag_name: "app", key: "app.kubernetes.io/name", from: "pod" }] },
|
|
115
|
+
pod_association: [{ sources: [{ from: "resource_attribute", name: "k8s.pod.ip" }] }, { sources: [{ from: "connection" }] }],
|
|
116
|
+
});
|
|
117
|
+
const resourcedetection = new c.ResourceDetectionProcessor({
|
|
118
|
+
detectors: ["env", "system", "gcp"],
|
|
119
|
+
timeout: "2s",
|
|
120
|
+
override: false,
|
|
121
|
+
system: { hostname_sources: ["os"] },
|
|
122
|
+
});
|
|
123
|
+
const filter = new c.FilterProcessor({
|
|
124
|
+
name: "health",
|
|
125
|
+
error_mode: "ignore",
|
|
126
|
+
traces: { span: ['attributes["http.route"] == "/healthz"'] },
|
|
127
|
+
metrics: { datapoint: ['metric.name == "up" and value_int == 1'] },
|
|
128
|
+
logs: { log_record: ["severity_number < SEVERITY_NUMBER_INFO"] },
|
|
129
|
+
});
|
|
130
|
+
const transform = new c.TransformProcessor({
|
|
131
|
+
error_mode: "ignore",
|
|
132
|
+
trace_statements: [{ context: "span", conditions: ["kind == SPAN_KIND_SERVER"], statements: ['set(attributes["tier"], "edge")'] }],
|
|
133
|
+
metric_statements: ['delete_key(datapoint.attributes, "pod_ip")'],
|
|
134
|
+
log_statements: [{ context: "log", statements: ['set(severity_text, "WARN") where severity_number == 13'] }],
|
|
135
|
+
});
|
|
136
|
+
const redaction = new c.RedactionProcessor({
|
|
137
|
+
allow_all_keys: true,
|
|
138
|
+
blocked_key_patterns: ["^gen_ai\\.(prompt|completion)"],
|
|
139
|
+
blocked_values: ["4[0-9]{12}(?:[0-9]{3})?"],
|
|
140
|
+
hash_function: "sha3",
|
|
141
|
+
summary: "silent",
|
|
142
|
+
});
|
|
143
|
+
const tailSampling = new c.TailSamplingProcessor({
|
|
144
|
+
decision_wait: "10s",
|
|
145
|
+
num_traces: 50000,
|
|
146
|
+
sample_on_first_match: true,
|
|
147
|
+
policies: [
|
|
148
|
+
{ name: "errors", type: "status_code", status_code: { status_codes: ["ERROR"] } },
|
|
149
|
+
{ name: "slow", type: "latency", latency: { threshold_ms: 1000, upper_threshold_ms: 60000 } },
|
|
150
|
+
{ name: "vip", type: "boolean_attribute", boolean_attribute: { key: "vip", value: true } },
|
|
151
|
+
{ name: "sized", type: "span_count", span_count: { min_spans: 2, max_spans: 500 } },
|
|
152
|
+
{ name: "both", type: "and", and: { and_sub_policy: [{ name: "a", type: "rate_limiting", rate_limiting: { spans_per_second: 100 } }, { name: "b", type: "always_sample" }] } },
|
|
153
|
+
{ name: "rest", type: "probabilistic", probabilistic: { sampling_percentage: 5, hash_salt: "s" } },
|
|
154
|
+
],
|
|
155
|
+
});
|
|
156
|
+
const probabilistic = new c.ProbabilisticSamplerProcessor({ sampling_percentage: 12.5, mode: "proportional", sampling_precision: 4 });
|
|
157
|
+
|
|
158
|
+
const otlpOut = new c.OtlpExporter({
|
|
159
|
+
name: "backend",
|
|
160
|
+
endpoint: "backend:4317",
|
|
161
|
+
compression: "zstd",
|
|
162
|
+
headers: { "x-api-key": "${env:BACKEND_KEY}" },
|
|
163
|
+
tls: { insecure: false, ca_file: "/etc/ca.pem", min_version: "1.3" },
|
|
164
|
+
balancer_name: "round_robin",
|
|
165
|
+
timeout: "10s",
|
|
166
|
+
retry_on_failure: { enabled: true, max_elapsed_time: "2m" },
|
|
167
|
+
sending_queue: { enabled: true, num_consumers: 4, queue_size: 2000, sizer: "requests" },
|
|
168
|
+
});
|
|
169
|
+
const otlphttp = new c.OtlpHttpExporter({ endpoint: "https://otlp.example.com", encoding: "json", logs_endpoint: "https://logs.example.com/v1/logs" });
|
|
170
|
+
const debug = new c.DebugExporter({ verbosity: "detailed", sampling_initial: 5, sampling_thereafter: 200 });
|
|
171
|
+
const prometheusOut = new c.PrometheusExporter({
|
|
172
|
+
endpoint: "0.0.0.0:8889",
|
|
173
|
+
namespace: "otel",
|
|
174
|
+
const_labels: { cluster: "prod" },
|
|
175
|
+
metric_expiration: "5m",
|
|
176
|
+
resource_to_telemetry_conversion: { enabled: true },
|
|
177
|
+
});
|
|
178
|
+
const googlecloud = new c.GoogleCloudExporter({
|
|
179
|
+
project: "my-project",
|
|
180
|
+
metric: { prefix: "custom.googleapis.com", resource_filters: [{ prefix: "k8s." }] },
|
|
181
|
+
trace: { attribute_mappings: [{ key: "http.route", replacement: "/http/route" }] },
|
|
182
|
+
log: { default_log_name: "otel" },
|
|
183
|
+
});
|
|
184
|
+
const loadbalancing = new c.LoadBalancingExporter({
|
|
185
|
+
routing_key: "service",
|
|
186
|
+
protocol: { otlp: { timeout: "1s", tls: { insecure: true } } },
|
|
187
|
+
resolver: { k8s: { service: "sampling.observability", ports: [4317], return_hostnames: true } },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const spanmetrics = new c.SpanMetricsConnector({
|
|
191
|
+
namespace: "span.metrics",
|
|
192
|
+
dimensions: [{ name: "http.route" }, { name: "env", default: "dev" }],
|
|
193
|
+
histogram: { unit: "s", exponential: { max_size: 160 } },
|
|
194
|
+
exemplars: { enabled: true, max_per_data_point: 5 },
|
|
195
|
+
metrics_flush_interval: "15s",
|
|
196
|
+
aggregation_temporality: "AGGREGATION_TEMPORALITY_DELTA",
|
|
197
|
+
});
|
|
198
|
+
const servicegraph = new c.ServiceGraphConnector({
|
|
199
|
+
latency_histogram_buckets: ["10ms", "100ms", "1s"],
|
|
200
|
+
dimensions: ["k8s.cluster.name"],
|
|
201
|
+
store: { ttl: "2s", max_items: 1000 },
|
|
202
|
+
virtual_node_peer_attributes: ["db.name"],
|
|
203
|
+
});
|
|
204
|
+
const routing = new c.RoutingConnector({
|
|
205
|
+
table: [{ context: "resource", condition: 'attributes["tenant"] == "acme"', pipelines: ["traces/acme"] }],
|
|
206
|
+
default_pipelines: ["traces/rest"],
|
|
207
|
+
error_mode: "ignore",
|
|
208
|
+
});
|
|
209
|
+
const forward = new c.ForwardConnector({});
|
|
210
|
+
const count = new c.CountConnector({
|
|
211
|
+
spans: { "span.count": { description: "Spans by service", attributes: [{ key: "service.name", default_value: "unknown" }] } },
|
|
212
|
+
});
|
|
213
|
+
const sum = new c.SumConnector({
|
|
214
|
+
spans: { "span.bytes": { source_attribute: "bytes", description: "Bytes by route", conditions: ['attributes["bytes"] != nil'] } },
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const health = new c.HealthCheckExtension({ endpoint: "0.0.0.0:13133", path: "/health", response_body: { healthy: "ok" } });
|
|
218
|
+
const pprof = new c.PprofExtension({ endpoint: "localhost:1777", block_profile_fraction: 3 });
|
|
219
|
+
const zpages = new c.ZPagesExtension({ endpoint: "localhost:55679" });
|
|
220
|
+
|
|
221
|
+
return [
|
|
222
|
+
otlp,
|
|
223
|
+
prometheusIn,
|
|
224
|
+
hostmetrics,
|
|
225
|
+
filelog,
|
|
226
|
+
cluster,
|
|
227
|
+
kubelet,
|
|
228
|
+
batch,
|
|
229
|
+
memoryLimiter,
|
|
230
|
+
resource,
|
|
231
|
+
attributes,
|
|
232
|
+
k8sattributes,
|
|
233
|
+
resourcedetection,
|
|
234
|
+
filter,
|
|
235
|
+
transform,
|
|
236
|
+
redaction,
|
|
237
|
+
tailSampling,
|
|
238
|
+
probabilistic,
|
|
239
|
+
otlpOut,
|
|
240
|
+
otlphttp,
|
|
241
|
+
debug,
|
|
242
|
+
prometheusOut,
|
|
243
|
+
googlecloud,
|
|
244
|
+
loadbalancing,
|
|
245
|
+
spanmetrics,
|
|
246
|
+
servicegraph,
|
|
247
|
+
routing,
|
|
248
|
+
forward,
|
|
249
|
+
count,
|
|
250
|
+
sum,
|
|
251
|
+
health,
|
|
252
|
+
pprof,
|
|
253
|
+
zpages,
|
|
254
|
+
new Pipeline({
|
|
255
|
+
signal: "traces",
|
|
256
|
+
receivers: [otlp],
|
|
257
|
+
processors: [memoryLimiter, k8sattributes, resourcedetection, resource, attributes, filter, transform, redaction, probabilistic],
|
|
258
|
+
exporters: [spanmetrics, servicegraph, count, sum, forward, routing, loadbalancing],
|
|
259
|
+
}),
|
|
260
|
+
new Pipeline({ signal: "traces", name: "acme", receivers: [routing], processors: [tailSampling, batch], exporters: [otlpOut] }),
|
|
261
|
+
new Pipeline({ signal: "traces", name: "rest", receivers: [routing, forward], processors: [batch], exporters: [googlecloud, debug] }),
|
|
262
|
+
new Pipeline({
|
|
263
|
+
signal: "metrics",
|
|
264
|
+
receivers: [otlp, prometheusIn, hostmetrics, cluster, kubelet, spanmetrics, servicegraph, count, sum],
|
|
265
|
+
processors: [memoryLimiter, filter, batch],
|
|
266
|
+
exporters: [prometheusOut, otlphttp],
|
|
267
|
+
}),
|
|
268
|
+
new Pipeline({ signal: "logs", receivers: [otlp, filelog], processors: [memoryLimiter, redaction, batch], exporters: [otlphttp, debug] }),
|
|
269
|
+
new Service({
|
|
270
|
+
extensions: [health, zpages, pprof],
|
|
271
|
+
telemetry: { logs: { level: "warn", encoding: "json" }, metrics: { level: "normal" }, resource: { "service.name": "gw" } },
|
|
272
|
+
}),
|
|
273
|
+
];
|
|
274
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# A two-tier trace gateway in one file. The front tier spreads spans across
|
|
2
|
+
# the sampling tier by trace id with the loadbalancing exporter; the sampling
|
|
3
|
+
# tier keeps whole traces with tail_sampling and derives RED metrics from
|
|
4
|
+
# every span with spanmetrics before sampling drops any.
|
|
5
|
+
|
|
6
|
+
receivers:
|
|
7
|
+
otlp:
|
|
8
|
+
protocols:
|
|
9
|
+
grpc:
|
|
10
|
+
endpoint: 0.0.0.0:4317
|
|
11
|
+
http:
|
|
12
|
+
endpoint: 0.0.0.0:4318
|
|
13
|
+
otlp/sampling:
|
|
14
|
+
protocols:
|
|
15
|
+
grpc:
|
|
16
|
+
endpoint: 0.0.0.0:14317
|
|
17
|
+
|
|
18
|
+
processors:
|
|
19
|
+
memory_limiter:
|
|
20
|
+
check_interval: 1s
|
|
21
|
+
limit_percentage: 75
|
|
22
|
+
spike_limit_percentage: 15
|
|
23
|
+
batch:
|
|
24
|
+
timeout: 5s
|
|
25
|
+
send_batch_size: 8192
|
|
26
|
+
tail_sampling:
|
|
27
|
+
decision_wait: 10s
|
|
28
|
+
num_traces: 100000
|
|
29
|
+
expected_new_traces_per_sec: 2000
|
|
30
|
+
decision_cache:
|
|
31
|
+
sampled_cache_size: 100000
|
|
32
|
+
policies:
|
|
33
|
+
- name: errors
|
|
34
|
+
type: status_code
|
|
35
|
+
status_code:
|
|
36
|
+
status_codes: [ERROR]
|
|
37
|
+
- name: slow
|
|
38
|
+
type: latency
|
|
39
|
+
latency:
|
|
40
|
+
threshold_ms: 1500
|
|
41
|
+
- name: checkout
|
|
42
|
+
type: string_attribute
|
|
43
|
+
string_attribute:
|
|
44
|
+
key: service.name
|
|
45
|
+
values: [checkout, payments]
|
|
46
|
+
- name: noisy-health
|
|
47
|
+
type: drop
|
|
48
|
+
drop:
|
|
49
|
+
drop_sub_policy:
|
|
50
|
+
- name: health-route
|
|
51
|
+
type: string_attribute
|
|
52
|
+
string_attribute:
|
|
53
|
+
key: http.route
|
|
54
|
+
values: [/healthz, /readyz]
|
|
55
|
+
- name: budget
|
|
56
|
+
type: composite
|
|
57
|
+
composite:
|
|
58
|
+
max_total_spans_per_second: 1000
|
|
59
|
+
policy_order: [errors-first, rest]
|
|
60
|
+
composite_sub_policy:
|
|
61
|
+
- name: errors-first
|
|
62
|
+
type: status_code
|
|
63
|
+
status_code:
|
|
64
|
+
status_codes: [ERROR]
|
|
65
|
+
- name: rest
|
|
66
|
+
type: always_sample
|
|
67
|
+
rate_allocation:
|
|
68
|
+
- policy: errors-first
|
|
69
|
+
percent: 60
|
|
70
|
+
- policy: rest
|
|
71
|
+
percent: 40
|
|
72
|
+
|
|
73
|
+
exporters:
|
|
74
|
+
loadbalancing:
|
|
75
|
+
routing_key: traceID
|
|
76
|
+
protocol:
|
|
77
|
+
otlp:
|
|
78
|
+
timeout: 2s
|
|
79
|
+
tls:
|
|
80
|
+
insecure: true
|
|
81
|
+
resolver:
|
|
82
|
+
dns:
|
|
83
|
+
hostname: otel-sampling-headless.observability.svc.cluster.local
|
|
84
|
+
port: "14317"
|
|
85
|
+
otlp/tempo:
|
|
86
|
+
endpoint: tempo.observability:4317
|
|
87
|
+
headers:
|
|
88
|
+
authorization: "Bearer ${env:TEMPO_TOKEN}"
|
|
89
|
+
tls:
|
|
90
|
+
insecure: false
|
|
91
|
+
ca_file: /etc/tls/ca.pem
|
|
92
|
+
sending_queue:
|
|
93
|
+
enabled: true
|
|
94
|
+
queue_size: 10000
|
|
95
|
+
retry_on_failure:
|
|
96
|
+
enabled: true
|
|
97
|
+
max_elapsed_time: 5m
|
|
98
|
+
prometheus:
|
|
99
|
+
endpoint: "0.0.0.0:8889"
|
|
100
|
+
namespace: gateway
|
|
101
|
+
resource_to_telemetry_conversion:
|
|
102
|
+
enabled: true
|
|
103
|
+
|
|
104
|
+
connectors:
|
|
105
|
+
spanmetrics:
|
|
106
|
+
namespace: traces.span.metrics
|
|
107
|
+
dimensions:
|
|
108
|
+
- name: http.route
|
|
109
|
+
- name: deployment.environment
|
|
110
|
+
default: "${env:DEPLOY_ENV}"
|
|
111
|
+
histogram:
|
|
112
|
+
explicit:
|
|
113
|
+
buckets: [10ms, 50ms, 100ms, 250ms, 1s, 5s]
|
|
114
|
+
exemplars:
|
|
115
|
+
enabled: true
|
|
116
|
+
metrics_flush_interval: 15s
|
|
117
|
+
|
|
118
|
+
extensions:
|
|
119
|
+
health_check:
|
|
120
|
+
endpoint: 0.0.0.0:13133
|
|
121
|
+
pprof:
|
|
122
|
+
endpoint: localhost:1777
|
|
123
|
+
zpages: {}
|
|
124
|
+
|
|
125
|
+
service:
|
|
126
|
+
extensions: [zpages, health_check]
|
|
127
|
+
telemetry:
|
|
128
|
+
logs:
|
|
129
|
+
level: info
|
|
130
|
+
encoding: json
|
|
131
|
+
metrics:
|
|
132
|
+
level: detailed
|
|
133
|
+
readers:
|
|
134
|
+
- pull:
|
|
135
|
+
exporter:
|
|
136
|
+
prometheus:
|
|
137
|
+
host: 0.0.0.0
|
|
138
|
+
port: 8888
|
|
139
|
+
resource:
|
|
140
|
+
service.name: otel-gateway
|
|
141
|
+
pipelines:
|
|
142
|
+
traces:
|
|
143
|
+
receivers: [otlp]
|
|
144
|
+
processors: [memory_limiter]
|
|
145
|
+
exporters: [loadbalancing]
|
|
146
|
+
traces/sampling:
|
|
147
|
+
receivers: [otlp/sampling]
|
|
148
|
+
processors: [memory_limiter, tail_sampling, batch]
|
|
149
|
+
exporters: [otlp/tempo, spanmetrics]
|
|
150
|
+
metrics/red:
|
|
151
|
+
receivers: [spanmetrics]
|
|
152
|
+
processors: [batch]
|
|
153
|
+
exporters: [prometheus]
|