Skip to content

Commit 5dff28f

Browse files
feat: add possibility to add custom_attributes to traces, metrics and logs (#1248)
1 parent 3c88ed3 commit 5dff28f

File tree

3 files changed

+63
-25
lines changed

3 files changed

+63
-25
lines changed

agent-control/src/instrumentation/config/otel.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ pub struct OtelConfig {
5555
/// serde serialization and deserialization.
5656
#[serde(skip)]
5757
pub(crate) proxy: ProxyConfig,
58+
/// Custom attributes to be added to all traces/metrics.
59+
#[serde(default)]
60+
pub(crate) custom_attributes: HashMap<String, String>,
5861
}
5962

6063
fn default_insecure_level() -> String {
@@ -67,6 +70,14 @@ impl OtelConfig {
6770
Self { proxy, ..self }
6871
}
6972

73+
/// Returns a new configuration including custom_attributes
74+
pub fn with_custom_attributes(self, custom_attributes: HashMap<String, String>) -> Self {
75+
Self {
76+
custom_attributes,
77+
..self
78+
}
79+
}
80+
7081
/// Returns the otel endpoint to report traces to.
7182
pub(crate) fn traces_endpoint(&self) -> String {
7283
self.target_endpoint(TRACES_SUFFIX)
@@ -193,6 +204,9 @@ insecure_level: "newrelic_agent_control=info,off"
193204
endpoint: https://otlp.nr-data.net:4318
194205
headers: {}
195206
client_timeout: 10s
207+
custom_attributes:
208+
cluster_name: "test"
209+
environment: production
196210
metrics:
197211
enabled: true
198212
interval: 120s
@@ -224,6 +238,7 @@ logs:
224238
headers: Default::default(),
225239
client_timeout: Default::default(),
226240
proxy: Default::default(),
241+
custom_attributes: Default::default(),
227242
}
228243
}
229244
}

agent-control/src/instrumentation/tracing_layers/otel.rs

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,20 @@ use crate::http::config::HttpConfig;
33
use crate::instrumentation::config::otel::OtelConfig;
44
use crate::instrumentation::tracing::LayerBox;
55
use opentelemetry::trace::TracerProvider;
6+
use opentelemetry::KeyValue;
67
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
78
use opentelemetry_http::HttpClient as OtelHttpClient;
89
use opentelemetry_otlp::{ExporterBuildError, WithExportConfig, WithHttpConfig};
910
use opentelemetry_sdk::logs::{BatchLogProcessor, SdkLoggerProvider};
1011
use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider};
1112
use opentelemetry_sdk::trace::{BatchSpanProcessor, SdkTracerProvider};
1213
use opentelemetry_sdk::Resource;
13-
use std::sync::LazyLock;
1414
use thiserror::Error;
1515
use tracing_opentelemetry::MetricsLayer;
1616
use tracing_subscriber::{EnvFilter, Layer};
1717

1818
const TRACER_NAME: &str = "agent-control-self-instrumentation";
1919

