Brian Hawk
Brian Hawk

Reputation: 806

How to get call stack of a handled exception using EurekaLog

I'm using Delphi 7 and EurekaLog 7 (in compatibility mode) and simply want to get call stack of a handled exception, like

procedure CrossThreadFunc;
begin
  try
    SomeCode;
  except
    on E: Exception do
      Log(CallStackOf(E));
  end;
end;

It's a multi-threaded application so I'd like to see the call stack of the calling thread if possible. Also, since this is a handled exception, do I still need to use EurekaLog's OnExceptionRaise event? (which I don't want to).

Edit: CrossThreadFunc() is being called many times with some arguments and what I need to know is exactly where I called it that eventually caused SomeCode() to raise an exception.

Upvotes: 1

Views: 420

Answers (1)

Alex
Alex

Reputation: 5668

There are several ways to do this, described in EurekaLog's help:

Option 1

(Delphi 2009+ only)

Assuming you only need a textual representation:

except
  on E: Exception do
    Memo1.Lines.Text := E.StackTrace;
end;

Option 2

Assuming you have access to RTL's exception object:

uses
  EExceptionManager, // for ExceptionManager
  EException,        // for TEurekaExceptionInfo 
  ECallStack;        // for TEurekaBaseStackList 

var
  EI: TEurekaExceptionInfo;
  CallStack: TEurekaBaseStackList;
// ...
  except
    on E: Exception do
    begin
      EI := ExceptionManager.Info(E);
      // EI would be nil, if EurekaLog is disabled
      // or event handlers instruct EurekaLog to skip this exception
      if Assigned(EI) then 
        CallStack := EI.CallStack;
    end;
  end;

Note: due to bugs in some older IDEs, you may need to write like this:

EI := ExceptionManager.Info(Pointer(E));

Option 3

Assuming you have access to EurekaLog's exception information object (such as argument in an event handler):

uses
  EException, // for TEurekaExceptionInfo 
  ECallStack; // for TEurekaBaseStackList

{ ... } AExceptionInfo: TEurekaExceptionInfo; { ... }
var
  CallStack: TEurekaBaseStackList;
begin
  CallStack := AExceptionInfo.CallStack;
end;

Option 4

Assuming you want call stack for last (e.g. most recent) exception in current thread:

uses
  EExceptionManager, // for ExceptionManager
  EException,        // for TEurekaExceptionInfo 
  ECallStack;        // for TEurekaBaseStackList 

var
  EI: TEurekaExceptionInfo;
  CallStack: TEurekaBaseStackList;
begin
  EI := ExceptionManager.LastThreadException;
  // EI would be nil, if EurekaLog is disabled
  // or event handlers instruct EurekaLog to skip this exception
  if Assigned(EI) then 
    CallStack := EI.CallStack;
end;

Notes:

  • If you need current call stack - use GetCurrentCallStack function from ECallStack unit:

    uses ECallStack; // for TEurekaBaseStackList and GetCurrentCallStack

    procedure TForm1.Button1Click(Sender: TObject); var CallStack: TEurekaBaseStackList; begin CallStack := GetCurrentCallStack; // You can also use other functions from ECallStack unit try Memo1.Lines.Text := CallStack.ToString; // You can also use CallStackToString(s) routines to customize textual formatting finally FreeAndNil(CallStack); end; end;

  • If you need call stacks from other threads - just set it up in options, call stacks for all enabled threads will be included in the same call stack object. You can distingush between threads by ThreadID property of call stack entry.

  • If you need to convert retrieved call stack to text/string representation for logging purposes, see this:

Option 1

Use StackTrace property of exception object (Delphi 2009+):

except
  on E: Exception do
    Memo1.Lines.Text := E.StackTrace;
end;

Option 2

Use ToString method to convert call stack to single string with default formatting:

var
  CallStack: TEurekaBaseStackList;
begin
  CallStack := { ... somehow retrieve call stack ... };
  Memo1.Lines.Text := CallStack.ToString;
end;

Option 3

Use Assign method to convert call stack to TStrings object with default formatting:

var
  CallStack: TEurekaBaseStackList;
begin
  CallStack := { ... somehow retrieve call stack ... };
  Memo1.Lines.Assign(CallStack);
end;

Option 4

Use CallStackToString function from ECallStack unit:

// (CallStackToString function allows you to override header and formatting)

var
  CallStack: TEurekaBaseStackList;
  Formatter: TCompactStackFormatter;
begin
  CallStack := { ... somehow retrieve call stack ... };

  // A): Default formatting and header:
  Memo1.Lines.Text := CallStackToString(CallStack);

  // B): With custom header:
  Memo1.Lines.Text := CallStackToString(CallStack, 'Error Details:');

  // C): Custom formatting:
  Formatter := TCompactStackFormatter.Create;
  try
    // <- here you can customize Formatter (for example: alter captions for columns, etc.)
    Memo1.Lines.Text := CallStackToString(CallStack, '', Formatter);
  finally
    FreeAndNil(Formatter);
  end;
end;

Option 5

Use CallStackToStrings function from ECallStack unit:

// (CallStackToStrings function allows you to override header and formatting)

var
  CallStack: TEurekaBaseStackList;
  Formatter: TCompactStackFormatter;
begin
  CallStack := { ... somehow retrieve call stack ... };

  // A): Default formatting and header:
  CallStackToStrings(CallStack, Memo1.Lines);

  // B): With custom header:
  CallStackToStrings(CallStack, Memo1.Lines, 'Error Details:');

  // C): Custom formatting:
  Formatter := TCompactStackFormatter.Create;
  try
    // <- here you can customize Formatter (for example: alter captions for columns, etc.)
    CallStackToStrings(CallStack, Memo1.Lines, '', Formatter);
  finally
    FreeAndNil(Formatter);
  end;
end;

Available formatters are:

  • TEurekaStackFormatter - general formatter for EurekaLog-style call stack (i.e. fixed-width table with columns) - best to be used in text files
  • TEurekaStackFormatterV6 - backward-compatibility formatter to produce call stack in EurekaLog V6 format (less columns)
  • TSimpleStackFormatter - produces list-like view of call stack (no columns) suitable for variable-width fonts (best to be used in message boxes)
  • TCompactStackFormatter - similar to TSimpleStackFormatter, but produces more compact output with less details (good for quick preview)

P.S. You may also want to consider EurekaLog's logging procedures.

Upvotes: 1

Related Questions