10

I have a Delphi 10 project using the latest version of EurekaLog. I'm currently using EurekaLog to help me debug problems in my production clients.

I noticed that EurekaLog wasn't registering errors that happened within threads. After I started reading up on it, I found that I need to change from TThread to TThreadEx, and add the following code at the start of my Execute overriden method.

SetEurekaLogStateInThread(ThreadID, true);

Despite this, when an error happens, it does not generate an event in the EL file.

If I add ExceptionManager.StandardEurekaError('TThrdSincArquivos.Execute => ' + ex.Message); on the try..except, it does log. But the stack trace is displayed as if the error occurred on the line where I call StandardEurekaLog(), not on the line where the error actually occurred. This defeats the purpose of the whole thing.

Another problem is that it displays a dialog box, which I don't want, since the error occurred inside a background thread. I just want it logged. I should get a dialog only with errors on the main thread.

How can I achieve theses results within the thread?

  • Actually log the error with the correct stack.

  • When on the main thread, display the dialog, but within a thread, just log with no dialog.

EDIT

Below is my EurekaLog Muti-threading configuration

Call Stack option Multi-threading option

Here is my thread declaration:

unit ThrdSincArquivos;

interface

uses
  System.Classes, System.SysUtils, System.Generics.Collections, REST.Client, REST.Types,
  System.JSON, Data.DB, Datasnap.DBClient, FireDAC.Comp.Client, FireDAC.Stan.Param, System.SyncObjs, EBase, EExceptionManager, EClasses;

type
  TThrdSincArquivos = class(TThreadEx)
  private

My thread's Create

constructor TThrdSincArquivos.Create(pPrimeiraExec: boolean; tipoSincParam: TTipoSinc);
begin
  inherited Create(true);

  NameThreadForDebugging('TThrdSincArquivos');
  primeiraExec := pPrimeiraExec;
  tipoSinc := tipoSincParam;
  executadoThreadSinc := false;
  FreeOnTerminate := true
end;

The start of my Execute

procedure TThrdSincArquivos.Execute;
var
  contador: Integer;
begin
  inherited;

  try

and the end of the Execute

  except
    on ex: Exception do
    begin
      oLog.GravarLog(ex, 'TThrdSincArquivos.Execute => FIM');
    end;
  end;
end;

It refuses to log any exception to the Elf file. I tried to add a raise after my own log routine, but it still didn't help. It should log, but it isn't, unless I explicitly call the StandardEurekaError, but I get the stack wrong, and I get the dialog.

