Skip to content
Merged
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

**Features**:

- Add configuration to allow high cardinality tags in metrics. ([#4805](https://github.com/getsentry/relay/pull/4805))

## 25.6.0

**Features**:
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ smallvec = { version = "1.13.2", features = ["serde"] }
socket2 = "0.5.8"
sqlparser = "0.44.0"
sqlx = { version = "0.8.2", default-features = false }
statsdproxy = { version = "0.3.0", default-features = false }
statsdproxy = { version = "0.4.1", default-features = false }
symbolic-common = { version = "12.12.3", default-features = false }
symbolic-unreal = { version = "12.12.3", default-features = false }
syn = { version = "2.0.90" }
Expand Down
14 changes: 14 additions & 0 deletions relay-config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,14 @@ pub struct Metrics {
///
/// Defaults to `true`.
pub aggregate: bool,
/// Allows emission of metrics with high cardinality tags.
///
/// High cardinality tags are dynamic values attached to metrics,
/// such as project IDs. When enabled, these tags will be included
/// in the emitted metrics. When disabled, the tags will be omitted.
///
/// Defaults to `false`.
pub allow_high_cardinality_tags: bool,
}

impl Default for Metrics {
Expand All @@ -569,6 +577,7 @@ impl Default for Metrics {
sample_rate: 1.0,
periodic_secs: 5,
aggregate: true,
allow_high_cardinality_tags: false,
}
}
}
Expand Down Expand Up @@ -2100,6 +2109,11 @@ impl Config {
self.values.metrics.aggregate
}

/// Returns whether high cardinality tags should be removed before sending metrics.
pub fn metrics_allow_high_cardinality_tags(&self) -> bool {
self.values.metrics.allow_high_cardinality_tags
}

/// Returns the interval for periodic metrics emitted from Relay.
///
/// `None` if periodic metrics are disabled.
Expand Down
71 changes: 53 additions & 18 deletions relay-statsd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@
//!
//! ```no_run
//! # use std::collections::BTreeMap;
//! # use relay_statsd::MetricsClientConfig;
//!
//! relay_statsd::init("myprefix", "localhost:8125", BTreeMap::new(), 1.0, true);
//! relay_statsd::init(MetricsClientConfig {
//! prefix: "myprefix",
//! host: "localhost:8125",
//! default_tags: BTreeMap::new(),
//! sample_rate: 1.0,
//! aggregate: true,
//! allow_high_cardinality_tags: false
//! });
//! ```
//!
//! ## Macro Usage
Expand Down Expand Up @@ -56,11 +64,14 @@
//! ```
//!
//! [Metric Types]: https://github.com/statsd/statsd/blob/master/docs/metric_types.md
pub use statsdproxy::config::DenyTagConfig;

use cadence::{Metric, MetricBuilder, StatsdClient};
use parking_lot::RwLock;
use rand::distributions::{Distribution, Uniform};
use statsdproxy::cadence::StatsdProxyMetricSink;
use statsdproxy::config::AggregateMetricsConfig;
use statsdproxy::middleware::deny_tag::DenyTag;
use std::collections::BTreeMap;
use std::net::ToSocketAddrs;
use std::ops::{Deref, DerefMut};
Expand All @@ -85,6 +96,23 @@ pub struct MetricsClient {
pub rx: Option<crossbeam_channel::Receiver<Vec<u8>>>,
}

/// Client configuration used for initialization of [`MetricsClient`].
#[derive(Debug)]
pub struct MetricsClientConfig<'a, A: ToSocketAddrs> {
/// Prefix which is appended to all metric names.
pub prefix: &'a str,
/// Host of the metrics upstream.
pub host: A,
/// Tags that are added to all metrics.
pub default_tags: BTreeMap<String, String>,
/// Sample rate for metrics, between 0.0 (= 0%) and 1.0 (= 100%)
pub sample_rate: f32,
/// If metrics should be batched or send immediately upstream.
pub aggregate: bool,
/// If high cardinality tags should be removed from metrics.
pub allow_high_cardinality_tags: bool,
}

