Skip to content

Commit 21cef69

Browse files
committed
Add more instrumentation
1 parent 0a2e928 commit 21cef69

File tree

10 files changed

+370
-63
lines changed

10 files changed

+370
-63
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.

examples/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.

linera-base/src/tracing.rs

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ use std::{
1212

1313
use is_terminal::IsTerminal as _;
1414
use tracing::Subscriber;
15+
#[cfg(all(not(target_arch = "wasm32"), feature = "tempo"))]
16+
use tracing_subscriber::filter::{filter_fn, FilterExt as _};
1517
use tracing_subscriber::{
16-
filter::filter_fn,
1718
fmt::{
1819
self,
1920
format::{FmtSpan, Format, Full},
@@ -59,20 +60,33 @@ pub fn init(log_name: &str) {
5960
/// ## Span Filtering for Performance
6061
///
6162
/// By default, spans created by `#[instrument]` are logged to console AND sent
62-
/// to OpenTelemetry. To disable console output for performance-critical functions
63-
/// while keeping OpenTelemetry tracing, use:
63+
/// to OpenTelemetry. In order to not spam stderr, you can set a low level, and use the
64+
/// `telemetry_only` target:
6465
///
6566
/// ```rust
66-
/// use tracing::instrument;
67+
/// use tracing::{instrument, Level};
68+
///
69+
/// // Always sent to telemetry; console output controlled by level
70+
/// #[instrument(level = "trace", target = "telemetry_only")]
71+
/// fn my_called_too_frequently_function() {
72+
/// // Will be sent to OpenTelemetry regardless of RUST_LOG level
73+
/// // Will only appear in console if RUST_LOG includes trace level
74+
/// }
6775
///
68-
/// #[instrument(target = "telemetry_only")]
69-
/// fn my_performance_critical_function() {
70-
/// // This span will ONLY be sent to OpenTelemetry, not logged to console
76+
/// // Higher level - more likely to appear in console
77+
/// #[instrument(level = "info", target = "telemetry_only")]
78+
/// fn my_important_function() {
79+
/// // Will be sent to OpenTelemetry regardless of RUST_LOG level
80+
/// // Will appear in console if RUST_LOG includes info level or higher
7181
/// }
7282
/// ```
7383
///
74-
/// All explicit log calls (tracing::info!(), etc.) are always printed regardless
75-
/// of span filtering.
84+
/// **Key behaviors:**
85+
/// - If span level >= RUST_LOG level: span goes to BOTH telemetry AND console (regardless of target)
86+
/// - If span level < RUST_LOG level AND target = "telemetry_only": span goes to telemetry ONLY
87+
/// - If span level < RUST_LOG level AND target != "telemetry_only": span is filtered out completely
88+
/// - Default level for `telemetry_only` should be `trace` for minimal console noise
89+
/// - All explicit log calls (tracing::info!(), etc.) are always printed regardless of span filtering
7690
pub async fn init_with_opentelemetry(log_name: &str) {
7791
#[cfg(feature = "tempo")]
7892
{
@@ -102,28 +116,13 @@ fn init_internal(log_name: &str, with_opentelemetry: bool) {
102116
let color_output =
103117
!std::env::var("NO_COLOR").is_ok_and(|x| !x.is_empty()) && std::io::stderr().is_terminal();
104118

105-
// Create a filter that:
106-
// 1. Allows all explicit events (tracing::info!(), etc.)
107-
// 2. Allows spans from #[instrument] by default
108-
// 3. Blocks spans ONLY if they have target = "telemetry_only"
109-
let console_filter = filter_fn(|metadata| {
110-
if metadata.is_span() {
111-
// Block spans that explicitly request telemetry-only via target = "telemetry_only"
112-
metadata.target() != "telemetry_only"
113-
} else {
114-
// Always allow explicit log events (tracing::info!(), etc.)
115-
true
116-
}
117-
});
118-
119119
let stderr_layer = prepare_formatted_layer(
120120
format.as_deref(),
121121
fmt::layer()
122122
.with_span_events(span_events.clone())
123123
.with_writer(std::io::stderr)
124124
.with_ansi(color_output),
125-
)
126-
.with_filter(console_filter.clone());
125+
);
127126

128127
let maybe_log_file_layer = open_log_file(log_name).map(|file_writer| {
129128
prepare_formatted_layer(
@@ -133,7 +132,6 @@ fn init_internal(log_name: &str, with_opentelemetry: bool) {
133132
.with_writer(Arc::new(file_writer))
134133
.with_ansi(false),
135134
)
136-
.with_filter(console_filter.clone())
137135
});
138136

139137
#[cfg(any(target_arch = "wasm32", not(feature = "tempo")))]
@@ -173,7 +171,18 @@ fn init_internal(log_name: &str, with_opentelemetry: bool) {
173171
global::set_tracer_provider(tracer_provider.clone());
174172

175173
let tracer = tracer_provider.tracer("linera");
176-
let opentelemetry_layer = OpenTelemetryLayer::new(tracer);
174+
175+
let telemetry_only_filter =
176+
filter_fn(|metadata| metadata.is_span() && metadata.target() == "telemetry_only");
177+
178+
let otel_env_filter = tracing_subscriber::EnvFilter::builder()
179+
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
180+
.from_env_lossy();
181+
182+
let opentelemetry_filter = otel_env_filter.or(telemetry_only_filter);
183+
184+
let opentelemetry_layer =
185+
OpenTelemetryLayer::new(tracer).with_filter(opentelemetry_filter);
177186

178187
tracing_subscriber::registry()
179188
.with(env_filter)

linera-chain/src/chain.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use linera_views::{
3535
views::{ClonableView, CryptoHashView, RootView, View},
3636
};
3737
use serde::{Deserialize, Serialize};
38+
use tracing::instrument;
3839

3940
use crate::{
4041
block::{Block, ConfirmedBlock},
@@ -391,6 +392,9 @@ where
391392
self.context().extra().chain_id()
392393
}
393394

395+
#[instrument(target = "telemetry_only", skip_all, fields(
396+
chain_id = %self.chain_id(),
397+
))]
394398
pub async fn query_application(
395399
&mut self,
396400
local_time: Timestamp,
@@ -408,6 +412,10 @@ where
408412
.with_execution_context(ChainExecutionContext::Query)
409413
}
410414

415+
#[instrument(target = "telemetry_only", skip_all, fields(
416+
chain_id = %self.chain_id(),
417+
application_id = %application_id
418+
))]
411419
pub async fn describe_application(
412420
&mut self,
413421
application_id: ApplicationId,
@@ -419,6 +427,11 @@ where
419427
.with_execution_context(ChainExecutionContext::DescribeApplication)
420428
}
421429

430+
#[instrument(target = "telemetry_only", skip_all, fields(
431+
chain_id = %self.chain_id(),
432+
target = %target,
433+
height = %height
434+
))]
422435
pub async fn mark_messages_as_received(
423436
&mut self,
424437
target: &ChainId,
@@ -506,6 +519,9 @@ where
506519

507520
/// Verifies that this chain is up-to-date and all the messages executed ahead of time
508521
/// have been properly received by now.
522+
#[instrument(target = "telemetry_only", skip_all, fields(
523+
chain_id = %self.chain_id()
524+
))]
509525
pub async fn validate_incoming_bundles(&self) -> Result<(), ChainError> {
510526
let chain_id = self.chain_id();
511527
let pairs = self.inboxes.try_load_all_entries().await?;
@@ -567,6 +583,11 @@ where
567583
/// round timeouts.
568584
///
569585
/// Returns `true` if incoming `Subscribe` messages created new outbox entries.
586+
#[instrument(target = "telemetry_only", skip_all, fields(
587+
chain_id = %self.chain_id(),
588+
origin = %origin,
589+
bundle_height = %bundle.height
590+
))]
570591
pub async fn receive_message_bundle(
571592
&mut self,
572593
origin: &ChainId,
@@ -661,6 +682,9 @@ where
661682
}
662683

