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}