# LoggerHandlerKit
📚 Logger Handler Kit is an _educational_ Hex package! The goal is to help people
understand how to write and test logger handlers. This is a great package if you
want to:
* learn more about logging
* write your own logger handler
* bootstrap tests
* improve your test coverage
* learn how to make your tests async
You can use this package in a number of ways:
* Read through the docs to learn about concepts
* Read through the code to learn how to make things work
* Add as a test dependency for your app and actually use test helpers
## AAA
Since the package's primary goal is to aid with tests, its most important parts
are organized into three modules that embody what is called an AAA test pattern. The
AAA stands for three main stages of a test: Arrange, Act and Assert:
* `LoggerHandlerKit.Arrange` helps with setting up handlers for tests
* `LoggerHandlerKit.Act` suggests a menu of interesting test cases
* `LoggerHandlerKit.Assert` provides a single assert function with a very important job
## Example Test Suite
Logger Handler Kit comes with a fully asynchronous test suite for the default
Elixir logger handler, inspired by tests in `Sentry`, `DiscoLog` and Elixir
itself. It demonstrates how a handler can be tested with a fully async suite by
employing pretty much every trick in LoggerHandlerKit's repertoire:
1. It creates a dedicated handler for each test.
2. It guards the test handler against irrelevant log events with an ownership filter.
3. It configures the `logger_std_h` handler to write to a fake IO device instead of
stdout. The device relays all received writes back to the test process.
4. It uses `LoggerHandlerKit.Arrange.ensure_per_handler_translation/1` to enable
switching `handle_otp_reports` and `handle_sasl_reports` without impacting the rest
of the application.
Here's a visualization of how test setup compares to the regular application
setup. Note how test process logs use a separate "path" thanks to a dedicated
logger handler guarded by the ownership filter:
```mermaid
---
config:
look: handDrawn
theme: neutral
---
flowchart TD
subgraph before[Normal Setup]
test_pid[Test Process] --> test_log
other_tests[Other Tests] --> other_log
test_log@{shape: rounded, label: [log event]} --> translator
other_log@{shape: rounded, label: [log event]} --> translator
subgraph primary[Primary Filters]
translator[:logger_translator]
end
subgraph handlers
default_handler
end
translator --> default_handler[:default handler]
default_handler --> stdout[standard output]
end
subgraph after[Async Setup]
test_pid2[Test Process] --> test_log2
other_tests2[Other Tests] --> other_log2
test_log2@{shape: rounded, label: [log event]} --> no_filters
other_log2@{shape: rounded, label: [log event]} --> no_filters
subgraph primary2[Primary Filters]
no_filters@{shape: braces, label: [No Filters]}
end
no_filters --> default_translator
no_filters --> ownership_filter
subgraph handlers2[Handlers]
default_translator[:logger_translator] --> default_handler2[:default handler]
ownership_filter[ownership filter] --> test_translator[:logger_translator]
test_translator[:logger_translator] --> test_handler[test handler]
end
default_handler2 --> stdout2[standard output]
test_handler[test handler] --> fakeio[FakeIODevice]
end
```
## Getting ~~Started~~ Good
1. Add the package to your project as a test dependency
```elixir
def deps do
[
# The package only makes sense for tests!
{:logger_handler_kit, only: :test, "~> 0.1.0"}
]
end
```
2. Replace `MyHandler` with your handler name and make this test green:
```elixir
defmodule MyHandlerTest do
use ExUnit.Case, async: true
setup_all {LoggerHandlerKit.Arrange, :ensure_per_handler_translation}
setup %{test: test} = context do
{context, on_exit} =
LoggerHandlerKit.Arrange.add_handler(
test,
MyHandler,
%{}
)
on_exit(on_exit)
context
end
test "string message", %{handler_ref: ref} do
LoggerHandlerKit.Act.string_message()
LoggerHandlerKit.Assert.assert_logged(ref)
end
end
```
3. Add assertions that make sense for your handler to the test and make it green again.
4. Write a passing test for each function in the `LoggerHandlerKit.Act` module.
🎉 Congratulations, your logger handler is now pretty good and you have a decent asynchronous test suite on your hands.
## TODO
* [ ] cover metadata
* [ ] cover overload protection
* [ ] cover encoding/serialization
* [ ] cover logging packages overview