Skip to content

Commit 0128218

Browse files
committed
feat(logging): use dynamic node ids for spans
1 parent c46bcbe commit 0128218

File tree

3 files changed

+97
-77
lines changed

3 files changed

+97
-77
lines changed

ant-logging/src/layers.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,43 @@ fn get_log_level_from_str(log_level: &str) -> Result<Level> {
330330
use std::collections::HashMap;
331331
use std::sync::{Arc, Mutex};
332332
use tracing_appender::non_blocking::NonBlocking;
333+
use tracing::field::{Field, Visit};
334+
use tracing::span::{Attributes, Id};
335+
336+
/// Metadata stored with each node span for routing purposes
337+
#[derive(Debug)]
338+
struct NodeMetadata {
339+
node_name: String,
340+
}
341+
342+
/// Visitor to extract node_id field from span attributes
343+
struct NodeIdVisitor {
344+
node_id: Option<usize>,
345+
}
346+
347+
impl Visit for NodeIdVisitor {
348+
fn record_u64(&mut self, field: &Field, value: u64) {
349+
if field.name() == "node_id" {
350+
self.node_id = Some(value as usize);
351+
}
352+
}
353+
354+
fn record_i64(&mut self, field: &Field, value: i64) {
355+
if field.name() == "node_id" {
356+
self.node_id = Some(value as usize);
357+
}
358+
}
359+
360+
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
361+
if field.name() == "node_id" {
362+
// Try to extract from debug representation as fallback
363+
let debug_str = format!("{value:?}");
364+
if let Ok(parsed) = debug_str.parse::<usize>() {
365+
self.node_id = Some(parsed);
366+
}
367+
}
368+
}
369+
}
333370

334371
/// Layer that routes events to different file appenders based on span context
335372
pub struct NodeRoutingLayer {
@@ -363,6 +400,22 @@ where
363400
Filter::enabled(&self.targets_filter, meta, &ctx)
364401
}
365402

