From 80ae3211dbc78d4efb31f175f0618a0015dfb546 Mon Sep 17 00:00:00 2001 From: Jesse Ditson Date: Wed, 30 Aug 2023 08:09:46 -0700 Subject: [PATCH] WASM Support (#57) ## Motivation I was attempting to use this library with a wasm target in a browser. If you call `std::time::{Instant, SystemTime}::now` in a wasm target, it will panic at runtime. This library makes use of both `::now()` calls, which will result in a panic when attempting to record a trace. ## Solution In the library that this library calls (`opentelemetry-api`), this is resolved by replacing calls to `SystemTime::now()` with an implementation that returns a `SystemTime` constructed from `js_sys::Date`. To preserve the ability to pass `SystemTime`-typed args to `opentelemetry_api` methods, I just added the same mechanism to this library. Because `Instant` is never used in `opentelemetry_api`, we can instead replace the implementation entirely with the api-compatible "polyfill" from `web_time`. All of these changes are behind `cfg` and automatically enabled when building for `wasm32` targets, so this will not change any behavior/performance when built for other targets. --- Cargo.toml | 4 ++++ src/layer.rs | 11 +++++++---- src/lib.rs | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0d66c6c..9731964 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,10 @@ tracing-subscriber = { version = "0.3.0", default-features = false, features = [ [target.'cfg(not(target_os = "windows"))'.dev-dependencies] pprof = { version = "0.11.1", features = ["flamegraph", "criterion"] } +[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] +js-sys = "0.3.64" +web-time = "0.2.0" + [lib] bench = false diff --git a/src/layer.rs b/src/layer.rs index 52cd7c7..450f146 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -8,7 +8,8 @@ use std::any::TypeId; use std::fmt; use std::marker; use std::thread; -use std::time::{Instant, SystemTime}; +#[cfg(not(target_arch = "wasm32"))] +use std::time::Instant; use tracing_core::span::{self, Attributes, Id, Record}; use tracing_core::{field, Event, Subscriber}; #[cfg(feature = "tracing-log")] @@ -16,6 +17,8 @@ use tracing_log::NormalizeEvent; use tracing_subscriber::layer::Context; use tracing_subscriber::registry::LookupSpan; use tracing_subscriber::Layer; +#[cfg(target_arch = "wasm32")] +use web_time::Instant; const SPAN_NAME_FIELD: &str = "otel.name"; const SPAN_KIND_FIELD: &str = "otel.kind"; @@ -680,7 +683,7 @@ where let mut builder = self .tracer .span_builder(attrs.metadata().name()) - .with_start_time(SystemTime::now()) + .with_start_time(crate::time::now()) // Eagerly assign span id so children have stable parent id .with_span_id(self.tracer.new_span_id()); @@ -839,7 +842,7 @@ where let mut otel_event = otel::Event::new( String::new(), - SystemTime::now(), + crate::time::now(), vec![Key::new("level").string(meta.level().as_str()), target], 0, ); @@ -924,7 +927,7 @@ where // Assign end time, build and start span, drop span to export builder - .with_end_time(SystemTime::now()) + .with_end_time(crate::time::now()) .start_with_context(&self.tracer, &parent_cx); } } diff --git a/src/lib.rs b/src/lib.rs index fa7b2a1..1c7b839 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,3 +150,17 @@ pub struct OtelData { /// The otel span data recorded during the current tracing span. pub builder: opentelemetry::trace::SpanBuilder, } + +pub(crate) mod time { + use std::time::SystemTime; + + #[cfg(not(target_arch = "wasm32"))] + pub(crate) fn now() -> SystemTime { + SystemTime::now() + } + + #[cfg(target_arch = "wasm32")] + pub(crate) fn now() -> SystemTime { + SystemTime::UNIX_EPOCH + std::time::Duration::from_millis(js_sys::Date::now() as u64) + } +}