Getting lager handler info

2014-11-28 11:08:49 +0000

We use lager for our logging at Electric Imp. This morning I had cause to tweak the configuration at runtime on one of our staging boxes, but first I needed to figure out which handlers were already installed.

The following was done on a dev box, for demonstration purposes. The configuration is slightly different in production.

which_handlers

Lager uses gen_event to distribute log events, so I started with:

    (imp_server@roger-pc) 1> gen_event:which_handlers(lager_event).
    [lager_rabbitmq_backend,lager_folsom_backend,
     {lager_syslog_backend,{"imp_server",local7}},
     {lager_file_backend,"log/console.log"},
     {lager_file_backend,"log/error.log"},
     lager_console_backend,lager_backend_throttle]

Here you can see that we have several different backends registered:

However, this doesn’t tell us anything about the handler configuration. For that, we have to dig a little deeper.

lager_event state

One option is to use sys:get_state(lager_event), which queries the state of the lager_event event manager. In our example, it looks something like the following:

(imp_server@roger-pc) 2> sys:get_state(lager_event).
[{lager_rabbitmq_backend,false,
                         {state,{mask,15},
                                <0.119.0>,lager_rabbitmq_formatter,
                                [{process,"imp_server"},{environment,"development"}]}},
 {lager_folsom_backend,false,{state,imp_server,{mask,31}}},

…etc.

This shows the state of the event handlers, which is not necessarily the same as the initial configuration parameters. It’s still useful, though.

which_children

More useful, however, is the realisation that lager event handlers are supervised. This means that the supervisor must have a copy of the original configuration parameters – otherwise it couldn’t restart them properly.

Unfortunately, we can’t get hold of that information directly. What we can do is recognise that lager_handler_watcher_sup supervises lager_handler_watcher processes, and those, too, must have the original configuration somewhere:

We can get hold of the watchers:

(imp_server@roger-pc) 3> Watchers = supervisor:which_children(lager_handler_watcher_sup).
[{undefined,<0.80.0>,worker,[lager_handler_watcher]},
 {undefined,<0.82.0>,worker,[lager_handler_watcher]},
 {undefined,<0.84.0>,worker,[lager_handler_watcher]},
 {undefined,<0.101.0>,worker,[lager_handler_watcher]},
 {undefined,<0.122.0>,worker,[lager_handler_watcher]},
 {undefined,<0.91.0>,worker,[lager_handler_watcher]},
 {undefined,<0.76.0>,worker,[lager_handler_watcher]},
 {undefined,<0.78.0>,worker,[lager_handler_watcher]}]

And then we can inspect any one of those to get the original configuration:

(imp_server@roger-pc) 4> sys:get_state(pid(0,91,0)).                       
{state,lager_folsom_backend,
       [imp_server,warning],
       lager_event}

And there’s the backend and configuration: lager_folsom_backend,[imp_server,warning]. Note that this relies on the internal state of lager_handler_watcher, which might change.

We can make this prettier by use of the following:

[begin
    {state, Module, Config, Event} = sys:get_state(Pid),
    {Event, Module, Config}
 end || {_, Pid, _, _} <- supervisor:which_children(lager_handler_watcher_sup)].

This outputs something like the following (I’ve ellided some of the details):

[{lager_event,{lager_file_backend,"log/error.log"}, ...},
 {lager_event,{lager_file_backend,"log/console.log"}, ...},
 {lager_event,{lager_syslog_backend,{"imp_server",local7}}, ...},
 {lager_event,lager_rabbitmq_backend, ...},
 {error_logger,error_logger_lager_h,[500]},
 {lager_event,lager_folsom_backend,[imp_server,warning]},
 {lager_event,lager_backend_throttle,[20,5]},
 {lager_event,lager_console_backend,
              [info,{lager_imp_formatter,[]}]}]

Of interest here is that you can see all of our backends, with configuration, but also error_logger_lager_h, which is responsible for turning error_logger events into lager_event events; and also the configuration for lager_backend_throttle, which controls when it starts exerting back pressure.

Awesome. We’re done.