Skip to content

Commit 254ea9e

Browse files
authored
chore: various updates to improve debugging apps (#123)
* disable conn pooling in oauth job and reduce log level * avoid recording the current span twice * support including file and line number in log lines * update env var handling * add config trait
1 parent ba1b284 commit 254ea9e

File tree

9 files changed

+358
-67
lines changed

9 files changed

+358
-67
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ tokio = { version = "1.36.0", optional = true }
4848

4949
# Other
5050
axum = "0.8.1"
51+
eyre = { version = "0.6.12", optional = true }
5152
serde = { version = "1", features = ["derive"] }
5253
thiserror = "2.0.11"
5354
tower = "0.5.2"
@@ -72,7 +73,7 @@ tokio = { version = "1.43.0", features = ["macros"] }
7273
default = ["alloy", "rustls"]
7374
alloy = ["dep:alloy"]
7475
aws = ["alloy", "alloy?/signer-aws", "dep:async-trait", "dep:aws-config", "dep:aws-sdk-kms"]
75-
perms = ["dep:oauth2", "dep:tokio", "dep:reqwest", "dep:signet-tx-cache", "dep:futures-util"]
76+
perms = ["dep:eyre", "dep:oauth2", "dep:tokio", "dep:reqwest", "dep:signet-tx-cache", "dep:futures-util"]
7677
pylon = ["perms", "alloy/kzg"]
7778
block_watcher = ["dep:tokio"]
7879
rustls = ["dep:rustls", "rustls/aws-lc-rs"]

examples/ajj.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,29 @@
2323
//! http://localhost:8080/rpc
2424
//! ```
2525
use ajj::Router;
26-
use init4_bin_base::init4;
26+
use init4_bin_base::{
27+
utils::{from_env::FromEnv, metrics::MetricsConfig, tracing::TracingConfig},
28+
Init4Config,
29+
};
30+
31+
#[derive(Debug, FromEnv)]
32+
struct Config {
33+
tracing: TracingConfig,
34+
metrics: MetricsConfig,
35+
}
36+
37+
impl Init4Config for Config {
38+
fn tracing(&self) -> &TracingConfig {
39+
&self.tracing
40+
}
41+
fn metrics(&self) -> &MetricsConfig {
42+
&self.metrics
43+
}
44+
}
2745

2846
#[tokio::main]
2947
async fn main() -> Result<(), Box<dyn std::error::Error>> {
30-
let _guard = init4();
48+
let _config_and_guard = init4_bin_base::init::<Config>()?;
3149

3250
let router = Router::<()>::new()
3351
.route("helloWorld", || async {

examples/build-helper.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
1-
use init4_bin_base::init4;
1+
use init4_bin_base::{
2+
utils::{from_env::FromEnv, metrics::MetricsConfig, tracing::TracingConfig},
3+
Init4Config,
4+
};
25
use std::sync::{atomic::AtomicBool, Arc};
36

4-
fn main() {
7+
#[derive(Debug, FromEnv)]
8+
struct Config {
9+
tracing: TracingConfig,
10+
metrics: MetricsConfig,
11+
}
12+
13+
impl Init4Config for Config {
14+
fn tracing(&self) -> &TracingConfig {
15+
&self.tracing
16+
}
17+
fn metrics(&self) -> &MetricsConfig {
18+
&self.metrics
19+
}
20+
}
21+
22+
fn main() -> eyre::Result<()> {
523
let term: Arc<AtomicBool> = Default::default();
624

725
let _ = signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&term));
826

9-
init4();
27+
let _config_and_guard = init4_bin_base::init::<Config>()?;
28+
Ok(())
1029
}

examples/otlp-export.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,41 @@
77
//!
88
//! It can be killed via sigint or sigterm
99
10+
use eyre::WrapErr;
1011
use init4_bin_base::{
1112
deps::tracing::{info, info_span},
12-
init4,
13+
utils::{from_env::FromEnv, metrics::MetricsConfig, tracing::TracingConfig},
14+
Init4Config,
1315
};
1416
use std::sync::{
1517
atomic::{AtomicBool, Ordering},
1618
Arc,
1719
};
1820

21+
#[derive(Debug, FromEnv)]
22+
struct Config {
23+
tracing: TracingConfig,
24+
metrics: MetricsConfig,
25+
}
26+
27+
impl Init4Config for Config {
28+
fn tracing(&self) -> &TracingConfig {
29+
&self.tracing
30+
}
31+
fn metrics(&self) -> &MetricsConfig {
32+
&self.metrics
33+
}
34+
}
35+
1936
#[tokio::main]
20-
async fn main() -> Result<(), std::io::Error> {
37+
async fn main() -> eyre::Result<()> {
2138
let term: Arc<AtomicBool> = Default::default();
22-
signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&term))?;
23-
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&term))?;
39+
signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&term))
40+
.wrap_err("failed to register SIGTERM hook")?;
41+
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&term))
42+
.wrap_err("failed to register SIGINT hook")?;
2443

