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