403+
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
404+
let span = ctx.span(id).expect("Span should exist in registry");
405+
let span_name = span.name();
406+
407+
// Extract node_id from spans named "node"
408+
if span_name == "node" {
409+
let mut visitor = NodeIdVisitor { node_id: None };
410+
attrs.record(&mut visitor);
411+
412+
if let Some(node_id) = visitor.node_id {
413+
let node_name = format!("node_{node_id}");
414+
span.extensions_mut().insert(NodeMetadata { node_name });
415+
}
416+
}
417+
}
418+
366419
fn on_event(&self, event: &tracing::Event<'_>, ctx: Context<'_, S>) {
367420
// Find which node this event belongs to based on span hierarchy
368421
let mut target_node = None;
@@ -372,7 +425,15 @@ where
372425
while let Some(span) = current {
373426
let span_name = span.name();
374427

375-
// Check for standard node spans: node_1, node_2, etc.
428+
// Check for dynamic node spans with stored metadata
429+
if span_name == "node" {
430+
if let Some(metadata) = span.extensions().get::<NodeMetadata>() {
431+
target_node = Some(metadata.node_name.clone());
432+
break;
433+
}
434+
}
435+
436+
// Check for legacy node spans: node_1, node_2, etc. (backwards compatibility)
376437
if span_name.starts_with("node_") {
377438
target_node = Some(span_name.to_string());
378439
break;

ant-logging/tests/multi_node_logging.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ async fn test_multi_node_logging_e2e() {
2222
.initialize_with_multi_node_logging(2)
2323
.expect("Failed to initialize multi-node logging");
2424

25-
// Log messages from different nodes
26-
let node_1_span = tracing::info_span!("node_1");
25+
// Log messages from different nodes using new dynamic span format
26+
let node_1_span = tracing::info_span!("node", node_id = 1);
2727
let task1 = async {
2828
info!("Message from node 1");
2929
info!("Another message from node 1");
@@ -38,7 +38,7 @@ async fn test_multi_node_logging_e2e() {
3838
}
3939
.instrument(node_1_span);
4040

41-
let node_2_span = tracing::info_span!("node_2");
41+
let node_2_span = tracing::info_span!("node", node_id = 2);
4242
let task2 = async {
4343
info!("Message from node 2");
4444
}
@@ -63,6 +63,9 @@ async fn test_multi_node_logging_e2e() {
6363
let node_1_content = read_log_content(&node_1_dir).expect("Failed to read node 1 logs");
6464
let node_2_content = read_log_content(&node_2_dir).expect("Failed to read node 2 logs");
6565

66+
println!("Node 1 logs:\n{}", node_1_content);
67+
println!("Node 2 logs:\n{}", node_2_content);
68+
6669
// Check node 1 logs contain all its messages
6770
assert!(
6871
node_1_content.contains("Message from node 1"),
@@ -97,18 +100,37 @@ async fn test_multi_node_logging_e2e() {
97100
"Should contain target name"
98101
);
99102
assert!(
100-
node_1_content.contains("node_1"),
101-
"Should contain span information"
103+
node_1_content.contains("/node"),
104+
"Should contain span information with /node"
102105
);
103106
assert!(
104-
node_2_content.contains("node_2"),
105-
"Should contain span information"
107+
node_2_content.contains("/node"),
108+
"Should contain span information with /node"
106109
);
107110

108111
println!("Node 1 logs:\n{}", node_1_content);
109112
println!("Node 2 logs:\n{}", node_2_content);
110113
}
111114

115+
#[test]
116+
fn test_unlimited_node_span_creation() {
117+
// Test that we can create spans for nodes beyond the old 20-node limit
118+
// This tests the span creation functionality without requiring a full logging setup
119+
120+
let test_nodes = vec![1, 15, 21, 25, 50, 100];
121+
122+
for &node_id in &test_nodes {
123+
// This should work for any node_id now (no hardcoded limit)
124+
let node_span = tracing::info_span!("node", node_id = node_id);
125+
126+
// Verify the span can be entered and used
127+
let _enter = node_span.enter();
128+
// If we get here without panicking, the span creation works
129+
}
130+
131+
println!("Successfully created spans for node IDs: {:?}", test_nodes);
132+
}
133+
112134
/// Helper function to read log content from a node directory
113135
fn read_log_content(node_dir: &PathBuf) -> Result<String, Box<dyn std::error::Error>> {
114136
let mut content = String::new();

ant-node/src/spawn/network_spawner.rs

Lines changed: 6 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -14,73 +14,11 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr};
1414
use std::path::PathBuf;
1515
use tracing::Instrument;
1616

17-
/// Macro to create node spans in a clean way.
18-
/// This handles the limitation that tracing span names must be compile-time constants
19-
/// while still providing a clean interface for node IDs up to 20 nodes.
20-
macro_rules! create_node_span {
21-
(1) => {
22-
tracing::info_span!("node_1")
23-
};
24-
(2) => {
25-
tracing::info_span!("node_2")
26-
};
27-
(3) => {
28-
tracing::info_span!("node_3")
29-
};
30-
(4) => {
31-
tracing::info_span!("node_4")
32-
};
33-
(5) => {
34-
tracing::info_span!("node_5")
35-
};
36-
(6) => {
37-
tracing::info_span!("node_6")
38-
};
39-
(7) => {
40-
tracing::info_span!("node_7")
41-
};
42-
(8) => {
43-
tracing::info_span!("node_8")
44-
};
45-
(9) => {
46-
tracing::info_span!("node_9")
47-
};
48-
(10) => {
49-
tracing::info_span!("node_10")
50-
};
51-
(11) => {
52-
tracing::info_span!("node_11")
53-
};
54-
(12) => {
55-
tracing::info_span!("node_12")
56-
};
57-
(13) => {
58-
tracing::info_span!("node_13")
59-
};
60-
(14) => {
61-
tracing::info_span!("node_14")
62-
};
63-
(15) => {
64-
tracing::info_span!("node_15")
65-
};
66-
(16) => {
67-
tracing::info_span!("node_16")
68-
};
69-
(17) => {
70-
tracing::info_span!("node_17")
71-
};
72-
(18) => {
73-
tracing::info_span!("node_18")
74-
};
75-
(19) => {
76-
tracing::info_span!("node_19")
77-
};
78-
(20) => {
79-
tracing::info_span!("node_20")
80-
};
81-
($id:expr) => {
82-
tracing::info_span!("node_other", node_id = $id)
83-
};
17+
/// Create a node span with unlimited node ID support.
18+
/// Uses a static span name "node" with the node_id as a field to work around
19+
/// the tracing library's requirement for compile-time span names.
20+
fn create_node_span(node_id: usize) -> tracing::Span {
21+
tracing::info_span!("node", node_id = node_id)
8422
}
8523

8624
pub struct NetworkSpawner {
@@ -251,8 +189,7 @@ async fn spawn_network(
251189
for i in 0..size {
252190
let node_id = i + 1;
253191
// Create a span with a unique name for this specific node
254-
// Since tracing requires compile-time span names, we use a cleaner macro approach
255-
let node_span = create_node_span!(node_id);
192+
let node_span = create_node_span(node_id);
256193

257194
// Instrument the entire node spawn with the node_id span
258195
let spawn_future = async {

0 commit comments

Comments
 (0)