libprettylogger 3.1.0

Fancy logger library.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
![libprettylogger logo](https://raw.githubusercontent.com/tpaau-17DB/libprettylogger/main/img/libprettylogger-logo.png)

![CI Ubuntu](https://img.shields.io/github/actions/workflow/status/tpaau-17DB/libprettylogger/Ubuntu.yml?branch=main)
![Crates.io](https://img.shields.io/crates/v/libprettylogger.svg)
![MSRV](https://img.shields.io/badge/MSRV-1.79.0-blue)

## Table of Contents
* [TL;DR]#tldr
* [The Logger]#the-logger
    * [Log Filtering]#the-logger_log-filtering
    * [Logger Templates]#the-logger_logger-templates
    * [Global `Logger` instance]#the-logger_global-logger
* [Log Formatting]#log-formatting
    * [Log Formatter]#log-formatting_log-formatter
    * [Log Format]#log-formatting_log-format
    * [Using the `LogStruct`]#log-formatting_using-log-struct
* [Log Outputs]#log-outputs
    * [Log Output (parent)]#log-outputs_log-output
    * [Stderr Stream]#log-outputs_stderr-stream
    * [Buffer Stream]#log-outputs_buffer-stream
    * [File Stream]#log-outputs_file-stream
        * [Automatic Log Buffer Flushing]#log-outputs_file-stream_auto-log-buffer-flushing
        * [Locking the Log File]#log-outputs_file-stream_locking-log-file


<a name="tldr"></a>
## TL;DR
### Installing the library
```bash
cargo add libprettylogger
```

### Quick start
```rust
# use prettylogger::{debug, info, warn, err, fatal};
debug!("This is a debug message!");
info!("This is an info message!");
warn!("This is a warning!");
err!("This is an error!");
fatal!("This is a fatal error!");
```

<a name="the-logger"></a>
## The Logger
The `Logger` struct only handles log filtering, relying on `LogFormatter` and
`LogOutput` for formatting and outputting the logs.

Creating a `Logger` struct with default configuration:
```rust
# use prettylogger::Logger;
let mut logger = Logger::default();
```

<a name="the-logger_log-filtering"></a>
### Log filtering
Logs are filtered based on their importance and the verbosity setting.

Setting logger verbosity:
```rust
# use prettylogger::{Logger, config::Verbosity};
# let mut logger = Logger::default();
logger.set_verbosity(Verbosity::All);
```

Toggling log filtering:
```rust
# use prettylogger::{Logger, config::Verbosity};
# let mut logger = Logger::default();
logger.enable_log_filtering();
logger.disable_log_filtering();
```

<a name="the-logger_logger-templates"></a>
### Logger templates
A **Logger template** is serialized `Logger` struct in JSON format. Logger
templates can be used to easily manage and store logger configurations in files.

Here’s an example of what a serialized `Logger` struct looks like in JSON:
```json
{
  "formatter": {
    "log_header_color_enabled": true,
    "debug_color": "Blue",
    "info_color": "Green",
    "warning_color": "Yellow",
    "error_color": "Red",
    "fatal_color": "Magenta",
    "debug_header": "DBG",
    "info_header": "INF",
    "warning_header": "WAR",
    "error_header": "ERR",
    "fatal_header": "FATAL",
    "log_format": "[%h] %m",
    "datetime_format": "%Y-%m-%d %H:%M:%S"
  },
  "output": {
    "stderr_output": {
      "enabled": true
    },
    "file_output": {
      "enabled": false,
      "max_buffer_size": 128,
      "on_drop_policy": "DiscardLogBuffer"
    },
    "buffer_output": {
      "enabled": false
    },
    "enabled": true
  },
  "verbosity": "Standard",
  "filtering_enabled": true
}
```

Loading `Logger` from a template file:
```rust
# use prettylogger::Logger;
# let mut path = std::env::temp_dir();
# path.push("libprettylogger-tests/readme-logger-loading.json");
# let path = path.to_str().unwrap().to_string();
# Logger::default().save_template(&path);
let mut logger = Logger::from_template(&path);
```

Deserializing `Logger` from a JSON string:
```rust
# use prettylogger::Logger;
// Obtain a deserializable string
let raw_json = serde_json::to_string(&Logger::default())
    .expect("Failed to serialize logger!");

// Deserialize `Logger` from a string
let logger = Logger::from_template_str(&raw_json)
    .expect("Failed to deserialize logger!");
# assert_eq!(Logger::default(), logger);
```

Saving `Logger` to a template file:
```rust
# use prettylogger::Logger;
# let mut path = std::env::temp_dir();
# path.push("libprettylogger-tests/readme-logger-saving.json");
# let path = &path.to_str().unwrap().to_string();
let mut logger = Logger::default();
logger.save_template(path);
```

<a name="the-logger_global-logger"></a>
### Global logger instance

The `prettylogger` crate defines a global logger instance wrapped in `RwLock`,
which you can share between multiple threads.

Modifying the global logger configuration:
```rust
# use prettylogger::{glob::LOGGER, config::Verbosity};
// Get write access to the logger
let mut logger = LOGGER.write().unwrap();

// Modify the logger
logger.set_verbosity(Verbosity::All);
```

Using the global logger:
```rust
# use prettylogger::glob::LOGGER;
// Get read access to the logger
let logger = LOGGER.read().unwrap();

// Print some logs
logger.info("Hello, World!");
```

Requiring read access to the global logger in every function you use is tedious,
isn't it? Furthermore, because its methods only accept `&str` as message
arguments, you’d have to handle all message formatting yourself.

Fortunately, there is a set of macros designed just to solve this issue.

Using logging macros to print some messages with the global logger:
```rust
# use prettylogger::{debug, info, warn, err, fatal};
debug!("This is a debug message!");
info!("This is an info message!");
warn!("This is a warning!");
err!("This is an error!");
fatal!("This is a fatal error!");
```

Logging macros accept arguments just like the `format!` macro:
```rust
# use prettylogger::info;
let some_value = 32;
let name = "User";

info!("Hello {name}, `some_value` is {some_value}.");
```

> [!WARNING]
> Since the logging macros acquire read access to the global logger under the
> hood, they will block your thread if there is another process with write
> access to the logger.
>
> This will block the thread:
> ```no_run
> # use prettylogger::{info, glob::LOGGER};
> // Get write access to the logger
> let mut logger = LOGGER.write().unwrap();
>
> // Trying to use logging macro blocks the thread
> info!("This will never be shown!");
> ```


<a name="log-formatting"></a>
## Log formatting

<a name="log-formatting_log-formatter"></a>
### `LogFormatter`
The `LogFormatter` struct manages log formatting. It's accessible as a field
within `Logger`, but can also operate independently.

Using a `LogFormatter`:
```rust
# use prettylogger::{
#    config::LogStruct,
#    format::LogFormatter,
# };
// Create a `LogFormatter` with default configuration
let mut formatter = LogFormatter::default();

// Set a log format
formatter.set_log_format("[ %h %m ]");

// Obtain a formatted log from a `LogStruct`
let log = formatter.format_log(&LogStruct::debug("Hello from LogStruct!"));

// Print the formatted log message
print!("{}", &log);
```

<a name="log-formatting_log-format"></a>
### Log format
A log consists of several headers:
* **Log Type** **** The type of the log (debug, info, warning etc.)
* **Timestamp** **** Contains the date and time the log was created
* **Message** **** The actual log message

These headers can then be formatted using a log format string, similarly to how
you would format datetime with a datetime format string.

Here is a log message with all its headers marked:
```markup
[ DEBUG 21:52:37 An example debug message ]
  ^^^^^ ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
  |     |        |
  |     |        the message
  |     timestamp
  log type
```
This specific effect was achieved by setting the datetime format to `%H:%M:%S`,
log format to `[ %h %d %m ]` and the debug log type header to `DEBUG`.

Setting datetime format of a `LogFormatter`:
```rust
# use prettylogger::format::LogFormatter;
let mut formatter = LogFormatter::default();
formatter.set_datetime_format("%H:%M:%S");
```

Setting a custom log format:
```rust
# use prettylogger::format::LogFormatter;
let mut formatter = LogFormatter::default();
formatter.set_log_format("[ %h %d %m ]");
```

> [!NOTE]
> The `%m` (message) placeholder is mandatory. You will get an error unless you
> include it in your format string.

Customizing log headers:
```rust
# use prettylogger::format::LogFormatter;
let mut formatter = LogFormatter::default();
formatter.set_debug_header("DEBUG");
formatter.set_info_header("INFO");
formatter.set_warning_header("WARNING");
formatter.set_error_header("ERROR");
formatter.set_fatal_header("FATAL ERROR");
```

Setting custom log header colors:
```rust
# use prettylogger::{
#     format::LogFormatter,
#     colors::Color
# };
let mut formatter = LogFormatter::default();
formatter.set_debug_color(Color::Blue);
formatter.set_info_color(Color::Green);
formatter.set_warning_color(Color::Yellow);
formatter.set_error_color(Color::Red);
formatter.set_fatal_color(Color::Magenta);
```

<a name="log-formatting_using-log-struct"></a>
### Using the `LogStruct`
`LogStruct` is a type that represents a single log entry. This is the raw,
non-formatted log message used internally by `Logger`, `LogFormatter` and
log streams.

Creating a `LogStruct` and formatting it with a `LogFormatter`:
```rust
# use prettylogger::{
#     format::LogFormatter,
#     config::LogStruct
# };
let mut formatter = LogFormatter::default();

// Create a `LogStruct`
let raw_log = LogStruct::debug("Hello from a struct!");

// Format the `LogStruct`
let formatted_log = formatter.format_log(&raw_log);

// Print the formatted log
print!("{}", &formatted_log);
```


<a name="log-outputs"></a>
## Log outputs
Log outputs determine how messages are routed, delivering logs to specific
destinations  like standard error (`StderrStream`) or a dedicated log file
(`FileStream`). Each output can be selectively toggled. Additionally, the
parent output provides an overall control mechanism; disabling it effectively
halts all child streams.

<a name="log-outputs_log-output"></a>
### `LogOutput` (parent)
`LogOutput` is used internally by the `Logger` struct for handling it's child
output streams. Toggling it affects all of its child streams.

<a name="log-outputs_stderr-stream"></a>
### `StderrStream`
This is the simplest of the log outputs. It formats the given log using the
formatter and prints it to `stderr`.

Printing a log to `stderr`:
```rust
# use prettylogger::{
#     output::StderrStream,
#     format::LogFormatter,
#     config::LogStruct,
# };
// Required by `StderrStream` for parsing logs
let mut formatter = LogFormatter::default();

// Enabled by default
let mut stderr_output = StderrStream::default();

// Print "Hello, World!" in a neat format
stderr_output.out(&LogStruct::debug("Hello, World!"), &mut formatter);
```

<a name="log-outputs_buffer-stream"></a>
### `BufferStream`
When enabled, `BufferStream` stores raw logs in an internal buffer. This means
that it doesn't need a formatter.

Using `BufferStream`:
```rust
# use prettylogger::{
#     output::BufferStream,
#     output::Toggleable,
#     config::LogStruct,
# };
let mut buffer_stream = BufferStream::default();

// Enable the buffer stream
buffer_stream.enable();

// Write to the buffer 128 times
for i in 0..128 {
    buffer_stream.out(&LogStruct::debug(&format!("Log number {}", i)));
}

// Get a reference to the log buffer
let buffer = buffer_stream.get_log_buffer();
# assert_eq!(buffer.len(), 128);

// Do whatever you wish with the log buffer here

// Clear the log buffer
buffer_stream.clear();
```

<a name="log-outputs_file-stream"></a>
### `FileStream`
`FileStream` is used for storing logs in a log file. `FileStream` utilizes an
internal log buffer for storing already formatted log messages until they are
written to the log file.

Using `FileStream`:
```rust
# use prettylogger::{
#     config::LogStruct,
#     format::LogFormatter,
#     output::{Toggleable, FileStream},
# };
# let mut path = std::env::temp_dir();
# path.push("libprettylogger-tests/readme-file-stream-doc1.log");
# let path = &path.to_str().unwrap().to_string();
let mut formatter = LogFormatter::default();

let mut file_stream = FileStream::default();

// Set the log file path before enabling the stream
file_stream.set_log_file_path(&path)
    .expect("Failed setting log file path!");

// Enable the stream after the log file path has been set
file_stream.enable()
    .expect("Failed enabling the file stream!");

// Write to the log buffer
file_stream.out(&LogStruct::debug("Hello from a file!"), &mut formatter)
    .expect("Failed outing to the file stream!");

// Write the contents of the log buffer to the log file
file_stream.flush()
    .expect("Failed flushing the file stream!");
```

> [!NOTE]
> The log file path has to be set in order to enable and use the file stream.

<a name="log-outputs_file-stream_auto-log-buffer-flushing"></a>
#### Automatic log buffer flushing
`FileStream` can automatically write to the log file when its log buffer
exceeds a specific limit. Setting this limit to `None` will disable this
feature.

Example:
```rust
# use prettylogger::{
#     output::{FileStream, Toggleable},
#     format::LogFormatter,
#     config::LogStruct,
# };
# let mut path = std::env::temp_dir();
# path.push("libprettylogger-tests/readme-file-stream-doc2.log");
# let path = &path.to_str().unwrap().to_string();
let mut formatter = LogFormatter::default();

// Configure the `FileStream`
let mut file_stream = FileStream::default();
file_stream.set_log_file_path(&path)
    .expect("Failed setting log file path!");
file_stream.enable()
    .expect("Failed enabling the file stream!");

// Set the log file buffer limit to 128
file_stream.set_max_buffer_size(Some(128));

// Write to the log buffer 128 times
for i in 0..128 {
    file_stream.out(&LogStruct::debug("Hello!"), &mut formatter)
        .expect("Failed to out to the log buffer!");
}
// Here the log buffer will automatically be flushed.
```

<a name="log-outputs_file-stream_locking-log-file"></a>
#### Locking the log file
The log file can be locked to prevent race conditions when there are multiple
threads accessing it at the same time. It stops `FileStream` from writing to
it until the lock has been released. The lock is only ignored when
`FileStream` is being dropped and the `OnDropPolicy` is set to
`IgnoreLogFileLock` (off by default).

Toggling the lock:
```rust
# use prettylogger::output::FileStream;
# let mut file_stream = FileStream::default();
// Lock the log file
file_stream.lock_file();

// Unlock the log file
file_stream.unlock_file();
```

Setting on drop policy:
```rust
# use prettylogger::{
#     output::FileStream,
#     config::OnDropPolicy,
# };
# let mut file_stream = FileStream::default();
file_stream.set_on_drop_policy(OnDropPolicy::IgnoreLogFileLock);
```