Skip to content

Commit 913b56b

Browse files
committed
feat: add tracing
1 parent ff03ea9 commit 913b56b

13 files changed

Lines changed: 971 additions & 12 deletions

File tree

Cargo.lock

Lines changed: 468 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
"crates/charon-k1util",
1212
"crates/charon-p2p",
1313
"crates/charon-testutil",
14+
"crates/tracing",
1415
]
1516
resolver = "3"
1617

@@ -42,6 +43,11 @@ base64 = { version = "0.22.1" }
4243
sha3 = { version = "0.10.8" }
4344
k256 = { version = "0.13.4", features = ["ecdsa", "sha256"] }
4445
criterion = "0.7.0"
46+
tracing = "0.1.32"
47+
tracing-subscriber = { version = "0.3.9", features = ["env-filter"] }
48+
tracing-loki = "0.2.6"
49+
vise = "0.3.2"
50+
vise-exporter = "0.3.2"
4551

4652
# Crates in the workspace
4753
charon-build-proto = { path = "crates/charon-build-proto" }
@@ -50,6 +56,7 @@ charon-eth2 = { path = "crates/charon-eth2" }
5056
charon-k1util = { path = "crates/charon-k1util" }
5157
charon-p2p = { path = "crates/charon-p2p" }
5258
charon-testutil = { path = "crates/charon-testutil" }
59+
charon-tracing = { path = "crates/charon-tracing" }
5360

5461
[workspace.lints.rust]
5562
missing_docs = "deny"

crates/tracing/Cargo.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "charon-tracing"
3+
version.workspace = true
4+
edition.workspace = true
5+
repository.workspace = true
6+
license.workspace = true
7+
publish.workspace = true
8+
9+
[dependencies]
10+
tracing.workspace = true
11+
tracing-subscriber.workspace = true
12+
tracing-loki.workspace = true
13+
thiserror.workspace = true
14+
tokio.workspace = true
15+
vise.workspace = true
16+
17+
[[example]]
18+
name = "basic"
19+
20+
[dev-dependencies]
21+
vise-exporter.workspace = true
22+
tokio = { workspace = true, features = ["full"] }
23+
24+
[lints]
25+
workspace = true

