Howto log source line causing exception and add custom information?
Our application log the source line causing exception with JCL and it works great. I use D2007. I have a TApplicationEvents.OnException event that do the actual logging. Consider this:
function MyFunc: String;
begin
// Codelines that may raise exception.
// Call functions that also may raise exception
end;
procedure ComplexFunc(aVariable: String);
begin
// also here can it be exceptions....
// Code here that is the cause of exception
end;
procedure foo;
var
myVar: String;
begin
myvar := MyFunc;
ComplexFunc(myvar);
end;
procedure TMainForm.ApplicationEvents1Exception(Sender: TObject; E: Exception);
begin
LogLastException(E, 'Unhandled Exception (%s)', [E.Message], 20);
end;
I have 3 methods and my onException event. LogLastException log the callstack when an exception occurs. The problem is that I cannot add information to the E.Message without loose the sourceline that cause exception. Pretend it is the second line in ComplexFunc that raise exception. I also want to log the value of myvar variable. So I change the code to:
function MyFunc: String;
begin
// Codelines that may raise exception.
// Call functions that also may raise exception
end;
procedure ComplexFunc(aVariable: String);
begin
// also here can it be exceptions....
// Code here that is the cause of exception
end;
procedure foo;
var
myVar: String;
begin
try
myvar := MyFunc;
ComplexFunc(myvar);
except
on E: Exception do
raise TException.CreateFmt('myvar = %s', [myvar]);
end;
end;
procedure TMainForm.ApplicationEvents1Exception(Sender: TObject; E: Exception);
begin
LogLastException(E, 'Unhandled Exception (%s)', [E.Message], 20);
end;
Now the value of myvar i开发者_高级运维s logged, BUT at the price of I loose the original sourceline of the exception. Instead the line with raise TException.CreateFmt is logged. Any suggestion of how to do both ?
Regards
In addition to Marjan Vennema's answer (which I would follow), you can raise your new exception and make it look like it came from the address of the old exception.
except
on E: Exception do
raise Exception.CreateFmt('myvar = %s', [myvar]) at ExceptAddr;
end;
You are losing the original source line of the exception, because
except
on E: Exception do
raise TException.CreateFmt('myvar = %s', [myvar]);
end;
effectively handles the original exception (making it go away) and raising a new one. Which, of course then will have its own "source line of exception."
@balazs' solution preserves the source line of the original exception in the message of the new exception. @Stephane's solution comes close to the one I would use. Unfortunately he is replacing the original message with only the value of myvar. What I would do is add a line on top of the original message and then just re-raise the exception:
except
on E: Exception do
begin
E.Message := Format('%s'#13#10'%s', [Format('MyVar: %s', [MyVar]), E.Message]);
raise;
end;
end;
I've you tried something like that ?
try
myvar := MyFunc;
ComplexFunc(myvar);
except
on E: Exception do
begin
e.message := format('myvar = %s', [myvar]);
raise ;
end;
end;
I don't know what LogLastException do, but if you can redirect it's result into a string rather than your log, you can reraise the exception like this:
except
on E: Exception do
begin
str := LogLastExceptionToString(E, 'Unhandled Exception (%s)', [E.Message], 20);
raise TException.CreateFmt( str + 'myvar = %s message so far:' , [myvar]);
end;
end;
A simple approach would be to define a global variable, assign the extra information to it, and then add its contents to your log when you log the exception information.
精彩评论