impl Deref for MetricsClient {
type Target = StatsdClient;

Expand Down Expand Up @@ -221,20 +249,14 @@ pub fn disable() {
}

/// Tell the metrics system to report to statsd.
pub fn init<A: ToSocketAddrs>(
prefix: &str,
host: A,
default_tags: BTreeMap<String, String>,
sample_rate: f32,
aggregate: bool,
) {
let addrs: Vec<_> = host.to_socket_addrs().unwrap().collect();
pub fn init<A: ToSocketAddrs>(config: MetricsClientConfig<A>) {
let addrs: Vec<_> = config.host.to_socket_addrs().unwrap().collect();
if !addrs.is_empty() {
relay_log::info!("reporting metrics to statsd at {}", addrs[0]);
}

// Normalize sample_rate
let sample_rate = sample_rate.clamp(0., 1.);
let sample_rate = config.sample_rate.clamp(0., 1.);
relay_log::debug!(
"metrics sample rate is set to {sample_rate}{}",
if sample_rate == 0.0 {
Expand All @@ -244,12 +266,21 @@ pub fn init<A: ToSocketAddrs>(
}
);

let statsd_client = if aggregate {
let deny_config = DenyTagConfig {
starts_with: match config.allow_high_cardinality_tags {
true => vec![],
false => vec!["hc_".to_owned()],
},
tags: vec![],
ends_with: vec![],
};

let statsd_client = if config.aggregate {
let statsdproxy_sink = StatsdProxyMetricSink::new(move || {
let upstream = statsdproxy::middleware::upstream::Upstream::new(addrs[0])
.expect("failed to create statsdproxy metric sink");

statsdproxy::middleware::aggregate::AggregateMetrics::new(
let aggregate = statsdproxy::middleware::aggregate::AggregateMetrics::new(
AggregateMetricsConfig {
aggregate_gauges: true,
aggregate_counters: true,
Expand All @@ -258,21 +289,25 @@ pub fn init<A: ToSocketAddrs>(
max_map_size: None,
},
upstream,
)
);

DenyTag::new(deny_config.clone(), aggregate)
});

StatsdClient::from_sink(prefix, statsdproxy_sink)
StatsdClient::from_sink(config.prefix, statsdproxy_sink)
} else {
let statsdproxy_sink = StatsdProxyMetricSink::new(move || {
statsdproxy::middleware::upstream::Upstream::new(addrs[0])
.expect("failed to create statsdproxy metric sind")
let upstream = statsdproxy::middleware::upstream::Upstream::new(addrs[0])
.expect("failed to create statsdproxy metric sind");

DenyTag::new(deny_config.clone(), upstream)
});
StatsdClient::from_sink(prefix, statsdproxy_sink)
StatsdClient::from_sink(config.prefix, statsdproxy_sink)
};

set_client(MetricsClient {
statsd_client,
default_tags,
default_tags: config.default_tags,
sample_rate,
rx: None,
});
Expand Down
14 changes: 8 additions & 6 deletions relay/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use anyhow::Context;
use anyhow::Result;
use relay_config::{Config, RelayMode};
use relay_server::MemoryStat;
use relay_statsd::MetricsClientConfig;

/// Validates that the `batch_size_bytes` of the configuration is correct and doesn't lead to
/// deadlocks in the buffer.
Expand Down Expand Up @@ -110,13 +111,14 @@ pub fn init_metrics(config: &Config) -> Result<()> {
default_tags.insert(hostname_tag.to_owned(), hostname);
}
}
relay_statsd::init(
config.metrics_prefix(),
&addrs[..],
relay_statsd::init(MetricsClientConfig {
prefix: config.metrics_prefix(),
host: &addrs[..],
default_tags,
config.metrics_sample_rate(),
config.metrics_aggregate(),
);
sample_rate: config.metrics_sample_rate(),
aggregate: config.metrics_aggregate(),
allow_high_cardinality_tags: config.metrics_allow_high_cardinality_tags(),
});

Ok(())
}
Loading