Using SmartInspect Logging in Delphi Dlls

Introduction
Requirements
Configuration
Instrumenting
Closing Words

Introduction

Ever needed to debug or trace a Dll written in Delphi? If so, then this article is for you. In this article I describe the SmartInspect way to add logging to your Delphi Dlls.

Let's start with explaining the example application used throughout this article. The example is the simplest possible case of a Delphi application which makes use of a Dll. The host application consists of a single form with a button. When the button is pressed, an external function of a Dll is called and the result is returned. In this article I explain step-by-step how to add logging to both the host application and the Dll.

The example host application
The example host application
The code of the example Dll
The code of the example Dll

Requirements

One requirement when using logging in different application components (host application and its Dlls, for example) is that all components use the same logging objects and thus the same configuration and settings. It's desirable that all components can write to the same log file or use the same network connection to transmit log messages. With SmartInspect and Delphi, this can be achieved by using the SmartInspect runtime BPL packages. When both the host application and its Dll use this package, they can easily share the same SmartInspect objects and configuration.

Configuring projects to use runtime packages Configuring projects to use runtime packages

The screenshot above shows how to configure a Delphi project to use SmartInspect runtime packages (in Delphi 2006, other Delphi versions look similar). Both projects, the host application and the Dll, need to be configured this way. After the project configurations have been adjusted to use runtime packages, all that's left to do before configuring logging and instrumenting the code with logging statements is to add the required SmartInspect units to the uses clauses as usual.

Configuration

When using multiple independent application components, it's a bit unclear where to add the logging configuration code like enabling logging, setting the log level or logging destinations and so on. In our scenario, the best way to handle the logging configuration is to add the code to the host application, since there can be multiple Dlls but only one host. In the initialization code of the host application, we set up the configuration and enable logging as follows:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Si.Connections := 'tcp(host=localhost)';
  Si.Enabled := True;
end;

Although it is very handy and useful that the Dll and the host can share the same configuration and SmartInspect objects, it can often make sense to differentiate between these two applications components. For this purpose, SmartInspect provides the so-called sessions.

A session contains the actual logging methods like LogMessage or LogObject. Each session has a SmartInspect object as parent. This SmartInspect object is responsible for the logging configuration and the actual transport of messages like writing messages to a file or sending it via TCP directly to the Console. It is possible and common that multiple sessions share the same SmartInspect object and thus the same configuration and connections. Besides the parent, each session has a name, a background color in the Console and can be enabled/disabled independently from other sessions.

Now, to differentiate between the host and the Dll, it makes sense that both components use their own session. To share the configuration and connections, both sessions should have the same parent. The host application uses the default main session called SiMain and the Dll adds a new session with the same parent as SiMain.

var
  GSession: TSiSession; { Unit global variable }

...

begin
  GSession := Si.AddSession('Dll');
end;

Instrumenting

After including the SmartInspect runtime package and configuring the logging, we can start instrumenting the application components with the actual logging statements. Since our example application is quite small, this is done by adding a few lines of code only:

{ Host }
procedure TForm1.Button1Click(Sender: TObject);
var
  LResult: Integer;
begin
  SiMain.EnterMethod(Self, 'Button1Click');
  LResult := Add(2, 3); { Call external function. }
  ShowMessage(IntToStr(LResult));
  SiMain.LeaveMethod(Self, 'Button1Click');
end;

...

{ Dll }
function Add(const N, M: Integer): Integer;
begin
  GSession.EnterMethod('Add');
  Result := N + M;
  GSession.LogInteger('Result', Result);
  GSession.LeaveMethod('Add');
end;

As you can see, we simply add some calls to the EnterMethod/LeaveMethod methods and log the result of the Dll function. Each application component uses its own session for logging as explained in the previous chapter. Among other things, this enables us to conveniently filter the log in the Console by creating a View for each application component. These Views only show the log messages of the corresponding component (either the host or the Dll). This is especially useful to view the execution flow of one component independently from the other.

The SmartInspect Console
The resulting log in the SmartInspect Console

Closing Words

The example project files of this article can be downloaded here. If you have any questions about this topic, feel free to contact me at tg@gurock.com.

HostDll.zip (11 KB)

Try SmartInspect

Get started in minutes and try SmartInspect free for 30 days.

Try SmartInspect