flogging_macros/
lib.rs

1//
2// File Name:    lib.rs
3// Project Name: flogger_macros
4//
5// Copyright (C) 2025 Bradley Willcott
6//
7// SPDX-License-Identifier: GPL-3.0-or-later
8//
9// This library (crate) is free software: you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// This library (crate) is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17// GNU General Public License for more details.
18//
19// You should have received a copy of the GNU General Public License
20// along with this library (crate).  If not, see <https://www.gnu.org/licenses/>.
21//
22
23//!
24//! # Flogging Macros
25//!
26//! This is a **supporting crate** for the `flogging` crate.
27//!
28//! It is _not_ meant to be used on its own. In fact, it would not work without the
29//! other crate. Further, it should not be separately added to your project. Add
30//! `flogging` instead, and this will be included as a dependent to that crate.
31//!
32//! ```text
33//! $ cargo add flogging
34//! ```
35//! Alternatively, add the following to your project's `Cargo.toml` file:
36//! ```text
37//! [dependencies]
38//! flogging = "0.4.0"
39//! ```
40//!
41//! ## Special Note
42//!
43//! For the macros that accept the parameter: `msg`, the following is true:
44//!
45//! - They accept parameters the same as for [`std::format!`](https://doc.rust-lang.org/std/macro.format.html)
46//!     - plain text `&str`: `("It's your time.")`
47//!     - format `&str` with interpolated variables: `("Var: {var}")`
48//!     - format `&str` with supporting parameters: `("Var: {}", var)`
49//!     - Combination of the last two: `("Vars {var1} - {}:{}", var2, var3)`
50//! - Additional Feature
51//!     - Just one or more variables: `(var1, var2, var3)`
52//!     - In this case, a default format string will be used: `"{}, {}, {}"`
53//!     - The number of `"{}"` will depend on the number of parameters.
54//!     - Ideal for logging concrete instances that have very good `Display` implementations,
55//!       or you just need their data without further explanation.
56//! - Special Cases
57//!     - [entering!] and [exiting!]
58//!     - These two macros have the same features as the others,
59//!       but they may also be used _without_ any parameters. In such
60//!       a case, their defaults will be used.
61//!
62
63mod format;
64mod logger;
65
66extern crate dyn_fmt;
67extern crate proc_macro;
68extern crate proc_macro_error;
69
70use crate::{format::format_impl, logger::logger_impl};
71use proc_macro::TokenStream;
72// use proc_macro_error::proc_macro_error;
73
74///
75/// Log a CONFIG message.
76///
77/// CONFIG is a message level for static configuration messages.
78///
79/// CONFIG messages are intended to provide a variety of static
80/// configuration information, to assist in debugging problems
81/// that may be associated with particular configurations.
82///
83/// For example, a CONFIG message might include the CPU type, the
84/// graphics depth, the GUI look-and-feel, etc.
85///
86/// If the logger is currently enabled for the CONFIG message level
87/// then the given message is forwarded to all the registered output
88/// Handler objects.
89///
90/// ## Parameters
91/// - `msg` - See [Special Note](index.html#special-note)
92///
93/// ## Examples
94///
95/// ```no_run
96/// use flogging::*;
97/// use chrono::Local;
98///
99/// const_logger!({
100///     Logger::console_logger(module_path!())
101/// });
102///
103/// #[logger]
104/// pub fn my_func(data: &str) {
105///     config!("Some text to store.");
106///
107///     let time = Local::now();
108///
109///     config!(time);
110///     config!(time, data);
111///     config!("The configuration as at: {}", time);
112///     config!("The configuration as at: {time}: {}", data);
113///     config!("The configuration as at: {time:?}: {data}");
114/// }
115///
116/// fn main(){
117///     let data = "Some data";
118///     my_func(data);
119/// }
120/// ```
121/// Output:
122/// ```text
123/// |flogging->my_func| [CONFIG ] Some text to store.
124/// |flogging->my_func| [CONFIG ] 2025-07-18 19:52:06.927418853 +08:00
125/// |flogging->my_func| [CONFIG ] 2025-07-18 19:52:06.927418853 +08:00, Some data
126/// |flogging->my_func| [CONFIG ] The configuration as at: 2025-07-18 19:52:06.927418853 +08:00
127/// |flogging->my_func| [CONFIG ] The configuration as at: 2025-07-18 19:52:06.927418853 +08:00: Some data
128/// |flogging->my_func| [CONFIG ] The configuration as at: 2025-07-18T19:52:06.927418853+08:00: Some data
129/// ```
130/// [format]: https://doc.rust-lang.org/std/macro.format.html
131///
132// #[proc_macro_error]
133#[proc_macro]
134pub fn config(msg: TokenStream) -> TokenStream {
135    format_impl("__log.config({&__fmt});\n", msg)
136}
137
138///
139/// Log entry into a function/method.
140///
141/// This is a convenience macro that can be used to log entry into to a function/method. It can be used
142/// without an alternative message. A possible use is to provide the function/method's parameters
143/// to track what is being passed-in.
144///
145/// If no alternative message is provided, then the default message, "Entry", is used.
146///
147/// A `LogEntry` is created with a log level of FINER, that is then logged.
148///
149/// ## Parameters
150/// - `msg` - (Optional) The same as for [`std::format!`](https://doc.rust-lang.org/std/macro.format.html)
151///
152/// ## Examples
153///```no_run
154/// #[logger]
155/// pub fn add_student(name: String, age: u8) {
156///     entering!("name: {name}, age: {age}");
157/// }
158///
159/// fn main(){
160///     let name = "Mary Jane Thompson".to_string();
161///     let age = 18;
162///
163///     add_student(name, age);
164/// }
165/// ```
166/// Output:
167/// ```text
168/// |flogging->add_student| [FINER  ] Entry: (name: Mary Jane Thompson, age: 18)
169/// ```
170///
171#[proc_macro]
172pub fn entering(_msg: TokenStream) -> TokenStream {
173    if _msg.to_string().is_empty() {
174        "__log.entering();\n".parse().unwrap_or_default()
175    } else {
176        format_impl("__log.entering_with({&__fmt});\n", _msg)
177    }
178}
179
180///
181/// Log return from a function/method.
182///
183/// This is a convenience macro that can be used to log exiting from a function/method. It can be used
184/// without an alternative message. A possible use is to provide the function/method's return value
185/// to track what is being passed-out.
186///
187/// If no alternative message is provided, then the default message, "Return", is used.
188///
189/// A `LogEntry` is created with a log level of FINER, that is then logged.
190///
191/// ## Parameters
192/// - `msg` - (Optional) The same as for [`std::format!`](https://doc.rust-lang.org/std/macro.format.html)
193///
194/// ## Examples
195///```no_run
196/// #[logger]
197/// pub fn add_student(name: String, age: u8) -> bool {
198///     let mut rtn = false;
199///
200///     entering!("name: {name}, age: {age}");
201///
202///     /* Some processing that provides a result (rtn) */
203///     rtn = true;
204///
205///     exiting!("rtn: {rtn}");
206///     rtn
207/// }
208///
209/// fn main(){
210///     let name = "Mary Jane Thompson".to_string();
211///     let age = 18;
212///
213///     if add_student(name, age) {
214///         println!("Success");
215///     } else {
216///         println!("Failure!");
217///     }
218/// }
219/// ```
220/// Output:
221/// ```text
222/// |flogging->add_student| [FINER  ] Entry: (name: Mary Jane Thompson, age: 18)
223/// |flogging->add_student| [FINER  ] Return: (rtn: true)
224/// Success
225/// ```
226///
227#[proc_macro]
228pub fn exiting(_msg: TokenStream) -> TokenStream {
229    if _msg.to_string().is_empty() {
230        "__log.exiting();\n".parse().unwrap_or_default()
231    } else {
232        format_impl("__log.exiting_with({&__fmt});\n", _msg)
233    }
234}
235
236///
237/// Log a FINE message.
238///
239/// FINE is a message level providing tracing information.
240///
241/// All of FINE, FINER, and FINEST are intended for relatively
242/// detailed tracing. The exact meaning of the three levels will
243/// vary between subsystems, but in general, FINEST should be
244/// used for the most voluminous detailed output, FINER for somewhat
245/// less detailed output, and FINE for the lowest volume (and most
246/// important) messages.
247///
248/// In general the FINE level should be used for information that
249/// will be broadly interesting to developers who do not have a
250/// specialized interest in the specific subsystem.
251///
252/// FINE messages might include things like minor (recoverable)
253/// failures. Issues indicating potential performance problems are
254/// also worth logging as FINE.
255///
256/// If the logger is currently enabled for the FINE message level
257/// then the given message is forwarded to all the registered output
258/// Handler objects.
259///
260/// ## Parameters
261/// - `msg` - The same as for [`std::format!`](https://doc.rust-lang.org/std/macro.format.html)
262///
263/// ## Examples
264///
265/// See [config](macro.config.html#examples). The syntax/usage is the same.
266/// Just substitute `fine!` for `config!`.
267///
268#[proc_macro]
269pub fn fine(msg: TokenStream) -> TokenStream {
270    format_impl("__log.fine({&__fmt});\n", msg)
271}
272
273///
274/// Log a FINER message.
275///
276/// FINER indicates a fairly detailed tracing message.
277/// Suggest logging calls for entering, returning,
278/// or `Error`s, such as returned via `Result`, are traced at
279/// this level.
280///
281/// If the logger is currently enabled for the FINER message level
282/// then the given message is forwarded to all the registered output
283/// Handler objects.
284///
285/// ## Parameters
286/// - `msg` - The same as for [`std::format!`](https://doc.rust-lang.org/std/macro.format.html)
287///
288/// ## Examples
289///
290/// See [config](macro.config.html#examples). The syntax/usage is the same.
291/// Just substitute `finer!` for `config!`.
292///
293#[proc_macro]
294pub fn finer(msg: TokenStream) -> TokenStream {
295    format_impl("__log.finer({&__fmt});\n", msg)
296}
297
298///
299/// Log a FINEST message.
300///
301/// FINEST indicates a highly detailed tracing message.
302///
303/// If the logger is currently enabled for the FINEST message level
304/// then the given message is forwarded to all the registered output
305/// Handler objects.
306///
307/// ## Parameters
308/// - `msg` - The same as for [`std::format!`](https://doc.rust-lang.org/std/macro.format.html)
309///
310/// ## Examples
311///
312/// See [config](macro.config.html#examples). The syntax/usage is the same.
313/// Just substitute `finest!` for `config!`.
314///
315#[proc_macro]
316pub fn finest(msg: TokenStream) -> TokenStream {
317    format_impl("__log.finest({&__fmt});\n", msg)
318}
319
320///
321/// Get the required `Handler`.
322///
323/// ## Examples
324/// ```no_run
325/// extern crate flogging;
326/// use flogging::*;
327///
328/// // Setting up the module level logger.
329/// const_logger!({
330///     Logger::builder(module_path!())
331///         .add_string_handler()
332///         .set_level(Level::ALL)
333///         .build()
334/// });
335///
336/// #[logger]
337/// fn my_func(){
338///     info!("Some text to store.");
339///     warning!("Rain is wet!");
340///     severe!("Hurricanes are windy!");
341///
342///     if let Some(h) = get_handler!(Handler::String) {
343///         println!(
344///             "\n(h.get_log())\n======v======\n{}\n======^======",
345///             h.get_log()
346///         );
347///     } else {
348///         println!("Sorry. Not there!");
349///     }
350/// }
351/// ```
352/// Output:
353/// ```text
354/// (h.get_log())
355/// ======v======
356/// |flogging->my_func| [INFO   ] Some text to store.
357/// |flogging->my_func| [WARNING] Rain is wet!
358/// |flogging->my_func| [SEVERE ] Hurricanes are windy!
359///
360/// ======^======
361/// ```
362///
363#[proc_macro]
364pub fn get_handler(handler: TokenStream) -> TokenStream {
365    format!("__log.get_handler({handler})")
366        .parse()
367        .unwrap_or_default()
368}
369
370///
371/// Log an INFO message.
372///
373/// INFO is a message level for informational messages.
374///
375/// Typically INFO messages will be written to the console or its
376/// equivalent. So the INFO level should only be used for reasonably
377/// significant messages that will make sense to end users and system
378/// administrators.
379///
380/// **\[default level]** : This is the default level used when a logger is created.
381/// See [set_level!] for changing this.
382///
383/// If the logger is currently enabled for the INFO message level
384/// then the given message is forwarded to all the registered output
385/// Handler objects.
386///
387/// ## Parameters
388/// - `msg` - The same as for [`std::format!`](https://doc.rust-lang.org/std/macro.format.html)
389///
390/// ## Examples
391///
392/// See [config](macro.config.html#examples). The syntax/usage is the same.
393/// Just substitute `info!` for `config!`.
394///
395#[proc_macro]
396pub fn info(msg: TokenStream) -> TokenStream {
397    format_impl("__log.info({&__fmt});\n", msg)
398}
399
400///
401/// Provides for logging within the attributed function/method.
402///
403/// This is required to be able to use the [macros](index.html#macros-1).
404/// It sets up the local variable used by the other macros, and it also registers the function/method
405/// name used by the log entries (if included in the formatter's `fmt_string`).
406///
407/// ```no_run
408/// #[logger]
409/// pub fn my_func(msg: &str){
410///     entering!();
411///     fine!("msg: {msg}");
412///
413///     ...
414/// }
415/// ```
416///
417#[proc_macro_attribute]
418pub fn logger(attr: TokenStream, item: TokenStream) -> TokenStream {
419    logger_impl(attr, item)
420}
421
422///
423/// Set the logging level for this `Logger` instance.
424///
425/// The default level is INFO.
426///
427/// ## Parameters
428/// - `level` - The required logging level.
429///
430/// ## Examples
431/// ```
432/// #[logger]
433/// pub fn my_func(msg: &str){
434///     entering!("msg: {msg}");
435///     fine!("Everything is just fine!");
436///
437///     // ...
438/// }
439///
440/// fn main(){
441///     set_level!(Level::FINER);
442///
443///     let msg = "Just some text to work with.";
444///
445///     my_func(msg);
446/// }
447///
448/// ```
449/// Output:
450/// ```text
451/// |flogging->my_func| [FINER  ] Entry: (msg: Just some text to work with.)
452/// |flogging->my_func| [FINE   ] Everything is just fine!
453/// ```
454///
455#[proc_macro]
456pub fn set_level(level: TokenStream) -> TokenStream {
457    format!("__log.set_level({level});\n")
458        .parse()
459        .unwrap_or_default()
460}
461
462///
463/// Log a SEVERE message.
464///
465/// SEVERE is a message level indicating a serious failure.
466///
467/// In general SEVERE messages should describe events that are of
468/// considerable importance and which will prevent normal program
469/// execution. They should be reasonably intelligible to end users
470/// and to system administrators.
471///
472/// If the logger is currently enabled for the SEVERE message level
473/// then the given message is forwarded to all the registered output
474/// Handler objects.
475///
476/// ## Parameters
477/// - `msg` - The same as for [`std::format!`](https://doc.rust-lang.org/std/macro.format.html)
478///
479/// ## Examples
480///
481/// See [config](macro.config.html#examples). The syntax/usage is the same.
482/// Just substitute `severe!` for `config!`.
483///
484#[proc_macro]
485pub fn severe(msg: TokenStream) -> TokenStream {
486    format_impl("__log.severe({&__fmt});\n", msg)
487}
488
489///
490/// Log a WARNING message.
491///
492/// WARNING is a message level indicating a potential problem.
493///
494/// In general WARNING messages should describe events that will be
495/// of interest to end users or system managers, or which indicate
496/// potential problems.
497///
498/// If the logger is currently enabled for the WARNING message level
499/// then the given message is forwarded to all the registered output
500/// Handler objects.
501///
502/// ## Parameters
503/// - `msg` - The same as for [`std::format!`](https://doc.rust-lang.org/std/macro.format.html)
504///
505/// ## Examples
506///
507/// See [config](macro.config.html#examples). The syntax/usage is the same.
508/// Just substitute `warning!` for `config!`.
509///
510#[proc_macro]
511pub fn warning(msg: TokenStream) -> TokenStream {
512    format_impl("__log.warning({&__fmt});\n", msg)
513}