Using madExcept in Web CGI stand-alone executable

delphi package - automated exception handling
Post Reply
XanderH
Posts: 8
Joined: Tue May 19, 2020 8:18 am

Using madExcept in Web CGI stand-alone executable

Post by XanderH »

Hello,

My company uses madExcept within our web applications and I've been having problems getting the exceptions emailed to us when used in a .exe web application. Our website uses both a .exe and an ISAPI .dll, and we've got the same madExcept settings setup in both. The .dll worked great with madExcept immediately, but in the .exe the bug report is generated and shown on screen but the email was not being sent.
I managed to track this down to an issue that the .exe thought it had finished all it's work, since the main thread was no longer running, so was being freed from memory before madExcept had time to actually send the email. In the end I have got it working but not with a very elegant solution and I think there must be a better way.

In the main project source for the application there are these 3 lines which are auto-generated when you create the project:

Code: Select all

Application.Initialize;
Application.WebModuleClass := WebModuleClass;
Application.Run;
I added this code after it:

Code: Select all

while Application.ActiveCount > 0 do
    Sleep(100);

if madexcept.AmOnline then
    Sleep(2000);
I couldn't find documentation on what .AmOnline does but it seems to become true if madExcept catches an exception but doesn't go back to false once the work finishes, since first I tried using a while loop but that got stuck forever. This gives madExcept enough time to send the email, but using Sleep doesn't feel like a very nice way to handle this.

The other thing I tried was using the OnException event in my TWebApplication to call HandleException manually. I think I'd still have to use Sleep after calling it to ensure the thread doesn't "finish" before madExcept has a chance to, but I don't think this can work since madExcept catches all uncaught exceptions before they make it to the OnException event. I was looking for a way to have madExcept enabled so that I can call HandleException but not automatically catch uncaught exceptions, but this doesn't seem to be possible?

Thanks.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Using madExcept in Web CGI stand-alone executable

Post by madshi »

AmOnline just checks if madExcept has internet access, nothing else.

I'd suggest that you call RegisterExceptActionHandler(). The callback will notify when madExcept is about to send a bug report (eaSendBugReport) and also when sending has completed (eaSendBugReport3). That way hopefully you can delay process shutdown more intelligently, and only when it's needed?
XanderH
Posts: 8
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Post by XanderH »

Oh that's interesting, because AmOnline definitely seems to be false when an exception hasn't been raised, as normal requests definitely aren't taking 2 seconds longer to run.

Will take a look into those, thanks!
XanderH
Posts: 8
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Post by XanderH »

OK, I'm now trying to use RegisterExceptActionHandler() but it isn't behaving how I'd expect it to.

In the BeforeDispatch of my TWebApplication I am running the following code:

Code: Select all

madExcept.RegisterExceptActionHandler(CheckMadexceptAction, stDontSync);
madExcept.PauseMadExcept(True);
where CheckMadexceptAction looks like this:

Code: Select all

procedure TPDWebModule.CheckMadexceptAction(action: TExceptAction; const exceptIntf: IMEException; var Handled: Boolean);
begin
  if action = TExceptAction.eaSendBugReport then
    fWait := True
  else if action = TExceptAction.eaSendBugReport3 then
    fWait := False;
end;
I paused madExcept so that I could manually call HandleException in the OnException event of my application, and then I can check the fWait variable to see what it's value is:

Code: Select all

procedure TPDWebModule.WebModuleException(Sender: TObject; E: Exception; var Handled: Boolean);
begin
  madExcept.HandleException(etNormal, E);
  while fWait do
    Sleep(50);
end;
However this still causes the application to terminate before the email has been sent. If I set fWait to true before my while loop, the application get stuck forever as if eaSendBugReport3 never happens, even though I do get the email. And I know the ExceptActionHandler is working since if I change exceptIntf.MailSubject, that works.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Using madExcept in Web CGI stand-alone executable

Post by madshi »

madExcept works in threads. When you call HandleException(), it takes a while until madExcept is ready to send the bug report, although HandleException() returns pretty quickly. You could try setting "fWait := true" before you call HandleException and then wait until eaSendBugReport3 clears the flag. Maybe add a timeout value, so you don't wait too long in case something unexpected happens.

Edit: Oh I see you already tried that? So basically you're saying eaSendBugReport3 does not ever happen? Can you add some logging or a Windows.MessageBox to double check that eaSendBugReport3 doesn't happen? Please don't use a VCL window for this test, because VCL windows are not thread safe. But Windows.MessageBox should work. eaSendBugReport and eaSendBugReport2 happen, though?
XanderH
Posts: 8
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Post by XanderH »