25-
let _guard = init4();
44+
let _config_and_guard = init4_bin_base::init::<Config>()?;
2645
let mut counter = 0;
2746
let _outer = info_span!("outer span").entered();
2847

src/lib.rs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
#![deny(unused_must_use, rust_2018_idioms)]
1313
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
1414

15+
use crate::utils::{
16+
from_env::{FromEnv, FromEnvErr},
17+
metrics::MetricsConfig,
18+
otlp::OtelGuard,
19+
tracing::TracingConfig,
20+
};
21+
1522
#[cfg(feature = "perms")]
1623
/// Permissioning and authorization utilities for Signet builders.
1724
pub mod perms;
@@ -85,7 +92,8 @@ pub mod deps {
8592
///
8693
/// [`init_tracing`]: utils::tracing::init_tracing
8794
/// [`init_metrics`]: utils::metrics::init_metrics
88-
pub fn init4() -> Option<utils::otlp::OtelGuard> {
95+
#[deprecated(since = "0.18.0-rc.11", note = "use `init` instead")]
96+
pub fn init4() -> Option<OtelGuard> {
8997
let guard = utils::tracing::init_tracing();
9098
utils::metrics::init_metrics();
9199

@@ -96,3 +104,72 @@ pub fn init4() -> Option<utils::otlp::OtelGuard> {
96104

97105
guard
98106
}
107+
108+
/// Trait for config types that can be used with [`init`].
109+
///
110+
/// Implementors must provide access to [`TracingConfig`] and [`MetricsConfig`],
111+
/// and must be loadable from the environment via [`FromEnv`].
112+
///
113+
/// # Example
114+
///
115+
/// ```ignore
116+
/// #[derive(Debug, FromEnv)]
117+
/// pub struct MyConfig {
118+
/// pub tracing: TracingConfig,
119+
/// pub metrics: MetricsConfig,
120+
/// #[from_env(var = "MY_THING", desc = "some app-specific value")]
121+
/// pub my_thing: String,
122+
/// }
123+
///
124+
/// impl Init4Config for MyConfig {
125+
/// fn tracing(&self) -> &TracingConfig { &self.tracing }
126+
/// fn metrics(&self) -> &MetricsConfig { &self.metrics }
127+
/// }
128+
/// ```
129+
pub trait Init4Config: FromEnv {
130+
/// Get the tracing configuration.
131+
fn tracing(&self) -> &TracingConfig;
132+
/// Get the metrics configuration.
133+
fn metrics(&self) -> &MetricsConfig;
134+
}
135+
136+
/// The result of [`init`]: the loaded config and an optional OTLP guard.
137+
///
138+
/// The [`OtelGuard`] (if present) must be kept alive for the lifetime of the
139+
/// program to ensure the OTLP exporter continues to send data.
140+
#[derive(Debug)]
141+
pub struct ConfigAndGuard<T> {
142+
/// The loaded configuration.
143+
pub config: T,
144+
/// The OTLP guard, if OTLP was enabled.
145+
pub guard: Option<OtelGuard>,
146+
}
147+
148+
/// Load config from the environment and initialize metrics and tracing.
149+
///
150+
/// This will perform the following:
151+
/// - Load `T` from environment variables via [`FromEnv`]
152+
/// - Read tracing configuration from the loaded config
153+
/// - Determine whether to enable OTLP
154+
/// - Install a global tracing subscriber, using the OTLP provider if enabled
155+
/// - Read metrics configuration from the loaded config
156+
/// - Install a global metrics recorder and serve it over HTTP on 0.0.0.0
157+
///
158+
/// See [`init_tracing`] and [`init_metrics`] for more
159+
/// details on specific actions taken and env vars read.
160+
///
161+
/// [`init_tracing`]: utils::tracing::init_tracing
162+
/// [`init_metrics`]: utils::metrics::init_metrics
163+
pub fn init<T: Init4Config>() -> Result<ConfigAndGuard<T>, FromEnvErr> {
164+
let config = T::from_env()?;
165+
166+
let guard = utils::tracing::init_tracing_with_config(config.tracing().clone());
167+
utils::metrics::init_metrics_with_config(*config.metrics());
168+
169+
// This will install the AWS-LC-Rust TLS provider for rustls, if no other
170+
// provider has been installed yet
171+
#[cfg(feature = "rustls")]
172+
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
173+
174+
Ok(ConfigAndGuard { config, guard })
175+
}

src/perms/oauth.rs

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
//! Service responsible for authenticating with the cache with Oauth tokens.
22
//! This authenticator periodically fetches a new token every set amount of seconds.
3-
use crate::{deps::tracing::error, utils::from_env::FromEnv};
4-
use core::{error::Error, fmt};
3+
use crate::{
4+
deps::tracing::{debug, warn, Instrument},
5+
utils::from_env::FromEnv,
6+
};
7+
use core::fmt;
8+
use eyre::eyre;
59
use oauth2::{
610
basic::{BasicClient, BasicTokenType},
711
AccessToken, AuthUrl, ClientId, ClientSecret, EmptyExtraTokenFields, EndpointNotSet,
@@ -12,8 +16,8 @@ use std::{future::IntoFuture, pin::Pin};
1216
use tokio::{
1317
sync::watch::{self, Ref},
1418
task::JoinHandle,
19+
time::MissedTickBehavior,
1520
};
16-
use tracing::{debug, Instrument};
1721

1822
type Token = StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>;
1923

@@ -88,10 +92,16 @@ impl Authenticator {
8892
.set_auth_uri(AuthUrl::from_url(config.oauth_authenticate_url.clone()))
8993
.set_token_uri(TokenUrl::from_url(config.oauth_token_url.clone()));
9094

91-
// NB: this is MANDATORY
95+
// NB: redirect policy none is MANDATORY
9296
// https://docs.rs/oauth2/latest/oauth2/#security-warning
97+
//
98+
// Disable connection pooling to avoid stale connection errors.
99+
// OAuth refreshes are infrequent (typically every 60s), so idle
100+
// connections are almost always closed by the server or
101+
// intermediary (e.g. Istio envoy) before the next request.
93102
let rq_client = reqwest::Client::builder()
94103
.redirect(reqwest::redirect::Policy::none())
104+
.pool_max_idle_per_host(0)
95105
.build()
96106
.unwrap();
97107

@@ -159,31 +169,20 @@ impl Authenticator {
159169

160170
/// Create a future that contains the periodic refresh loop.
161171
async fn task_future(self) {
162-
let interval = self.config.oauth_token_refresh_interval;
172+
let duration = tokio::time::Duration::from_secs(self.config.oauth_token_refresh_interval);
173+
let mut interval = tokio::time::interval(duration);
174+
interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
163175

164176
loop {
177+
interval.tick().await;
165178
debug!("Refreshing oauth token");
166179
match self.authenticate().await {
167-
Ok(_) => {
168-
debug!("Successfully refreshed oauth token");
169-
}
170-
Err(err) => {
171-
let mut current = &err as &dyn Error;
172-
173-
// This is a little hacky, but the oauth library nests
174-
// errors quite deeply, so we need to walk the source chain
175-
// to get the full picture.
176-
let mut source_chain = Vec::new();
177-
while let Some(source) = current.source() {
178-
source_chain.push(source.to_string());
179-
current = source;
180-
}
181-
let source_chain = source_chain.join("\n\n Caused by: \n");
182-
183-
error!(%err, %source_chain, "Failed to refresh oauth token");
184-
}
180+
Ok(_) => debug!("Successfully refreshed oauth token"),
181+
Err(error) => warn!(
182+
error = %format!("{:#}", eyre!(error)),
183+
"Failed to refresh oauth token"
184+
),
185185
};
186-
let _sleep = tokio::time::sleep(tokio::time::Duration::from_secs(interval)).await;
187186
}
188187
}
189188

0 commit comments

Comments
 (0)