1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#![doc(html_logo_url = "https://avatars2.githubusercontent.com/u/52050279?s=200&v=4")]
//! # wasmCloud Logging Actor Interface
//!
//! This crate provides an abstraction over the `wasmcloud:logging` contract. This
//! allows actors to use normal log macros (like `info!`, `warn!`, `error!`, etc)
//! to write logs from within the actor.
//!
//! Example:
//! ```rust
//! extern crate wasmcloud_actor_http_server as http;
//! extern crate wasmcloud_actor_logging as logging;
//! extern crate wasmcloud_actor_core as actor;
//! use wapc_guest::HandlerResult;
//! use http::{Request, Response, Handlers};
//! use log::{info, warn, error, trace, debug};
//!
//! #[actor::init]
//! pub fn init() {
//!     http::Handlers::register_handle_request(method_logger);
//!     /// Initialize the logger to enable log macros
//!     logging::enable_macros();
//! }
//!
//! /// Actor must be signed with `wasmcloud:logging` to log messages
//! fn method_logger(msg: http::Request) -> HandlerResult<http::Response> {
//!     /// Logs can be directly written via `write_log`
//!     logging::default().write_log("", "trace", "Coercing Rust String to str");
//!     
//!     /// After initialization, logs can be directly written from the actor using macros
//!     match &*msg.method {
//!         "GET" => info!("Received a GET request"),
//!         "POST" => info!("Received a POST request"),
//!         "PUT" => info!("Received a PUT request"),
//!         "DELETE" => warn!("Received a DELETE request"),
//!         req => error!("Received an unsupported HTTP Request: {}", req),
//!     };
//!     debug!("Finished matching HTTP method, returning OK");
//!     Ok(http::Response::ok())
//! }
//! ```

mod generated;
#[allow(unused_imports)]
pub use generated::*;

// The operation used to request writing a log
pub const OP_LOG: &str = "WriteLog";

#[cfg(feature = "guest")]
#[doc(hidden)]
static LOG_LEVELS: [&str; 5] = ["error", "warn", "info", "debug", "trace"];

#[cfg(feature = "guest")]
impl Host {
    /// Writes a log message to specified target and level
    ///
    /// # Arguments
    ///
    /// * `target` - Used to filter logs to a specific target, e.g. actor name. Can be left blank
    /// * `level` - Log level, accepts `error`, `warn`, `info`, `debug`, `trace`. Defaults to `info`
    /// * `text` - Text to log
    ///
    pub fn write_log(&self, target: &str, level: &str, text: &str) -> HandlerResult<()> {
        let log_level = if LOG_LEVELS.contains(&level.to_ascii_lowercase().as_str()) {
            level
        } else {
            "info"
        };
        self._write_log(target.to_string(), log_level.to_string(), text.to_string())
    }
}

// Begin implementation of automatic log macro interception

#[cfg(feature = "guest")]
use lazy_static::lazy_static;
#[cfg(feature = "guest")]
use log::{Metadata, Record};
#[cfg(feature = "guest")]
use std::sync::{Arc, RwLock};
#[cfg(feature = "guest")]
use wapc_guest::HandlerResult;

#[cfg(feature = "guest")]
lazy_static! {
    static ref CURRENT_BINDING: Arc<RwLock<String>> = Arc::new(RwLock::new("default".to_string()));
}

#[cfg(feature = "guest")]
static LOGGER: Host = Host {};

/// Initializes the logger to use standard log macros
///
/// This function must be called before attempting to use log macros
/// such as `info!` or `debug!` or the logs will not be written by the logger
#[cfg(feature = "guest")]
pub fn enable_macros() {
    if log::set_logger(&LOGGER).is_ok() {};
    log::set_max_level(log::LevelFilter::Trace);
}

#[cfg(feature = "guest")]
fn set_binding(binding: &str) {
    *CURRENT_BINDING.write().unwrap() = binding.to_string();
}

#[cfg(feature = "guest")]
impl log::Log for Host {
    fn enabled(&self, _metadata: &Metadata) -> bool {
        true
    }

    fn log(&self, record: &Record) {
        use log::Level::*;
        let level = match record.level() {
            Error => "error",
            Warn => "warn",
            Info => "info",
            Debug => "debug",
            Trace => "trace",
        };
        let _ = self._write_log(
            record.target().to_string(),
            level.to_string(),
            format!("{}", record.args()),
        );
    }

    fn flush(&self) {}
}