{"id":28214849,"url":"https://github.com/martosaur/logger_handler_kit","last_synced_at":"2025-07-27T08:05:39.441Z","repository":{"id":290679982,"uuid":"969851818","full_name":"martosaur/logger_handler_kit","owner":"martosaur","description":"Your guide to Elixir logger handlers","archived":false,"fork":false,"pushed_at":"2025-06-23T22:32:18.000Z","size":79,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-22T12:26:34.932Z","etag":null,"topics":["elixir","logging"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/martosaur.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-21T03:26:34.000Z","updated_at":"2025-06-23T22:30:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"4e8bfe09-28db-4075-a189-b53f68b91d1b","html_url":"https://github.com/martosaur/logger_handler_kit","commit_stats":null,"previous_names":["martosaur/logger_handler_kit"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/martosaur/logger_handler_kit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martosaur%2Flogger_handler_kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martosaur%2Flogger_handler_kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martosaur%2Flogger_handler_kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martosaur%2Flogger_handler_kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/martosaur","download_url":"https://codeload.github.com/martosaur/logger_handler_kit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martosaur%2Flogger_handler_kit/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267327534,"owners_count":24069442,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-27T02:00:11.917Z","response_time":82,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","logging"],"created_at":"2025-05-17T21:08:29.954Z","updated_at":"2025-07-27T08:05:39.432Z","avatar_url":"https://github.com/martosaur.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LoggerHandlerKit\n\n📚 Logger Handler Kit is an _educational_ Hex package! The goal is to help people\nunderstand how to write and test logger handlers. This is a great package if you\nwant to:\n\n* learn more about logging\n* write your own logger handler\n* bootstrap tests\n* improve your test coverage\n* learn how to make your tests async\n\nYou can use this package in a number of ways:\n\n* Read through the docs to learn about concepts\n* Read through the code to learn how to make things work\n* Add as a test dependency for your app and actually use test helpers\n\n## AAA\n\nSince the package's primary goal is to aid with tests, its most important parts\nare organized into three modules that embody what is called an AAA test pattern. The\nAAA stands for three main stages of a test: Arrange, Act and Assert:\n\n* `LoggerHandlerKit.Arrange` helps with setting up handlers for tests\n* `LoggerHandlerKit.Act` suggests a menu of interesting test cases\n* `LoggerHandlerKit.Assert` provides a single assert function with a very important job\n\n## Example Test Suite\n\nLogger Handler Kit comes with a fully asynchronous test suite for the default\nElixir logger handler, inspired by tests in `Sentry`, `DiscoLog` and Elixir\nitself. It demonstrates how a handler can be tested with a fully async suite by\nemploying pretty much every trick in LoggerHandlerKit's repertoire:\n\n1. It creates a dedicated handler for each test. \n2. It guards the test handler against irrelevant log events with an ownership filter. \n3. It configures the `logger_std_h` handler to write to a fake IO device instead of\nstdout. The device relays all received writes back to the test process. \n4. It uses `LoggerHandlerKit.Arrange.ensure_per_handler_translation/1` to enable\nswitching `handle_otp_reports` and `handle_sasl_reports` without impacting the rest\nof the application.\n\nHere's a visualization of how test setup compares to the regular application\nsetup. Note how test process logs use a separate \"path\" thanks to a dedicated\nlogger handler guarded by the ownership filter:\n\n\n```mermaid\n---\nconfig:\n    look: handDrawn\n    theme: neutral\n---\nflowchart TD\n    subgraph before[Normal Setup]\n        test_pid[Test Process] --\u003e test_log\n        other_tests[Other Tests] --\u003e other_log \n        test_log@{shape: rounded, label: [log event]} --\u003e translator\n        other_log@{shape: rounded, label: [log event]} --\u003e translator\n        \n        subgraph primary[Primary Filters]\n            translator[:logger_translator]\n        end\n        \n        subgraph handlers\n            default_handler\n        end\n        \n        translator --\u003e default_handler[:default handler]\n        default_handler --\u003e stdout[standard output]\n    end\n    subgraph after[Async Setup]\n        test_pid2[Test Process] --\u003e test_log2\n        other_tests2[Other Tests] --\u003e other_log2 \n        test_log2@{shape: rounded, label: [log event]} --\u003e no_filters\n        other_log2@{shape: rounded, label: [log event]} --\u003e no_filters\n        subgraph primary2[Primary Filters]\n            no_filters@{shape: braces, label: [No Filters]}\n        end\n        no_filters --\u003e default_translator\n        no_filters --\u003e ownership_filter\n        subgraph handlers2[Handlers]\n            default_translator[:logger_translator] --\u003e default_handler2[:default handler]\n            ownership_filter[ownership filter] --\u003e test_translator[:logger_translator]\n            test_translator[:logger_translator] --\u003e test_handler[test handler]\n        end\n        default_handler2 --\u003e stdout2[standard output]\n        test_handler[test handler] --\u003e fakeio[FakeIODevice]\n    end\n```\n\n## Getting ~~Started~~ Good\n\n1. Add the package to your project as a test dependency\n\n```elixir\ndef deps do\n  [\n    # The package only makes sense for tests!\n    {:logger_handler_kit, only: :test, \"~\u003e 0.3.0\"}\n  ]\nend\n```\n\n2. Replace `MyHandler` with your handler name and make this test green:\n\n```elixir\ndefmodule MyHandlerTest do\n  use ExUnit.Case, async: true\n\n  setup_all {LoggerHandlerKit.Arrange, :ensure_per_handler_translation}\n\n  setup %{test: test} = context do\n    {context, on_exit} =\n      LoggerHandlerKit.Arrange.add_handler(\n        test,\n        MyHandler,\n        %{}\n      )\n\n    on_exit(on_exit)\n    context\n  end\n\n  test \"string message\", %{handler_ref: ref} do\n    LoggerHandlerKit.Act.string_message()\n    LoggerHandlerKit.Assert.assert_logged(ref)\n  end\nend\n```\n\n3. Add assertions that make sense for your handler to the test and make it green again.\n4. Write a passing test for each function in the `LoggerHandlerKit.Act` module.\n\n🎉 Congratulations, your logger handler is now pretty good and you have a decent asynchronous test suite on your hands.\n\n## TODO\n\n* [x] cover metadata\n* [ ] cover overload protection\n* [x] cover encoding/serialization\n* [ ] cover logging packages overview","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartosaur%2Flogger_handler_kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmartosaur%2Flogger_handler_kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartosaur%2Flogger_handler_kit/lists"}