Building rock-solid Delphi applications with SmartInspect and EurekaLog

By: Dennis Gurock

Abstract: This article demonstrates how to combine and benefit from both the advanced logging capabilities of SmartInspect to log application data and user actions, and from the excellent crash reporting capabilities of EurekaLog to receive exception and crash information from end-users via email and web.

    Introduction

Integrating SmartInspect's logging capabilities and EurekaLog's exception reporting mechanism is a great way to improve the reliability of your Delphi applications. Used together, these two tools allow you to easily report and solve errors that your customers and users experience.

What are the key strengths of both tools? SmartInspect itself is a logging tool. This means that you can use SmartInspect to generate log files, monitor applications in real-time (via TCP and named pipes, for example) and use advanced techniques to diagnose thread issues and problems in distributed applications. SmartInspect allows you to easily log messages, exceptions, files, database results, objects and a lot more. You can use the SmartInspect Console (the viewer application) to analyze the generated log files, filter log entries and monitor your application's variables and behavior.

EurekaLog on the other hand is an exception and memory leak reporting tool. Whenever your application crashes or when a memory leak is detected, it allows the end-user to send a crash report back to the vendor (you) via emails or web services. This allows you to find out what kind of bugs and issues your users experience and, more importantly, provides you with information such as the exception/memory leak stack trace, loaded DLLs, operation system version and a lot more to solve the reported issues. EurekaLog is especially useful when you cannot control the execution environment of your software. This is the case if your software is distributed and used on many different machines.

This article will show you how to integrate both tools to build more reliable and robust Delphi applications and to dramatically decrease time spent on solving customer issues (and thus improving your customer support).

    Getting Started with SmartInspect

Getting started with SmartInspect is easy. To follow the code examples and to give SmartInspect a try, just download the free SmartInspect Trial. We will use an empty Win32 Forms application to demonstrate the integration of both tools in this article.

Let's start by adding and enabling SmartInspect logging in the project. The SmartInspect setup should have already added the SmartInspect Delphi library to your Delphi's environment options and we are good to go. To get started with SmartInspect, we just have to use SmartInspect's SiAuto unit and enable SmartInspect by setting the Si.Enabled property to true. We do this directly in the application's DPR file:

program TestProject;

uses
  SiAuto,
  Forms,
  MainUnit in 'MainUnit.pas' {MainForm};

{$R *.res}

begin
  Si.Enabled := True;
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TMainForm, MainForm);
  Application.Run;
end.

The SiAuto unit automatically creates all required SmartInspect objects and configures SmartInspect for real-time logging via named pipes (you can also create the SmartInspect objects yourself, but SiAuto makes it a bit easier to get started). Please also note that it is not required to enable SmartInspect in the application's main file. We could also enable and configure SmartInspect in the MainForm's OnCreate event or load all settings from a SmartInspect configuration file.

Now that we have SmartInspect enabled, we can log all kinds of log entries and application data such as messages, exceptions, variable values, objects and more. Let's start by adding the SiAuto unit to the main unit, adding a button to the main form, and logging some messages:

procedure TMainForm.LogButtonClick(Sender: TObject);
begin
  SiMain.EnterMethod(Self, 'LogButtonClick');

  { Log simple messages, warnings and exceptions }
  SiMain.LogMessage('This is a test message');
  SiMain.LogError('And this is an error');
  try
    raise EInvalidOperation.Create('Test Exception');
  except
    SiMain.LogException;
  end;

  { Log variables values or any other object }
  SiMain.LogInteger('Integer Value', 255);
  SiMain.LogDouble('Float Value', 1.35);
  SiMain.LogObject('Main Form', Self);

  SiMain.LeaveMethod(Self, 'LogButtonClick');
end;

If you now start the SmartInspect Console and run the example project, you will automatically receive the logging data in the Console and can view the log entries and associated data. The following screenshot shows the SmartInspect Console in action (to show some of the more advanced capabilitis of the Console, the screenshot includes logging data from another demo application):

Hide image
SmartInspect Console in action

Adding SmartInspect log calls to key modules and methods of your application allows you to easily figure out what any particular thread, the user or external application modules did before your application crashed. SmartInspect also lets you monitor things such as database connections, allocated system resources and other important values.

Instead of using live logging via named pipes as we did in the above example, we can also write the same logging information to a log file by adding just one line of code. We can then use the same powerful tools of the SmartInspect Console to analyze log files that were generated on customer machines:

Si.Connections := 'file(filename=c:\temp\log.sil)';
Si.Enabled := True;

This short introduction only highlights SmartInspect's basic usage. To learn more about SmartInspect and how to use it to improve your software's quality and reliability, please take the SmartInspect tour or download the free trial (and make sure to take a look at the Delphi tutorial in the online help).

    Getting Started with EurekaLog

This short introduction will show you how to integrate EurekaLog into your Delphi application to add crash reporting capabilities to your project. To get started with EurekaLog, download the trial version and let the setup integrate it into the Delphi IDE. To enable and configure EurekaLog, you can use the EurekaLog Options dialog which is automatically added to Delphi's Project menu. Enable EurekaLog by selecting the Activate checkbox in the bottom left corner of the dialog. We also enter the required SMTP parameters to allow EurekaLog to send crash reports via email (EurekaLog can alternatively use the email client installed on user machines):

