Skip to content

Commit f4ce43f

Browse files
authored
Update README.md
Completed bulk of README rewrite
1 parent 825c010 commit f4ce43f

File tree

1 file changed

+67
-68
lines changed

1 file changed

+67
-68
lines changed

README.md

Lines changed: 67 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88

99
# Discussion: Compositional Loggers
1010

11-
When constructing complicated "log plumbing" systems, LoggingExtras allows for routing logged information to different places. Built upon the concept of simple parts composed together, LoggingExtras provides a powerful and flexible definition of your logging system without a need to define any custom loggers by subtyping `AbstractLogger`.
12-
When we talk about composability, the composition of any set of Loggers is itself a Logger, and LoggingExtras is a composable logging system.
11+
LoggingExtras allows routing logged information to different places when constructing complicated "log plumbing" systems. Built upon the concept of simple parts composed together, subtyping `AbstractLogger` provides a powerful and flexible definition for your logging system without a need to define any custom loggers. When we talk about composability, the composition of any set of Loggers is itself a Logger, and LoggingExtras is a composable logging system.
1312

14-
Loggers can be broken down into 4 types:
13+
Loggers break down into four types:
1514
- *Sinks*: Sinks are the endpoint of a log message journey. They write it to file or display it on the console or set off a red flashing light in the laboratory. A Sink should never decide what to accept, only what to do with it.
1615
- *Filters*: Filters wrap around other loggers and decide whether or not to pass on a message. When that decision occurs, they can be further broken down (See `ActiveFilteredLogger` vs `EarlyFilteredLogger`).
1716
- *Transformers*: Transformers modify the content of log messages before passing them on, including metadata, such as severity level. Unlike Filters, they can't block a log message, but they could drop its level down to say Debug so that usually no one would see it.
@@ -21,7 +20,7 @@ This is a complete taxonomy of all compositional loggers, with this package impl
2120

2221
It is worth understanding the idea of logging purity. The loggers defined in this package are all pure. The Filters only filter, the Sinks only sink, and the transformers only Transform.
2322

24-
We can contrast this to the `ConsoleLogger` (the standard logger in the REPL). The `ConsoleLogger` is an impure sink. As well as displaying logs to the user (as a Sink); it uses the log content, in the form of the `max_log` to decide if a log should be displayed (Active Filtering); and it has a min_enabled_level setting, that controls if it will accept a message at all (Early Filtering, in particular see `MinLevelLogger`).
23+
We can contrast this to the `ConsoleLogger` (the standard logger in the REPL). The `ConsoleLogger` is an impure sink. As well as displaying logs to the user (as a Sink); it uses the log content, in the form of the `max_log`, to decide whether to display a log (Active Filtering); and it has a min_enabled_level setting that controls if it will accept a message at all (Early Filtering, in particular, see `MinLevelLogger`).
2524
If defined in a compositional way, we would write something along the lines of:
2625
```julia
2726
ConsoleLogger(stream, min_level) =
@@ -35,14 +34,14 @@ ConsoleLogger(stream, min_level) =
3534

3635

3736
# Usage
38-
Load the package with `using LoggingExtras`.
37+
Load the package `using LoggingExtras`.
3938
For convenience, this also re-exports the `Logging` standard library.
4039

4140

4241
### Basics of working with loggers
4342
For full details, see the [Julia documentation on Logging](https://docs.julialang.org/en/v1/stdlib/Logging/index.html)
4443

45-
To use a `logger` in a given scope do
44+
To use a `logger` in a given scope, do
4645
```julia
4746
with_logger(logger) do
4847
#things
@@ -66,45 +65,44 @@ logger = global_logger()
6665

6766