663684
/// Removes the incoming message bundles in the block from the inboxes.
685+
#[instrument(target = "telemetry_only", skip_all, fields(
686+
chain_id = %self.chain_id(),
687+
))]
664688
pub async fn remove_bundles_from_inboxes(
665689
&mut self,
666690
timestamp: Timestamp,
@@ -750,6 +774,10 @@ where
750774

751775
/// Executes a block: first the incoming messages, then the main operation.
752776
/// Does not update chain state other than the execution state.
777+
#[instrument(target = "telemetry_only", skip_all, fields(
778+
chain_id = %block.chain_id,
779+
block_height = %block.height
780+
))]
753781
#[expect(clippy::too_many_arguments)]
754782
async fn execute_block_inner(
755783
chain: &mut ExecutionStateView<C>,
@@ -859,6 +887,10 @@ where
859887

860888
/// Executes a block: first the incoming messages, then the main operation.
861889
/// Does not update chain state other than the execution state.
890+
#[instrument(target = "telemetry_only", skip_all, fields(
891+
chain_id = %self.chain_id(),
892+
block_height = %block.height
893+
))]
862894
pub async fn execute_block(
863895
&mut self,
864896
block: &ProposedBlock,
@@ -919,6 +951,10 @@ where
919951
/// Applies an execution outcome to the chain, updating the outboxes, state hash and chain
920952
/// manager. This does not touch the execution state itself, which must be updated separately.
921953
/// Returns the set of event streams that were updated as a result of applying the block.
954+
#[instrument(target = "telemetry_only", skip_all, fields(
955+
chain_id = %self.chain_id(),
956+
block_height = %block.inner().inner().header.height
957+
))]
922958
pub async fn apply_confirmed_block(
923959
&mut self,
924960
block: &ConfirmedBlock,
@@ -953,6 +989,10 @@ where
953989

954990
/// Adds a block to `preprocessed_blocks`, and updates the outboxes where possible.
955991
/// Returns the set of streams that were updated as a result of preprocessing the block.
992+
#[instrument(target = "telemetry_only", skip_all, fields(
993+
chain_id = %self.chain_id(),
994+
block_height = %block.inner().inner().header.height
995+
))]
956996
pub async fn preprocess_block(
957997
&mut self,
958998
block: &ConfirmedBlock,
@@ -979,6 +1019,10 @@ where
9791019
}
9801020

