Skip to content

Commit 7b9e836

Browse files
committed
refactor(wasm): replace tracing-web with wasm-tracing, move logging to sdk-core
- Move platform-agnostic logging types (LoggingLevel, LoggingConfig, CrateLogFilter, SpanEvent) and filter logic to sdk-core so mobile can reuse them - Replace tracing-web with wasm-tracing to remove web-sys from the WASM dependency tree - Remove unused time crate dependency - Clean up dead .with_timer().without_time() chain
1 parent dfb07c2 commit 7b9e836

10 files changed

Lines changed: 470 additions & 249 deletions

File tree

Cargo.lock

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

crates/sdk-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ spansy = { workspace = true }
3333
thiserror = { workspace = true }
3434
tokio = { workspace = true, features = ["rt", "sync"] }
3535
tracing = { workspace = true }
36+
tracing-subscriber = { workspace = true }
3637
wasm-bindgen-futures = { workspace = true, optional = true }
3738

3839
[dev-dependencies]

crates/sdk-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub mod config;
4545
pub mod error;
4646
pub mod handler;
4747
pub mod io;
48+
pub mod logging;
4849
pub mod prover;
4950
mod spawn;
5051
pub mod types;

crates/sdk-core/src/logging.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//! Platform-agnostic logging configuration and filtering.
2+
//!
3+
//! This module provides logging types and filter logic that can be reused
4+
//! across platforms (WASM, iOS, Android, native). Each platform provides
5+
//! its own tracing Layer/writer — this module only handles configuration.
6+
7+
use serde::Deserialize;
8+
use tracing::{Level, Metadata};
9+
use tracing_subscriber::fmt::format::FmtSpan;
10+
11+
/// Logging verbosity level.
12+
#[derive(Debug, Default, Clone, Copy, Deserialize)]
13+
pub enum LoggingLevel {
14+
/// Disable all logging for this target.
15+
Off,
16+
/// Most verbose — includes all messages.
17+
Trace,
18+
/// Detailed debugging information.
19+
Debug,
20+
/// Informational messages (default).
21+
#[default]
22+
Info,
23+
/// Warnings only.
24+
Warn,
25+
/// Errors only.
26+
Error,
27+
}
28+
29+
impl LoggingLevel {
30+
/// Returns true if this level disables all logging.
31+
pub fn is_off(&self) -> bool {
32+
matches!(self, LoggingLevel::Off)
33+
}
34+
}
35+
36+
impl From<LoggingLevel> for Level {
37+
fn from(value: LoggingLevel) -> Self {
38+
match value {
39+
// Off maps to ERROR as a fallback, but is_off() should be checked first.
40+
LoggingLevel::Off => Level::ERROR,
41+
LoggingLevel::Trace => Level::TRACE,
42+
LoggingLevel::Debug => Level::DEBUG,
43+
LoggingLevel::Info => Level::INFO,
44+
LoggingLevel::Warn => Level::WARN,
45+
LoggingLevel::Error => Level::ERROR,
46+
}
47+
}
48+
}
49+
50+
/// Span lifecycle events to log.
51+
#[derive(Debug, Clone, Copy, Deserialize)]
52+
pub enum SpanEvent {
53+
/// Log when a span is created.
54+
New,
55+
/// Log when a span is closed.
56+
Close,
57+
/// Log when a span becomes active.
58+
Active,
59+
}
60+
61+
impl From<SpanEvent> for FmtSpan {
62+
fn from(value: SpanEvent) -> Self {
63+
match value {
64+
SpanEvent::New => FmtSpan::NEW,
65+
SpanEvent::Close => FmtSpan::CLOSE,
66+
SpanEvent::Active => FmtSpan::ACTIVE,
67+
}
68+
}
69+
}
70+
71+
/// Top-level logging configuration.
72+
#[derive(Debug, Default, Clone, Deserialize)]
73+
pub struct LoggingConfig {
74+
/// Global default log level.
75+
pub level: Option<LoggingLevel>,
76+
/// Per-crate log level overrides.
77+
pub crate_filters: Option<Vec<CrateLogFilter>>,
78+
/// Which span lifecycle events to log.
79+
pub span_events: Option<Vec<SpanEvent>>,
80+
}
81+
82+
/// Per-crate log level override.
83+
#[derive(Debug, Clone, Deserialize)]
84+
pub struct CrateLogFilter {
85+
/// Log level for this crate.
86+
pub level: LoggingLevel,
87+
/// Crate name to match (case-insensitive).
88+
pub name: String,
89+
}
90+
91+
/// Creates a filter function from a [`LoggingConfig`].
92+
///
93+
/// The returned closure checks each tracing event's target against the
94+
/// configured crate filters (case-insensitive match on the first path
95+
/// segment). Events that don't match any filter use the global default level.
96+
pub fn filter(config: LoggingConfig) -> impl Fn(&Metadata) -> bool {
97+
let default_level = config.level.unwrap_or(LoggingLevel::Info);
98+
let crate_filters = config
99+
.crate_filters
100+
.unwrap_or_default()
101+
.into_iter()
102+
.map(|filter| (filter.name, filter.level))
103+
.collect::<Vec<_>>();
104+
105+
move |meta| {
106+
let logging_level = if let Some(crate_name) = meta.target().split("::").next() {
107+
crate_filters
108+
.iter()
109+
.find_map(|(filter_name, filter_level)| {
110+
if crate_name.eq_ignore_ascii_case(filter_name) {
111+
Some(*filter_level)
112+
} else {
113+
None
114+
}
115+
})
116+
.unwrap_or(default_level)
117+
} else {
118+
default_level
119+
};
120+
121+
// Off disables all logging for this target.
122+
if logging_level.is_off() {
123+
return false;
124+
}
125+
126+
meta.level() <= &Level::from(logging_level)
127+
}
128+
}

