sp_tracing/
types.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use alloc::{vec, vec::Vec};
19use codec::{Decode, Encode};
20/// Types for wasm based tracing. Loosely inspired by `tracing-core` but
21/// optimised for the specific use case.
22use core::{fmt::Debug, format_args};
23
24/// The Tracing Level – the user can filter by this
25#[derive(Clone, Encode, Decode, Debug)]
26pub enum WasmLevel {
27	/// This is a fatal errors
28	ERROR,
29	/// This is a warning you should be aware of
30	WARN,
31	/// Nice to now info
32	INFO,
33	/// Further information for debugging purposes
34	DEBUG,
35	/// The lowest level, keeping track of minute detail
36	TRACE,
37}
38
39impl From<&tracing_core::Level> for WasmLevel {
40	fn from(l: &tracing_core::Level) -> WasmLevel {
41		match *l {
42			tracing_core::Level::ERROR => WasmLevel::ERROR,
43			tracing_core::Level::WARN => WasmLevel::WARN,
44			tracing_core::Level::INFO => WasmLevel::INFO,
45			tracing_core::Level::DEBUG => WasmLevel::DEBUG,
46			tracing_core::Level::TRACE => WasmLevel::TRACE,
47		}
48	}
49}
50
51impl core::default::Default for WasmLevel {
52	fn default() -> Self {
53		WasmLevel::TRACE
54	}
55}
56
57/// A parameter value provided to the span/event
58#[derive(Encode, Decode, Clone)]
59pub enum WasmValue {
60	U8(u8),
61	I8(i8),
62	U32(u32),
63	I32(i32),
64	I64(i64),
65	U64(u64),
66	Bool(bool),
67	Str(Vec<u8>),
68	/// Debug or Display call, this is most-likely a print-able UTF8 String
69	Formatted(Vec<u8>),
70	/// SCALE CODEC encoded object – the name should allow the received to know
71	/// how to decode this.
72	Encoded(Vec<u8>),
73}
74
75impl core::fmt::Debug for WasmValue {
76	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
77		match self {
78			WasmValue::U8(ref i) => f.write_fmt(format_args!("{}_u8", i)),
79			WasmValue::I8(ref i) => f.write_fmt(format_args!("{}_i8", i)),
80			WasmValue::U32(ref i) => f.write_fmt(format_args!("{}_u32", i)),
81			WasmValue::I32(ref i) => f.write_fmt(format_args!("{}_i32", i)),
82			WasmValue::I64(ref i) => f.write_fmt(format_args!("{}_i64", i)),
83			WasmValue::U64(ref i) => f.write_fmt(format_args!("{}_u64", i)),
84			WasmValue::Bool(ref i) => f.write_fmt(format_args!("{}_bool", i)),
85			WasmValue::Formatted(ref i) | WasmValue::Str(ref i) => {
86				if let Ok(v) = core::str::from_utf8(i) {
87					f.write_fmt(format_args!("{}", v))
88				} else {
89					f.write_fmt(format_args!("{:?}", i))
90				}
91			},
92			WasmValue::Encoded(ref v) => {
93				f.write_str("Scale(")?;
94				for byte in v {
95					f.write_fmt(format_args!("{:02x}", byte))?;
96				}
97				f.write_str(")")
98			},
99		}
100	}
101}
102
103impl From<u8> for WasmValue {
104	fn from(u: u8) -> WasmValue {
105		WasmValue::U8(u)
106	}
107}
108
109impl From<&i8> for WasmValue {
110	fn from(inp: &i8) -> WasmValue {
111		WasmValue::I8(*inp)
112	}
113}
114
115impl From<&str> for WasmValue {
116	fn from(inp: &str) -> WasmValue {
117		WasmValue::Str(inp.as_bytes().to_vec())
118	}
119}
120
121impl From<&&str> for WasmValue {
122	fn from(inp: &&str) -> WasmValue {
123		WasmValue::Str((*inp).as_bytes().to_vec())
124	}
125}
126
127impl From<bool> for WasmValue {
128	fn from(inp: bool) -> WasmValue {
129		WasmValue::Bool(inp)
130	}
131}
132
133impl From<core::fmt::Arguments<'_>> for WasmValue {
134	fn from(inp: core::fmt::Arguments<'_>) -> WasmValue {
135		let mut buf = alloc::string::String::default();
136		core::fmt::write(&mut buf, inp).expect("Writing of arguments doesn't fail");
137		WasmValue::Formatted(buf.into_bytes())
138	}
139}
140
141impl From<i8> for WasmValue {
142	fn from(u: i8) -> WasmValue {
143		WasmValue::I8(u)
144	}
145}
146
147impl From<i32> for WasmValue {
148	fn from(u: i32) -> WasmValue {
149		WasmValue::I32(u)
150	}
151}
152
153impl From<&i32> for WasmValue {
154	fn from(u: &i32) -> WasmValue {
155		WasmValue::I32(*u)
156	}
157}
158
159impl From<u32> for WasmValue {
160	fn from(u: u32) -> WasmValue {
161		WasmValue::U32(u)
162	}
163}
164
165impl From<&u32> for WasmValue {
166	fn from(u: &u32) -> WasmValue {
167		WasmValue::U32(*u)
168	}
169}
170
171impl From<u64> for WasmValue {
172	fn from(u: u64) -> WasmValue {
173		WasmValue::U64(u)
174	}
175}
176
177impl From<i64> for WasmValue {
178	fn from(u: i64) -> WasmValue {
179		WasmValue::I64(u)
180	}
181}
182
183/// The name of a field provided as the argument name when constructing an
184/// `event!` or `span!`.
185/// Generally generated automatically via `stringify` from an `'static &str`.
186/// Likely print-able.
187#[derive(Encode, Decode, Clone)]
188pub struct WasmFieldName(Vec<u8>);
189
190impl core::fmt::Debug for WasmFieldName {
191	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
192		if let Ok(v) = core::str::from_utf8(&self.0) {
193			f.write_fmt(format_args!("{}", v))
194		} else {
195			for byte in self.0.iter() {
196				f.write_fmt(format_args!("{:02x}", byte))?;
197			}
198			Ok(())
199		}
200	}
201}
202
203impl From<Vec<u8>> for WasmFieldName {
204	fn from(v: Vec<u8>) -> Self {
205		WasmFieldName(v)
206	}
207}
208
209impl From<&str> for WasmFieldName {
210	fn from(v: &str) -> Self {
211		WasmFieldName(v.as_bytes().to_vec())
212	}
213}
214
215/// A list of `WasmFieldName`s in the order provided
216#[derive(Encode, Decode, Clone, Debug)]
217pub struct WasmFields(Vec<WasmFieldName>);
218
219impl WasmFields {
220	/// Iterate over the fields
221	pub fn iter(&self) -> core::slice::Iter<'_, WasmFieldName> {
222		self.0.iter()
223	}
224}
225
226impl From<Vec<WasmFieldName>> for WasmFields {
227	fn from(v: Vec<WasmFieldName>) -> WasmFields {
228		WasmFields(v)
229	}
230}
231
232impl From<Vec<&str>> for WasmFields {
233	fn from(v: Vec<&str>) -> WasmFields {
234		WasmFields(v.into_iter().map(|v| v.into()).collect())
235	}
236}
237
238impl WasmFields {
239	/// Create an empty entry
240	pub fn empty() -> Self {
241		WasmFields(Vec::with_capacity(0))
242	}
243}
244
245impl From<&tracing_core::field::FieldSet> for WasmFields {
246	fn from(wm: &tracing_core::field::FieldSet) -> WasmFields {
247		WasmFields(wm.iter().map(|s| s.name().into()).collect())
248	}
249}
250
251/// A list of `WasmFieldName`s with the given `WasmValue` (if provided)
252/// in the order specified.
253#[derive(Encode, Decode, Clone)]
254pub struct WasmValuesSet(Vec<(WasmFieldName, Option<WasmValue>)>);
255
256impl core::fmt::Debug for WasmValuesSet {
257	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
258		let mut wrt = f.debug_struct("");
259		let mut non_str = false;
260		for (f, v) in self.0.iter() {
261			if let Ok(s) = core::str::from_utf8(&f.0) {
262				match v {
263					Some(ref i) => wrt.field(s, i),
264					None => wrt.field(s, &(None as Option<WasmValue>)),
265				};
266			} else {
267				non_str = true;
268			}
269		}
270
271		// FIXME: replace with using `finish_non_exhaustive()` once stable
272		//        https://github.com/rust-lang/rust/issues/67364
273		if non_str {
274			wrt.field("..", &"..");
275		}
276
277		wrt.finish()
278	}
279}
280
281impl From<Vec<(WasmFieldName, Option<WasmValue>)>> for WasmValuesSet {
282	fn from(v: Vec<(WasmFieldName, Option<WasmValue>)>) -> Self {
283		WasmValuesSet(v)
284	}
285}
286impl From<Vec<(&&WasmFieldName, Option<WasmValue>)>> for WasmValuesSet {
287	fn from(v: Vec<(&&WasmFieldName, Option<WasmValue>)>) -> Self {
288		WasmValuesSet(v.into_iter().map(|(k, v)| ((**k).clone(), v)).collect())
289	}
290}
291
292impl From<Vec<(&&str, Option<WasmValue>)>> for WasmValuesSet {
293	fn from(v: Vec<(&&str, Option<WasmValue>)>) -> Self {
294		WasmValuesSet(v.into_iter().map(|(k, v)| ((*k).into(), v)).collect())
295	}
296}
297
298impl WasmValuesSet {
299	/// Create an empty entry
300	pub fn empty() -> Self {
301		WasmValuesSet(Vec::with_capacity(0))
302	}
303}
304
305impl tracing_core::field::Visit for WasmValuesSet {
306	fn record_debug(&mut self, field: &tracing_core::field::Field, value: &dyn Debug) {
307		self.0
308			.push((field.name().into(), Some(WasmValue::from(format_args!("{:?}", value)))))
309	}
310	fn record_i64(&mut self, field: &tracing_core::field::Field, value: i64) {
311		self.0.push((field.name().into(), Some(WasmValue::from(value))))
312	}
313	fn record_u64(&mut self, field: &tracing_core::field::Field, value: u64) {
314		self.0.push((field.name().into(), Some(WasmValue::from(value))))
315	}
316	fn record_bool(&mut self, field: &tracing_core::field::Field, value: bool) {
317		self.0.push((field.name().into(), Some(WasmValue::from(value))))
318	}
319	fn record_str(&mut self, field: &tracing_core::field::Field, value: &str) {
320		self.0.push((field.name().into(), Some(WasmValue::from(value))))
321	}
322}
323/// Metadata provides generic information about the specific location of the
324/// `span!` or `event!` call on the wasm-side.
325#[derive(Encode, Decode, Clone)]
326pub struct WasmMetadata {
327	/// The name given to `event!`/`span!`, `&'static str` converted to bytes
328	pub name: Vec<u8>,
329	/// The given target to `event!`/`span!` – or module-name, `&'static str` converted to bytes
330	pub target: Vec<u8>,
331	/// The level of this entry
332	pub level: WasmLevel,
333	/// The file this was emitted from – useful for debugging;  `&'static str` converted to bytes
334	pub file: Vec<u8>,
335	/// The specific line number in the file – useful for debugging
336	pub line: u32,
337	/// The module path;  `&'static str` converted to bytes
338	pub module_path: Vec<u8>,
339	/// Whether this is a call  to `span!` or `event!`
340	pub is_span: bool,
341	/// The list of fields specified in the call
342	pub fields: WasmFields,
343}
344
345impl From<&tracing_core::Metadata<'_>> for WasmMetadata {
346	fn from(wm: &tracing_core::Metadata<'_>) -> WasmMetadata {
347		WasmMetadata {
348			name: wm.name().as_bytes().to_vec(),
349			target: wm.target().as_bytes().to_vec(),
350			level: wm.level().into(),
351			file: wm.file().map(|f| f.as_bytes().to_vec()).unwrap_or_default(),
352			line: wm.line().unwrap_or_default(),
353			module_path: wm.module_path().map(|m| m.as_bytes().to_vec()).unwrap_or_default(),
354			is_span: wm.is_span(),
355			fields: wm.fields().into(),
356		}
357	}
358}
359
360impl core::fmt::Debug for WasmMetadata {
361	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
362		f.debug_struct("WasmMetadata")
363			.field("name", &decode_field(&self.name))
364			.field("target", &decode_field(&self.target))
365			.field("level", &self.level)
366			.field("file", &decode_field(&self.file))
367			.field("line", &self.line)
368			.field("module_path", &decode_field(&self.module_path))
369			.field("is_span", &self.is_span)
370			.field("fields", &self.fields)
371			.finish()
372	}
373}
374
375impl core::default::Default for WasmMetadata {
376	fn default() -> Self {
377		let target = "default".as_bytes().to_vec();
378		WasmMetadata {
379			target,
380			name: Default::default(),
381			level: Default::default(),
382			file: Default::default(),
383			line: Default::default(),
384			module_path: Default::default(),
385			is_span: true,
386			fields: WasmFields::empty(),
387		}
388	}
389}
390
391fn decode_field(field: &[u8]) -> &str {
392	core::str::from_utf8(field).unwrap_or_default()
393}
394
395/// Span or Event Attributes
396#[derive(Encode, Decode, Clone, Debug)]
397pub struct WasmEntryAttributes {
398	/// the parent, if directly specified – otherwise assume most inner span
399	pub parent_id: Option<u64>,
400	/// the metadata of the location
401	pub metadata: WasmMetadata,
402	/// the Values provided
403	pub fields: WasmValuesSet,
404}
405
406impl From<&tracing_core::Event<'_>> for WasmEntryAttributes {
407	fn from(evt: &tracing_core::Event<'_>) -> WasmEntryAttributes {
408		let mut fields = WasmValuesSet(Vec::new());
409		evt.record(&mut fields);
410		WasmEntryAttributes {
411			parent_id: evt.parent().map(|id| id.into_u64()),
412			metadata: evt.metadata().into(),
413			fields,
414		}
415	}
416}
417
418impl From<&tracing_core::span::Attributes<'_>> for WasmEntryAttributes {
419	fn from(attrs: &tracing_core::span::Attributes<'_>) -> WasmEntryAttributes {
420		let mut fields = WasmValuesSet(Vec::new());
421		attrs.record(&mut fields);
422		WasmEntryAttributes {
423			parent_id: attrs.parent().map(|id| id.into_u64()),
424			metadata: attrs.metadata().into(),
425			fields,
426		}
427	}
428}
429
430impl core::default::Default for WasmEntryAttributes {
431	fn default() -> Self {
432		WasmEntryAttributes {
433			parent_id: None,
434			metadata: Default::default(),
435			fields: WasmValuesSet(vec![]),
436		}
437	}
438}
439
440#[cfg(feature = "std")]
441mod std_features {
442
443	use tracing_core::callsite;
444
445	/// Static entry use for wasm-originated metadata.
446	pub struct WasmCallsite;
447	impl callsite::Callsite for WasmCallsite {
448		fn set_interest(&self, _: tracing_core::Interest) {
449			unimplemented!()
450		}
451		fn metadata(&self) -> &tracing_core::Metadata {
452			unimplemented!()
453		}
454	}
455	static CALLSITE: WasmCallsite = WasmCallsite;
456	/// The identifier we are using to inject the wasm events in the generic `tracing` system
457	pub static WASM_TRACE_IDENTIFIER: &str = "wasm_tracing";
458	/// The fieldname for the wasm-originated name
459	pub static WASM_NAME_KEY: &str = "name";
460	/// The fieldname for the wasm-originated target
461	pub static WASM_TARGET_KEY: &str = "target";
462	/// The the list of all static field names we construct from the given metadata
463	pub static GENERIC_FIELDS: &[&str] =
464		&[WASM_TARGET_KEY, WASM_NAME_KEY, "file", "line", "module_path", "params"];
465
466	// Implementation Note:
467	// the original `tracing` crate generates these static metadata entries at every `span!` and
468	// `event!` location to allow for highly optimised filtering. For us to allow level-based
469	// emitting of wasm events we need these static metadata entries to inject into that system. We
470	// then provide generic `From`-implementations picking the right metadata to refer to.
471
472	static SPAN_ERROR_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
473		WASM_TRACE_IDENTIFIER,
474		WASM_TRACE_IDENTIFIER,
475		tracing::Level::ERROR,
476		None,
477		None,
478		None,
479		tracing_core::field::FieldSet::new(
480			GENERIC_FIELDS,
481			tracing_core::identify_callsite!(&CALLSITE),
482		),
483		tracing_core::metadata::Kind::SPAN,
484	);
485
486	static SPAN_WARN_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
487		WASM_TRACE_IDENTIFIER,
488		WASM_TRACE_IDENTIFIER,
489		tracing::Level::WARN,
490		None,
491		None,
492		None,
493		tracing_core::field::FieldSet::new(
494			GENERIC_FIELDS,
495			tracing_core::identify_callsite!(&CALLSITE),
496		),
497		tracing_core::metadata::Kind::SPAN,
498	);
499	static SPAN_INFO_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
500		WASM_TRACE_IDENTIFIER,
501		WASM_TRACE_IDENTIFIER,
502		tracing::Level::INFO,
503		None,
504		None,
505		None,
506		tracing_core::field::FieldSet::new(
507			GENERIC_FIELDS,
508			tracing_core::identify_callsite!(&CALLSITE),
509		),
510		tracing_core::metadata::Kind::SPAN,
511	);
512
513	static SPAN_DEBUG_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
514		WASM_TRACE_IDENTIFIER,
515		WASM_TRACE_IDENTIFIER,
516		tracing::Level::DEBUG,
517		None,
518		None,
519		None,
520		tracing_core::field::FieldSet::new(
521			GENERIC_FIELDS,
522			tracing_core::identify_callsite!(&CALLSITE),
523		),
524		tracing_core::metadata::Kind::SPAN,
525	);
526
527	static SPAN_TRACE_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
528		WASM_TRACE_IDENTIFIER,
529		WASM_TRACE_IDENTIFIER,
530		tracing::Level::TRACE,
531		None,
532		None,
533		None,
534		tracing_core::field::FieldSet::new(
535			GENERIC_FIELDS,
536			tracing_core::identify_callsite!(&CALLSITE),
537		),
538		tracing_core::metadata::Kind::SPAN,
539	);
540
541	static EVENT_ERROR_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
542		WASM_TRACE_IDENTIFIER,
543		WASM_TRACE_IDENTIFIER,
544		tracing::Level::ERROR,
545		None,
546		None,
547		None,
548		tracing_core::field::FieldSet::new(
549			GENERIC_FIELDS,
550			tracing_core::identify_callsite!(&CALLSITE),
551		),
552		tracing_core::metadata::Kind::EVENT,
553	);
554
555	static EVENT_WARN_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
556		WASM_TRACE_IDENTIFIER,
557		WASM_TRACE_IDENTIFIER,
558		tracing::Level::WARN,
559		None,
560		None,
561		None,
562		tracing_core::field::FieldSet::new(
563			GENERIC_FIELDS,
564			tracing_core::identify_callsite!(&CALLSITE),
565		),
566		tracing_core::metadata::Kind::EVENT,
567	);
568
569	static EVENT_INFO_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
570		WASM_TRACE_IDENTIFIER,
571		WASM_TRACE_IDENTIFIER,
572		tracing::Level::INFO,
573		None,
574		None,
575		None,
576		tracing_core::field::FieldSet::new(
577			GENERIC_FIELDS,
578			tracing_core::identify_callsite!(&CALLSITE),
579		),
580		tracing_core::metadata::Kind::EVENT,
581	);
582
583	static EVENT_DEBUG_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
584		WASM_TRACE_IDENTIFIER,
585		WASM_TRACE_IDENTIFIER,
586		tracing::Level::DEBUG,
587		None,
588		None,
589		None,
590		tracing_core::field::FieldSet::new(
591			GENERIC_FIELDS,
592			tracing_core::identify_callsite!(&CALLSITE),
593		),
594		tracing_core::metadata::Kind::EVENT,
595	);
596
597	static EVENT_TRACE_METADATA: tracing_core::Metadata<'static> = tracing::Metadata::new(
598		WASM_TRACE_IDENTIFIER,
599		WASM_TRACE_IDENTIFIER,
600		tracing::Level::TRACE,
601		None,
602		None,
603		None,
604		tracing_core::field::FieldSet::new(
605			GENERIC_FIELDS,
606			tracing_core::identify_callsite!(&CALLSITE),
607		),
608		tracing_core::metadata::Kind::EVENT,
609	);
610
611	// FIXME: this could be done a lot in 0.2 if they opt for using `Cow<str,'static>` instead
612	// 			https://github.com/paritytech/substrate/issues/7134
613	impl From<&crate::WasmMetadata> for &'static tracing_core::Metadata<'static> {
614		fn from(wm: &crate::WasmMetadata) -> &'static tracing_core::Metadata<'static> {
615			match (&wm.level, wm.is_span) {
616				(&crate::WasmLevel::ERROR, true) => &SPAN_ERROR_METADATA,
617				(&crate::WasmLevel::WARN, true) => &SPAN_WARN_METADATA,
618				(&crate::WasmLevel::INFO, true) => &SPAN_INFO_METADATA,
619				(&crate::WasmLevel::DEBUG, true) => &SPAN_DEBUG_METADATA,
620				(&crate::WasmLevel::TRACE, true) => &SPAN_TRACE_METADATA,
621				(&crate::WasmLevel::ERROR, false) => &EVENT_ERROR_METADATA,
622				(&crate::WasmLevel::WARN, false) => &EVENT_WARN_METADATA,
623				(&crate::WasmLevel::INFO, false) => &EVENT_INFO_METADATA,
624				(&crate::WasmLevel::DEBUG, false) => &EVENT_DEBUG_METADATA,
625				(&crate::WasmLevel::TRACE, false) => &EVENT_TRACE_METADATA,
626			}
627		}
628	}
629
630	impl From<crate::WasmEntryAttributes> for tracing::Span {
631		fn from(a: crate::WasmEntryAttributes) -> tracing::Span {
632			let name = std::str::from_utf8(&a.metadata.name).unwrap_or_default();
633			let target = std::str::from_utf8(&a.metadata.target).unwrap_or_default();
634			let file = std::str::from_utf8(&a.metadata.file).unwrap_or_default();
635			let line = a.metadata.line;
636			let module_path = std::str::from_utf8(&a.metadata.module_path).unwrap_or_default();
637			let params = a.fields;
638			let metadata: &tracing_core::metadata::Metadata<'static> = (&a.metadata).into();
639
640			tracing::span::Span::child_of(
641				a.parent_id.map(tracing_core::span::Id::from_u64),
642				metadata,
643				&tracing::valueset! { metadata.fields(), target, name, file, line, module_path, ?params },
644			)
645		}
646	}
647
648	impl crate::WasmEntryAttributes {
649		/// convert the given Attributes to an event and emit it using `tracing_core`.
650		pub fn emit(self: crate::WasmEntryAttributes) {
651			let name = std::str::from_utf8(&self.metadata.name).unwrap_or_default();
652			let target = std::str::from_utf8(&self.metadata.target).unwrap_or_default();
653			let file = std::str::from_utf8(&self.metadata.file).unwrap_or_default();
654			let line = self.metadata.line;
655			let module_path = std::str::from_utf8(&self.metadata.module_path).unwrap_or_default();
656			let params = self.fields;
657			let metadata: &tracing_core::metadata::Metadata<'static> = (&self.metadata).into();
658
659			tracing_core::Event::child_of(
660				self.parent_id.map(tracing_core::span::Id::from_u64),
661				metadata,
662				&tracing::valueset! { metadata.fields(), target, name, file, line, module_path, ?params },
663			)
664		}
665	}
666}
667
668#[cfg(feature = "std")]
669pub use std_features::*;