{"id":15063669,"url":"https://github.com/carburetor/log","last_synced_at":"2026-01-26T20:12:49.344Z","repository":{"id":57517699,"uuid":"212281963","full_name":"Carburetor/log","owner":"Carburetor","description":"Elixir logger with log filtering through environment variables","archived":false,"fork":false,"pushed_at":"2020-04-29T00:15:48.000Z","size":129,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-04-25T23:20:53.984Z","etag":null,"topics":["elixir","filtering","logger","logger-backend"],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Carburetor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-02T07:42:06.000Z","updated_at":"2021-10-04T17:11:17.000Z","dependencies_parsed_at":"2022-09-26T18:01:27.716Z","dependency_job_id":null,"html_url":"https://github.com/Carburetor/log","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Carburetor%2Flog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Carburetor%2Flog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Carburetor%2Flog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Carburetor%2Flog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Carburetor","download_url":"https://codeload.github.com/Carburetor/log/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243779012,"owners_count":20346650,"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","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","filtering","logger","logger-backend"],"created_at":"2024-09-25T00:05:49.508Z","updated_at":"2026-01-26T20:12:49.296Z","avatar_url":"https://github.com/Carburetor.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Log\n\nA `Logger` backend and frontend with enhanced filtering capabilities,\nflexible configuration and utility macros.\n\n## Usage\n\nThe package can be installed by adding `log` to your list of dependencies\nin `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:log, \"\u003e= 0.3\"}\n  ]\nend\n```\n\nConfigure the `Logger` to use `Log.Backend` instead of `:console`\n\n```elixir\nconfig :logger,\n  backends: [Log.Backend]\n```\n\nTo use the backend it's sufficient to use `Logger`:\n\n```elixir\nrequire Logger\nLogger.info(\"This is a message\", tags: [:a_message])\n```\n\n`Log` provides an advanced frontend to `Logger` which adds new levels,\noutput filtering and other features that can be useful during development\nand in production.\nTo access these features is necessary to use the module `Log` instead of\n`Logger`:\n\n```elixir\nrequire Log\nLog.info(\"This is a message\", tags: [:a_message])\n```\n\nMore advanced functionalities are explained in\n[Advanced Usage](#advanced-usage) section.\n\n### Advanced Usage\n\n- Tagging\n- `do` syntax\n- `inspect`\n- `@log_tags` attribute\n- Custom Logger with pre-defined tags\n- Available Log Levels\n- `Log.Args`\n\n#### Tagging\n\nThe core feature of `Log` is to be able to tag messages and later on filter\nmessages not including some tags.\n\n```elixir\nLog.info(\"message 1\", tags: [:tag1, :tag2])\nLog.info(\"message 2\", tag: :tag1)\n```\n\nWith the environment variable `LOG_TAGS` it's possible to filter some\nmessages:\n\n- `LOG_TAGS=tag2,tag1` displays both messages\n- `LOG_TAGS=-tag2,tag1` displays only message 2\n- `LOG_TAGS=-tag2` displays only message 2 (any message without tag2)\n- `LOG_TAGS=-tag1` displays no messages\n- `LOG_TAGS=tag3` displays no messages\n\n##### Syntax\n\nExample:\n\n```\nLOG_TAGS=tag1,tag2,tag3,tag4\n```\n\nWill allow output of any message with any (minimum 1) of the following tags:\n\n- `tag1`\n- `tag2`\n- `tag3`\n- `tag4`\n\nThe syntax for `LOG_TAGS` is the following:\n\n- `,` (comma) is used as a separator for tags\n- Empty strings are ignored, so `LOG_TAGS=tag1,,,,tag2` is valid and it will\n  filter any message without `tag1` or `tag2`\n\n\n###### Special Tags\n\n- `_untagged` includes any message without tags\n- `_all` includes all messages, tagged and untagged. Must be used alone\n\n###### Modifiers\n\nA tag can be prefixed with `+` and `-` modifiers.\nWhen a tag has `-` modifier, it must be **absent** from the message tags.\n\nWhen a tag has `+` modifier, it must be **present** from the message tags.\n\n```\nLOG_TAGS=tag1,tag2,+tag3\n```\n\nA message is output only if it has:\n\n- tag1 and tag3\n- tag2 and tag3\n- tag1, tag2 and tag3\n\n#### Do Syntax\n\nIn addition to passing a string or a function as log message, to lazy\nevaluate the content, it's also possible to use the `do` syntax to achieve\nthe same lazy-evaluation:\n\n```elixir\nrequire Log\nLog.info(tags: [:a_tag, :another_tag]) do\n  \"This message #{interpolates} some variables\"\nend\n```\n\n#### Inspect\n\n`IO.inspect` is a widely used function. `Log` provides a custom logger\nnamed `Log.Inspect` which behaves like\n[IO.inspect/2](https://hexdocs.pm/elixir/IO.html#inspect/2),\nwhile preserving the ability to filter log messages:\n\n```elixir\na = 1\nb = Log.Inspect.info(a) + 2\n# b is now 3\n```\n\nLike `inspect`, the return value of `Log.Inspect` is the data structure\npassed. Log output is:\n\n```\n[2000-01-01T01:01:01.001Z] INFO:\n1\n```\n\nThe same options available for `IO.inspect` are available, such as `:label`:\n\n```elixir\nLog.Inspect.info(%{some: \"data\"}, label: \"a message\")\n```\n\nWhich outputs:\n\n```\n[2000-01-01T01:01:01.001Z] INFO: a message\n%{some: \"data\"}\n```\n\nNotice that differently from `IO.inspect`, `Log.Inspect` defaults `pretty` to\n`true`.\nThe last feature of `Log.Inspect` is that it tags all the messages with the\ntag `:inspect`, so removing inspect messages can be easily performed by\nsetting the environment variable `LOG_TAGS` to include `-inspect`.\n\n#### Passthrough\n\nA common use-case is logging within a pipe. The `Log.Passthrough` module\nreturns the first argument passed to the log function and writes as log message\nwhat is written in the `:label` option.\n\n```elixir\na =\n  [1, 2]\n  |\u003e Enum.map(fn x -\u003e x * 2 end)\n  |\u003e Log.Passthrough.info(label: \"Mapping completed\")\n  |\u003e Enum.map(fn x -\u003e x + 1 end)\n# b is now [3, 5]]\n```\n\n#### Log Tags Attribute\n\nWhen the attribute `@log_tags` is defined, any `Log` message will include\nthe tags specified in the attribute\n\n```elixir\ndefmodule MyModule do\n  require Log\n  @log_tags [:tag1, :tag2]\n\n  def hello do\n    Log.info(\"message 1\")\n    Log.info(\"message 2\")\n  end\n\n  @log_tags [:tag3]\n\n  def world do\n    Log.info(\"message 3\")\n  end\n\n  def run do\n    hello()\n    world()\n  end\nend\n\nMyModule.run()\n```\n\nMessage 1 and 2 will be both tagged with `:tag1` and `:tag2`, while\nmessage 3 will be tagged only with `:tag3`.\n\n#### Custom Logger With Pre-Defined Tags\n\nIt's possible to create a Logger with some tags always added to every message\nwhere the module is used:\n\n```elixir\ndefmodule MyLog do\n  use Log, tags: [:tag1, :tag2]\nend\n```\n\n```elixir\ndefmodule MyModule do\n  require Log\n\n  @log_tags [:tag3]\n\n  def run do\n    Log.info(\"message\")\n  end\nend\n\nMyModule.run()\n```\n\n\"message\" will have tags: `:tag1`, `:tag2` and `:tag3`\n\n\n#### Available Log Levels\n\nIf using `Log` frontend module instead of `Logger` directly, the following\nlevels are available:\n\n- `trace`\n- `debug`\n- `info`\n- `warn`\n- `error`\n- `fatal`\n\nOtherwise the log levels are limited to the `Logger` levels:\n\n- `debug`\n- `info`\n- `warn`\n- `error`\n\n#### Log.Args\n\nA common scenario is logging the entry point of a function, in such cases,\ndisplaying the arguments of the function is important. The module\n`Log.Args` helps by formatting variables in a readable way:\n\n```elixir\nrequire Log.Args\nLog.Args.info({\"a message\", %{some_id: 123, some_name: \"Jon\"}})\n```\n\nWill output:\n\n```\n[2000-01-01T01:01:01.001Z] INFO: a message (SomeId: 123, SomeName: Jon)\n```\n\nIf `String` keys are used, no transformation to pascal case is performed.\nIt's possible to use a keyword instead of a map.\n\n## Configuration\n\nReplace `Logger` `:console` backend with `Log.Backend` in your configuration.\n\n### Output Filtering Configuration\n\nThe environment variables are an interface to filter log output dinamically.\nThe available environment variables are the following:\n\n- `CONSOLE_DEVICE` = `stdout | stderr` defaults to **stderr**\n- `LOG_TAGS` = `_all | _untagged | tag_name | -tag_name | +tag_name`\n  - `-` requires the tag to be missing\n  - `+` requires the tag to be present\n  - No sign means \"One or more of no sign tags must be present\"\n- `LOG_TAGS_LEVEL` =\n  `_min | _max | trace | debug | info | warn | error | fatal`\n  Any message at this level or above will not be filtered out by tags.\n  Useful to always display warns or errors\n  - `_min` is an alias for `trace`\n  - `_max` is an alias for `fatal`\n  - User defined levels are also supported in `LOG_TAGS_LEVEL`\n- `LOG_LEVEL` =\n  `_none | _min | _max | trace | debug | info | warn | error | fatal`\n  Any message below this level won't be displayed\n  - `_none` means no message will be logged\n  - `_min` is an alias for `trace`\n  - `_max` is an alias for `fatal`\n  - User defined levels are also supported in `LOG_LEVEL`\n- `LOG_DEBUG` = `on | off` when set to **on** prints debug messages and\n  errors, as well as tags information\n- `LOG_FORMATTERS` = `on | off` when set to **on**, colorizes output\n- `LOG_FORMAT_TAGS` = `on | off` when set to **on**, displays the tags of the\n  message\n- `LOG_MODULE` = `on | off` when set to **on**, displays the module where\n  log line is being invoked\n\n### Timestamp Configuration\n\nIt is recommended, but not required, to set logging timestamp to UTC, to avoid\nconfusing issues with DST changes. This can be done in `config.exs` using:\n\n```elixir\nimport Config\n\nconfig :logger, utc_log: true\n```\n\nOr with the native `Logger` function:\n\n```elixir\nLogger.configure(utc_log: true)\n```\n\nThe timestamp is always formatted according to ISO-8601 format, however no\ntimezone modifier is displayed except for UTC.\nIn case timestam is configured to use UTC, `Z` is appended to the timestamp.\n\n### Additional configuration\n\nOther configuration options are provided that can be set directly on the\n`Logger` backend:\n\n- Alias a module namespace\n- Exclude some namespaces from writing output\n- Change output color on a per-level basis\n\nThese options can be set either through `config.exs`\n\n```elixir\nimport Config\n\nconfig :logger, Log.Backend,\n  module_alias: %{\n    LogTest.Deeply.Nested.Module.WithLog =\u003e \"\"\n  },\n  exclude_namespaces: [],\n  colors: %{}\n```\n\nOr with the native `Logger` function:\n\n```elixir\nLogger.configure_backend(\n  Log.Backend,\n  [\n    module_alias: %{},\n    exclude_namespaces: [],\n    colors: %{}\n  ]\n)\n```\n\n#### Alias a Module Namespace\n\nGiven the modules:\n\n- `A.Module.Namespace.For.Something`\n- `Other.Module.Namespace.For.SomethingElse`\n\nand the configuration:\n\n```elixir\nLogger.configure_backend(\n  Log.Backend,\n  [\n    module_alias: %{\n      A.Module.Namespace =\u003e \"\",\n      Other.Module.Namespace =\u003e \"OMN\"\n    }\n  ]\n)\n```\n\nWhen the log message \"a message\" is written from module\n`A.Module.Namespace.For.Something` it will be displayed as:\n\n```\n[2000-01-01T01:01:01.001Z] For.Something INFO: a message\n```\n\nWhen the log message \"a message\" is written from module\n`Other.Module.Namespace.For.SomethingElse` it will be displayed as:\n\n```\n[2000-01-01T01:01:01.001Z] OMN.For.SomethingElse INFO: a message\n```\n\n#### Exclude Namespaces\n\nGiven the modules:\n\n- `A.Module.Namespace.For.Something`\n- `A.Module.Namespace.For.SomethingElse`\n\nand the configuration:\n\n```elixir\nLogger.configure_backend(\n  Log.Backend,\n  [\n    exclude_namespaces: [\n      A.Module.Namespace\n    ]\n  ]\n)\n```\n\nWhen the log message \"a message\" is written from module\n`A.Module.Namespace.For.Something` or from\n`A.Module.Namespace.For.SomethingElse`, no message is written.\n\n#### Change Output Color\n\nGiven the following configuration:\n\n```elixir\nLogger.configure_backend(\n  Log.Backend,\n  [\n    colors: %{\n      debug: IO.ANSI.green(),\n      error: [IO.ANSI.red(), IO.ANSI.bright()]\n    }\n  ]\n)\n```\n\n- `error` color will is bold, red\n- `debug` color is green\n\nThe `colors` map accepts level (as atoms) as keys, and\n[IO.ANSI.ansidata](https://hexdocs.pm/elixir/IO.ANSI.html#t:ansidata/0) as\nvalues.\n\n## Customized Logger\n\nIt's possible to create a customized logger, which accepts a data structure\nof your choice, as well as return a value of your choice.\nIt's sufficient to override the `bare_log` by following `Log.Args`\nfootprint:\n\n```elixir\ndefmodule UpLog do\n  use Log, tags: [:upcase]\n\n  @impl true\n  def bare_log(data, meta)\n\n  def bare_log(data, meta) when is_function(data) do\n    Log.API.bare_log(\n      fn -\u003e\n        data.() |\u003e String.upcase()\n      end,\n      meta\n    )\n  end\n\n  def bare_log(data, meta) do\n    Log.API.bare_log(\u0026String.upcase/1, meta)\n  end\nend\n```\n\n`UpLog` can be used like `Log`:\n\n```elixir\nrequire UpLog\nUpLog.info(\"a message\")\n```\n\nAnd will output the following message:\n\n```\n[2000-01-01T01:01:01.001Z] INFO: A MESSAGE\n```\n\n## TODO\n\n- [x] Guidelines for logging\n- [ ] LOG_TAGS_LEVEL should be renamed. It should bypass any form of filtering\n  for the set level and above\n- [ ] Restructure filters\n- [ ] Testing\n- [ ] Performance\n\n## Thanks\n\nThis project is deeply inspired by [Eventide](https://eventide-project.org/)\nand its [logger](http://docs.eventide-project.org/user-guide/logging).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarburetor%2Flog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcarburetor%2Flog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarburetor%2Flog/lists"}