flogging_macros/src/lib.rs

Lines

100.00 %

Functions

100.00 %

Regions

100.00 %

LineCountSource
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
65
mod format;
66
mod logger;
67
68
extern crate dyn_fmt;
69
extern crate proc_macro;
70
extern crate proc_macro_error;
71
72
use crate::{format::format_impl, logger::logger_impl};
73
use 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]
1369
pub fn config(msg: TokenStream) -> TokenStream {
1379
    format_impl("__log.config({&__fmt});\n", msg)
1389
}
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]
1748
pub fn entering(_msg: TokenStream) -> TokenStream {
1758
    if _msg.to_string().is_empty() {
1765
        "__log.entering();\n".parse().unwrap_or_default()
177
    } else {
1783
        format_impl("__log.entering_with({&__fmt});\n", _msg)
179
    }
1808
}
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]
2307
pub fn exiting(_msg: TokenStream) -> TokenStream {
2317
    if _msg.to_string().is_empty() {
2325
        "__log.exiting();\n".parse().unwrap_or_default()
233
    } else {
2342
        format_impl("__log.exiting_with({&__fmt});\n", _msg)
235
    }
2367
}
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]
2713
pub fn fine(msg: TokenStream) -> TokenStream {
2723
    format_impl("__log.fine({&__fmt});\n", msg)
2733
}
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]
2962
pub fn finer(msg: TokenStream) -> TokenStream {
2972
    format_impl("__log.finer({&__fmt});\n", msg)
2982
}
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]
3182
pub fn finest(msg: TokenStream) -> TokenStream {
3192
    format_impl("__log.finest({&__fmt});\n", msg)
3202
}
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]
3661
pub fn get_handler(handler: TokenStream) -> TokenStream {
3671
    format!("__log.get_handler({handler})")
3681
        .parse()
3691
        .unwrap_or_default()
3701
}
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]
3985
pub fn info(msg: TokenStream) -> TokenStream {
3995
    format_impl("__log.info({&__fmt});\n", msg)
4005
}
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]
4402
pub fn is_logging(_msg: TokenStream) -> TokenStream {
4412
    "__log.is_logging()".to_string().parse().unwrap_or_default()
4422
}
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]
46211
pub fn logger(attr: TokenStream, item: TokenStream) -> TokenStream {
46311
    logger_impl(attr, item)
46411
}
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]
5002
pub fn set_level(level: TokenStream) -> TokenStream {
5012
    format!("__log.set_level({level});\n")
5022
        .parse()
5032
        .unwrap_or_default()
5042
}
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]
5293
pub fn severe(msg: TokenStream) -> TokenStream {
5303
    format_impl("__log.severe({&__fmt});\n", msg)
5313
}
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]
5554
pub fn warning(msg: TokenStream) -> TokenStream {
5564
    format_impl("__log.warning({&__fmt});\n", msg)
5574
}