plctag_log/
lib.rs

1// plctag-rs
2//
3// a rust wrapper of libplctag, with rust style APIs and useful extensions.
4// Copyright: 2022, Joylei <leingliu@gmail.com>
5// License: MIT
6
7/*!
8# plctag-log
9
10log adapter for `libplctag`, one component of `plctag` rust bindings
11
12[![crates.io](https://img.shields.io/crates/v/plctag-log.svg)](https://crates.io/crates/plctag-log)
13[![docs](https://docs.rs/plctag-log/badge.svg)](https://docs.rs/plctag-log)
14[![build](https://github.com/joylei/plctag-rs/workflows/build/badge.svg?branch=master)](https://github.com/joylei/plctag-rs/actions?query=workflow%3A%22build%22)
15[![license](https://img.shields.io/crates/l/plctag.svg)](https://github.com/joylei/plctag-rs/blob/master/LICENSE)
16
17## Usage
18
19please use it with [plctag](https://crates.io/crates/plctag)
20
21by default, `libplctag` logs internal messages to stdout, if you set debug level other than none.
22you can register your own logger by calling [`register_logger`].
23For convenient, [`log_adapt`] register a logger for you and will forward internal log messages to crate`log`.
24
25Add `plctag-log` to your Cargo.toml
26
27```toml
28[dependencies]
29plctag-log= "0.1"
30```
31
32### Note
33
34`libplctag` will print log messages to stdout even if you register your own logger by `register_logger`.
35
36### Examples
37
38```rust,no_run
39use plctag_log::*;
40
41log_adapt(); //register logger
42set_debug_level(DebugLevel::Info); // set debug level
43
44// now, you can receive log messages by any of logging implementations of crate `log`
45```
46
47## License
48
49MIT
50
51*/
52#![warn(missing_docs)]
53
54extern crate plctag_core;
55#[macro_use]
56extern crate log;
57
58pub use plctag_core::builder::DebugLevel;
59
60use plctag_core::ffi;
61use std::ffi::{CStr, CString};
62use std::os::raw::c_char;
63
64/// set debug level of `libplctag`
65///
66/// #Note
67/// `libplctag` will print logs to stdout even if you register your own logger by `plc::register_logger`
68#[inline]
69pub fn set_debug_level(debug: DebugLevel) {
70    let level = debug as u8;
71    unsafe { ffi::plc_tag_set_debug_level(level as i32) };
72}
73
74/// retrieve debug level
75#[inline(always)]
76pub fn get_debug_level() -> DebugLevel {
77    let v = get_int_attr("debug");
78    (v as u8).into()
79}
80
81/// register a custom logger to receive inner message of `libplctag`
82///
83/// # Note
84/// `libplctag` will print logs to stdout even if you register your own logger by `register_logger`
85pub use ffi::plc_tag_register_logger as register_logger;
86pub use ffi::plc_tag_unregister_logger as unregister_logger;
87
88#[inline(always)]
89fn get_int_attr(attr: &str) -> i32 {
90    let attr = CString::new(attr).unwrap();
91    unsafe { ffi::plc_tag_get_int_attribute(0, attr.as_ptr(), 0) }
92}
93
94#[doc(hidden)]
95unsafe extern "C" fn log_route(_tag_id: i32, level: i32, message: *const c_char) {
96    let msg = CStr::from_ptr(message).to_string_lossy();
97    match level {
98        1 => error!("{}", msg),
99        2 => warn!("{}", msg),
100        3 => info!("{}", msg),
101        4 => debug!("{}", msg),
102        5 => trace!("{}", msg),
103        6 => trace!("{}", msg),
104        _ => (),
105    }
106}
107
108/// by default, `libplctag` logs internal messages to stdout, if you set debug level other than none.
109/// you can register your own logger by calling [`register_logger`].
110/// For convenient, this method will register a logger for you and will forward internal log messages to crate`log`.
111///
112/// # Note
113/// `libplctag` will print log messages to stdout even if you register your own logger by `register_logger`.
114///
115/// # Examples
116/// ```rust,no_run
117/// use plctag_log::*;
118///
119/// log_adapt(); //register logger
120/// set_debug_level(DebugLevel::Info); // set debug level
121///
122/// // now, you can receive log messages by any of logging implementations of crate `log`
123///
124/// ```
125pub fn log_adapt() {
126    unsafe {
127        ffi::plc_tag_unregister_logger();
128        let rc = ffi::plc_tag_register_logger(Some(log_route));
129        debug_assert_eq!(rc, ffi::PLCTAG_STATUS_OK as i32);
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use log::*;
137    use plctag_core::RawTag;
138    use std::sync::{Arc, Mutex};
139
140    struct MemLogger {
141        buf: Arc<Mutex<Vec<String>>>,
142    }
143
144    impl MemLogger {
145        fn new() -> Self {
146            Self {
147                buf: Arc::new(Mutex::new(vec![])),
148            }
149        }
150
151        fn buf(&self) -> Vec<String> {
152            self.buf.lock().unwrap().clone()
153        }
154
155        fn init(&self) {
156            log::set_max_level(LevelFilter::Trace);
157            let _ = log::set_boxed_logger(Box::new(self.clone()));
158        }
159    }
160
161    impl Clone for MemLogger {
162        fn clone(&self) -> Self {
163            Self {
164                buf: self.buf.clone(),
165            }
166        }
167    }
168
169    impl Log for MemLogger {
170        fn enabled(&self, meta: &log::Metadata<'_>) -> bool {
171            meta.level() <= Level::Error
172        }
173        fn log(&self, record: &log::Record<'_>) {
174            self.buf
175                .lock()
176                .unwrap()
177                .push(format!("{} - {}", record.target(), record.args()));
178        }
179        fn flush(&self) {}
180    }
181
182    #[test]
183    fn test_log_adapt() {
184        let logger = MemLogger::new();
185        logger.init();
186        log_adapt();
187        set_debug_level(DebugLevel::Detail);
188
189        let res = RawTag::new("make=system&family=library&name=debug&debug=4", 100);
190        assert!(res.is_ok());
191        let tag = res.unwrap();
192        let status = tag.status();
193        assert!(status.is_ok());
194
195        let buf = logger.buf();
196        assert!(buf.len() > 0);
197        let msg = buf.join("\r\n");
198        assert!(msg.contains("plc_tag_create"));
199    }
200}