eaSendBugReport3 does seem to happen, but it still gets stuck forever...

Code: Select all

20-May-2020 16:52:55:697=eaSendBugReport
20-May-2020 16:52:55:713=eaSendBugReport3
20-May-2020 16:52:55:713=eaContinueApplication
20-May-2020 16:52:55:713=eaSaveBugReport
20-May-2020 16:52:55:713=eaSaveBugReport3
XanderH
Posts: 8
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Post by XanderH »

Seemed to just be my while loop not working as expected!

Instead I wrote and call this procedure after calling HandleException, and now everything works as expected:

Code: Select all

procedure TPDWebModule.CheckForWait;
begin
  if fWait then
    begin
      Sleep(20);
      CheckForWait;
    end;
end;
Still feels a bit wrong to have to use Sleep to stall the thread, but I'm not aware of any other way and the Delphi documentation about how the threading in Web Applications work is pretty rubbish, so I think this will have to do!

Thanks for the help madshi.

EDIT: Oh, also I think it might be helpful for future people if you add the detail of eaSaveBugReport3, eaSendBugReport3 and I guess also eaPrintBugReport3 to http://help.madshi.net/madExceptUnit.htm?
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Using madExcept in Web CGI stand-alone executable

Post by madshi »

That looks like endless recursion to me? I don't really see why that would work better than a loop, to be honest. I think a loop would be better/cleaner.

Anyway, using Sleep() like this is exactly what you should be doing, if you want to delay a (secondary) thread. Unless you want/need to handle Messages while waiting. In that case it becomes more complicated.
XanderH
Posts: 8
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Post by XanderH »

The function only calls itself again if fWait is still true so it's not endless. Theoretically I think this code should be the same as the while loop I tried to use initially, but that does get stuck infinitely.
iconic
Site Admin
Posts: 1065
Joined: Wed Jun 08, 2005 5:08 am

Re: Using madExcept in Web CGI stand-alone executable

Post by iconic »

Code: Select all

procedure TPDWebModule.CheckForWait;
begin
   while (fWait) do
   Sleep(20);
end;
Agree with Madshi. A loop is a better choice because you don't have recursive function re-entry which can be more expensive and is not needed. Just let the loop evaluate fWait each iteration and sleep for 20 ms while the condition remains satisfied. Of course if fWait never becomes false then the loop will never break so be careful with that (maybe set a max timeout or something) Also, your code looks like a stack overflow waiting to happen. The return address has to be pushed onto the stack each time you make the function call to itself, that consumes stack space.

--Iconic
XanderH
Posts: 8
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Post by XanderH »

Sorry to bring back an old thread, but I'm still struggling to get this to work as expected!

It seems that something about my previous solution didn't work and in the end I just added a Sleep(500) after calling madExcept.HandleException, which sometimes resulted in the email being sent. I've just been swapping out the smtp details for these applications and would really love to make this work properly.

I've registered an except action handler that sends to Slack each action that madExcept does. My WebModuleException procedure looks like this:

Code: Select all

fWait := True;
madExcept.HandleException(etNormal, E);
WebCore.SendMessageToSlack('Checking for wait');
CheckForWait;
WebCore.SendMessageToSlack('Wait is ' + BooleanToText(fWait) + ', sleeping another 5 seconds.');
Sleep(5000);
and the CheckForWait procedure:

Code: Select all

procedure TPDWebModule.CheckForWait;
begin
  while fWait do
    begin
      WebCore.SendMessageToSlack('Waited at ' + FormatDateTime('hh:nn:ss:zzzz', Now), 'WTDev', 'wt_dev_alerts');
      Sleep(20);
    end;
end;
Slack shows that all the madExcept actions happen before CheckForWait is even called:
  • Action is eaSendBugReport
    Action is eaSendBugReport3
    Action is eaContinueApplication
    Action is eaSaveBugReport
    Action is eaSaveBugReport3
    Checking for wait
    Wait is False, sleeping another 5 seconds
Without the extra 5 second wait, I do not receive the bug report despite eaSendBugReport3 supposedly having happened. Additionally the "send bug report in background" option doesn't seem to make any difference.

Any suggestions would be hugely appreciated, many thanks.
XanderH
Posts: 8
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Post by XanderH »

Any thoughts on this madshi? Would be greatly appreciated!
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Re: Using madExcept in Web CGI stand-alone executable

Post by madshi »

Originally you added code directly after "Application.Run". Why did you go away from that? It seemed to make sense for me. You could just check "fWait" there instead of "AmOnline".
Post Reply