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[](https://crates.io/crates/plctag-log)
13[](https://docs.rs/plctag-log)
14[](https://github.com/joylei/plctag-rs/actions?query=workflow%3A%22build%22)
15[](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}