Home

Awesome

flatlog

A custom formatter for the logger application that turns maps into single line text logs.

Why?

Structured logging is a better approach to logging in general. Fields are more clearly defined, tool-assisted parsing and consuming of logs is simpler, and the structure is amenable to good filtering in global or handler filters for the Erlang logger library.

You could, for example, emit all your logs as structured logs (just maps), and set up multiple handlers for them:

This can be done transparently and after the fact, without major structural impact to the call site. It lets you far more easily decouple log generation from its consumption at no heavy cost.

This formatter focuses on providing a text-based single-line format for structured logs, which can be human-readable, while being useful to people who use grep or awk to process logs, or want to forward them to a consumer like syslogd.

Usage

It is recommended that if you are providing a library, you do not add this project as a dependency. A code formatter of this kind should be added to a project in its release repository as a top-level final presentational concern.

Once the project is added, replace the formatter of the default handler (or add a custom handler) for structured logging to your sys.config file:

[
 {kernel, [
    {logger, [
        {handler, default, logger_std_h,
         #{formatter => {flatlog, #{
            map_depth => 3,
            term_depth => 50
          }}}
        }
    ]},
    {logger_level, info}
 ]}
].

The logging output will then be supported. Calling the logger like:

?LOG_ERROR(
    #{type => event, in => config, user => #{name => <<"bobby">>, id => 12345},
      action => change_password, result => error, details => {entropy, too_low},
      txt => <<"user password not strong enough">>}
)

Will produce a single log line like:

when=2018-11-15T18:16:03.411822+00:00 level=error pid=<0.134.0>
at=config:update/3:450 user_name=bobby user_id=12345 type=event
txt="user password not strong enough" result=error in=config
details={entropy,too_low} action=change_password

Do note that the user map gets flattened such that #{user => #{name => bobby}} gets turned into user_name=bobby, ensuring that various subfields in distinct maps will not clash.

The default template supplied with the library also includes optional fields for identifiers as used in distributed tracing framework which can be set in the metadata for the logger framework, either explicitly or as a process state. The fields are:

Logs that are not reports (maps) are going to be formatted and handled such that they can be put inside a structured log. For example:

?LOG_INFO("hello ~s", ["world"])

Will result in:

when=2018-11-15T18:16:03.411822+00:00 level=info pid=<0.134.0>
at=some:code/0:15 unstructured_log="hello world"

Do note that if you are building a release, you will need to manually add the flatlog dependency to your relx configuration, since it is technically not a direct dependency of any application in your system.

Test

rebar3 check

Features

Caveats

Roadmap

Changelog