use codec::{Decode, Encode};
use core::{fmt::Debug, format_args};
use sp_std::{vec, vec::Vec, Writer};
#[derive(Clone, Encode, Decode, Debug)]
pub enum WasmLevel {
ERROR,
WARN,
INFO,
DEBUG,
TRACE,
}
impl From<&tracing_core::Level> for WasmLevel {
fn from(l: &tracing_core::Level) -> WasmLevel {
match *l {
tracing_core::Level::ERROR => WasmLevel::ERROR,
tracing_core::Level::WARN => WasmLevel::WARN,
tracing_core::Level::INFO => WasmLevel::INFO,
tracing_core::Level::DEBUG => WasmLevel::DEBUG,
tracing_core::Level::TRACE => WasmLevel::TRACE,
}
}
}
impl core::default::Default for WasmLevel {
fn default() -> Self {
WasmLevel::TRACE
}
}
#[derive(Encode, Decode, Clone)]
pub enum WasmValue {
U8(u8),
I8(i8),
U32(u32),
I32(i32),
I64(i64),
U64(u64),
Bool(bool),
Str(Vec<u8>),
Formatted(Vec<u8>),
Encoded(Vec<u8>),
}
impl core::fmt::Debug for WasmValue {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
WasmValue::U8(ref i) => f.write_fmt(format_args!("{}_u8", i)),
WasmValue::I8(ref i) => f.write_fmt(format_args!("{}_i8", i)),
WasmValue::U32(ref i) => f.write_fmt(format_args!("{}_u32", i)),
WasmValue::I32(ref i) => f.write_fmt(format_args!("{}_i32", i)),
WasmValue::I64(ref i) => f.write_fmt(format_args!("{}_i64", i)),
WasmValue::U64(ref i) => f.write_fmt(format_args!("{}_u64", i)),
WasmValue::Bool(ref i) => f.write_fmt(format_args!("{}_bool", i)),
WasmValue::Formatted(ref i) | WasmValue::Str(ref i) => {
if let Ok(v) = core::str::from_utf8(i) {
f.write_fmt(format_args!("{}", v))
} else {
f.write_fmt(format_args!("{:?}", i))
}
},
WasmValue::Encoded(ref v) => {
f.write_str("Scale(")?;
for byte in v {
f.write_fmt(format_args!("{:02x}", byte))?;
}
f.write_str(")")
},
}
}
}
impl From<u8> for WasmValue {
fn from(u: u8) -> WasmValue {
WasmValue::U8(u)
}
}
impl From<&i8> for WasmValue {
fn from(inp: &i8) -> WasmValue {
WasmValue::I8(*inp)
}
}
impl From<&str> for WasmValue {
fn from(inp: &str) -> WasmValue {
WasmValue::Str(inp.as_bytes().to_vec())
}
}
impl From<&&str> for WasmValue {
fn from(inp: &&str) -> WasmValue {
WasmValue::Str((*inp).as_bytes().to_vec())
}
}
impl From<bool> for WasmValue {
fn from(inp: bool) -> WasmValue {
WasmValue::Bool(inp)
}
}
impl From<core::fmt::Arguments<'_>> for WasmValue {
fn from(inp: core::fmt::Arguments<'_>) -> WasmValue {
let mut buf = Writer::default();
core::fmt::write(&mut buf, inp).expect("Writing of arguments doesn't fail");
WasmValue::Formatted(buf.into_inner())
}
}
impl From<i8> for WasmValue {
fn from(u: i8) -> WasmValue {
WasmValue::I8(u)
}
}
impl From<i32> for WasmValue {
fn from(u: i32) -> WasmValue {
WasmValue::I32(u)
}
}
impl From<&i32> for WasmValue {
fn from(u: &i32) -> WasmValue {
WasmValue::I32(*u)
}
}
impl From<u32> for WasmValue {
fn from(u: u32) -> WasmValue {
WasmValue::U32(u)
}
}
impl From<&u32> for WasmValue {
fn from(u: &u32) -> WasmValue {
WasmValue::U32(*u)
}
}
impl From<u64> for WasmValue {
fn from(u: u64) -> WasmValue {
WasmValue::U64(u)
}
}
impl From<i64> for WasmValue {
fn from(u: i64) -> WasmValue {
WasmValue::I64(u)
}
}
#[derive(Encode, Decode, Clone)]
pub struct WasmFieldName(Vec<u8>);
impl core::fmt::Debug for WasmFieldName {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
if let Ok(v) = core::str::from_utf8(&self.0) {
f.write_fmt(format_args!("{}", v))
} else {
for byte in self.0.iter() {
f.write_fmt(format_args!("{:02x}", byte))?;
}
Ok(())
}
}
}
impl From<Vec<u8>> for WasmFieldName {
fn from(v: Vec<u8>) -> Self {
WasmFieldName(v)
}
}
impl From<&str> for WasmFieldName {
fn from(v: &str) -> Self {
WasmFieldName(v.as_bytes().to_vec())
}
}
#[derive(Encode, Decode, Clone, Debug)]
pub struct WasmFields(Vec<WasmFieldName>);
impl WasmFields {
pub fn iter(&self) -> core::slice::Iter<'_, WasmFieldName> {
self.0.iter()
}
}
impl From<Vec<WasmFieldName>> for WasmFields {
fn from(v: Vec<WasmFieldName>) -> WasmFields {
WasmFields(v)
}
}
impl From<Vec<&str>> for WasmFields {
fn from(v: Vec<&str>) -> WasmFields {
WasmFields(v.into_iter().map(|v| v.into()).collect())
}
}
impl WasmFields {
pub fn empty() -> Self {
WasmFields(Vec::with_capacity(0))
}
}
impl From<&tracing_core::field::FieldSet> for WasmFields {
fn from(wm: &tracing_core::field::FieldSet) -> WasmFields {
WasmFields(wm.iter().map(|s| s.name().into()).collect())
}
}
#[derive(Encode, Decode, Clone)]
pub struct WasmValuesSet(Vec<(WasmFieldName, Option<WasmValue>)>);
impl core::fmt::Debug for WasmValuesSet {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let mut wrt = f.debug_struct("");
let mut non_str = false;
for (f, v) in self.0.iter() {
if let Ok(s) = core::str::from_utf8(&f.0) {
match v {
Some(ref i) => wrt.field(s, i),
None => wrt.field(s, &(None as Option<WasmValue>)),
};
} else {
non_str = true;
}
}
if non_str {
wrt.field("..", &"..");
}
wrt.finish()
}
}
impl From<Vec<(WasmFieldName, Option<WasmValue>)>> for WasmValuesSet {
fn from(v: Vec<(WasmFieldName, Option<WasmValue>)>) -> Self {
WasmValuesSet(v)
}
}
impl From<Vec<(&&WasmFieldName, Option<WasmValue>)>> for WasmValuesSet {
fn from(v: Vec<(&&WasmFieldName, Option<WasmValue>)>) -> Self {
WasmValuesSet(v.into_iter().map(|(k, v)| ((**k).clone(), v)).collect())
}
}
impl From<Vec<(&&str, Option<WasmValue>)>> for WasmValuesSet {
fn from(v: Vec<(&&str, Option<WasmValue>)>) -> Self {
WasmValuesSet(v.into_iter().map(|(k, v)| ((*k).into(), v)).collect())
}
}
impl WasmValuesSet {
pub fn empty() -> Self {
WasmValuesSet(Vec::with_capacity(0))
}
}
impl tracing_core::field::Visit for WasmValuesSet {
fn record_debug(&mut self, field: &tracing_core::field::Field, value: &dyn Debug) {
self.0
.push((field.name().into(), Some(WasmValue::from(format_args!("{:?}", value)))))
}
fn record_i64(&mut self, field: &tracing_core::field::Field, value: i64) {
self.0.push((field.name().into(), Some(WasmValue::from(value))))
}
fn record_u64(&mut self, field: &tracing_core::field::Field, value: u64) {
self.0.push((field.name().into(), Some(WasmValue::from(value))))
}
fn record_bool(&mut self, field: &tracing_core::field::Field, value: bool) {
self.0.push((field.name().into(), Some(WasmValue::from(value))))
}
fn record_str(&mut self, field: &tracing_core::field::Field, value: &str) {
self.0.push((field.name().into(), Some(WasmValue::from(value))))
}
}
#[derive(Encode, Decode, Clone)]
pub struct WasmMetadata {
pub name: Vec<u8>,
pub target: Vec<u8>,
pub level: WasmLevel,
pub file: Vec<u8>,
pub line: u32,
pub module_path: Vec<u8>,
pub is_span: bool,
pub fields: WasmFields,
}
impl From<&tracing_core::Metadata<'_>> for WasmMetadata {
fn from(wm: &tracing_core::Metadata<'_>) -> WasmMetadata {
WasmMetadata {
name: wm.name().as_bytes().to_vec(),
target: wm.target().as_bytes().to_vec(),
level: wm.level().into(),
file: wm.file().map(|f| f.as_bytes().to_vec()).unwrap_or_default(),
line: wm.line().unwrap_or_default(),
module_path: wm.module_path().map(|m| m.as_bytes().to_vec()).unwrap_or_default(),
is_span: wm.is_span(),
fields: wm.fields().into(),
}
}
}
impl core::fmt::Debug for WasmMetadata {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("WasmMetadata")
.field("name", &decode_field(&self.name))
.field("target", &decode_field(&self.target))
.field("level", &self.level)
.field("file", &decode_field(&self.file))
.field("line", &self.line)
.field("module_path", &decode_field(&self.module_path))
.field("is_span", &self.is_span)
.field("fields", &self.fields)
.finish()
}
}
impl core::default::Default for WasmMetadata {
fn default() -> Self {
let target = "default".as_bytes().to_vec();
WasmMetadata {
target,
name: Default::default(),
level: Default::default(),
file: Default::default(),
line: Default::default(),
module_path: Default::default(),
is_span: true,
fields: WasmFields::empty(),
}
}
}
fn decode_field(field: &[u8]) -> &str {
core::str::from_utf8(field).unwrap_or_default()
}
#[derive(Encode, Decode, Clone, Debug)]
pub struct WasmEntryAttributes {
pub parent_id: Option<u64>,
pub metadata: WasmMetadata,
pub fields: WasmValuesSet,
}
impl From<&tracing_core::Event<'_>> for WasmEntryAttributes {
fn from(evt: &tracing_core::Event<'_>) -> WasmEntryAttributes {
let mut fields = WasmValuesSet(Vec::new());
evt.record(&mut fields);
WasmEntryAttributes {
parent_id: evt.parent().map(|id| id.into_u64()),
metadata: evt.metadata().into(),
fields,
}
}
}
impl From<&tracing_core::span::Attributes<'_>> for WasmEntryAttributes {
fn from(attrs: &tracing_core::span::Attributes<'_>) -> WasmEntryAttributes {
let mut fields = WasmValuesSet(Vec::new());
attrs.record(&mut fields);
WasmEntryAttributes {
parent_id: attrs.parent().map(|id| id.into_u64()),
metadata: attrs.metadata().into(),
fields,
}
}
}
impl core::default::Default for WasmEntryAttributes {
fn default() -> Self {
WasmEntryAttributes {
parent_id: None,
metadata: Default::default(),
fields: WasmValuesSet(vec![]),
}
}
}
#[cfg(feature = "std")]
mod std_features {
use tracing_core::callsite;
pub struct WasmCallsite;
impl callsite::Callsite for WasmCallsite {
fn set_interest(&self, _: tracing_core::Interest) {
unimplemented!()
}
fn metadata(&self) -> &tracing_core::Metadata {
unimplemented!()
}
}
static CALLSITE: WasmCallsite = WasmCallsite;
pub static WASM_TRACE_IDENTIFIER: &str = "wasm_tracing";
pub static WASM_NAME_KEY: &str = "name";
pub static WASM_TARGET_KEY: &str = "target";
pub static GENERIC_FIELDS: &[&str] =
&[WASM_TARGET_KEY, WASM_NAME_KEY, "file", "line", "module_path", "params"];
static SPAN_ERROR_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::ERROR,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::SPAN,
);
static SPAN_WARN_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::WARN,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::SPAN,
);
static SPAN_INFO_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::INFO,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::SPAN,
);
static SPAN_DEBUG_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::DEBUG,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::SPAN,
);
static SPAN_TRACE_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::TRACE,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::SPAN,
);
static EVENT_ERROR_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::ERROR,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::EVENT,
);
static EVENT_WARN_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::WARN,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::EVENT,
);
static EVENT_INFO_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::INFO,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::EVENT,
);
static EVENT_DEBUG_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::DEBUG,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::EVENT,
);
static EVENT_TRACE_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
WASM_TRACE_IDENTIFIER,
WASM_TRACE_IDENTIFIER,
tracing::Level::TRACE,
None,
None,
None,
tracing_core::field::FieldSet::new(
GENERIC_FIELDS,
tracing_core::identify_callsite!(&CALLSITE),
),
tracing_core::metadata::Kind::EVENT,
);
impl From<&crate::WasmMetadata> for &'static tracing_core::Metadata<'static> {
fn from(wm: &crate::WasmMetadata) -> &'static tracing_core::Metadata<'static> {
match (&wm.level, wm.is_span) {
(&crate::WasmLevel::ERROR, true) => &SPAN_ERROR_METADATA,
(&crate::WasmLevel::WARN, true) => &SPAN_WARN_METADATA,
(&crate::WasmLevel::INFO, true) => &SPAN_INFO_METADATA,
(&crate::WasmLevel::DEBUG, true) => &SPAN_DEBUG_METADATA,
(&crate::WasmLevel::TRACE, true) => &SPAN_TRACE_METADATA,
(&crate::WasmLevel::ERROR, false) => &EVENT_ERROR_METADATA,
(&crate::WasmLevel::WARN, false) => &EVENT_WARN_METADATA,
(&crate::WasmLevel::INFO, false) => &EVENT_INFO_METADATA,
(&crate::WasmLevel::DEBUG, false) => &EVENT_DEBUG_METADATA,
(&crate::WasmLevel::TRACE, false) => &EVENT_TRACE_METADATA,
}
}
}
impl From<crate::WasmEntryAttributes> for tracing::Span {
fn from(a: crate::WasmEntryAttributes) -> tracing::Span {
let name = std::str::from_utf8(&a.metadata.name).unwrap_or_default();
let target = std::str::from_utf8(&a.metadata.target).unwrap_or_default();
let file = std::str::from_utf8(&a.metadata.file).unwrap_or_default();
let line = a.metadata.line;
let module_path = std::str::from_utf8(&a.metadata.module_path).unwrap_or_default();
let params = a.fields;
let metadata: &tracing_core::metadata::Metadata<'static> = (&a.metadata).into();
tracing::span::Span::child_of(
a.parent_id.map(tracing_core::span::Id::from_u64),
metadata,
&tracing::valueset! { metadata.fields(), target, name, file, line, module_path, ?params },
)
}
}
impl crate::WasmEntryAttributes {
pub fn emit(self: crate::WasmEntryAttributes) {
let name = std::str::from_utf8(&self.metadata.name).unwrap_or_default();
let target = std::str::from_utf8(&self.metadata.target).unwrap_or_default();
let file = std::str::from_utf8(&self.metadata.file).unwrap_or_default();
let line = self.metadata.line;
let module_path = std::str::from_utf8(&self.metadata.module_path).unwrap_or_default();
let params = self.fields;
let metadata: &tracing_core::metadata::Metadata<'static> = (&self.metadata).into();
tracing_core::Event::child_of(
self.parent_id.map(tracing_core::span::Id::from_u64),
metadata,
&tracing::valueset! { metadata.fields(), target, name, file, line, module_path, ?params },
)
}
}
}
#[cfg(feature = "std")]
pub use std_features::*;