Root > How to... > ...add/save exception to a log?

...add/save exception to a log?

Previous pageReturn to chapter overviewNext page   

Local bug report files

If you want to save a bug report about exception to a file - you can use the local bug report file feature of EurekaLog:

 

 

Local bug report options

 

Local bug report files are enabled by default. You can open default folder with bug reports by opening Start / Programs / EurekaLog / Bug reports menu item (for current user account only).

 

Local bug report file will contain one or more bug reports, consisting of general info, call stack, modules, processes, CPU and assembly, which you can also configure:

 

 

Bug report content options

 

You can also add any custom information to a bug report:

 

 

 

Custom text information in bug report

 

 

3rd party frameworks

You can also log exceptions using any 3rd party logging frameworks. We have examples for integration with the following logging frameworks:

However, you can use any logging framework. You can refer to the above examples to learn how to trigger on exceptions to send them to the logging framework of your choice.

 

Note: You can also use logging in EurekaLog.

 

 

Custom log files

You can also add exceptions to your own log files. For example, if you want to log all unhandled exceptions - you can assign OnExceptionNotify event handler:

 

uses
  EException,      // for TEurekaExceptionInfo
  EEvents,         // for RegisterEventExceptionNotify
  ESysInfo,        // for various info functions
  EJSON,           // for JSON routines
  EConsts;         // for UTF8 BOM
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  raise Exception.Create('Error Message');
end;
 
// Creates log file on startup to write into
function OpenLogFile: THandle;
var
  Written: Cardinal;
