Home

Awesome

syslog

A Syslog-based logging framework and/or OTP logger handler for Erlang. This project is inspired by the great work put in the two projects sasl_syslog and lager. In fact syslog tries to combine both approaches. In a nutshell syslog can be seen as a lightweight version of the lager logging framework supporting only a fully compliant, Erlang-only Syslog backend allowing remote logging.

The main difference between sasl_syslog and syslog is that sasl_syslog does only provide logging of error_logger reports. However, the error_logger is known for its bad memory consumption behaviour under heavy load (due to its asynchronous logging mechanism). Additionally, syslog provides an optional RFC 3164 (BSD Syslog) compliant protocol backend which is the only standard supported by old versions of syslog-ng and rsyslog.

Compared to lager, syslog has a very limited set of backends. As its name implies syslog is specialized in delivering its messages using Syslog only, there is no file or console backend, no custom-written and configurable log rotation, no line formatting and no tracing support. However, syslog does not rely on port drivers or NIFs to implement the Syslog protocol and it includes measures to enhance the overall robustness of a node, e.g. load distribution, back-pressure mechanisms, throughput optimization, etc.

Features

Configuration

The syslog application already comes with sensible defaults (except for the facilities used and the destination host). However, many things can be customized if desired. These are the advanced configuration options that are available and can be configured in the application environment:

Structured Data

syslog is capable of sending STRUCTURED-DATA. Please note that this will require the rfc5424 formatting. STRUCTURED-DATA can be sent using the syslog:msg/5 function. This function allows passing a list of structured data elements that will be formatted and sent to the remote receiver. However, no content checking will be done. If there's unescaped or unallowed content in the provided structured data elements invalid Syslog messages will be sent.

Another useful feature of syslog is the optional ability to store the metadata from a logger or lager log event/message into the STRUCTURED-DATA part of an RFC 5424 message. A structured data mapping has the form of a 2-tuple, e.g.

{"sdata_id", [application, pid]}

where sdata_id is being used as the STRUCTURED-DATA id and the second element being a list of atoms corresponding to the metadata keys whose values you want to pack. Only metadata matching the configured keys will be included into the STRUCTURED-DATA. If metadata does not contain a configured key, the key will be skipped. Please turn to the respective integration section to learn how to define structured data mappings.

OTP-21 Logger Integration

The syslog application uses the recommended way to integrate with the OTP-21 logger by utilizing the logger:add_handlers/1 function on application startup. This enables user to configure the integration through the sys.config of their release. By default, syslog will add a single logger handler with the id syslog.

To use syslog as the one (and only) default handler in your release you'll need something like the following in your sys.config:

[
 {kernel, [{logger, [{handler, default, undefined}]}]},
 {syslog, [{logger, [{handler, default, syslog_logger_h, #{}}]}]}
]

However, keep in mind that it is not necessary to make syslog your default/primary log handler. It can also only be an additional handler.

Similar to the lager backend, the logger handler is capable of conversion of metadata to structured data, e.g. if you want to include the metadata mappings for the keys application and pid (if available) as structured data in log messages you could configure the handlers like so:

{syslog,
 [
  {logger,
   [
    {handler,
     syslog,
     syslog_logger_h,
     #{config => #{structured_data => [{"sdata_id", [application, pid]}]}}}
   ]}
 ]}

Of course it is allowed to configure multiple structured data mappings, by default no metadata is packed as structured data.

Additional handler configuration may be passed using the handler argument map like it would be done with other logger handlers. However, progress report filtering is strictly controlled by the syslog application environment and filtering (discarding) of remote log events is always enabled.

Finally, to completely disable the logger integration (similar to setting syslog_error_logger to false in pre-OTP-21 releases) you'll have to configure an empty handler list:

{syslog, [{logger, []}]}

Error Logger Integration

The following configuration options are related to the error_logger handler/integration. If there is no error_logger available (e.g. OTP-21 or syslog_error_logger =:= false), these configurations have no effect.

The syslog application will disable the standard error_logger TTY output on application startup. This has nothing to do with the standard SASL logging. It only disables non-SASL logging via, for example error_logger:info_msg/1,2. This kind of standard logging can be re-enabled at any time using the following:

error_logger:tty(true).

This behaviour can also be configured in the application environment. If you don't want syslog to mess with your TTY settings use {disable_tty, false}.

The syslog application will not touch the standard SASL report handlers attached to the error_logger when SASL starts. However, having SASL progress reports on TTY can be quite annoying when trying to use the shell. The correct way to disable this output is to configure the SASL application in the sys.config of a release, for example the following line will instruct SASL not to attach any TTY handlers to the error_logger:

{sasl, [{sasl_error_logger, false}]}

Lager Integration

The syslog application comes with a built-in, optional backend for lager. This is especially useful if your release has dependencies that require lager although you wish to forward logging using syslog only. To forward lager logging into syslog you can use something like the following in your sys.config:

{lager, [{handlers, [{syslog_lager_backend, []}]}]}

It is also possible to explicitly specify the logging level

{lager, [{handlers, [{syslog_lager_backend, [info]}]}]}

It will also handle custom formatters like other existing lager backends. And there are even more advanced features that may be configured, e.g.:

{lager,
 [
  {handlers,
   [
    {syslog_lager_backend,
     [
      debug,                                 %% Log Level
      {"sdata_id", [application, pid]},      %% STRUCTURED-DATA mappings
      {lager_default_formatter, [message]},  %% Lager formatting
      true                                   %% Use application field from
                                             %% lager metadata for appname
     ]}
   ]}
 ]}

If you don't want to forward any metadata as structured data or are using RFC 3164 then you can supply an empty tuple {}.

Danger Zone

If your application really needs fast asynchronous logging and you like to live dangerously, the syslog application should be configured with {async, true} and {msg_queue_limit, infinity}. This sets syslog into asynchronous delivery mode and all message queues are allowed to grow indefinitely.

Usage

Although, the syslog application will log everything that is logged using the standard error_logger (pre-OTP-21) API, this should not be used for ordinary application logging. For pre-OTP-21 release it is recommended to use the logging API provided by the syslog module. These functions are similar to the ones provided by the error_logger module and should feel familiar (see the *msg/1,2 functions) while providing overload protection for your application.

For post-OTP-21 releases the recommended way of logging is (of course) to use the official logger API. Due to the logger handler abstraction design, syslog is able to provide overload protection for this function without the need for an additional, custom API.

Performance

Performance profiling has been made with a the benchmark script located in the benchmark subdirectory. The figures below show the results of best-of-five runs on an Intel(R) Core(TM) i7-4790 CPU running OTP 17.4.

The benchmark simulates a hard burst by spawning N processes that each send log messages, using a specific logging framework, in a tight loop for 10000ms. All log messages will be delivered over UDP (faked remote Syslog) to a socket opened by the benchmark itself. The total duration is the time it took to spawn the processes, send the messages and the time it took to receive all sent messages at the socket that the benchmark process listens on. The benchmark has two profiles. The large profiles uses log messages with a size of ~840bytes and the small profile uses log messages with a size of ~40bytes.

All frameworks are used in their default configuration with their native logging function (not error_logger).

To be able to compare lager with the other frameworks, the benchmark contains a very simple backend, that forwards log messages formatted with the lager_default_formatter using gen_udp, e.g. like syslog does it. The sasl_syslog application is left out of scope, simply because it crashes the Erlang VM on every run (due to its purely asynchronous logging).

Now let's see the numbers.

Small Profile

For a relatively small number of senders, all frameworks have a pretty good throughput and the completion time is almost equal. While memory consumption is generally low, the lager application uses significant more memory. This is due to the dynamic switching between synchronous and asynchronous message delivery: When delivery starts in asynchronous mode many log messages pile up in the internal gen_event's message queue. When the framework finally switches to synchronous mode the event manager is busy sending out these messages while senders get blocked.

<img src="https://cloud.githubusercontent.com/assets/404313/16836745/5af8562e-49bf-11e6-8ba0-89a99d1a73f5.png" alt="small benchmark" />

Adding more processes to the party makes the above mentioned effect more dramatic. While log4erl and syslog perform quite well by evening the load on the internal event manager or server using synchronous logging, lager piles up a hugh amount of messages in its internal gen_event message queue. It takes the framework almost two minutes to process these messages.

When looking at the memory usage it can be observed that lager's backend throttling (which was also used in syslog up to this version) is effectively useless to protect from bursts with small log messages. Switching is simply too slow and once messages begin to pile up, the node keeps being busy sending these messages. Additionally, senders may get blocked an indefinite amount of time (remember that gen_event:sync_notify/2 uses infinity for timeouts). During the test it could be observed that some senders were blocked over 50 seconds!

Large Profile

Numbers get interesting when large strings need to be formatted and copied around. log4erl as well as lager do the actual message formatting inside the internal event managers. Additionally, these frameworks pass around the unprocessed format string and its argument list.

syslog on the other hand, is now able to score by offloading the formatting work into the logging processes and by exchanging binaries with the internal logging server.

<img src="https://cloud.githubusercontent.com/assets/404313/16836747/5f04b848-49bf-11e6-9c28-603eb66036f4.png" alt="large benchmark" />

Interestingly, the total duration is excellent for all frameworks. Most probably, copying the large terms makes the logging processes slow enough that lager's backend throttling can kick in and prevent the internal message queue from growing too much. However, it could be observed again that some senders were blocked over 10 seconds.

History

Master (5.0.0)

4.0.0

3.4.5

3.4.4

3.4.3

3.4.2

3.4.1

3.4.0

3.3.0

3.2.0

3.1.0

3.0.0

Version 2.0.1

Version 2.0.0

Version 1.0.0

Version 0.0.9

Supervision

<img src="https://cloud.githubusercontent.com/assets/404313/16836729/43c90a66-49bf-11e6-9ec5-d39451c25deb.png" alt="syslog supervision" />

For the curious; the above illustration shows the very simple supervision hierarchy used by the syslog application. Please note that when OTP-21 is used the syslog_logger_h handler is monitored/supervised by the logger framework itself.