crates/tracing/examples/basic.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//! Basic example demonstrating the charon-tracing functionality.
2+
//!
3+
//! Run the following command to start the test infrastructure:
4+
//! ```bash
5+
//! docker compose -f test-infra/docker-compose.yml up -d
6+
//! ```
7+
//!
8+
//! Run the following command to start the example:
9+
//! ```bash
10+
//! cargo run --example basic
11+
//! ```
12+
//!
13+
//! You can see the logs in Grafana at http://localhost:3000.
14+
use std::{collections::HashMap, net::SocketAddr};
15+
16+
use charon_tracing::{LokiConfig, config::TracingConfig, init::init};
17+
use tracing::{debug, error, info, instrument, trace, warn};
18+
use vise_exporter::MetricsExporter;
19+
20+
#[tokio::main]
21+
async fn main() {
22+
// Initialize tracing with default console config
23+
let config = TracingConfig::builder()
24+
.with_default_console()
25+
.with_metrics(true)
26+
.loki(LokiConfig {
27+
loki_url: "http://localhost:3100".to_string(),
28+
labels: HashMap::new(),
29+
extra_fields: HashMap::new(),
30+
})
31+
.override_env_filter("debug")
32+
.build();
33+
34+
let background_task = init(&config)
35+
.expect("Failed to initialize tracing")
36+
.unwrap();
37+
38+
tokio::spawn(background_task);
39+
40+
let bind_address = SocketAddr::from(([0, 0, 0, 0], 9464));
41+
42+
let exporter = MetricsExporter::default().bind(bind_address).await.unwrap();
43+
tokio::spawn(async move {
44+
exporter.start().await.unwrap();
45+
});
46+
47+
// Test various log levels
48+
trace!("This is a trace message");
49+
debug!("This is a debug message");
50+
info!("This is an info message");
51+
warn!("This is a warning message");
52+
error!("This is an error message");
53+
54+
// Test structured logging with fields
55+
info!(user_id = 42, action = "login", "User performed an action");
56+
57+
instrumented_function();
58+
59+
// Test spans
60+
let span = tracing::info_span!("processing", request_id = "abc-123");
61+
let _guard = span.enter();
62+
63+
info!("Processing started");
64+
debug!("Debug info inside span");
65+
info!("Processing completed");
66+
67+
// Wait for 10 seconds to see the logs in Loki
68+
std::thread::sleep(std::time::Duration::from_secs(10));
69+
}
70+
71+
#[instrument]
72+
fn instrumented_function() {
73+
info!("Instrumented function started");
74+
debug!("Debug info inside instrumented function");
75+
info!("Instrumented function completed");
76+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
version: "3"
2+
3+
services:
4+
loki:
5+
image: grafana/loki:2.9.0
6+
ports:
7+
- "3100:3100"
8+
command: -config.file=/etc/loki/local-config.yaml
9+
volumes:
10+
- loki-data:/loki
11+
12+
promtail:
13+
image: grafana/promtail:2.9.0
14+
volumes:
15+
- /var/log:/var/log
16+
- ./promtail-config.yml:/etc/promtail/config.yml
17+
command: -config.file=/etc/promtail/config.yml
18+
19+
prometheus:
20+
image: prom/prometheus:latest
21+
volumes:
22+
- ./prometheus.yml:/etc/prometheus/prometheus.yml
23+
- prometheus-data:/prometheus
24+
command:
25+
- '--config.file=/etc/prometheus/prometheus.yml'
26+
- '--storage.tsdb.path=/prometheus'
27+
extra_hosts:
28+
- "host.docker.internal:host-gateway" # Allows access to host machine
29+
30+
grafana:
31+
image: grafana/grafana:latest
32+
ports:
33+
- "3000:3000"
34+
environment:
35+
- GF_SECURITY_ADMIN_PASSWORD=admin
36+
volumes:
37+
- grafana-data:/var/lib/grafana
38+
39+
volumes:
40+
loki-data:
41+
grafana-data:
42+
prometheus-data:
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
global:
2+
scrape_interval: 15s
3+
evaluation_interval: 15s
4+
5+
scrape_configs:
6+
- job_name: 'local-exporter'
7+
static_configs:
8+
- targets: ['host.docker.internal:9464']
9+
labels:
10+
instance: 'local-exporter'
11+
environment: 'dev'
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
server:
2+
http_listen_port: 9080
3+
grpc_listen_port: 0
4+
5+
positions:
6+
filename: /tmp/positions.yaml
7+
8+
clients:
9+
- url: http://loki:3100/loki/api/v1/push
10+
11+
scrape_configs:
12+
- job_name: system
13+
static_configs:
14+
- targets:
15+
- localhost
16+
labels:
17+
job: varlogs
18+
__path__: /var/log/*log

crates/tracing/src/config.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use std::collections::HashMap;
2+
3+
/// Configuration for the tracing.
4+
#[derive(Debug, Clone, Default)]
5+
pub struct TracingConfig {
6+
/// Loki configuration. Enables loki logging if provided. If not - no loki
7+
/// logging is enabled.
8+
pub loki: Option<LokiConfig>,
9+
10+
/// Console configuration. Enables console logging if provided. If not - no
11+
/// console logging is enabled.
12+
pub console: Option<ConsoleConfig>,
13+
14+
/// Enables metrics logging. If not - no metrics logging is enabled.
15+
pub metrics: bool,
16+
17+
/// Overrides the environment filter. If not - the environment filter is
18+
/// used.
19+
pub override_env_filter: Option<String>,
20+
}
21+
22+
/// Configuration for the loki logging.
23+
#[derive(Debug, Clone)]
24+
pub struct LokiConfig {
25+
/// URL of the Loki instance.
26+
pub loki_url: String,
27+
28+
/// Labels to add to the Loki logs.
29+
pub labels: HashMap<String, String>,
30+
31+
/// Extra fields to add to the Loki logs.
32+
pub extra_fields: HashMap<String, String>,
33+
}
34+
35+
/// Configuration for the console logging.
36+
#[derive(Debug, Clone)]
37+
pub struct ConsoleConfig {
38+
/// Whether to include the target module in logs.
39+
pub with_target: bool,
40+
41+
/// Whether to include the log level in logs.
42+
pub with_level: bool,
43+
44+
/// Whether to include thread IDs in logs.
45+
pub with_thread_ids: bool,
46+
47+
/// Whether to include the source file name in logs.
48+
pub with_file: bool,
49+
50+
/// Whether to include line numbers in logs.
51+
pub with_line_number: bool,
52+
53+
/// Whether to use ANSI colors in logs.
54+
pub with_ansi: bool,
55+
}
56+
57+
impl Default for ConsoleConfig {
58+
fn default() -> Self {
59+
Self {
60+
with_target: true,
61+
with_level: true,
62+
with_thread_ids: false,
63+
with_file: false,
64+
with_line_number: false,
65+
with_ansi: true,
66+
}
67+
}
68+
}
69+
70+
/// Builder for [`TracingConfig`].
71+
#[derive(Debug, Clone, Default)]
72+
pub struct TracingConfigBuilder {
73+
tracing_config: TracingConfig,
74+
}
75+
76+
impl TracingConfigBuilder {
77+
/// Creates a new builder with default values.
78+
pub fn new() -> Self {
79+
Self::default()
80+
}
81+
82+
/// Sets the Loki configuration.
83+
pub fn loki(mut self, config: LokiConfig) -> Self {
84+
self.tracing_config.loki = Some(config);
85+
self
86+
}
87+
88+
/// Sets the console configuration.
89+
pub fn console(mut self, config: ConsoleConfig) -> Self {
90+
self.tracing_config.console = Some(config);
91+
self
92+
}
93+
94+
/// Enables console logging with default configuration.
95+
pub fn with_default_console(mut self) -> Self {
96+
self.tracing_config.console = Some(ConsoleConfig::default());
97+
self
98+
}
99+
100+
/// Enables console logging and configures whether to include the target
101+
/// module.
102+
pub fn console_with_target(mut self, with_target: bool) -> Self {
103+
self.tracing_config
104+
.console
105+
.get_or_insert_with(ConsoleConfig::default)
106+
.with_target = with_target;
107+
self
108+
}
109+
110+
/// Enables console logging and configures whether to include the log level.
111+
pub fn console_with_level(mut self, with_level: bool) -> Self {
112+
self.tracing_config
113+
.console
114+
.get_or_insert_with(ConsoleConfig::default)
115+
.with_level = with_level;
116+
self
117+
}
118+
119+
/// Enables console logging and configures whether to include thread IDs.
120+
pub fn console_with_thread_ids(mut self, with_thread_ids: bool) -> Self {
121+
self.tracing_config
122+
.console
123+
.get_or_insert_with(ConsoleConfig::default)
124+
.with_thread_ids = with_thread_ids;
125+
self
126+
}
127+
128+
/// Enables console logging and configures whether to include the source
129+
/// file name.
130+
pub fn console_with_file(mut self, with_file: bool) -> Self {
131+
self.tracing_config
132+
.console
133+
.get_or_insert_with(ConsoleConfig::default)
134+
.with_file = with_file;
135+
self
136+
}
137+
138+
/// Enables console logging and configures whether to include line numbers.
139+
pub fn console_with_line_number(mut self, with_line_number: bool) -> Self {
140+
self.tracing_config
141+
.console
142+
.get_or_insert_with(ConsoleConfig::default)
143+
.with_line_number = with_line_number;
144+
self
145+
}
146+
147+
/// Enables console logging and configures whether to use ANSI colors.
148+
pub fn console_with_ansi(mut self, with_ansi: bool) -> Self {
149+
self.tracing_config
150+
.console
151+
.get_or_insert_with(ConsoleConfig::default)
152+
.with_ansi = with_ansi;
153+
self
154+
}
155+
156+
/// Enables metrics logging.
157+
pub fn with_metrics(mut self, enabled: bool) -> Self {
158+
self.tracing_config.metrics = enabled;
159+
self
160+
}
161+
162+
/// Sets whether metrics logging is enabled.
163+
pub fn metrics(mut self, enabled: bool) -> Self {
164+
self.tracing_config.metrics = enabled;
165+
self
166+
}
167+
168+
/// Sets the environment filter override.
169+
pub fn override_env_filter(mut self, filter: impl Into<String>) -> Self {
170+
self.tracing_config.override_env_filter = Some(filter.into());
171+
self
172+
}
173+
174+
/// Builds the [`TracingConfig`].
175+
pub fn build(self) -> TracingConfig {
176+
self.tracing_config
177+
}
178+
}
179+
180+
impl TracingConfig {
181+
/// Creates a new builder for [`TracingConfig`].
182+
pub fn builder() -> TracingConfigBuilder {
183+
TracingConfigBuilder::new()
184+
}
185+
}

0 commit comments

Comments
 (0)