begin
  // Create a new log file
  Result := CreateFile(PChar(GetFolderAppData + 'BugReport.json'),
    GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ,
    nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
  Win32Check(Result <> INVALID_HANDLE_VALUE);
 
  // Write UTF8 BOM to start of a new file
  SetLastError(0);
  Win32Check(WriteFile(Result, BOM_UTF8, SizeOf(BOM_UTF8), Written, nil));
  Win32Check(Written = SizeOf(BOM_UTF8));
end;
 
// Tell EurekaLog to log crash info
procedure LogException(const ACustom: Pointer;
  AExceptionInfo: TEurekaExceptionInfo;
  var AHandle: Boolean;
  var ACallNextHandler: Boolean);
var
  JSON, SubJSON, CallStackEntry: IJSONValues;
  E: Exception;
  CallStackItems: array of Variant;
  LogEntry: String;
  EncodedLogEntry: UTF8String;
  Written: Cardinal;
  X: Integer;
begin
  // Create JSON about the event
  JSON := JSONCreate;
 
  // The code below is just an example
  // Modify it as you want

  // For example, you can replace it with 

  // code to add a database entry
  JSON['version'] := 1;
  JSON['created'] := Now;
 
  SubJSON := JSONCreate;
  JSON['application'] := SubJSON;
  SubJSON['executable'] := ExtractFileName(ParamStr(0));
  SubJSON['version'] := GetFileVersion(ParamStr(0));
  SubJSON['started'] := GetStartDate;
  SubJSON['compiled'] := GetCurrentModuleCompilationDate;
  SubJSON['elevated'] := IsElevated;
  SubJSON['integrityLevel'] := GetIntegrityLevel;
 
  SubJSON := JSONCreate;
  JSON['user'] := SubJSON;
  SubJSON['name'] := GetUserName;
  if GetUserEMail <> '' then
    SubJSON['email'] := GetUserEMail;
  SubJSON['fullName'] := GetUserFullName;

  if GetCompanyName <> '' then
  SubJSON['company'] := GetCompanyName;
  if IsAdministrator = Boolean(-1) then
    SubJSON['admin'] := 'limited'
  else
    SubJSON['admin'] := IsAdministrator;
 
  SubJSON := JSONCreate;
  JSON['system'] := SubJSON;
  SubJSON['name'] := GetComputerName;
  SubJSON['cpu'] := GetProcessor;
  SubJSON['video'] := GetDisplayMode;
  if GetVirtualMachineVersion <> '' then
    SubJSON['vm'] := GetVirtualMachineVersion;
 
  SubJSON := JSONCreate;
  JSON['os'] := SubJSON;
  SubJSON['name'] := GetOSTypeStr;
  SubJSON['build'] := GetOSBuild;
  SubJSON['update'] := GetOSUpdate;
  SubJSON['language'] := GetOSUILanguage;
  SubJSON['uac'] := IsUACEnabled;
 
  JSON['eventType'] := 'exception';
 
  SubJSON := JSONCreate;
  JSON['exception'] := SubJSON;
 
  SubJSON['id'] := AExceptionInfo.BugIDStr;
  SubJSON['class'] := AExceptionInfo.ExceptionClass;
  SubJSON['message'] := AExceptionInfo.ExceptionMessage;
 
  // Check if exception is of Exception class (usually: yes)
  if AExceptionInfo.ExceptionNative and

     (AExceptionInfo.ExceptionObject <> nil) and
     TObject(AExceptionInfo.ExceptionObject).InheritsFrom(Exception) then
  begin
    E := Exception(AExceptionInfo.ExceptionObject);
    // Here you can cast E to EWin32Error, EDBException, or any other class

    //

    // IMPORTANT NOTE: Please note that the E may be unavailable even for Delphi exceptions!

    // For example, if the exception object was already deleted:

    //

    // try

    //   raise Exception.Create('Inner Exception'); // - will be deleted

    // except

    //   on E: Exception do

    //     raise Exception.Create('Outer Exception');

    // end; 

    // 

    // See also.

    // That is why we check for NIL in the example above.

    // For this reason we highly recommend to use properties of AExceptionInfo when possible,

    // Such as .ExceptionClass and .ExceptionMessage

  end;
 
  // Log exception's call stack
  SetLength(CallStackItems, AExceptionInfo.CallStack.Count);
  for X := 0 to AExceptionInfo.CallStack.Count - 1 do
  begin
    CallStackEntry := JSONCreate;
    CallStackItems[X] := CallStackEntry;
 
    CallStackEntry['module'] := AExceptionInfo.CallStack[X].Location.ModuleName;
    CallStackEntry['unit'] := AExceptionInfo.CallStack[X].Location.UnitName;
    if AExceptionInfo.CallStack[X].Location.SourceName <> '' then
      CallStackEntry['source'] := AExceptionInfo.CallStack[X].Location.SourceName;
    if AExceptionInfo.CallStack[X].Location.ClassName <> '' then
      CallStackEntry['class'] := AExceptionInfo.CallStack[X].Location.ClassName;
    CallStackEntry['routine'] := AExceptionInfo.CallStack[X].Location.ProcedureName;
    CallStackEntry['line'] := AExceptionInfo.CallStack[X].Location.LineNumber;
  end;
  SubJSON['callstack'] := VarArrayOf(CallStackItems);
 
  // You may also log other properties of AExceptionInfo
  // Or you can use routines from ESysInfo to log process and environment info
  // Of you can use BuildBugReport function to compose bug report text
 
  // Prepare log entry text
  LogEntry := JSON.ToString + ',';
  EncodedLogEntry := UTF8Encode(LogEntry);
 
  // The code above will produce the following JSON text:
  // {
  //   "version": 1,
  //   "created": "2022.03.07 20:21:42",
  //   "application": {
  //     "executable": "Project1.exe",
  //     "version": "1.0.0.0",
  //     "started": "2022.03.07 20:21:39",
  //     "compiled": "2022.03.07 20:21:37",
  //     "elevated": false,
  //     "integrityLevel": "Medium"
  //   },
  //   "user": {
  //     "name": "username",
  //     "email": "user@domain.com",
  //     "fullName": "User Name",
  //     "company": "Litware LLC",
  //     "admin": "limited"
  //   },
  //     "system": {
  //     "name": "HOMEPC",
  //     "cpu": "Intel(R) Core(TM) i7-3930K CPU @ 3.20GHz",
  //     "video": "1920x1080x32 120 DPI",
  //     "vm": "VirtualBox"
  //   },
  //   "os": {
  //     "name": "Microsoft Windows 10 (64 bit)",
  //     "build": "2009 (10.0.19044.1526)",
  //     "update": "Authum 2021 Update [21H2] #1526",
  //     "language": "Russian (0419)",
  //     "uac": true
  //   },
  //   "eventType": "exception",
  //   "exception": {
  //     "id": "2314182A",
  //     "class": "Exception",
  //     "message": "Error Message.",
  //     "callstack": [
  //       {
  //         "module": "C:\\Projects\\Win32\\Debug\\Project1.exe",
  //         "unit": "Unit1",
  //         "source": "Unit1.pas",
  //         "class": "TForm1",
  //         "routine": "Button1Click",
  //         "line": 36
  //       },
  //       {
  //         "module": "C:\\Projects\\Win32\\Debug\\Project1.exe",
  //         "unit": "Vcl.Controls",
  //         "source": "Vcl.Controls.pas",
  //         "class": "TControl",
  //         "routine": "Click",
  //         "line": 7660
  //       },
  //       ...
  //     ]
  //   }
  // }
 
  // Write the log entry to a file
  // No sync is necessary, as the log entry is written as single buffer
  SetLastError(0);
  Win32Check(WriteFile(THandle(ACustom), Pointer(EncodedLogEntry)^, Length(EncodedLogEntry), Written, nil));
  Win32Check(Written = Cardinal(Length(EncodedLogEntry)));
end;
 
initialization
  // Tell EurekaLog to log crash info into a file
  RegisterEventExceptionNotify(Pointer(OpenLogFile), LogException);
end.

 

 

See also:




Send feedback... Build date: 2024-09-30
Last edited: 2023-08-09
PRIVACY STATEMENT
The documentation team uses the feedback submitted to improve the EurekaLog documentation. We do not use your e-mail address for any other purpose. We will remove your e-mail address from our system after the issue you are reporting has been resolved. While we are working to resolve this issue, we may send you an e-mail message to request more information about your feedback. After the issues have been addressed, we may send you an email message to let you know that your feedback has been addressed.


Permanent link to this article: https://www.eurekalog.com/help/eurekalog/how_to_add_exception_to_a_log.php