Alog is a stackable logging framework for the Ada programming language. It aims to be straight forward to use and is easily extendable. It provides support for various logger types, log facilities, loglevel policies and message transformations.

Framework Architecture

Prerequisites

Alog is written in Ada so you need to have an Ada Compiler such as GNAT installed to build Alog.

Download

Release Version

The current release version of alog is available at https://www.codelabs.ch/download/.

Verify a Release

To verify the integrity and authenticity of the distribution tarball type the following commands:

$ wget -q https://www.codelabs.ch/keys/0xBB793815pub.asc -O - | gpg --import
$ gpg --verify libalog-{version}.tar.bz2.sig

The key fingerprint of the public key (0xBB793815) is:

Key fingerprint = A2FB FF56 83FB 67D8 017B  C50C F8C5 F8B5 BB79 3815

Development Version

The current development version of alog is available through its git repository:

$ git clone https://git.codelabs.ch/git/alog.git

A browsable version of the repository is also available here: https://git.codelabs.ch/?p=alog.git

Licence

Copyright (C) 2008-2019 Reto Buerki
Copyright (C) 2008-2019 Adrian-Ken Rueegsegger

Alog is free software; you can redistribute it and/or modify it under the terms
of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2.1 of the License, or (at your option) any later
version.

Installation

The Alog library has no special dependencies. To run the testcases, you need to have the Ahven Unit Test-Framework installed:

The building and installation process of Alog is simple. Just type in the following commands. You must be root to install the library system wide.

$ tar -xzf libalog-{version}.tar.bz2
$ cd libalog-{version}
$ make
$ make PREFIX=/usr/local install

This will compile and install the Alog library. If no PREFIX is specified, $(HOME)/libraries is used as installation directory.

Tests

After compiling and linking Alog, you can test if everything works as expected by typing the following command:

$ make tests

You should then see PASS behind each of the tests.

Usage

Logger

Logger instances are used to manage an arbitrary number of log facilities and message transformations. Various logger types exist which are suitable for different scenarios:

Logger

This is the basic Alog logger type. This logger is easy to use and good enough for most situations. However, it does not provide thread safety.

Tasked Logger

The Alog tasked logger encapsulates a basic logger instance inside a server task to provide safe concurrent logging. Since calls to this logger are potentially blocking operations, it cannot be used from within a protected action.

Active Logger

The Alog active logger provides task safe concurrent logging from any context.

Facility

Another basic entity in the Alog framework is called a Facility. Facilities are log destinations and used to log messages to different backends, e.g. a file or a database. Currently, the framework provides the following log facilities:

File_Descriptor

Writes log messages to file or console.

Syslog

Writes log messages to syslog.

Transformation

Transformations are used to modify a log message before it is passed on to a facility. The following message transformations are available:

Casing (toUpper / toLower)

Convert a log message to upper/lower case.

Policy

Alog supports source and destination filtering by means of loglevel policies. Refer to the example section for information on how to setup such policies.

Examples

The examples presented in this section will give an introduction on how to use the Alog framework in your own project.

Logger

The following example uses a basic logger instance to log messages to standard output. Furthermore, a file based facility is attached which writes log messages to a file.

with Alog.Logger;
with Alog.Facilities.File_Descriptor;

use Alog;

--  Alog logger example.
procedure Logger_Example1 is
   --  Initialize logger instance with default file descriptor facility
   --  (logs to stdout).
   Log : Logger.Instance (Init => True);
begin
   --  Write a message with loglevel 'Info' to stdout.
   Log.Log_Message
     (Level => Info,
      Msg   => "This is a testmessage from Alog logger");

   Attach_FD_Facility :
   declare
      FD : constant Facilities.File_Descriptor.Handle :=
        new Facilities.File_Descriptor.Instance;
   begin
      FD.Set_Logfile (Path => "/tmp/alog.log");
      Log.Attach_Facility (Facility => Facilities.Handle (FD));

      --  Log a message to file and stdout.
      Log.Log_Message (Source => "Example",
                       Level  => Warning,
                       Msg    => "Another testmessage");
   end Attach_FD_Facility;
