Using madExcept in Web CGI stand-alone executable

delphi package - automated exception handling

Using madExcept in Web CGI stand-alone executable

Postby XanderH » Tue May 19, 2020 8:33 am

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.
XanderH
 
Posts: 6
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Postby madshi » Tue May 19, 2020 12:35 pm

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?
madshi
Site Admin
 
Posts: 10274
Joined: Sun Mar 21, 2004 5:25 pm

Re: Using madExcept in Web CGI stand-alone executable

Postby XanderH » Tue May 19, 2020 1:29 pm

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: 6
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Postby XanderH » Wed May 20, 2020 3:32 pm

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.
XanderH
 
Posts: 6
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Postby madshi » Wed May 20, 2020 3:38 pm

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?
madshi
Site Admin
 
Posts: 10274
Joined: Sun Mar 21, 2004 5:25 pm

Re: Using madExcept in Web CGI stand-alone executable

Postby XanderH » Wed May 20, 2020 3:55 pm

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: 6
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Postby XanderH » Wed May 20, 2020 4:22 pm

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?
XanderH
 
Posts: 6
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Postby madshi » Thu May 21, 2020 8:22 am

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.
madshi
Site Admin
 
Posts: 10274
Joined: Sun Mar 21, 2004 5:25 pm

Re: Using madExcept in Web CGI stand-alone executable

Postby XanderH » Thu May 21, 2020 10:56 am

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.
XanderH
 
Posts: 6
Joined: Tue May 19, 2020 8:18 am

Re: Using madExcept in Web CGI stand-alone executable

Postby iconic » Thu May 21, 2020 9:00 pm

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
iconic
Site Admin
 
Posts: 967
Joined: Wed Jun 08, 2005 5:08 am


Return to madExcept

Who is online

Users browsing this forum: No registered users and 20 guests