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
#![allow(clippy::new_without_default)]

/*!
# ll - Lightweight logging library with support for async/await

The main focus of this library is to provide a lightweight API that can
wrap parts of the execution flow, measure how long it took to run these flows
and log results as separate events to different event drains (log handlers).


```
# async fn db_query() -> Vec<i32> {
#   vec![1, 2, 3]
# }
# #[tokio::main]
# async fn main() -> () {
let l = ll::Logger::stdout(); // new logger with STDOUT drain configured

l.event("expensive_computation", |e| {
    let x = 10000;
    e.add_data("elements_in_vec", x);
    Ok((1..x).into_iter().collect::<Vec<i32>>())
}).unwrap();

l.async_event("db_query", |e| async move {
    let result = db_query().await;
    e.add_data("rows_returned", result.len());
    Ok(result)
}).await.unwrap();

# }

```

Will result in the following output being printed to STDERR:
```txt
 [2020-07-29T23:46:48Z] expensive_computation                                       |     10ms
 |      elements_in_vec: 10000
 [2020-07-29T23:46:48Z] db_query                                                    |     55ms
   |      rows_returned: 3
```


# Adding default data to a logger

All loggers are clonable objects that can be configured and passed down to different parts of the app.

```
# fn get_request_id() -> &'static str {
#     "ab49f92h49"
# }
let mut l = ll::Logger::stdout();

// add hostname that will be present in all events
l.add_data("hostname", "devserver.123.com");

fn handle_http_request(mut request_logger: ll::Logger) {
    // Add a `request id` that will be logged only for events within
    // specific request
    request_logger.add_data("request_id", get_request_id());

    request_logger
        .event("http_request", |_| {
            // do things...
            Ok(())
        })
        .unwrap();
}

// Clone the logger and pass it to every incoming http request
handle_http_request(l.clone());
```

will result in:
```txt
[2020-07-30T00:02:48Z] http_request                                                |     0ms
  |      hostname: devserver.123.com
  |      request_id: ab49f92h49
```


# Setting the log level

ll comes with three levels of logging
- Info (Will log only Info level events, which is default)
- Debug (Will log Debug and Info)
- Trace (Will log everything)

By default ll uses `Info` level and will only log `Info` events and data, but it can be configured
```
# let mut l = ll::Logger::new();
l.set_log_level(ll::Level::Trace);
```

Events and data properties can use hashtags to configure their individual log levels.
```
let mut l = ll::Logger::stdout();
l.set_log_level(ll::Level::Debug);

l.event("wont_be_logged #trace", |_| Ok(())).unwrap();

l.event("will_be_logged #debug", |e| {
    e.add_data("this_data_will_be_logged #info", true);
    e.add_data("this_data_wont #trace", true);
    Ok(())
})
.unwrap();
```

```txt
[2020-07-30T00:10:34Z] will_be_logged                                              |     0ms
  |      this_data_will_be_logged: true

```

# Custom drains

Most of the time events need to be not only logged to stdout, but also different log processing platforms
or even a `.log` file in the filesystem.

ll logger can proxy every event to multiple drains. Implementing a drain is easy and only requires implementing a single `log_event` function

```
struct DbgDrain;

impl ll::Drain for DbgDrain {
    fn log_event(&self, e: &ll::Event) {
        dbg!(&e.name);
    }
}

// new() returns a logger instance with no drains setup
let mut l = ll::Logger::new();

l.add_drain(std::sync::Arc::new(DbgDrain));

l.event("hello", |_| Ok(())).unwrap();
```

```txt
[main.rs:251] &e.name = "hello"
```

# Hashtags

Every event can have any number of hashtags in its name (space separated strings that are prefixed with `#`)
These hastags are parsed into a `BTreeSet<String>` when event is created and can later affect the way we handle event logging in drains.

For example, if there's a need to log something to a database but not to STDOUT, `#dontprint` hashtag can be used. `StdoutDrain` will later
check for the existence of this hashtag, and if present, it will skip printing it out to STDOUT alltogether.

```
# struct AnalyticsDBDrain;
# impl ll::Drain for AnalyticsDBDrain {
#    fn log_event(&self, _e: &ll::Event) {}
# }
let mut l = ll::Logger::stdout();
l.add_drain(std::sync::Arc::new(AnalyticsDBDrain));

l.event(
    "will_be_logged_to_analytics_db_but_not_printed #dontprint",
    |_| Ok(()),
)
.unwrap();

l.event("will_be_printed", |e| {
    e.add_data("but_this_data_wont #dontprint", 1);
    Ok(())
})
.unwrap();
```

```txt
[2020-07-30T00:21:47Z] will_be_printed                                             |     0ms
```
*/
pub mod drains;
mod event_data;
mod events;
mod level;
mod logger;
mod types;

mod utils;

pub use drains::Drain;
pub use event_data::DataValue;
pub use events::{Event, OngoingEvent};
pub use level::Level;
pub use logger::Logger;

pub use logger::{async_event, event};