20-
static RESOURCE: LazyLock<Resource> =
21-
LazyLock::new(|| Resource::builder().with_service_name(TRACER_NAME).build());
22-
2320
/// Enumerates the possible error building OpenTelemetry providers.
2421
#[derive(Debug, Error)]
2522
pub enum OtelBuildError {
@@ -64,23 +61,40 @@ impl OtelLayers {
6461
where
6562
C: OtelHttpClient + Send + Sync + Clone + 'static,
6663
{
67-
let traces_provider = config
68-
.traces
69-
.enabled
70-
.then(|| Self::traces_provider(client.clone(), config))
71-
.transpose()?;
72-
73-
let metrics_provider = config
74-
.metrics
75-
.enabled
76-
.then(|| Self::metrics_provider(client.clone(), config))
77-
.transpose()?;
78-
79-
let logs_provider = config
80-
.logs
81-
.enabled
82-
.then(|| Self::logs_provider(client, config))
83-
.transpose()?;
64+
let mut traces_provider = None;
65+
let mut metrics_provider = None;
66+
let mut logs_provider = None;
67+
68+
if config.traces.enabled || config.metrics.enabled || config.logs.enabled {
69+
let attributes: Vec<KeyValue> = config
70+
.custom_attributes
71+
.iter()
72+
.map(|(k, v)| KeyValue::new(k.clone(), v.clone()))
73+
.collect();
74+
75+
let resource = Resource::builder()
76+
.with_service_name(TRACER_NAME)
77+
.with_attributes(attributes)
78+
.build();
79+
80+
traces_provider = config
81+
.traces
82+
.enabled
83+
.then(|| Self::traces_provider(client.clone(), config, resource.clone()))
84+
.transpose()?;
85+
86+
metrics_provider = config
87+
.metrics
88+
.enabled
89+
.then(|| Self::metrics_provider(client.clone(), config, resource.clone()))
90+
.transpose()?;
91+
92+
logs_provider = config
93+
.logs
94+
.enabled
95+
.then(|| Self::logs_provider(client, config, resource))
96+
.transpose()?;
97+
}
8498

8599
let filter = EnvFilter::builder()
86100
.parse(&config.insecure_level)
@@ -100,6 +114,7 @@ impl OtelLayers {
100114
fn traces_provider<C>(
101115
client: C,
102116
config: &OtelConfig,
117+
resource: Resource,
103118
) -> Result<SdkTracerProvider, OtelBuildError>
104119
where
105120
C: OtelHttpClient + Send + Sync + 'static,
@@ -117,13 +132,14 @@ impl OtelLayers {
117132

118133
Ok(SdkTracerProvider::builder()
119134
.with_span_processor(batch_processor)
120-
.with_resource(RESOURCE.clone())
135+
.with_resource(resource)
121136
.build())
122137
}
123138

124139
fn metrics_provider<C>(
125140
client: C,
126141
config: &OtelConfig,
142+
resource: Resource,
127143
) -> Result<SdkMeterProvider, OtelBuildError>
128144
where
129145
C: OtelHttpClient + Send + Sync + 'static,
@@ -141,11 +157,15 @@ impl OtelLayers {
141157

142158
Ok(SdkMeterProvider::builder()
143159
.with_reader(periodic_reader)
144-
.with_resource(RESOURCE.clone())
160+
.with_resource(resource)
145161
.build())
146162
}
147163

148-
fn logs_provider<C>(client: C, config: &OtelConfig) -> Result<SdkLoggerProvider, OtelBuildError>
164+
fn logs_provider<C>(
165+
client: C,
166+
config: &OtelConfig,
167+
resource: Resource,
168+
) -> Result<SdkLoggerProvider, OtelBuildError>
149169
where
150170
C: OtelHttpClient + Send + Sync + 'static,
151171
{
@@ -162,7 +182,7 @@ impl OtelLayers {
162182

163183
Ok(SdkLoggerProvider::builder()
164184
.with_log_processor(batch_processor)
165-
.with_resource(RESOURCE.clone())
185+
.with_resource(resource)
166186
.build())
167187
}
168188

@@ -190,6 +210,7 @@ impl OtelLayers {
190210
#[cfg(test)]
191211
mod tests {
192212
use http::Response;
213+
use opentelemetry_sdk::Resource;
193214
use tracing::{debug, info, trace};
194215
use tracing_subscriber::layer::SubscriberExt;
195216
use tracing_subscriber::EnvFilter;
@@ -232,6 +253,7 @@ mod tests {
232253
},
233254
..Default::default()
234255
},
256+
Resource::builder().build(),
235257
)
236258
.unwrap();
237259

docs/CONFIG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ self_instrumentation:
102102
endpoint: https://otlp.nr-data.net:4318 # HTTPS endpoint to report instrumentation to.
103103
headers: {} # Headers that will be included in any request to the endpoint
104104
client_timeout: 10s # Timeout for performing requests, defaults to 30s.
105+
custom_attributes: {} # Attributes to be decorated in all metrics, traces and logs
105106
metrics:
106107
enabled: true # Defaults to false.
107108
interval: 120s # Interval to report metrics, it defaults to 60s.

0 commit comments

Comments
 (0)