Audit logging application for Erlang.
Switch branches/tags
Nothing to show
Pull request Compare This branch is 1 commit behind freza:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



Lightweight audit logging library.


[ ] Prototype
[ ] Development
[X] Production


  • 2005-2010 Chandrashekhar Mullaparthi
  • 2010-2012 Jachym Holecek


  • Persistent logs, automatically opened on startup.
  • Log messages treated as opaque data.
  • Synchronous interface.
  • Good throughput.
  • Automatic file change when size or age limit reached.
  • Automatic cleanup of old logfiles.
  • All options configurable per log.
  • User controls tradeoff between write latency and reliability.
  • Filename disambiguates completed logs from currently open ones.

Type definitions

Args         = [term()].
Fmt          = string().
KB = MS      = integer() >= 0.
Log          = atom().
NN = HH = DD = integer() > 0 | infinity.
Stat         = {Items_written, Cur_file}.
Opts         = [Opt]
Opt          = {cache_size, KB}
             | {cache_time, MS}
             | {size_limit, NN}
             | {time_limit, HH}
             | {lifetime, DD}
             | {with_node, Flag}
             | {suffix, Str}
             | {dir, Path}.

Log Options

{cache_size, KB}
  • Maximum file driver write buffer size, in kilobytes. Flush is forced when this threshold is reached. The value of 0 disables write buffering. Default value is 128KB.
{cache_time, MS}
  • File driver write buffer time to live, in milliseconds. Flush is forced when oldest buffered entry exceeds this age. The value of 0 disables write buffering. Default value is 1000ms.
{size_limit, NN}
  • Maximum number of entries per log file before file change is forced. Default value is 200k.
{time_limit, HH}
  • Maximum age, in hours, of log file before file change is forced. Change time will be aligned to multiples of this setting. Default value is 24h, aligns to midnight.
{lifetime, DD}
  • Maximum age, in days, of log file before it's automatically cleaned up. Old files are automatically deleted every midnight, but can be forced manually.
{with_node, Flag}
  • Boolean indicating whether to embed node name into file names for this log. Defaults to false.
{suffix, Str}
  • Suffix to use for log file names, excluding the leading ".". Defaults to "audit".
{dir, Path}.
  • Directory in which log files should be located. See also default_dir application option.

Public API

User API

audit_log:syslog_msg(Fmt, Args) -> ok.
audit_log:syslog_msg(iodata()) -> ok.

audit_log:audit_msg(Log, Fmt, Args) -> ok.
audit_log:audit_msg(Log, iodata()) -> ok.
  • Write message to given log, or implicitely created syslog. After a call to this function returns, it is guaranteed that message has hit disk or file driver write cache, if enabled.

Management API

audit_log:open_log(Log[, Opts]) -> {ok, started | running | added} | {error, _}.
audit_log:close_log(Log) -> ok.
  • Open or close given log. Opening involves recording log (and its default options, if any) into Mnesia and starting corresponding disk writer process. Symetrically, closing stops disk writer process and removes log from persistent configuration. These functions may be called even if audit_log application isn't running yet, such as during early system installation.
audit_log:change_file(Log) -> ok | {error, _}.
  • Close current logfile and open a new one. Provided for operator convenience as this is normally done automatically according to log options.
audit_log:clean_old(Log) -> ok | {error, _}.
  • Cleanup of logfiles. Provided for operator convenience as this is normally done automatically according to log options.
audit_log:rediscover_logs() -> ok.
audit_log:rediscover_logs(App) -> ok.
  • Ensure all logs are up and running, either for given application or for all loaded applications. List of logs and default options are read from audit_log key in application resource file (*.app) ensuring all logs are loaded in configuration table. Subsequently it is ensured worker processes are running for all entries in that table.

Maintenance API

audit_log:get_config() -> [{Log, Opt}].
audit_log:get_config(Log) -> Opts.
  • Return complete current configuration of all logs or of single selected log.
audit_log:set_config([Log, ]Opts) -> ok.
  • Apply selected configuration values to all logs, or to single selected log. Options are merged with existing configuration.
audit_log:get_status() -> [{Log, Stat | down}].
audit_log:get_status(Log) -> Stat.
  • Return runtime status of all logs, or single selected log.
audit_log:create_db() -> Mnesia_ret.
audit_log:create_db(Mnesia_opts) -> Mnesia_ret.
  • This can be used to manually create configuration table in cases where default table options aren't desirable. Should be called before audit_log is first started. By default configuration table is set up at application startup ensuring disc copies replica exists on current node and with local_content flag set.

Utility API

audit_log_lib:printable_date() -> iolist().
audit_log_lib:printable_date(now() | datetime()) -> iolist().
  • Format current or given point in time as "YYYY-MM-DD_hh:mm:ss". If a value of now is given the format will have millisecond precision: "YYYY-MM-DD_hh:mm:ss.nnn". Note that return value isn't a flat string but an iolist().

Application environment

application:set_env(audit_log, default_dir, Dir).
  • Default target directory for log files, defaults to "$ROOT/audit_logs/".


  • Obtain and compile:
$ git clone git://
$ cd audit_log
$ ./rebar clean && ./rebar compile && ./rebar xref
  • Try out afterwards:
$ cd /tmp
$ erl -pa /where/ever/audit_log/ebin -boot start_sasl -sname foo -audit_log default_dir '"/tmp/audit_logs"'
(foo@bar)1> mnesia:create_schema([node()]).
(foo@bar)2> application:start(mnesia).
(foo@bar)3> application:start(audit_log).
[... In other terminal run: tail -f /tmp/audit_logs/syslog*.open ...]
(foo@bar)4> audit_log:syslog_msg([audit_log_lib:printable_date(), " Hello!", $\n]).