1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//! `ResourceAttrs` — workspace-shared resource attribute set held by
//! the observer. Spec 20 § 2.1 / spec 94 § 2.7 / P1-E.
//!
//! The observer owns one `Arc<ArcSwap<ResourceAttrs>>` so OTLP /
//! Parquet / ClickHouse sinks can read the live snapshot at flush
//! time. Phase-7 work moved this off the per-sink `OtlpResourceAttrs`
//! so a config reload re-projects every sink without redeploying.
use std::collections::BTreeMap;
/// Resource attribute set carrying the OTel semantic-convention keys
/// every sink projects onto its outbound batch (`resource.attributes`
/// for OTLP, partition columns for Parquet, etc.).
#[derive(Debug, Clone, Default)]
pub struct ResourceAttrs {
/// `service.name` — typically `OTEL_SERVICE_NAME` or the observer's
/// configured service identity.
pub service_name: String,
/// `service.version`.
pub service_version: String,
/// `service.namespace` — logical service grouping (e.g. `payments`).
pub service_namespace: String,
/// `service.instance.id` — unique per process / replica.
pub service_instance_id: String,
/// `deployment.environment` — `production`, `staging`, `dev`, …
pub deployment_environment: String,
/// `host.name`.
pub host_name: String,
/// `host.arch` — `amd64`, `arm64`, …
pub host_arch: String,
/// Any additional `OTEL_RESOURCE_ATTRIBUTES` pairs that did not
/// land in a first-class slot.
pub extra: BTreeMap<String, String>,
}
impl ResourceAttrs {
/// Render the populated semconv keys as a flat `BTreeMap`. Useful
/// for sinks that project the attributes into a wire-format
/// `KeyValueList`.
#[must_use]
pub fn to_semconv_map(&self) -> BTreeMap<String, String> {
let mut m = self.extra.clone();
if !self.service_name.is_empty() {
m.insert("service.name".to_string(), self.service_name.clone());
}
if !self.service_version.is_empty() {
m.insert("service.version".to_string(), self.service_version.clone());
}
if !self.service_namespace.is_empty() {
m.insert(
"service.namespace".to_string(),
self.service_namespace.clone(),
);
}
if !self.service_instance_id.is_empty() {
m.insert(
"service.instance.id".to_string(),
self.service_instance_id.clone(),
);
}
if !self.deployment_environment.is_empty() {
m.insert(
"deployment.environment".to_string(),
self.deployment_environment.clone(),
);
}
if !self.host_name.is_empty() {
m.insert("host.name".to_string(), self.host_name.clone());
}
if !self.host_arch.is_empty() {
m.insert("host.arch".to_string(), self.host_arch.clone());
}
m
}
}