wasmcloud_actor_logging/
lib.rs

1#![doc(html_logo_url = "https://avatars2.githubusercontent.com/u/52050279?s=200&v=4")]
2//! # wasmCloud Logging Actor Interface
3//!
4//! This crate provides an abstraction over the `wasmcloud:logging` contract. This
5//! allows actors to use normal log macros (like `info!`, `warn!`, `error!`, etc)
6//! to write logs from within the actor.
7//!
8//! Example:
9//! ```rust
10//! extern crate wasmcloud_actor_http_server as http;
11//! extern crate wasmcloud_actor_logging as logging;
12//! extern crate wasmcloud_actor_core as actor;
13//! use wapc_guest::HandlerResult;
14//! use http::{Request, Response, Handlers};
15//! use log::{info, warn, error, trace, debug};
16//!
17//! #[actor::init]
18//! pub fn init() {
19//!     http::Handlers::register_handle_request(method_logger);
20//!     /// Initialize the logger to enable log macros
21//!     logging::enable_macros();
22//! }
23//!
24//! /// Actor must be signed with `wasmcloud:logging` to log messages
25//! fn method_logger(msg: http::Request) -> HandlerResult<http::Response> {
26//!     /// Logs can be directly written via `write_log`
27//!     logging::default().write_log("", "trace", "Coercing Rust String to str");
28//!     
29//!     /// After initialization, logs can be directly written from the actor using macros
30//!     match &*msg.method {
31//!         "GET" => info!("Received a GET request"),
32//!         "POST" => info!("Received a POST request"),
33//!         "PUT" => info!("Received a PUT request"),
34//!         "DELETE" => warn!("Received a DELETE request"),
35//!         req => error!("Received an unsupported HTTP Request: {}", req),
36//!     };
37//!     debug!("Finished matching HTTP method, returning OK");
38//!     Ok(http::Response::ok())
39//! }
40//! ```
41
42mod generated;
43#[allow(unused_imports)]
44pub use generated::*;
45
46// The operation used to request writing a log
47pub const OP_LOG: &str = "WriteLog";
48
49#[cfg(feature = "guest")]
50#[doc(hidden)]
51static LOG_LEVELS: [&str; 5] = ["error", "warn", "info", "debug", "trace"];
52
53#[cfg(feature = "guest")]
54impl Host {
55    /// Writes a log message to specified target and level
56    ///
57    /// # Arguments
58    ///
59    /// * `target` - Used to filter logs to a specific target, e.g. actor name. Can be left blank
60    /// * `level` - Log level, accepts `error`, `warn`, `info`, `debug`, `trace`. Defaults to `info`
61    /// * `text` - Text to log
62    ///
63    pub fn write_log(&self, target: &str, level: &str, text: &str) -> HandlerResult<()> {
64        let log_level = if LOG_LEVELS.contains(&level.to_ascii_lowercase().as_str()) {
65            level
66        } else {
67            "info"
68        };
69        self._write_log(target.to_string(), log_level.to_string(), text.to_string())
70    }
71}
72
73// Begin implementation of automatic log macro interception
74
75#[cfg(feature = "guest")]
76use lazy_static::lazy_static;
77#[cfg(feature = "guest")]
78use log::{Metadata, Record};
79#[cfg(feature = "guest")]
80use std::sync::{Arc, RwLock};
81#[cfg(feature = "guest")]
82use wapc_guest::HandlerResult;
83
84#[cfg(feature = "guest")]
85lazy_static! {
86    static ref CURRENT_BINDING: Arc<RwLock<String>> = Arc::new(RwLock::new("default".to_string()));
87}
88
89#[cfg(feature = "guest")]
90static LOGGER: Host = Host {};
91
92/// Initializes the logger to use standard log macros
93///
94/// This function must be called before attempting to use log macros
95/// such as `info!` or `debug!` or the logs will not be written by the logger
96#[cfg(feature = "guest")]
97pub fn enable_macros() {
98    if log::set_logger(&LOGGER).is_ok() {};
99    log::set_max_level(log::LevelFilter::Trace);
100}
101
102#[cfg(feature = "guest")]
103fn set_binding(binding: &str) {
104    *CURRENT_BINDING.write().unwrap() = binding.to_string();
105}
106
107#[cfg(feature = "guest")]
108impl log::Log for Host {
109    fn enabled(&self, _metadata: &Metadata) -> bool {
110        true
111    }
112
113    fn log(&self, record: &Record) {
114        use log::Level::*;
115        let level = match record.level() {
116            Error => "error",
117            Warn => "warn",
118            Info => "info",
119            Debug => "debug",
120            Trace => "trace",
121        };
122        let _ = self._write_log(
123            record.target().to_string(),
124            level.to_string(),
125            format!("{}", record.args()),
126        );
127    }
128
129    fn flush(&self) {}
130}