Skip to content

Commit 9a2466b

Browse files
author
Or Ricon
authored
feat(BOUN-1300): Add metrics to anonymization client (#3053)
This change allows setting a metrics registry when creating the canister client for log anonymization.
1 parent 49f6b11 commit 9a2466b

File tree

5 files changed

+144
-22
lines changed

5 files changed

+144
-22
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rs/boundary_node/anonymization/client/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ DEPENDENCIES = [
88
"//rs/types/types",
99
"@crate_index//:anyhow",
1010
"@crate_index//:candid",
11+
"@crate_index//:prometheus",
1112
"@crate_index//:rand",
1213
"@crate_index//:rsa",
1314
"@crate_index//:thiserror",

rs/boundary_node/anonymization/client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ async-trait = { workspace = true }
99
candid = { workspace = true }
1010
ic-canister-client = { path = "../../../canister_client" }
1111
ic-types = { path = "../../../types/types" }
12+
prometheus = { workspace = true }
1213
rand = { workspace = true }
1314
rsa = { workspace = true }
1415
thiserror = { workspace = true }

rs/boundary_node/anonymization/client/src/lib.rs

Lines changed: 134 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ use anyhow::{anyhow, Context, Error};
88
use async_trait::async_trait;
99
use candid::{Decode, Encode, Principal};
1010
use ic_canister_client::Agent;
11+
use prometheus::{
12+
register_histogram_vec_with_registry, register_int_counter_vec_with_registry, HistogramOpts,
13+
HistogramVec, IntCounterVec, Registry,
14+
};
1115
use rsa::{
1216
pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey},
1317
rand_core::CryptoRngCore,
@@ -20,7 +24,58 @@ use tokio::{sync::Mutex, time::sleep};
2024
const SALT_SIZE: usize = 64;
2125
const RSA_KEY_SIZE: usize = 2048;
2226

23-
struct WithLogs<T>(T);
27+
#[derive(Clone, Debug)]
28+
pub struct MetricParams {
29+
pub action: String,
30+
pub counter: IntCounterVec,
31+
pub recorder: HistogramVec,
32+
}
33+
34+
impl MetricParams {
35+
pub fn new(registry: &Registry, action: &str) -> Self {
36+
Self::new_with_opts(registry, action, &["status"], None)
37+
}
38+
39+
pub fn new_with_opts(
40+
registry: &Registry,
41+
action: &str,
42+
labels: &[&str],
43+
buckets: Option<&[f64]>,
44+
) -> Self {
45+
let mut recorder_opts = HistogramOpts::new(
46+
format!("{action}_duration_sec"), // name
47+
format!("Records the duration of {action} calls in seconds"), // description
48+
);
49+
50+
// Set histogram buckets if given
51+
buckets.inspect(|bs| {
52+
recorder_opts.buckets = bs.to_vec();
53+
});
54+
55+
Self {
56+
action: action.to_string(),
57+
58+
// Count
59+
counter: register_int_counter_vec_with_registry!(
60+
format!("{action}_total"), // name
61+
format!("Counts occurrences of {action} calls"), // description
62+
labels, // labels
63+
registry, // registry
64+
)
65+
.expect("failed to register counter"),
66+
67+
// Duration
68+
recorder: register_histogram_vec_with_registry!(
69+
recorder_opts, // options
70+
labels, // labels
71+
registry, // registry
72+
)
73+
.expect("failed to register histogram"),
74+
}
75+
}
76+
}
77+
78+
struct WithMetrics<T>(T, Option<MetricParams>);
2479

2580
pub struct ThrottleParams {
2681
pub d: Duration,
@@ -92,7 +147,7 @@ pub trait Register: Sync + Send {
92147
}
93148

94149
#[async_trait]
95-
impl<T: Register> Register for WithLogs<T> {
150+
impl<T: Register> Register for WithMetrics<T> {
96151
async fn register(&self, pubkey: &[u8]) -> Result<(), RegisterError> {
97152
let start_time = Instant::now();
98153

@@ -108,11 +163,24 @@ impl<T: Register> Register for WithLogs<T> {
108163

109164
let duration = start_time.elapsed().as_secs_f64();
110165

166+
// Log
111167
println!(
112168
"action = 'register', status = {status}, duration = {duration}, error = {:?}",
113169
out.as_ref().err()
114170
);
115171

172+
// Metrics
173+
if let Some(MetricParams {
174+
counter, recorder, ..
175+
}) = &self.1
176+
{
177+
// Count
178+
counter.with_label_values(&[status]).inc();
179+
180+
// Latency
181+
recorder.with_label_values(&[status]).observe(duration);
182+
}
183+
116184
return out;
117185
}
118186
}
@@ -181,7 +249,7 @@ pub trait Query: Sync + Send {
181249
}
182250

183251
#[async_trait]
184-
impl<T: Query> Query for WithLogs<T> {
252+
impl<T: Query> Query for WithMetrics<T> {
185253
async fn query(&self) -> Result<Vec<u8>, QueryError> {
186254
let start_time = Instant::now();
187255

@@ -202,11 +270,24 @@ impl<T: Query> Query for WithLogs<T> {
202270

203271
let duration = start_time.elapsed().as_secs_f64();
204272

273+
// Log
205274
println!(
206275
"action = 'query', status = {status}, duration = {duration}, error = {:?}",
207276
out.as_ref().err()
208277
);
209278

279+
// Metrics
280+
if let Some(MetricParams {
281+
counter, recorder, ..
282+
}) = &self.1
283+
{
284+
// Count
285+
counter.with_label_values(&[status]).inc();
286+
287+
// Latency
288+
recorder.with_label_values(&[status]).observe(duration);
289+
}
290+
210291
return out;
211292
}
212293
}
@@ -226,7 +307,7 @@ pub trait Submit: Sync + Send {
226307
}
227308

228309
#[async_trait]
229-
impl<T: Submit> Submit for WithLogs<T> {
310+
impl<T: Submit> Submit for WithMetrics<T> {
230311
async fn submit(&self, vs: &[Pair]) -> Result<(), SubmitError> {
231312
let start_time = Instant::now();
232313

@@ -242,11 +323,24 @@ impl<T: Submit> Submit for WithLogs<T> {
242323

243324
let duration = start_time.elapsed().as_secs_f64();
244325

326+
// Log
245327
println!(
246328
"action = 'submit', status = {status}, duration = {duration}, error = {:?}",
247329
out.as_ref().err()
248330
);
249331

332+
// Metrics
333+
if let Some(MetricParams {
334+
counter, recorder, ..
335+
}) = &self.1
336+
{
337+
// Count
338+
counter.with_label_values(&[status]).inc();
339+
340+
// Latency
341+
recorder.with_label_values(&[status]).observe(duration);
342+
}
343+
250344
return out;
251345
}
252346
}
@@ -424,23 +518,43 @@ pub struct CanisterMethods {
424518
submit: Arc<dyn Submit>,
425519
}
426520

427-
impl From<Canister> for CanisterMethods {
428-
fn from(value: Canister) -> Self {
521+
pub struct CanisterMethodsBuilder<'a> {
522+
canister: Canister,
523+
registry: Option<&'a Registry>,
524+
}
525+
526+
impl<'a> CanisterMethodsBuilder<'a> {
527+
pub fn new(c: Canister) -> Self {
429528
Self {
430-
register: Arc::new({
431-
let v = value.clone();
432-
let v = WithLogs(v);
433-
WithThrottle(v, ThrottleParams::new(Duration::from_secs(10)))
434-
}),
435-
query: Arc::new({
436-
let v = value.clone();
437-
let v = WithLogs(v);
438-
WithThrottle(v, ThrottleParams::new(Duration::from_secs(10)))
439-
}),
440-
submit: Arc::new({
441-
let v = value.clone();
442-
WithLogs(v)
443-
}),
529+
canister: c,
530+
registry: None,
531+
}
532+
}
533+
534+
pub fn with_metrics(mut self, r: &'a Registry) -> Self {
535+
self.registry = Some(r);
536+
self
537+
}
538+
539+
pub fn build(self) -> CanisterMethods {
540+
CanisterMethods {
541+
register: {
542+
let v = self.canister.clone();
543+
let v = WithMetrics(v, self.registry.map(|r| MetricParams::new(r, "register")));
544+
let v = WithThrottle(v, ThrottleParams::new(Duration::from_secs(10)));
545+
Arc::new(v)
546+
},
547+
query: {
548+
let v = self.canister.clone();
549+
let v = WithMetrics(v, self.registry.map(|r| MetricParams::new(r, "query")));
550+
let v = WithThrottle(v, ThrottleParams::new(Duration::from_secs(10)));
551+
Arc::new(v)
552+
},
553+
submit: {
554+
let v = self.canister.clone();
555+
let v = WithMetrics(v, self.registry.map(|r| MetricParams::new(r, "submit")));
556+
Arc::new(v)
557+
},
444558
}
445559
}
446560
}

rs/boundary_node/ic_boundary/src/core.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use std::{
77
};
88

99
use anonymization_client::{
10-
Canister as AnonymizationCanister, Track, Tracker as AnonymizationTracker,
10+
Canister as AnonymizationCanister,
11+
CanisterMethodsBuilder as AnonymizationCanisterMethodsBuilder, Track,
12+
Tracker as AnonymizationTracker,
1113
};
1214
use anyhow::{anyhow, Context, Error};
1315
use arc_swap::ArcSwapOption;
@@ -431,7 +433,10 @@ pub async fn main(cli: Cli) -> Result<(), Error> {
431433
// HTTP Logs Anonymization
432434
let tracker = if let Some(v) = cli.obs.obs_log_anonymization_canister_id {
433435
let canister = AnonymizationCanister::new(agent.clone().unwrap(), v);
434-
Some(AnonymizationTracker::new(Box::new(OsRng), canister.into())?)
436+
let cm = AnonymizationCanisterMethodsBuilder::new(canister)
437+
.with_metrics(&metrics_registry)
438+
.build();
439+
Some(AnonymizationTracker::new(Box::new(OsRng), cm)?)
435440
} else {
436441
None
437442
};

0 commit comments

Comments
 (0)