6867
# Loggers introduced by this package:
69-
This package introduces 8 new loggers.
70-
The `TeeLogger`, the `TransformerLogger`, 3 types of filtered logger, the `FileLogger`,
68+
This package introduces eight new loggers;
69+
the `TeeLogger`, the `TransformerLogger`, and three types of filtered logger, the `FileLogger`,
7170
the `DatetimeRotatingFileLogger` and the `FormatLogger`.
7271
All of them, except `FormatLogger`, just wrap existing loggers.
7372
- The `TeeLogger` sends the logs to multiple different loggers.
74-
- The 3 filter loggers are used to control if a message is written or not
75-
- The `MinLevelLogger` only allows messages to pass that are above a given level of severity
76-
- The `EarlyFilteredLogger` lets you write filter rules based on the `level`, `module`, `group` and `id` of the log message
77-
- The `ActiveFilteredLogger` lets you filter based on the full content
73+
- The filter loggers control whether to write a message or not.
74+
- The `MinLevelLogger` only allows messages to pass above a given severity level.
75+
- The `EarlyFilteredLogger` lets you write filter rules based on the log message's `level`, `module`, `group` and `id`.
76+
- The `ActiveFilteredLogger` lets you filter based on the full content.
7877
- The `TransformerLogger` applies a function to modify log messages before passing them on.
7978
- The `FileLogger` is a simple logger sink that writes to file.
8079
- The `DatetimeRotatingFileLogger` is a logger sink that writes to file, rotating logs based upon a user-provided `DateFormat`.
81-
- The `FormatLogger` is a logger sink that simply formats the message and writes to the logger stream.
80+
- The `FormatLogger` is a logger sink that formats the message and writes to the logger stream.
8281
- The `LevelOverrideLogger` for overriding the log level of other loggers
8382

84-
By combining `TeeLogger` with filter loggers you can arbitrarily route log messages, wherever you want.
83+
By combining `TeeLogger` with filter loggers, you can arbitrarily route log messages wherever you want.
8584

8685

8786
## `TeeLogger` (*Demux*)
8887

8988
The `TeeLogger` sends the log messages to multiple places.
9089
It takes a list of loggers.
9190
You often want to pass the `current_logger()` or `global_logger()`
92-
as one of those inputs so it keeps going to that one as well.
91+
as one of those inputs, so it also keeps going to that one.
9392

94-
It is up to those loggers to determine if they will accept it.
95-
Which they do using their methods for `shouldlog` and `min_enabled_level`.
96-
Or you can do, by wrapping them in a filtered logger as discussed below.
93+
It is up to those loggers to determine if they will accept it, which they do by using their methods for `shouldlog` and `min_enabled_level`.
94+
Or you can wrap them in a filtered logger, as discussed below.
9795

9896
## `ActiveFilteredLogger` (*Filter*)
9997

