slf4p - Simple Logging Facade for Pascal
June 7, 2026 · View on GitHub
The Simple Logging Facade for Pascal serves as a simple facade or abstraction for various logging frameworks (e.g. Log4D, LazLogger), allowing the end user to plug in the desired logging framework at build time.
Developed with Delphi 2009 and Lazarus 4.4, tested with DUnit and FPCUnit.
To register a specific logging framework, add one of the djLogOver... units to the project.
djLogOverNOPLoggerfor logging over NOPLoggerdjLogOverSimpleLoggerfor logging over SimpleLoggerdjLogOverLog4Dfor logging over Log4DdjLogOverLazLoggerfor logging over LazLogger
One of the LogOver... units must be added to the project. When no unit is added, a NOPLogger factory will be used as a fallback, and a message indicating the fallback will be printed.
Choosing Between SLF4P and Log4D
Before deciding which logging tool to use, it’s important to understand what each one does.
SLF4P is not a logging implementation on its own. It’s an abstraction layer that lets you plug in a logging framework at runtime, such as Log4D or LazLogger. You write your code against the SLF4P API and then link it to a logging backend via the source path.
Log4D, by contrast, is a complete logging implementation. It provides its own APIs, configuration mechanisms for generating logs, and powerful built-in features, including filtering, custom appenders, and advanced formatting.
Examples
NOPLogger
Since no unit for registering a logger factory is used, a factory for NOP loggers will be registered.
program HelloWorld;
uses
djLoggerFactory, djLogAPI;
procedure RunDemo;
var
Log: ILogger;
begin
Log := TLoggerFactory.GetLogger;
Log.Debug('Using slf4p %s', [SLF4P_VERSION]);
Log.Info('Hello, World!');
Log.Debug('Hit any key');
ReadLn;
end;
begin
RunDemo;
end.
Program output
SLF4P: Logger factory is not assigned
SLF4P: Defaulting to no-operation (NOP) logger implementation
SimpleLogger
The first unit used, djLogOverSimpleLogger, registers a factory for console loggers.
program HelloWorld;
uses
djLogOverSimpleLogger,
djLoggerFactory, djLogAPI;
procedure RunDemo;
var
Log: ILogger;
begin
Log := TLoggerFactory.GetLogger;
Log.Debug('Using slf4p %s', [SLF4P_VERSION]);
Log.Info('Hello, World!');
Log.Debug('Hit any key');
ReadLn;
end;
begin
RunDemo;
end.
Program output
[12:05:46.732] DEBUG - Using slf4p 1.0.8
[12:05:46.732] INFO - Hello, World!
[12:05:46.733] DEBUG - Hit any key
Log4D
The first unit used, djLogOverLog4D, registers a logger factory which created Log4D loggers.
program HelloWorld;
uses
djLogOverLog4D,
djLoggerFactory, djLogAPI,
LogConsoleAppender, Log4d;
procedure RunDemo;
var
Log: ILogger;
ConsoleAppender: ILogAppender;
begin
ConsoleAppender := TLogConsoleAppender.Create('console');
TLogBasicConfigurator.Configure(ConsoleAppender);
TLogLogger.GetRootLogger.Level := Debug;
WriteLn('Logging with Log4D version ' + Log4DVersion);
Log := TLoggerFactory.GetLogger('slf4p/log4d');
Log.Debug('Using slf4p %s', [SLF4P_VERSION]);
Log.Info('Hello, World!');
Log.Debug('Hit any key');
ReadLn;
end;
begin
RunDemo;
end.
Program output
Logging with Log4D version 1.2.12
debug - Using slf4p 1.0.8
info - Hello, World!
debug - Hit any key
LazLogger
The first unit used, djLogOverLazLogger, registers a logger factory which creates LazLogger loggers.
program HelloWorld;
uses
djLogOverLazLogger,
djLoggerFactory, djLogAPI;
var
Log: ILogger;
begin
Log := TLoggerFactory.GetLogger;
Log.Debug('Using slf4p %s', [SLF4P_VERSION]);
Log.Info('Hello, World!');
Log.Debug('Hit any key');
ReadLn;
end.
Program output
0 DEBUG - Using slf4p 1.0.8
0 INFO - Hello, World!
0 DEBUG - Hit any key
Named loggers
The example uses named loggers in the classes TFirstClass and TSecondClass. The logger is created in the constructor of each class, using the class type as a parameter for the GetLogger method. With the help of classic published RTTI, the example classes write their unit name and class name to the log.
program ClassAsLoggerName;
uses
djLogOverSimpleLogger, SimpleLogger,
djLoggerFactory, djLogAPI,
MyClasses in 'MyClasses.pas';
procedure RunDemo;
var
Log: ILogger;
Obj1: TFirstClass;
Obj2: TSecondClass;
begin
SimpleLogger.Configure('defaultLogLevel', 'trace');
Log := TLoggerFactory.GetLogger;
Log.Info('Using slf4p %s', [SLF4P_VERSION]);
Obj1 := TFirstClass.Create;
Obj2 := TSecondClass.Create;
try
Log.Info('Instances created');
finally
Obj2.Free;
Obj1.Free;
end;
Log.Info('Hit any key');
ReadLn;
end;
begin
RunDemo;
end.
With the help of classic published RTTI, the example classes write their unit name and class name to the log.
unit MyClasses;
interface
uses
djLoggerFactory, djLogAPI;
type
{$TYPEINFO ON}
TFirstClass = class(TObject)
private
Log: ILogger;
public
constructor Create;
destructor Destroy; override;
end;
TSecondClass = class(TFirstClass)
private
Log: ILogger;
public
constructor Create;
destructor Destroy; override;
end;
{$TYPEINFO OFF}
implementation
uses
slf4p;
{ TFirstClass }
constructor TFirstClass.Create;
begin
Log := TLoggerFactory.GetLogger(TFirstClass);
Log.Debug('in constructor');
end;
destructor TFirstClass.Destroy;
begin
Log.Debug('in destructor');
end;
constructor TSecondClass.Create;
begin
Log := TLoggerFactory.GetLogger(TSecondClass);
Log.Trace('entering constructor');
inherited;
Log.Trace('leaving constructor');
end;
destructor TSecondClass.Destroy;
begin
Log.Trace('entering destructor');
inherited;
Log.Trace('leaving destructor');
end;
end.
Program output
[16:16:22.818] INFO - Using slf4p 1.0.8
[16:16:22.818] DEBUG MyClasses.TFirstClass in constructor
[16:16:22.818] TRACE MyClasses.TSecondClass entering constructor
[16:16:22.818] DEBUG MyClasses.TFirstClass in constructor
[16:16:22.818] TRACE MyClasses.TSecondClass leaving constructor
[16:16:22.818] INFO - Instances created
[16:16:22.818] TRACE MyClasses.TSecondClass entering destructor
[16:16:22.818] DEBUG MyClasses.TFirstClass in destructor
[16:16:22.818] TRACE MyClasses.TSecondClass leaving destructor
[16:16:22.818] DEBUG MyClasses.TFirstClass in destructor
[16:16:22.818] INFO - Hit any key