Skip to content

Commit c276928

Browse files
feat: improve log messages (#66)
Adds structured logging improvements to make ethlambda's logs more useful for debugging and monitoring. ## New Log Messages **Block Events:** ``` INFO slot=5 proposer=5 block_root=d0fd6225 parent_root=0c3dd6a5 attestation_count=4 Received block from gossip INFO slot=5 proposer=5 block_root=d0fd6225 parent_root=0c3dd6a5 Block imported successfully INFO slot=5 proposer=5 block_root=d0fd6225 parent_root=0c3dd6a5 attestation_count=4 Published block to gossipsub ``` **Attestation Events:** ``` INFO slot=5 validator=2 head_root=a1b2c3d4 target_slot=5 target_root=e5f6g7h8 source_slot=0 source_root=i9j0k1l2 Received attestation from gossip INFO slot=5 validator=2 target_slot=5 target_root=e5f6g7h8 source_slot=0 source_root=i9j0k1l2 Attestation processed INFO slot=5 validator=2 target_slot=5 target_root=e5f6g7h8 source_slot=0 source_root=i9j0k1l2 Published attestation to gossipsub ``` **Consensus Events:** ``` INFO justified_slot=21 justified_root=f3876ca3 vote_count=5 threshold=4 Checkpoint justified INFO finalized_slot=18 finalized_root=b7ba899b previous_finalized=12 justified_slot=21 Checkpoint finalized INFO head_slot=26 head_root=b688cd00 previous_head_slot=24 previous_head_root=c577d911 justified_slot=21 finalized_slot=18 Fork choice head updated ``` **Network Events:** ``` INFO peer_id=16Uiu2HAm7TYV direction=inbound peer_count=5 their_finalized_slot=15 their_head_slot=26 Peer connected INFO peer_id=16Uiu2HAm7TYV direction=inbound reason=remote_close peer_count=4 Peer disconnected ``` ## Changes - Added `ShortRoot` display helper for consistent 8-char root truncation - Enhanced block reception, import, and publication logs with consistent field ordering - Enhanced attestation reception, processing, and publication logs with complete root information - Added finalization and justification event logging - Added fork choice head update logging - Added peer count to connection logs - Improved error context in block processing failures - Standardized field ordering: temporal → identity → identifiers → context → metadata --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent 5adc4ef commit c276928

9 files changed

Lines changed: 200 additions & 10 deletions

File tree

CLAUDE.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,51 @@ rm -rf leanSpec && make leanSpec/fixtures # Regenerate test fixtures (require
8787
let _timing = metrics::time_state_transition();
8888
```
8989

90+
### Logging Patterns
91+
92+
**Use tracing shorthand syntax for cleaner logs:**
93+
```rust
94+
// ✅ GOOD: Shorthand for simple variables
95+
let slot = block.slot;
96+
let proposer = block.proposer_index;
97+
info!(
98+
%slot, // Shorthand for slot = %slot (Display)
99+
proposer, // Shorthand for proposer = proposer
100+
block_root = %ShortRoot(&block_root.0), // Named expression
101+
"Block imported"
102+
);
103+
104+
// ❌ BAD: Verbose
105+
info!(
106+
slot = %slot,
107+
proposer = proposer,
108+
...
109+
);
110+
```
111+
112+
**Standardized field ordering (temporal → identity → identifiers → context → metadata):**
113+
```rust
114+
// Block logs
115+
info!(%slot, proposer, block_root = ..., parent_root = ..., attestation_count, "...");
116+
117+
// Attestation logs
118+
info!(%slot, validator, target_slot, target_root = ..., source_slot, source_root = ..., "...");
119+
120+
// Consensus events
121+
info!(finalized_slot, finalized_root = ..., previous_finalized, justified_slot, "...");
122+
123+
// Peer events
124+
info!(%peer_id, %direction, peer_count, our_finalized_slot, our_head_slot, "...");
125+
```
126+
127+
**Root hash truncation:**
128+
```rust
129+
use ethlambda_types::ShortRoot;
130+
131+
// Always use ShortRoot for consistent 8-char display (4 bytes)
132+
info!(block_root = %ShortRoot(&root.0), "...");
133+
```
134+
90135
### Relative Indexing (justified_slots)
91136
```rust
92137
// Bounded storage: index relative to finalized_slot

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.

crates/blockchain/src/lib.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use ethlambda_state_transition::is_proposer;
55
use ethlambda_storage::Store;
66
use ethlambda_types::primitives::H256;
77
use ethlambda_types::{
8+
ShortRoot,
89
attestation::{Attestation, AttestationData, SignedAttestation},
910
block::{BlockSignatures, BlockWithAttestation, SignedBlockWithAttestation},
1011
primitives::TreeHash,
@@ -277,6 +278,7 @@ impl BlockChainServer {
277278
let slot = signed_block.message.block.slot;
278279
let block_root = signed_block.message.block.tree_hash_root();
279280
let parent_root = signed_block.message.block.parent_root;
281+
let proposer = signed_block.message.block.proposer_index;
280282

281283
// Check if parent block exists before attempting to process
282284
if !self.store.contains_block(&parent_root) {
@@ -296,13 +298,26 @@ impl BlockChainServer {
296298
// Parent exists, proceed with processing
297299
match self.process_block(signed_block) {
298300
Ok(_) => {
299-
info!(%slot, "Block processed successfully");
301+
info!(
302+
%slot,
303+
proposer,
304+
block_root = %ShortRoot(&block_root.0),
305+
parent_root = %ShortRoot(&parent_root.0),
306+
"Block imported successfully"
307+
);
300308

301309
// Check if any pending blocks can now be processed
302310
self.process_pending_children(block_root);
303311
}
304312
Err(err) => {
305-
warn!(%slot, %err, "Failed to process block");
313+
warn!(
314+
%slot,
315+
proposer,
316+
block_root = %ShortRoot(&block_root.0),
317+
parent_root = %ShortRoot(&parent_root.0),
318+
%err,
319+
"Failed to process block"
320+
);
306321
}
307322
}
308323
}

crates/blockchain/src/store.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use ethlambda_state_transition::{
66
};
77
use ethlambda_storage::{ForkCheckpoints, SignatureKey, Store};
88
use ethlambda_types::{
9+
ShortRoot,
910
attestation::{AggregatedAttestation, Attestation, AttestationData, SignedAttestation},
1011
block::{
1112
AggregatedAttestations, AggregatedSignatureProof, AggregationBits, Block, BlockBody,
@@ -43,6 +44,22 @@ fn update_head(store: &mut Store) {
4344
info!(%old_head, %new_head, "Fork choice reorg detected");
4445
}
4546
store.update_checkpoints(ForkCheckpoints::head_only(new_head));
47+
48+
if old_head != new_head {
49+
let old_slot = store.get_block(&old_head).map(|b| b.slot).unwrap_or(0);
50+
let new_slot = store.get_block(&new_head).map(|b| b.slot).unwrap_or(0);
51+
let justified_slot = store.latest_justified().slot;
52+
let finalized_slot = store.latest_finalized().slot;
53+
info!(
54+
head_slot = new_slot,
55+
head_root = %ShortRoot(&new_head.0),
56+
previous_head_slot = old_slot,
57+
previous_head_root = %ShortRoot(&old_head.0),
58+
justified_slot,
59+
finalized_slot,
60+
"Fork choice head updated"
61+
);
62+
}
4663
}
4764

4865
/// Update the safe target for attestation.
@@ -196,7 +213,7 @@ pub fn on_gossip_attestation(
196213
return Err(StoreError::SignatureVerificationFailed);
197214
}
198215
}
199-
on_attestation(store, attestation, false)?;
216+
on_attestation(store, attestation.clone(), false)?;
200217

201218
if cfg!(not(feature = "skip-signature-verification")) {
202219
// Store signature for later lookup during block building
@@ -206,6 +223,20 @@ pub fn on_gossip_attestation(
206223
store.insert_gossip_signature(signature_key, signature);
207224
}
208225
metrics::inc_attestations_valid("gossip");
226+
227+
let slot = attestation.data.slot;
228+
let target_slot = attestation.data.target.slot;
229+
let source_slot = attestation.data.source.slot;
230+
info!(
231+
slot,
232+
validator = validator_id,
233+
target_slot,
234+
target_root = %ShortRoot(&attestation.data.target.root.0),
235+
source_slot,
236+
source_root = %ShortRoot(&attestation.data.source.root.0),
237+
"Attestation processed"
238+
);
239+
209240
Ok(())
210241
}
211242

crates/blockchain/state_transition/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ ethlambda-types.workspace = true
1414
ethlambda-metrics.workspace = true
1515

1616
thiserror.workspace = true
17+
tracing.workspace = true
1718

1819
[dev-dependencies]
1920
serde.workspace = true

crates/blockchain/state_transition/src/lib.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use std::collections::HashMap;
22

33
use ethlambda_types::{
4+
ShortRoot,
45
block::{AggregatedAttestations, Block, BlockHeader},
56
primitives::{H256, TreeHash},
67
state::{Checkpoint, JustificationValidators, State},
78
};
9+
use tracing::info;
810

911
mod justified_slots_ops;
1012
pub mod metrics;
@@ -296,6 +298,16 @@ fn process_attestations(
296298
target.slot,
297299
);
298300

301+
let justified_slot = target.slot;
302+
let threshold = (2 * validator_count).div_ceil(3);
303+
info!(
304+
justified_slot,
305+
justified_root = %ShortRoot(&target.root.0),
306+
vote_count,
307+
threshold,
308+
"Checkpoint justified"
309+
);
310+
299311
justifications.remove(&target.root);
300312

301313
// Consider whether finalization can advance.
@@ -307,6 +319,17 @@ fn process_attestations(
307319
state.latest_finalized = source;
308320
metrics::inc_finalizations("success");
309321

322+
let finalized_slot = source.slot;
323+
let previous_finalized = old_finalized_slot;
324+
let justified_slot = state.latest_justified.slot;
325+
info!(
326+
finalized_slot,
327+
finalized_root = %ShortRoot(&source.root.0),
328+
previous_finalized,
329+
justified_slot,
330+
"Checkpoint finalized"
331+
);
332+
310333
// Shift window to drop finalized slots from the front
311334
let delta = (state.latest_finalized.slot - old_finalized_slot) as usize;
312335
justified_slots_ops::shift_window(&mut state.justified_slots, delta);

crates/common/types/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,15 @@ pub mod genesis;
44
pub mod primitives;
55
pub mod signature;
66
pub mod state;
7+
8+
/// Display helper for truncated root hashes (8 hex chars)
9+
pub struct ShortRoot<'a>(pub &'a [u8; 32]);
10+
11+
impl std::fmt::Display for ShortRoot<'_> {
12+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13+
for byte in &self.0[..4] {
14+
write!(f, "{:02x}", byte)?;
15+
}
16+
Ok(())
17+
}
18+
}

crates/net/p2p/src/gossipsub/handler.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
use ethlambda_types::{attestation::SignedAttestation, block::SignedBlockWithAttestation};
1+
use ethlambda_types::{
2+
ShortRoot, attestation::SignedAttestation, block::SignedBlockWithAttestation,
3+
};
24
use libp2p::gossipsub::Event;
35
use ssz::{Decode, Encode};
46
use tracing::{error, info, trace};
7+
use tree_hash::TreeHash;
58

69
use super::{
710
encoding::{compress_message, decompress_message},
@@ -32,7 +35,18 @@ pub async fn handle_gossipsub_message(server: &mut P2PServer, event: Event) {
3235
return;
3336
};
3437
let slot = signed_block.message.block.slot;
35-
info!(%slot, "Received new block from gossipsub, sending for processing");
38+
let block_root = signed_block.message.block.tree_hash_root();
39+
let proposer = signed_block.message.block.proposer_index;
40+
let parent_root = signed_block.message.block.parent_root;
41+
let attestation_count = signed_block.message.block.body.attestations.len();
42+
info!(
43+
%slot,
44+
proposer,
45+
block_root = %ShortRoot(&block_root.0),
46+
parent_root = %ShortRoot(&parent_root.0),
47+
attestation_count,
48+
"Received block from gossip"
49+
);
3650
server.blockchain.notify_new_block(signed_block).await;
3751
}
3852
Some(ATTESTATION_TOPIC_KIND) => {
@@ -49,7 +63,16 @@ pub async fn handle_gossipsub_message(server: &mut P2PServer, event: Event) {
4963
};
5064
let slot = signed_attestation.message.slot;
5165
let validator = signed_attestation.validator_id;
52-
info!(%slot, %validator, "Received new attestation from gossipsub, sending for processing");
66+
info!(
67+
%slot,
68+
validator,
69+
head_root = %ShortRoot(&signed_attestation.message.head.root.0),
70+
target_slot = signed_attestation.message.target.slot,
71+
target_root = %ShortRoot(&signed_attestation.message.target.root.0),
72+
source_slot = signed_attestation.message.source.slot,
73+
source_root = %ShortRoot(&signed_attestation.message.source.root.0),
74+
"Received attestation from gossip"
75+
);
5376
server
5477
.blockchain
5578
.notify_new_attestation(signed_attestation)
@@ -77,7 +100,15 @@ pub async fn publish_attestation(server: &mut P2PServer, attestation: SignedAtte
77100
.behaviour_mut()
78101
.gossipsub
79102
.publish(server.attestation_topic.clone(), compressed)
80-
.inspect(|_| trace!(%slot, %validator, "Published attestation to gossipsub"))
103+
.inspect(|_| info!(
104+
%slot,
105+
validator,
106+
target_slot = attestation.message.target.slot,
107+
target_root = %ShortRoot(&attestation.message.target.root.0),
108+
source_slot = attestation.message.source.slot,
109+
source_root = %ShortRoot(&attestation.message.source.root.0),
110+
"Published attestation to gossipsub"
111+
))
81112
.inspect_err(|err| {
82113
tracing::warn!(%slot, %validator, %err, "Failed to publish attestation to gossipsub")
83114
});
@@ -86,6 +117,9 @@ pub async fn publish_attestation(server: &mut P2PServer, attestation: SignedAtte
86117
pub async fn publish_block(server: &mut P2PServer, signed_block: SignedBlockWithAttestation) {
87118
let slot = signed_block.message.block.slot;
88119
let proposer = signed_block.message.block.proposer_index;
120+
let block_root = signed_block.message.block.tree_hash_root();
121+
let parent_root = signed_block.message.block.parent_root;
122+
let attestation_count = signed_block.message.block.body.attestations.len();
89123

90124
// Encode to SSZ
91125
let ssz_bytes = signed_block.as_ssz_bytes();
@@ -99,7 +133,16 @@ pub async fn publish_block(server: &mut P2PServer, signed_block: SignedBlockWith
99133
.behaviour_mut()
100134
.gossipsub
101135
.publish(server.block_topic.clone(), compressed)
102-
.inspect(|_| info!(%slot, %proposer, "Published block to gossipsub"))
136+
.inspect(|_| {
137+
info!(
138+
%slot,
139+
proposer,
140+
block_root = %ShortRoot(&block_root.0),
141+
parent_root = %ShortRoot(&parent_root.0),
142+
attestation_count,
143+
"Published block to gossipsub"
144+
)
145+
})
103146
.inspect_err(
104147
|err| tracing::warn!(%slot, %proposer, %err, "Failed to publish block to gossipsub"),
105148
);

crates/net/p2p/src/lib.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,20 @@ async fn handle_swarm_event(server: &mut P2PServer, event: SwarmEvent<BehaviourE
246246
let direction = connection_direction(&endpoint);
247247
if num_established.get() == 1 {
248248
server.connected_peers.insert(peer_id);
249+
let peer_count = server.connected_peers.len();
249250
metrics::notify_peer_connected(&Some(peer_id), direction, "success");
250251
// Send status request on first connection to this peer
251252
let our_status = build_status(&server.store);
252-
info!(%peer_id, %direction, finalized_slot=%our_status.finalized.slot, head_slot=%our_status.head.slot, "Added connection to new peer, sending status request");
253+
let our_finalized_slot = our_status.finalized.slot;
254+
let our_head_slot = our_status.head.slot;
255+
info!(
256+
%peer_id,
257+
%direction,
258+
peer_count,
259+
our_finalized_slot,
260+
our_head_slot,
261+
"Peer connected"
262+
);
253263
server
254264
.swarm
255265
.behaviour_mut()
@@ -290,9 +300,18 @@ async fn handle_swarm_event(server: &mut P2PServer, event: SwarmEvent<BehaviourE
290300
};
291301
if num_established == 0 {
292302
server.connected_peers.remove(&peer_id);
303+
let peer_count = server.connected_peers.len();
293304
metrics::notify_peer_disconnected(&Some(peer_id), direction, reason);
305+
info!(
306+
%peer_id,
307+
%direction,
308+
%reason,
309+
peer_count,
310+
"Peer disconnected"
311+
);
312+
} else {
313+
info!(%peer_id, %direction, %reason, "Peer connection closed but other connections remain");
294314
}
295-
info!(%peer_id, %direction, %reason, "Peer disconnected");
296315
}
297316
SwarmEvent::OutgoingConnectionError { peer_id, error, .. } => {
298317
let result = if error.to_string().to_lowercase().contains("timed out") {

0 commit comments

Comments
 (0)