100-
The `ActiveFilteredLogger` exists to give more control over which messages should be logged.
101-
It warps any logger, and before sending messages to the logger to log,
98+
The `ActiveFilteredLogger` exists to give more control over the messages logged.
99+
It warps any logger and, before sending messages to the logger to log,
102100
checks them against a filter function.
103101
The filter function takes the full set of parameters of the message.
104102
(See it's docstring with `?ActiveFilteredLogger` for more details.)
105103

106104
### Demo
107-
We want to filter to only log strings staring with `"Yo Dawg!"`.
105+
We want to filter only log strings starting with `"Yo Dawg!"`.
108106

109107
```julia
110108
julia> function yodawg_filter(log_args)
@@ -127,8 +125,9 @@ end
127125
128126
### Respecting `maxlog` convention
129127
130-
An `ActiveFilterLogger` can be used to wrap another logger to obey `maxlog` directives, for example,
131-
similar to the `make_throttled_logger` example below,
128+
An `ActiveFilterLogger` can be used to wrap another logger to obey `maxlog` directives; for example,
129+
similar to the `make_throttled_logger` example below, it wraps another logger to filter logs that have already fired `maxlog` many times.
130+
See <https://docs.julialang.org/en/v1/stdlib/Logging/#Logging.@logmsg> for more on `maxlog`.
132131
```julia
133132
function make_maxlog_logger(logger)
134133
counts = Dict{Any,Int}()
@@ -146,22 +145,21 @@ function make_maxlog_logger(logger)
146145
end
147146
end
148147
```
149-
wraps another logger to filter logs that have already fired `maxlog` many times.
150-
See <https://docs.julialang.org/en/v1/stdlib/Logging/#Logging.@logmsg> for more on `maxlog`.
148+
151149
152150
## `EarlyFilteredLogger` (*Filter*)
153151
154-
The `EarlyFilteredLogger` is similar to the `ActiveFilteredLogger`,
155-
but it runs earlier in the logging pipeline.
156-
In particular it runs before the message is computed.
157-
It can be useful to filter things early if creating the log message is expensive.
152+
The `EarlyFilteredLogger` is similar to the `ActiveFilteredLogger`
153+
but runs earlier in the logging pipeline.
154+
In particular, it runs before the computation of the message.
155+
It can be useful to filter things early if creating the log message is expensive,
158156
E.g. if it includes summary statistics of the error.
159157
The filter function for early filter logging only has access to the
160158
`level`, `_module`, `id` and `group` fields of the log message.
161-
The most notable use of it is to filter based on modules,
159+
Its most notable use is filtering based on modules;
162160
see the HTTP example below.
163161
164-
Another example is using them to stop messages every being repeated within a given time period.
162+
Another example is using them to stop repeated messages within a given period.
165163
166164
```julia
167165
using Dates, LoggingExtras
@@ -200,8 +198,8 @@ end
200198
```
201199
202200
## `MinLevelLogger` (*Filter*)
203-
This is basically a special case of the early filtered logger,
204-
that just checks if the level of the message is above the level specified when it was created.
201+
This is a special case of the early filtered logger
202+
that checks if the message level is above the level specified when created.
205203
206204
### Demo: filter out all the log messages that are less severe than `Error`
207205
@@ -221,12 +219,12 @@ julia> with_logger(error_only_logger) do
221219
222220
## `TransformerLogger` (*Transformer*)
223221
The transformer logger allows for the modification of log messages.
224-
This modification includes such things as its log level, and content,
222+
This modification includes its log level, content,
225223
and all the other arguments passed to `handle_message`.
226224
227-
When constructing a `TransformerLogger` you pass in a transformation function,
228-
and a logger to be wrapped.
229-
The transformation function takes a named tuple containing all the log message fields,
225+
When constructing a `TransformerLogger` you pass in a transformation function
226+
and a logger to wrap.
227+
The transformation function takes a named tuple containing all the log message fields
230228
and should return a new modified named tuple.
231229
232230
A simple example of its use is truncating messages.
@@ -253,26 +251,26 @@ end
253251
[ Info: Not like this one, that is is short
254252
```
255253
256-
It can also be used to do things such as change the log level of messages from a particular module (see the example below).
254+
`TransformerLogger` can also be used to do things such as change the log level of messages from a particular module (see the example below).
257255
Or to set common properties for all log messages within the `with_logger` block,
258-
for example to set them all to the same `group`.
256+
for example, to set them all to the same `group`.
259257
260258
## `FileLogger` (*Sink*)
261259
The `FileLogger` does logging to file.
262-
It is just a convenience wrapper around the base julia `SimpleLogger`,
263-
to make it easier to pass in a filename, rather than a stream.
264-
It is really simple.
260+
It is just a convenience wrapper around the base Julia `SimpleLogger`,
261+
to make it easier to pass in a filename rather than a stream.
262+
It is straghtforward:
265263
- It takes a filename,
266-
- a kwarg to check if should `always_flush` (default: `true`).
267-
- a kwarg to `append` rather than overwrite (default `false`. i.e. overwrite by default)
268-
The resulting file format is similar to that which is shown in the REPL.
264+
- It uses a kwarg to check if it should `always_flush` (default: `true`).
265+
- Uses a kwarg to `append` rather than overwrite (default `false`. i.e. overwrite by default).
266+
The resulting file format is similar to that shown in the REPL.
269267
(Not identical, but similar)
270268
271-
**NOTE**: To print to the file in a specific format, e.g. to create a JSON log, use
269+
**NOTE**: To print the file in a specific format, e.g. to create a JSON log, use
272270
`FormatLogger` instead.
273271
274272
### Demo: `TeeLogger` and `FileLogger`
275-
We are going to log info and above to one file,
273+
We will log info and above to one file,
276274
and warnings and above to another.
277275
278276
```julia
@@ -308,8 +306,8 @@ shell> cat info.log
308306
309307
## `DatetimeRotatingFileLogger` (*Sink*)
310308
Use this sink to rotate your logs based upon a given `DateFormat`, automatically closing one file and opening another
311-
when the `DateFormat` would change the filename. Note that if you wish to have static portions of your filename, you must
312-
escape them so they are not interpreted by the `DateFormat` code. Example:
309+
when the `DateFormat` changes the filename. Note that if you wish to have static portions of your filename, you must
310+
escape them to prevent interpretation by the `DateFormat` code. Example:
313311
314312
```julia
315313
julia> using LoggingExtras
@@ -328,19 +326,19 @@ julia> filter(f -> endswith(f, ".log"), readdir(pwd()))
328326
"access-2020-07-13-13-25.log"
329327
```
330328
331-
The user implicitly controls when the files will be rolled over based on the `DateFormat` given.
332-
To post-process the newly rotated file pass `rotation_callback::Function` as a keyword argument.
329+
The user implicitly controls when the files are rolled over based on the `DateFormat` given.
330+
To post-process the newly rotated file, pass `rotation_callback::Function` as a keyword argument.
333331
See the docstring with (`?DatetimeRotatingFileLogger` in the REPL) for more details.
334332
335-
To control the logging output it is possible to pass a formatter function as the first argument
336-
in the constructor. See `FormatLogger` for the requirements on the formatter function.
333+
To control the logging output, passing a formatter function as the first argument in the constructor
334+
is possible. See FormatLogger for the requirements on the formatter function.
337335
338336
## `FormatLogger` (*Sink*)
339-
The `FormatLogger` is a sink that formats the message and prints to a wrapped IO.
340-
Formatting is done by providing a function `f(io::IO, log_args::NamedTuple)`.
337+
The `FormatLogger` is a sink that formats the message and prints it to a wrapped IO
338+
with formatting provided by providing a function `f(io::IO, log_args::NamedTuple)`.
341339
342-
`FormatLogger` can take as its second argument either a writeable `IO` or a filepath. The `append::Bool` keyword
343-
argument determines whether the file is opened in append mode (`"a"`) or truncate mode (`"w"`).
340+
`FormatLogger` can take either a writeable `IO` or a filepath as its second argument. The `append::Bool` keyword
341+
argument determines whether to open the file in append mode (`"a"`) or truncate mode (`"w"`).
344342
345343
```julia
346344
julia> using LoggingExtras
@@ -364,9 +362,10 @@ Main | [Warn] This is a warning, should take a look.
364362
## `LevelOverrideLogger` (*Filter*)
365363
Allows overriding the minimum log level set by the logger it wraps.
366364
Useful when debug logging
367-
and used in conjuction with `Logging.with_logger` or `LoggingExtras.withlevel` to
365+
and used in conjunction with `Logging.with_logger` or `LoggingExtras.withlevel` to
368366
temporarily modify the current logger with a custom level.
369-
More generally useful if you want to use the current/global logger as a _sink_ but don't know if it is going to have a problematically high min log level set (as julia's default logger sets min level to `Info`).
367+
More generally applicable if you want to use the current/global logger as a _sink_
368+
but don't know if it will have a problematically high min log level set (as julia's default logger sets min level to `Info`).
370369
371370
```julia
372371
julia> using LoggingExtras
@@ -381,24 +380,24 @@ julia> with_logger(logger) do
381380
```
382381
This is roughly complementary to the `MinLevelFilterLogger`.
383382
The `MinLevelFilterLogger` lets you effectively *raise* the level of any logger it wraps to meet the level you specify.
384-
The `LevelOverrideLogger` lets you *lower* (or *raise*) the level of the wrapped logger as it bypasses checks on it entirely.
383+
The `LevelOverrideLogger` enables you to *lower* (or *raise*) the level of the wrapped logger as it bypasses checks on it entirely.
385384
386385
# Utilities
387386
388387
## Verbosity macros
389-
Sometimes when logging, it is desirable to be able to specify a verbosity level along with
390-
the log level, and to be able to filter on verbosity levels. For example, you may want multiple levels
391-
of verbosity for `Debug` log statements. LoggingExtras.jl exports verbosity macros that act like their
392-
non-verbose counterparts, but allow specifying a verbosity level as well:
388+
Sometimes when logging, it is desirable to specify a verbosity level along with
389+
the log level and to be able to filter on verbosity levels. For example, you may want multiple
390+
verbosity levels for `Debug` log statements. LoggingExtras.jl exports verbosity macros that act like their
391+
non-verbose counterparts but allow specifying a verbosity level as well:
393392
* `@debugv N msg`
394393
* `@infov N msg`
395394
* `@warnv N msg`
396395
* `@errorv N msg`
397396
398-
For verbosity filtering, the `LoggingExtras.withlevel(f, Info; verbosity=0)` utlility is provided
399-
for temporarily (i.e. while `f()` is executed) allowing log messages with `level` and `verbosity`.
400-
This is very handy for allowing finer grained control in debug logging for long-running or complex user API function
401-
calls. For example:
397+
For verbosity filtering, the `LoggingExtras.withlevel(f, Info; verbosity=0)` utility is provided,
398+
temporarily (i.e. while `f()` is executed) allowing log messages with `level` and `verbosity`.
399+
This is very handy for allowing finer-grained debug logging control for long-running or complex user API function calls.
400+
For example:
402401
403402
```julia
404403
using LoggingExtras

0 commit comments

Comments
 (0)