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.
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/0xDBF6D7E1095FD0D9.asc -O - | gpg --import $ gpg --verify libalog-{version}.tar.bz2.sig
The key fingerprint of the public key (0xDBF6D7E1095FD0D9) is:
Key fingerprint = 298F 4B32 C3C4 1D88 5949 86F3 DBF6 D7E1 095F D0D9
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-2022 Reto Buerki Copyright (C) 2008-2022 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:
-
Ahven (Test-Framework): http://ahven.stronglytyped.org/
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.
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 illustrates how destination filtering works. 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). Furthermore it illustrates how
the default level can be used to filter out messages with lower loglevels.
with Alog.Dst_Filter;
with Alog.Logger;
with Alog.Facilities.File_Descriptor;
use Alog;
-- Alog destination loglevel policy example.
procedure Policy_Example1 is
Log : Logger.Instance (Init => True);
Facility_Name : constant String := "Application_Errors";
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 => Facility_Name);
Errors.Toggle_Write_Loglevel (State => True);
-- Set loglevel policy to 'Error' for destination 'Application_Errors'.
Dst_Filter.Set_Loglevel (Name => Facility_Name,
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");
-- Set global loglevel to only log messages with level higher than Warning
-- if no facility-specific level is set.
Dst_Filter.Set_Default_Level (Level => Warning);
-- This message will be filtered out.
Log.Log_Message (Level => Info,
Msg => "This message will be filtered");
-- This message will appear on stdout, but not in the error logfile.
Log.Log_Message (Level => Warning,
Msg => "This is a warning message");
end Policy_Example1;
The second policy example demonstrates how the protected policy database type of Alog can be used to implement source filtering in an application. It shows how logging policies can be constructed by specifying source specific loglevels and how this leads to log messages being filtered depending on their source.
with Alog.Policy_DB;
with Alog.Logger;
use Alog;
-- Alog source loglevel policy example.
procedure Policy_Example2 is
Src_Filter : Policy_DB.Protected_Policy_DB;
Log : Logger.Instance (Init => True);
begin
-- Set default loglevel to 'Info'.
Src_Filter.Set_Default_Loglevel (Level => Info);
-- Set source specific loglevel for all 'Example' sources to 'Debug'.
Src_Filter.Set_Loglevel (Identifier => "Example.*",
Level => Debug);
-- This message will be logged because it matches a source specific
-- loglevel (Example.*).
if Src_Filter.Accept_ID (Identifier => "Example.Source1",
Level => Debug)
then
Log.Log_Message (Source => "Example.Source1",
Level => Debug,
Msg => "This is a test message");
end if;
-- This message will not be logged because of the configured default 'Info'
-- loglevel. There's no configured source loglevel for 'Source2'.
if Src_Filter.Accept_ID (Identifier => "Source2",
Level => Debug)
then
Log.Log_Message (Source => "Source2",
Level => Debug,
Msg => "This will not be logged");
end if;
-- This message will be logged because of the configured default 'Info'
-- loglevel. There's no configured source loglevel for 'Source2'.
if Src_Filter.Accept_ID (Identifier => "Source2",
Level => Info)
then
Log.Log_Message (Source => "Source2",
Level => Info,
Msg => "This is another test message");
end if;
end Policy_Example2;