end Logger_Example1;

The logger will take care about cleaning up all the attached facilities when it goes out of scope. However, you can do this explicitly by calling Logger.Clear as well.

Facility

The following code sets up a file descriptor based facility to log messages to a file. If the file already exists, it will be overwritten.

with Alog.Log_Request;
with Alog.Facilities.File_Descriptor;

use Alog;

--  Alog file descriptor facility example.
procedure Facility_Example1 is
   Facility : Facilities.File_Descriptor.Instance;
begin
   --  Enable writing of loglevels.
   Facility.Toggle_Write_Loglevel (State => True);

   --  Use '/tmp/alog.log' as logfile, overwrite existing file.
   Facility.Set_Logfile (Path   => "/tmp/alog.log",
                         Append => False);

   --  Let the facility process a log request with loglevel 'Warning'.
   Facility.Process
     (Request => Log_Request.Create
        (Level   => Warning,
         Message => "This is a testmessage from Alog FD facility"));

   --  Teardown the facility.
   Facility.Teardown;
end Facility_Example1;

Policy

The first policy example uses the policy database of Alog to specify source specific loglevels. It shows how logging policies can be used to filter log messages depending on their source.

with Alog.Policy_DB;
with Alog.Logger;

use Alog;

--  Alog source loglevel policy example.
procedure Policy_Example1 is
   Log : Logger.Instance (Init => True);
begin
   --  Set default loglevel to 'Info'.
   Policy_DB.Set_Default_Loglevel (Level => Info);
   --  Set source specific loglevel for all 'Example' sources to 'Debug'.
   Policy_DB.Set_Loglevel (Identifier => "Example.*",
                           Level      => Debug);

   --  This message will be logged because it matches a source specific
   --  loglevel (Example.*).
   if Policy_DB.Accept_Src (Identifier => "Example.Source1",
                            Level      => Debug)
   then
      Log.Log_Message (Source => "Example.Source1",
                       Level  => Debug,
                       Msg    => "This is a testmessage");
   end if;

   --  This message will not be logged because of the configured default 'Info'
   --  loglevel. There's no configured source loglevel for 'Source2'.
   if Policy_DB.Accept_Src (Identifier => "Source2",
                            Level      => Debug)
   then
      Log.Log_Message (Source => "Source2",
                       Level  => Debug,
                       Msg    => "This will not be logged");
   end if;

   --  No source specified, will not be logged because of the default 'Info'
   --  loglevel.
   if Policy_DB.Accept_Src (Level => Debug) then
      Log.Log_Message (Level => Debug,
                       Msg   => "This will not be logged");
   end if;
end Policy_Example1;

The second policy example demonstrates the usage of destination filtering. Only log messages with loglevel Error or higher are written to the application error logfile. It shows how logging policies can be used to filter log messages depending on the destination (facility name).

with Alog.Policy_DB;
with Alog.Logger;
with Alog.Facilities.File_Descriptor;

use Alog;

--  Alog destination loglevel policy example.
procedure Policy_Example2 is
   Log    : Logger.Instance (Init => True);
   Errors : constant Facilities.File_Descriptor.Handle :=
     new Facilities.File_Descriptor.Instance;
begin
   --  Write all error messages to '/tmp/errors.log'.
   Errors.Set_Logfile (Path => "/tmp/errors.log");
   Errors.Set_Name (Name => "Application_Errors");
   Errors.Toggle_Write_Loglevel (State => True);

   --  Set loglevel policy to 'Error' for destination 'Application_Errors'.
   Policy_DB.Set_Loglevel (Identifier => "Application_Errors",
                           Level      => Error);

   Log.Attach_Facility (Facility => Facilities.Handle (Errors));

   --  This message will appear on stdout, but not in the error logfile.
   Log.Log_Message (Level  => Info,
                    Msg    => "This is not an error");
   --  This message will also be written to the error logfile.
   Log.Log_Message (Level  => Error,
                    Msg    => "This is an error");
end Policy_Example2;