# casual_logger
This logger with **few settings** to repeat practice of many programming tutorials.
Not for product use.
Code:
```rust
//! You can copy and paste and use immediately.
use casual_logger::Log;
fn main() {
Log::remove_old_logs();
Log::infoln("Hello, world!!");
Log::wait();
}
```
Output:
`default-2020-07-15.log.toml` automatically generated.
```toml
["Now=2020-07-15 17:52:36&Pid=10328&Thr=ThreadId(1)&Seq=1"]
Info = "Hello, world!!\r\n"
```
## Abstract
The concept used by beginners.
### 1. Used in one example, throw away
* **There is no** configuration file.
* **Rotate** log by date automatically.
* **Delete** old log files. (semi-automatic)
* Log files can **only be placed** in the working directory.
* Only write to **1 file**.
### 2. Human readable log
* TOML does not spoil it.
### 3. Possibility as a tutorial
* **Short introduction**.
* Write the log as a TOML table, it can be **easily parsed**.
### Disclaim
* (1) In trade off for running performance:
* **Don't forget wait** for logging to complete at **end of program**.
* There is a waiting time of **1 second or more** before the logger ends.
* (2) In trade off for ease of introduction:
* You can break the toml format. **Do not validate**.
* (3) In trade off for intelligence suggestion by text editor:
* It **differs** from the standard Rust log interface.
* (4) In trade off for not stopping running:
* If the log export fails, the **error is ignored**.
### Tested environment
* OS: `Windows 10`.
* Editor: `Visual studio code`.
## At first, Overall view
Your code:
```rust
//! All features are described in one copy and paste.
use casual_logger::{Extension, Level, Log, Table, LOGGER};
fn main() {
// Example of Log file name:
//
// 'tic-tac-toe-2020-07-11.log.toml'
// -----------
// Prefix -----------
// StartDate ----
// Suffix
// -----
// Extention
//
// - StartDate is automatically added.
//
// Set the prefix with 'set_file_name' method.
Log::set_file_name("tic-tac-toe");
// Log file extension.
//
// '.log.toml' or '.log'.
// '.log' for safety, include a word that
// clearly states that you can delete the file.
// If you don't like the .toml extension, change.
Log::set_file_ext(Extension::LogToml);
// The higher this level, the more will be omitted.
//
// |<-- Low Level --------------------- High level -->|
// |<-- High priority --------------- Low priority -->|
// |Fatal< Error < Warn < Notice < Info < Debug <Trace|
Log::set_level(Level::Trace);
// Remove old log files. This is determined by the
// StartDate in the filename.
Log::set_retention_days(2);
if let Ok(mut logger) = LOGGER.lock() {
// Do not call 'Log::xxxxx()' in this code block.
// Wait for seconds logging to complete.
// By default it's set to 30 seconds,
// so you probably don't need to set it.
logger.timeout_secs = 30;
// Set to true to allow Casual_logger to
// output information to stdout and stderr.
// By default it's set to false,
// so you probably don't need to set it.
logger.development = true;
}
// Remove old log files. This is determined by the
// StartDate in the filename.
Log::remove_old_logs();
// Multi-line string.
// The suffix "ln" adds a newline at the end.
Log::infoln(
"Hello, world!!
こんにちわ、世界!!",
);
// After explicitly checking the level.
if Log::enabled(Level::Info) {
let x = 100; // Time-consuming preparation, here.
Log::infoln(&format!("x is {}.", x));
}
// The level is implicitly confirmed.
Log::trace("( 1)TRACE");
Log::traceln("( 2)trace-line");
Log::debug("( 3)DEBUG");
Log::debugln("( 4)debug-line");
Log::info("( 5)INFO");
Log::infoln("( 6)info-line");
Log::notice("( 7)NOTICE");
Log::noticeln("( 8)notice-line");
Log::warn("( 9)WARN");
Log::warnln("(10)warn-line");
Log::error("(11)ERROR");
Log::errorln("(12)error-line");
Log::fatal("(13)FATAL");
Log::fatalln("(14)fatal-line");
// Fatal is Panic! Can be used as the first argument of.
// panic!(Log::fatal(&format!("Invalid number=|{}|", 99)));
// Suffix '_t'. TOML say a table. So-called map.
Log::infoln_t(
"The sky is from top to bottom!!
上から下まで空です!!",
Table::default()
.str(
// Do not include spaces in your key.
"ABird",
"fly in the sky.",
)
// Not enclose this value in quotation marks.
.literal("NumberOfSwimmingFish", "2")
.str(
"ThreeMonkeys",
"climb
a tall
tree.",
),
);
// |Fatal< Error < Warn < Notice < Info < Debug <Trace|
// | *****|
Log::set_level(Level::Trace);
Log::trace("(7)Trace on (7)Trace.");
Log::debug("(6)Debug on (7)Trace.");
Log::info("(5)Info on (7)Trace.");
Log::notice("(4)Notice on (7)Trace.");
Log::warn("(3)Warn on (7)Trace.");
Log::error("(2)Error on (7)Trace.");
Log::fatal("(1)Fatal on (7)Trace.");
// |Fatal< Error < Warn < Notice < Info < Debug <Trace|
// | ***** |
Log::set_level(Level::Debug);
Log::trace("(7)Trace on (6)debug. Skip!");
Log::debug("(6)Debug on (6)debug.");
Log::info("(5)Info on (6)debug.");
Log::notice("(4)Notice on (6)debug.");
Log::warn("(3)Warn on (6)debug.");
Log::error("(2)Error on (6)debug.");
Log::fatal("(1)Fatal on (6)debug.");
// |Fatal< Error < Warn < Notice < Info < Debug <Trace|
// | ***** |
Log::set_level(Level::Info);
Log::trace("(7)Trace on (5)Info. Skip!");
Log::debug("(6)Debug on (5)Info. Skip!");
Log::info("(5)Info on (5)Info.");
Log::notice("(4)Notice on (5)Info.");
Log::warn("(3)Warn on (5)Info.");
Log::error("(2)Error on (5)Info.");
Log::fatal("(1)Fatal on (5)Info.");
// |Fatal< Error < Warn < Notice < Info < Debug <Trace|
// | ****** |
Log::set_level(Level::Notice);
Log::trace("(7)Trace on (4)Notice. Skip!");
Log::debug("(6)Debug on (4)Notice. Skip!");
Log::info("(5)Info on (4)Notice. Skip!");
Log::notice("(4)Notice on (4)Notice.");
Log::warn("(3)Warn on (4)Notice.");
Log::error("(2)Error on (4)Notice.");
Log::fatal("(1)Fatal on (4)Notice.");
// |Fatal< Error < Warn < Notice < Info < Debug <Trace|
// | **** |
Log::set_level(Level::Warn);
Log::trace("(7)Trace on (3)Warn. Skip!");
Log::debug("(6)Debug on (3)Warn. Skip!");
Log::info("(5)Info on (3)Warn. Skip!");
Log::notice("(4)Notice on (3)Warn. Skip!");
Log::warn("(3)Warn on (3)Warn.");
Log::error("(2)Error on (3)Warn.");
Log::fatal("(1)Fatal on (3)Warn.");
// |Fatal< Error < Warn < Notice < Info < Debug <Trace|
// | ***** |
Log::set_level(Level::Error);
Log::trace("(7)Trace on (2)Error. Skip!");
Log::debug("(6)Debug on (2)Error. Skip!");
Log::info("(5)Info on (2)Error. Skip!");
Log::notice("(4)Notice on (2)Error. Skip!");
Log::warn("(3)Warn on (2)Error. Skip!");
Log::error("(2)Error on (2)Error.");
Log::fatal("(1)Fatal on (2)Error.");
// |Fatal< Error < Warn < Notice < Info < Debug <Trace|
// |***** |
Log::set_level(Level::Fatal);
Log::trace("(7)Trace on (1)Fatal. Skip!");
Log::debug("(6)Debug on (1)Fatal. Skip!");
Log::info("(5)Info on (1)Fatal. Skip!");
Log::notice("(4)Notice on (1)Fatal. Skip!");
Log::warn("(3)Warn on (1)Fatal. Skip!");
Log::error("(2)Error on (1)Fatal. Skip!");
Log::fatal("(1)Fatal on (1)Fatal.");
// Wait for logging to complete or to timeout.
Log::wait();
}
```
Output `./tic-tac-toe-2020-07-15.log.toml` auto generated:
```toml
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=1"]
Info = """
Hello, world!!
こんにちわ、世界!!\r\n
"""
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=2"]
Info = "x is 100.\r\n"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=3"]
Trace = "( 1)TRACE"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=4"]
Trace = "( 2)trace-line\r\n"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=5"]
Debug = "( 3)DEBUG"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=6"]
Debug = "( 4)debug-line\r\n"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=7"]
Info = "( 5)INFO"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=8"]
Info = "( 6)info-line\r\n"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=9"]
Notice = "( 7)NOTICE"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=10"]
Notice = "( 8)notice-line\r\n"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=11"]
Warn = "( 9)WARN"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=12"]
Warn = "(10)warn-line\r\n"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=13"]
Error = "(11)ERROR"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=14"]
Error = "(12)error-line\r\n"
["Now=2020-07-15 18:21:17&Pid=10432&Thr=ThreadId(1)&Seq=15"]
Fatal = "(13)FATAL"
["Now=2020-07-15 18:21:18&Pid=10432&Thr=ThreadId(1)&Seq=16"]
Fatal = "(14)fatal-line\r\n"
["Now=2020-07-15 18:21:19&Pid=10432&Thr=ThreadId(1)&Seq=17"]
Info = """
The sky is from top to bottom!!
上から下まで空です!!\r\n
"""
ABird = "fly in the sky."
NumberOfSwimmingFish = 2
ThreeMonkeys = """
climb
a tall
tree.
"""
["Now=2020-07-15 18:21:19&Pid=10432&Thr=ThreadId(1)&Seq=18"]
Trace = "(7)Trace on (7)Trace."
["Now=2020-07-15 18:21:19&Pid=10432&Thr=ThreadId(1)&Seq=19"]
Debug = "(6)Debug on (7)Trace."
["Now=2020-07-15 18:21:19&Pid=10432&Thr=ThreadId(1)&Seq=20"]
Info = "(5)Info on (7)Trace."
["Now=2020-07-15 18:21:19&Pid=10432&Thr=ThreadId(1)&Seq=21"]
Notice = "(4)Notice on (7)Trace."
["Now=2020-07-15 18:21:19&Pid=10432&Thr=ThreadId(1)&Seq=22"]
Warn = "(3)Warn on (7)Trace."
["Now=2020-07-15 18:21:19&Pid=10432&Thr=ThreadId(1)&Seq=23"]
Error = "(2)Error on (7)Trace."
["Now=2020-07-15 18:21:19&Pid=10432&Thr=ThreadId(1)&Seq=24"]
Fatal = "(1)Fatal on (7)Trace."
["Now=2020-07-15 18:21:20&Pid=10432&Thr=ThreadId(1)&Seq=25"]
Debug = "(6)Debug on (6)debug."
["Now=2020-07-15 18:21:20&Pid=10432&Thr=ThreadId(1)&Seq=26"]
Info = "(5)Info on (6)debug."
["Now=2020-07-15 18:21:20&Pid=10432&Thr=ThreadId(1)&Seq=27"]
Notice = "(4)Notice on (6)debug."
["Now=2020-07-15 18:21:20&Pid=10432&Thr=ThreadId(1)&Seq=28"]
Warn = "(3)Warn on (6)debug."
["Now=2020-07-15 18:21:20&Pid=10432&Thr=ThreadId(1)&Seq=29"]
Error = "(2)Error on (6)debug."
["Now=2020-07-15 18:21:20&Pid=10432&Thr=ThreadId(1)&Seq=30"]
Fatal = "(1)Fatal on (6)debug."
["Now=2020-07-15 18:21:21&Pid=10432&Thr=ThreadId(1)&Seq=31"]
Info = "(5)Info on (5)Info."
["Now=2020-07-15 18:21:21&Pid=10432&Thr=ThreadId(1)&Seq=32"]
Notice = "(4)Notice on (5)Info."
["Now=2020-07-15 18:21:21&Pid=10432&Thr=ThreadId(1)&Seq=33"]
Warn = "(3)Warn on (5)Info."
["Now=2020-07-15 18:21:21&Pid=10432&Thr=ThreadId(1)&Seq=34"]
Error = "(2)Error on (5)Info."
["Now=2020-07-15 18:21:21&Pid=10432&Thr=ThreadId(1)&Seq=35"]
Fatal = "(1)Fatal on (5)Info."
["Now=2020-07-15 18:21:22&Pid=10432&Thr=ThreadId(1)&Seq=36"]
Notice = "(4)Notice on (4)Notice."
["Now=2020-07-15 18:21:22&Pid=10432&Thr=ThreadId(1)&Seq=37"]
Warn = "(3)Warn on (4)Notice."
["Now=2020-07-15 18:21:22&Pid=10432&Thr=ThreadId(1)&Seq=38"]
Error = "(2)Error on (4)Notice."
["Now=2020-07-15 18:21:22&Pid=10432&Thr=ThreadId(1)&Seq=39"]
Fatal = "(1)Fatal on (4)Notice."
["Now=2020-07-15 18:21:23&Pid=10432&Thr=ThreadId(1)&Seq=40"]
Warn = "(3)Warn on (3)Warn."
["Now=2020-07-15 18:21:23&Pid=10432&Thr=ThreadId(1)&Seq=41"]
Error = "(2)Error on (3)Warn."
["Now=2020-07-15 18:21:23&Pid=10432&Thr=ThreadId(1)&Seq=42"]
Fatal = "(1)Fatal on (3)Warn."
["Now=2020-07-15 18:21:24&Pid=10432&Thr=ThreadId(1)&Seq=43"]
Error = "(2)Error on (2)Error."
["Now=2020-07-15 18:21:24&Pid=10432&Thr=ThreadId(1)&Seq=44"]
Fatal = "(1)Fatal on (2)Error."
["Now=2020-07-15 18:21:25&Pid=10432&Thr=ThreadId(1)&Seq=45"]
Fatal = "(1)Fatal on (1)Fatal."
```
Output to terminal:
```plain
casual_logger: 0 sec(s). 15 table(s) left.
casual_logger: 0 sec(s). Wait for 1 thread(s).
casual_logger: 0 sec(s). 3 table(s) left.
casual_logger: 0 sec(s). Wait for 1 thread(s).
casual_logger: 0 sec(s). Wait for 5 thread(s).
casual_logger: 0 sec(s). Wait for 4 thread(s).
casual_logger: 0 sec(s). Wait for 3 thread(s).
casual_logger: 0 sec(s). Wait for 2 thread(s).
casual_logger: 0 sec(s). Wait for 1 thread(s).
```
It is designed to use `Log::fatal()` as the first argument for `panic!()`. It is the abnormal termination of the program. There is a waiting time.
## At second, Description
Code:
```rust
use casual_logger::{Extension, Level, Log, Table, LOGGER};
```
At the timing of the first writing, a file with a
time stamp in its name is automatically generated.
For example: `./tic-tac-toe-2020-07-12.log.toml`
### File name
| `./` | file path | Working directory | |
| | | only. | |
| `tic-tac-toe` | Prefix | Editable. | `default` |
| `-2020-07-12` | StartDate | Auto generated. | |
| `.log` | Suffix | Editable. | `.log` |
| `.toml` | Extension | Editable. | `.toml` |
It is difficult to explain the **file path** for beginners.
Therefore, it does not move.
Excite yourself with a **prefix**.
**StartDate** is basically today.
If the rotation fails, it is the start date.
**Suffix** to be safe, include a word that
clearly states that you can delete the file.
If you don't like the .toml **extension**, leave
the suffix empty and the .log extension.
Extension:
| `Log` | `.log` | For logs that are too large | |
| | | to be colored in the editor. | |
| `LogToml` | `.log.toml` | Toml format. | Default |
Set up, Code:
```rust
fn main() {
// ...
// Prefix.
Log::set_file_name("tic-tac-toe");
// Extension.
Log::set_file_ext(Extension::LogToml);
// ...
}
```
### Log rotation
Code:
```rust
Log::set_retention_days(2);
Log::remove_old_logs();
```
Example:
* `retention_days` is 2.
* Today is 2020-07-12.
* Call `Log::remove_old_logs()` method.
* The `./default-2020-07-09.log.toml` file will be deleted.
* The `./default-2020-07-10.log.toml` remains.
* Delete old files by date in filename.
| `retention_days` | After this number of days, | `7` |
| | the file will be deleted. | |
### Log level
Code:
```rust
Log::set_level(Level::Trace);
```
| `level` | Used to switch between | `Trace` |
| | write and non-write. | |
Example:
* There are 7 log levels.
* `|Fatal< Error < Warn < Notice < Info < Debug <Trace|`
* `|<-- Small ------------------------------ Large -->|`
* `|<-- Concise -------------------------- Verbose -->|`
* `|<-- Low Level --------------------- High level -->|`
* `|<-- High priority --------------- Low priority -->|`
| `Fatal` | If the program cannot continue. |
| `Error` | I didn't get the expected result, |
| | so I'll continue with the other method. |
| `Warn` | It will be abnormal soon, |
| | but there is no problem and you can ignore it. |
| | For example: |
| | (1) He reported that it took longer to access |
| | than expected. |
| | (2) Report that capacity is approaching the limit. |
| `Notice` | It must be enabled in the server production |
| | environment. |
| | Record of passing important points correctly. |
| | We are monitoring that it is working properly. |
| `Info` | Report highlights. |
| | Everything that needs to be reported regularly in |
| | the production environment. |
| `Debug` | It should be in a place with many accidents. |
| | This level is disabled in production environments. |
| | Leave it in the source and enable it for |
| | troubleshooting. |
| | Often, this is the production level of a desktop |
| | operating environment. |
| `Trace` | Not included in the distribution. |
| | Remove this level from the source after using it |
| | for debugging. |
| | If you want to find a bug in the program, |
| | write a lot. |
Code:
```rust
// Multi-line string.
// The suffix "ln" adds a newline at the end.
Log::infoln(
"Hello, world!!
こんにちわ、世界!!",
);
// After explicitly checking the level.
if Log::enabled(Level::Info) {
let x = 100; // Time-consuming preparation, here.
Log::infoln(&format!("x is {}.", x));
}
// The level is implicitly confirmed.
Log::trace("( 1)TRACE");
Log::traceln("( 2)trace-line");
Log::debug("( 3)DEBUG");
Log::debugln("( 4)debug-line");
Log::info("( 5)INFO");
Log::infoln("( 6)info-line");
Log::notice("( 7)NOTICE");
Log::noticeln("( 8)notice-line");
Log::warn("( 9)WARN");
Log::warnln("(10)warn-line");
Log::error("(11)ERROR");
Log::errorln("(12)error-line");
Log::fatal("(13)FATAL");
Log::fatalln("(14)fatal-line");
```
A piece of advice.
```rust
// Fatal is Panic! Can be used as the first argument of.
panic!(Log::fatal(&format!("Invalid number=|{}|", 99)));
```
Fatal returns a string so you can try to record a panic message.
However, the last log may not be written if the program exits first.
So there is a **timeout_secs** parameter.
Code:
```rust
if let Ok(mut logger) = LOGGER.lock() {
// Wait for seconds logging to complete.
// By default it's set to 30 seconds,
// so you probably don't need to set it.
logger.timeout_secs = 30;
}
```
### Usage of Table
| `::default()` | Create a instance. |
| `.str(key, value)` | Insert a string. |
| | Multi-line string are |
| | output with multiple lines. |
| `.literal(key, value)` | Not enclose this value in |
| | quotation marks. |
| | You can break the toml format. |
| | Do not validate. |
Do not include spaces in the **key**. TOML collapses.
It is difficult to explain to beginners how to use TOML.
If you make a TOML that cannot be parsed **literal**ly,
please correct it.
Code:
```rust
// Suffix '_t'. TOML say a table. So-called map.
Log::infoln_t(
"The sky is from top to bottom!!
上から下まで空です!!",
Table::default()
.str(
// Do not include spaces in your key.
"ABird",
"fly in the sky.",
)
// Not enclose this value in quotation marks.
.literal("NumberOfSwimmingFish", "2")
.str(
"ThreeMonkeys",
"climb
a tall
tree.",
),
);
```
Output:
```toml
["Now=2020-07-12 18:35:23&Pid=20872&Thr=ThreadId(1)&Seq=18"]
Info = """
The sky is from top to bottom!!
上から下まで空です!!\r\n
"""
ABird = "fly in the sky."
NumberOfSwimmingFish = 2
ThreeMonkeys = """
climb
a tall
tree.
"""
```
### Don't forget wait for logging to complete at end of program
Code:
```rust
// Wait for logging to complete or to timeout.
Log::wait();
```
If you do not wait,
the program will exit before writing all the logs.
## TODO
* [ ] Dogfooding.
* [ ] More minimal.
## Appendix
### Customize method
Code: main.rs
```rust
use casual_logger::Log;
pub trait LogExt {
fn println(s: &str);
}
impl LogExt for Log {
/// Info level logging and add print to stdout.
fn println(s: &str) {
println!("{}", s);
Log::infoln(s);
}
}
```
Usage: other.rs
```rust
use crate::LogExt;
pub fn test() {
Log::println("Hello, world!!");
}
```