Pascal
  • 2,944
  • 7
  • 49
  • 78
  • https://www.eurekalog.com/help/eurekalog/index.php?multithreading.php – David Heffernan Oct 06 '17 at 23:15
  • 1
    BTW, there is no such thing as XE10 – David Heffernan Oct 06 '17 at 23:15
  • `TThreadEx` calls `SetEurekaLogStateInThread()` for you. Make sure your overridden `Execute()` is calling `inherited`. – Remy Lebeau Oct 06 '17 at 23:21
  • @DavidHeffernan Thanks for the XE 10 correction. I've already read-up on that link, and everything seems to me correctly configured. I've added my EurekaLog configuratoin settings and the thred's code. Thank you very much for your help – Pascal Oct 07 '17 at 14:25
  • @RemyLebeau Indeed, I wasn't calling the inherited on the Execute. I've added it, but it didn't help. I've added my EurekaLog configuratoin settings and the thred's code. Thank you very much for your help – Pascal Oct 07 '17 at 14:26
  • I've added a bounty since this has me stumped – Pascal Oct 09 '17 at 10:30
  • Did you ask the vendor for support? They will answer this easily and support is part of the deal. – David Heffernan Oct 09 '17 at 18:26
  • @DavidHeffernan I did, and posted on their forum.. got no response.. I'll accept an alternative that is not EurekaLog.. I just need to get the stack from my threads on the correct line without showing a Dialog box.. Thank you very much for your response – Pascal Oct 10 '17 at 15:02
  • 1
    That's pretty disappointing from EurekaLog. I personally use MadExcept and highly recommend it. – David Heffernan Oct 10 '17 at 15:14
  • @DavidHeffernan I've given up on EurekaLog and I'm evaluating madExcept. It does capture some exceptions that EurekaLog wouldn't capture. However, I get the same behavior on madExcept, where I get the dialog box even on errors inside background threads. Can I keep the dialog enabled for errors on main thread while logging the bug reports silently on the background threads? I didn't find a similar question on their forums. Thanks – Pascal Dec 04 '17 at 01:12
  • 1
    @Pascal You could use [TEurekaLogEvents component](https://www.eurekalog.com/help/eurekalog/index.php?topic_class_ecomponent_teurekalogevents.php). The component provides [OnExceptionAction]() event that allows to skip unnecessary steps when Eureka handles an exception. The event has **AEurekaAction** parameter to define what step is execution. The parameter value **atShowingExceptionInfo** signals us Eureka wants to display the error dialog. If you don't want to show the dialog, you should just set **AExecute** parameter to False. – Alex Kalabukhov Apr 25 '18 at 20:34
  • 1
    @Pascal Also there is **AExceptionInfo** parameter - it's class with couple of useful properties. You can check **ThreadID**, ThreadException and SynchronizeException to understand if an exception did appear in a background thread. – Alex Kalabukhov Apr 25 '18 at 20:39

1 Answers1

1

When you are using TThread class - it saves thread exception to .FatalException property, which you are supposed to handle in some way. Either from thread event, or from other (caller) thread. EurekaLog does not break this behaviour. E.g. your previosly written code will not change its behaviour when you enable EurekaLog. That way your properly written code would work correctly both with and without EurekaLog.

How your code is currently handling thread exceptions? Are you doing something like ShowMessage or custom logging? This obviosly would not work with EurekaLog, it does not know that you are processing exceptions with ShowMessage or your own custom logging code. You probably want something like Application.ShowException or re-raise in caller thread.

If you can not use default RTL/VCL processing (which is hooked by EurekaLog) for some reason - then you need to tell EurekaLog that you want to handle this particular exception. For example, from docs: you can use (for example) HandleException(E); from EBase unit:

Thread.WaitFor;
if Assigned(Thread.FatalException) then
begin
  // Your old code is here
  // Do your own thing: show message, log, etc.

  // Tell EurekaLog to do its thing:
  HandleException(Thread.FatalException);
end;

You would probably want to set exception filter or use events to disable dialogs for thread exceptions, because presumably you have already processed exception yourself (e.g. already showed message).

There is A LOT more ways to handle exception in threads, and EurekaLog's docs illustrate each thread case (like BeginThread, TThread, thread pools, etc.) with several possible options. It is just not reasonable to pack all this information into a single answer.

If, for some reason, you do not have code that processes .FatalException property of TThread - then you can use TThreadEx class and its .AutoHandleException property to handle exceptions automatically when thread exits, as described here:

type
  TMyThread = class(TThreadEx)
  protected
    procedure Execute; override;
  end;

procedure TMyThread.Execute;
begin
  // ... your code ...
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Thread: TMyThread;
begin
  Thread := TMyThread.Create(True, 'My thread');
  Thread.AutoHandleException := True; // <- added
  Thread.FreeOnTerminate := True;
  Thread.Start;
  Thread := nil; // never access thread var with FreeOnTerminate after Start
end;

However, be aware that you code will not work properly (e.g. will ignore exceptions) if you decide to disable EurekaLog in the future. Because if you remove EurekaLog from your project - then your project will have no code to handle thread exceptions!

P.S.

I need to change from TThread to TThreadEx, and add the following code at the start of my Execute overriden method.

SetEurekaLogStateInThread(ThreadID, true);

That is slightly incorrect: you can do either one or another, but not both. And there are other ways to tell EurekaLog that it should hook exceptions in this thread.

Basically, exception life has two stages: raise and handle. EurekaLog hooks both stages when they are implemented in default RTL/VCL code. You need to explicitly indicate which threads you want to hook, because you probably want to ignore system / 3rd party threads, which you have no control over. And it so happens that default processing for TThread does not exist in RTL/VCL. That is why there is nothing to hook.

Alex
  • 5,477
  • 2
  • 36
  • 56