9811021
/// Verifies that the block is valid according to the chain's application permission settings.
1022+
#[instrument(target = "telemetry_only", skip_all, fields(
1023+
block_height = %block.height,
1024+
num_transactions = %block.transactions.len()
1025+
))]
9821026
fn check_app_permissions(
9831027
app_permissions: &ApplicationPermissions,
9841028
block: &ProposedBlock,
@@ -1021,6 +1065,10 @@ where
10211065
}
10221066

10231067
/// Returns the hashes of all blocks we have in the given range.
1068+
#[instrument(target = "telemetry_only", skip_all, fields(
1069+
chain_id = %self.chain_id(),
1070+
next_block_height = %self.tip_state.get().next_block_height
1071+
))]
10241072
pub async fn block_hashes(
10251073
&self,
10261074
range: impl RangeBounds<BlockHeight>,
@@ -1066,6 +1114,10 @@ where
10661114
/// Updates the outboxes with the messages sent in the block.
10671115
///
10681116
/// Returns the set of all recipients.
1117+
#[instrument(target = "telemetry_only", skip_all, fields(
1118+
chain_id = %self.chain_id(),
1119+
block_height = %block.header.height
1120+
))]
10691121
async fn process_outgoing_messages(
10701122
&mut self,
10711123
block: &Block,
@@ -1157,6 +1209,10 @@ where
11571209
/// Updates the event streams with events emitted by the block if they form a contiguous
11581210
/// sequence (might not be the case when preprocessing a block).
11591211
/// Returns the set of updated event streams.
1212+
#[instrument(target = "telemetry_only", skip_all, fields(
1213+
chain_id = %self.chain_id(),
1214+
block_height = %block.header.height
1215+
))]
11601216
async fn process_emitted_events(
11611217
&mut self,
11621218
block: &Block,

linera-core/src/chain_worker/actor.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ where
244244
/// Runs the worker until there are no more incoming requests.
245245
#[instrument(
246246
skip_all,
247-
fields(chain_id = format!("{:.8}", self.chain_id)),
247+
fields(chain_id = format!("{:.8}", self.chain_id), long_lived_services = %self.config.long_lived_services),
248248
)]
249249
async fn handle_requests(
250250
self,
@@ -276,9 +276,12 @@ where
276276
self.chain_id,
277277
service_runtime_endpoint,
278278
)
279+
.instrument(span.clone())
279280
.await?;
280281

281-
Box::pin(worker.handle_request(request).instrument(span)).await;
282+
Box::pin(worker.handle_request(request))
283+
.instrument(span)
284+
.await;
282285

283286
loop {
284287
futures::select! {
@@ -287,7 +290,7 @@ where
287290
let Some((request, span)) = maybe_request else {
288291
break; // Request sender was dropped.
289292
};
290-
Box::pin(worker.handle_request(request).instrument(span)).await;
293+
Box::pin(worker.handle_request(request)).instrument(span).await;
291294
}
292295
}
293296
}

0 commit comments

Comments
 (0)