crates/sdk-core/src/prover.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ impl State {
6868
impl SdkProver {
6969
/// Creates a new SDK Prover with the given configuration.
7070
pub fn new(config: ProverConfig) -> Result<Self> {
71+
if config.server_name.is_empty() {
72+
return Err(SdkError::config("server_name cannot be empty"));
73+
}
74+
if config.max_sent_data == 0 {
75+
return Err(SdkError::config("max_sent_data must be > 0"));
76+
}
77+
if config.max_recv_data == 0 {
78+
return Err(SdkError::config("max_recv_data must be > 0"));
79+
}
80+
7181
Ok(SdkProver {
7282
config,
7383
state: State::Initialized,
@@ -332,3 +342,59 @@ async fn send_request(conn: TlsConnection, request: HttpRequest) -> Result<HttpR
332342
},
333343
})
334344
}
345+
346+
#[cfg(test)]
347+
mod tests {
348+
use super::*;
349+
use crate::{
350+
config::{NetworkSetting, ProverConfig},
351+
error::ErrorKind,
352+
};
353+
354+
fn valid_config() -> ProverConfig {
355+
ProverConfig::builder("example.com")
356+
.max_sent_data(4096)
357+
.max_recv_data(16384)
358+
.network(NetworkSetting::Latency)
359+
.build()
360+
}
361+
362+
#[test]
363+
fn new_with_valid_config() {
364+
let prover = SdkProver::new(valid_config());
365+
assert!(prover.is_ok());
366+
}
367+
368+
#[test]
369+
fn new_rejects_empty_server_name() {
370+
let config = ProverConfig::builder("")
371+
.max_sent_data(4096)
372+
.max_recv_data(16384)
373+
.build();
374+
let err = SdkProver::new(config).err().expect("should fail");
375+
assert_eq!(err.kind(), ErrorKind::Config);
376+
assert!(err.to_string().contains("server_name"));
377+
}
378+
379+
#[test]
380+
fn new_rejects_zero_max_sent_data() {
381+
let config = ProverConfig::builder("example.com")
382+
.max_sent_data(0)
383+
.max_recv_data(16384)
384+
.build();
385+
let err = SdkProver::new(config).err().expect("should fail");
386+
assert_eq!(err.kind(), ErrorKind::Config);
387+
assert!(err.to_string().contains("max_sent_data"));
388+
}
389+
390+
#[test]
391+
fn new_rejects_zero_max_recv_data() {
392+
let config = ProverConfig::builder("example.com")
393+
.max_sent_data(4096)
394+
.max_recv_data(0)
395+
.build();
396+
let err = SdkProver::new(config).err().expect("should fail");
397+
assert_eq!(err.kind(), ErrorKind::Config);
398+
assert!(err.to_string().contains("max_recv_data"));
399+
}
400+
}

crates/sdk-core/src/types.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,7 @@ impl TryFrom<HttpRequest> for hyper::Request<Full<Bytes>> {
112112
let body = match body {
113113
// If the JSON value is a plain string, use its contents directly
114114
// to avoid double-serialization (wrapping in extra quotes).
115-
Body::Json(serde_json::Value::String(s)) => {
116-
Full::new(Bytes::from(s))
117-
}
115+
Body::Json(serde_json::Value::String(s)) => Full::new(Bytes::from(s)),
118116
// For other JSON values, serialize to bytes (infallible).
119117
Body::Json(value) => Full::new(Bytes::from(
120118
serde_json::to_vec(&value).expect("Value serialization is infallible"),

crates/wasm/Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,9 @@ rayon = { workspace = true }
3434
serde = { workspace = true, features = ["derive"] }
3535
serde_json = { version = "1.0" }
3636
serde-wasm-bindgen = { version = "0.6" }
37-
time = { version = "=0.3.37", features = ["wasm-bindgen"] }
3837
tracing = { workspace = true }
39-
tracing-subscriber = { workspace = true, features = ["time"] }
40-
tracing-web = { version = "0.1" }
38+
tracing-subscriber = { workspace = true }
39+
wasm-tracing = { version = "0.1" }
4140
tsify-next = { version = "0.5", default-features = false, features = ["js"] }
4241
wasm-bindgen = { version = "0.2" }
4342
wasm-bindgen-futures = { version = "0.4" }

crates/wasm/src/io.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ struct AdapterState {
5252
eof: bool,
5353
/// Pending read future.
5454
pending_read: Option<JsFuture>,
55-
/// Pending write future.
56-
pending_write: Option<JsFuture>,
57-
/// Pending close future.
58-
pending_close: Option<JsFuture>,
5955
/// Waker for when data becomes available.
6056
read_waker: Option<Waker>,
6157
/// Whether the stream is closed.
@@ -88,8 +84,6 @@ impl JsIoAdapter {
8884
read_buffer: VecDeque::new(),
8985
eof: false,
9086
pending_read: None,
91-
pending_write: None,
92-
pending_close: None,
9387
read_waker: None,
9488
closed: false,
9589
error: None,
@@ -205,7 +199,7 @@ impl AsyncRead for JsIoAdapter {
205199
impl AsyncWrite for JsIoAdapter {
206200
fn poll_write(
207201
self: Pin<&mut Self>,
208-
cx: &mut Context<'_>,
202+
_cx: &mut Context<'_>,
209203
buf: &[u8],
210204
) -> Poll<std::io::Result<usize>> {
211205
let this = self.get_mut();

0 commit comments

Comments
 (0)