Hide image
EurekaLog Options

To test the crash reporting capabilities of EurekaLog, we need to add an unhandled exception to the test application. To do this, add a second button to the form and add the following code the button's OnClick event handler:

procedure TMainForm.CrashButtonClick(Sender: TObject);
var
  I: Integer;
begin
  { Generate an unhandled exception }
  I := 0;
  ShowMessage(FloatToStr(10 / I)); // raises an EZeroDivide exception
end;

When you click the crash button to raise our test exception, EurekaLog automatically collects crash information such as the current call stack, loaded DLLs and general information about the execution environment such as the operating system version and so on. EurekaLog then replaces the standard Delphi error dialog and pops up a dialog asking the user if he wants to submit the occurred error to the vendor. The default EurekaLog error dialog looks and behaves pretty much like the Windows error report dialog, making it easy for end-users to use and understand. However, you can easily change the default look, translate it to different languages or even replace it with your own custom dialog:

Hide image
EurekaLog Crash Dialog

If the end-user decides to send the error report, EurekaLog automatically sends an email with the collected crash information back to you. Alternatively, you can configure EurekaLog to submit the crash report to your web-based bug tracker such as Mantis, FogBugz or BugZilla instead of sending an email. The collected crash information that is attached the resulting email contains an EurekaLog file that can be conveniently opened with the EurekaLogs Viewer application to analyze the exception, call stack and related information:

Hide image
EurekaLog Options

To learn more about EurekaLog, make sure to visit its website and to watch the video tutorials.

    Combining Both Tools

Combining SmartInspect and EurekaLog is a great way to benefit from both the advanced logging capabilities of SmartInspect to log application data and user actions, and from the excellent crash reporting capabilities of EurekaLog to receive exception and crash information from end-users via email and web.

Instead of just receiving the crash reporting information generated by EurekaLog, an ideal integration of both tools would also add a SmartInspect log of the most recent user actions and application data to EurekaLog's email. To do this, we first need to configure SmartInspect to be as unobtrusive during normal application usage as possible. As we only need the logging data when the application crashes, we do not want the logging to impact the application's performance.

To reduce the impact of logging drastically (take a look at the benchmarks), we can configure SmartInspect to use its unique memory protocol and log to memory instead of logging to a file, and only dump the log when the application crashes. We can then attach the resulting log file to EurekaLog's crash email and receive both the EurekaLog exception information and the SmartInspect log file for inspection and analysis. Using SmartInspect's memory protocol means that we can keep logging enabled at all times and get the exact logging information we need when an error occurs: the application data and user actions just before the application crashed. To enable the SmartInspect memory protocol, just change the connection string (you can also use the SmartInspect Configuration Builder to generate a connection string or configuration file, so you do not have to look up the available options in the online help):

Si.Connections := 'mem(maxsize=2048)';
Si.Enabled := True;

The specified connection string configures SmartInspect to log to memory and use a maximum of 2MB of RAM to store the logging data. When SmartInspect reaches the end of the reserved memory area, it starts from the beginning of the buffer and replaces the oldest logging information. This approach makes sure that the application doesn't use too much memory for the logging data, and also enables us to always keep the last 2MB worth of logging information available in case of an application crash.

When a crash occurs, we need to save the logging information stored in memory to a log file, and attach it to the EurekaLog crash email. EurekaLog has a special event to attach additional files to crash emails: the AttachedFilesRequest event. The easiest way to implement this event is to drop a TEurekaLog component onto the main form and adding an event handler to the OnAttachedFilesRequest event of this component.

The following code shows how to save the logging information to a temporary log file and how to attach it to EurekaLog's crash email.

procedure TMainForm.EurekaLogAttachedFilesRequest(
  EurekaExceptionRecord: TEurekaExceptionRecord;
  AttachedFiles: TStrings);
var
  LStream: TFileStream;
  LTempPath: array[0..MAX_PATH+1] of Char;
  LFileName: String;
begin
  { Get a temporary filename }
  if GetTempPath(MAX_PATH, LTempPath) = 0 then
    Exit;

  LFileName := IncludeTrailingPathDelimiter(LTempPath) + 'CrashLog.sil';

  { Save the log file }
  LStream := TFileStream.Create(LFileName, fmCreate);
  try
    Si.Dispatch('mem', 0, LStream);
  finally
    LStream.Free;
  end;

  { Attach the file to EurekaLog's crash report }
  AttachedFiles.Add(LFileName);
end;

When you run the demo application and send the crash report, a SmartInspect log file is now automatically added to the email:

Hide image
Crash report

    Closing Notes

This tutorial explained how to integrate SmartInspect and EurekaLog by attaching SmartInspect log files to EurekaLog crash reports. In addition to the options shown and explained in this article, you can of course customize and extend both the SmartInspect logging and the EurekaLog behavior to your likening. The approach explained here is already being used by numerous developers and companies and has turned out to be extremely useful for identifying and solving support requests and crashes. If you have any questions regarding the integration of both tools, do not hesitate to contact me.

Server Response from: ETNASC01