numcodecs_wasm_host_reproducible/
logging.rs

1use std::sync::OnceLock;
2
3use log::Level;
4use wasm_component_layer::{
5    AsContextMut, EnumType, Func, FuncType, InterfaceIdentifier, Linker, PackageIdentifier,
6    PackageName, TypeIdentifier, Value, ValueType,
7};
8
9pub fn add_to_linker(linker: &mut Linker, ctx: impl AsContextMut) -> Result<(), anyhow::Error> {
10    const LEVEL_CASES: [&str; 6] = ["trace", "debug", "info", "warn", "error", "critical"];
11    const LOG_LEVELS: [Level; 6] = [
12        Level::Trace,
13        Level::Debug,
14        Level::Info,
15        Level::Warn,
16        Level::Error,
17        Level::Error,
18    ];
19
20    let WasiLoggingInterface {
21        logging: wasi_logging_interface,
22    } = WasiLoggingInterface::get();
23
24    let wasi_logging_instance = linker.define_instance(wasi_logging_interface.clone())?;
25
26    let level_ty = EnumType::new(
27        Some(TypeIdentifier::new(
28            "level",
29            Some(wasi_logging_interface.clone()),
30        )),
31        LEVEL_CASES,
32    )?;
33
34    let log = Func::new(
35        ctx,
36        FuncType::new(
37            [
38                ValueType::Enum(level_ty.clone()),
39                ValueType::String,
40                ValueType::String,
41            ],
42            [],
43        ),
44        move |_ctx, args, _results| {
45            let [Value::Enum(level), Value::String(context), Value::String(message)] = args else {
46                anyhow::bail!("invalid wasi:logging/logging#log arguments");
47            };
48
49            anyhow::ensure!(
50                level.ty() == level_ty,
51                "invalid wasi:logging/logging#log level type"
52            );
53
54            let Some(level) = LOG_LEVELS.get(level.discriminant()) else {
55                anyhow::bail!("invalid wasi:logging/logging#log level kind");
56            };
57
58            log!(target: context, *level, "{message}");
59
60            Ok(())
61        },
62    );
63
64    wasi_logging_instance.define_func("log", log)?;
65
66    Ok(())
67}
68
69#[non_exhaustive]
70pub struct WasiLoggingInterface {
71    pub logging: InterfaceIdentifier,
72}
73
74impl WasiLoggingInterface {
75    #[must_use]
76    pub fn get() -> &'static Self {
77        static WASI_LOGGING_INTERFACE: OnceLock<WasiLoggingInterface> = OnceLock::new();
78
79        WASI_LOGGING_INTERFACE.get_or_init(|| Self {
80            logging: InterfaceIdentifier::new(
81                PackageIdentifier::new(PackageName::new("wasi", "logging"), None),
82                "logging",
83            ),
84        })
85    }
86}