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 [
46                Value::Enum(level),
47                Value::String(context),
48                Value::String(message),
49            ] = args
50            else {
51                anyhow::bail!("invalid wasi:logging/logging#log arguments");
52            };
53
54            anyhow::ensure!(
55                level.ty() == level_ty,
56                "invalid wasi:logging/logging#log level type"
57            );
58
59            let Some(level) = LOG_LEVELS.get(level.discriminant()) else {
60                anyhow::bail!("invalid wasi:logging/logging#log level kind");
61            };
62
63            log!(target: context, *level, "{message}");
64
65            Ok(())
66        },
67    );
68
69    wasi_logging_instance.define_func("log", log)?;
70
71    Ok(())
72}
73
74#[non_exhaustive]
75pub struct WasiLoggingInterface {
76    pub logging: InterfaceIdentifier,
77}
78
79impl WasiLoggingInterface {
80    #[must_use]
81    pub fn get() -> &'static Self {
82        static WASI_LOGGING_INTERFACE: OnceLock<WasiLoggingInterface> = OnceLock::new();
83
84        WASI_LOGGING_INTERFACE.get_or_init(|| Self {
85            logging: InterfaceIdentifier::new(
86                PackageIdentifier::new(PackageName::new("wasi", "logging"), None),
87                "logging",
88            ),
89        })
90    }
91}