10 November, 2009

Tips & Tricks in EurekaLog, part 1

Many our customers often ask trivial questions about how to do different things with EurekaLog. Well, we have a bit of documentation (chm-file and online one), which describes some public internals, which you can use to customize default behavior. Apparently, this is “dictionary”-like style documentation. In order to use it, you must study all pieces and then come with some way of putting them together to get the desired behavior. Looks like this is too much work for many people, as they continue to ask trivia questions like “My application runs in unattended environment. How can I disable EurekaLog's dialog?”.

So, I've planned few posts about typical usage cases and how you can implement them. This will be sort of Q&A/FAQ-style articles. There are simple questions and answers on them. Answers usually include changing project options or writing a simple bits of code (usually events handlers). Here goes part one.

Q: How can I determinate if EurekaLog is active or not?

A: We recommend to use IFDEF-checks, for example:

  CurrentEurekaLogOptions ...
Alternatively there are functions (in ExceptionLog unit) like
function IsEurekaLogActive: Boolean;
function IsEurekaLogActiveInThread(ThreadID: DWord): Boolean;
function IsEurekaLogInstalled: Boolean;
But that means that you need to include EurekaLog's code in your application even though EurekaLog was disabled for your project.

For description of the functions see our documentation at http://www.eurekalog.com/docs/

Note, that there is a difference between "project compiled without EurekaLog" and "project compiled with EurekaLog, but EL is not active".

Q: How can I send error reports automatically, without dialog (which requires operator response)?

A: There are at least 3 ways of doing it.

1. Set "Dialog type" to "(none)". This option is located on "Exceptions dialogs" tab. This will disable dialog for all exceptions.

You can also do this in runtime: CurrentEurekaLogOptions.ExceptionDialogType := edtNone (units ExceptionLog and ECore).

2. You can use exception filters. Add new filter, select exception type and set handler to EurekaLog and set dialog type to "(none)". This will disable dialog for certain exception types. Note, that if you set handler to none or RTL - the bug-report will not be sended. See other questions below for example.

3. You can use the ExceptionActionNotify event. You can assign your own custom event handler, where you should pay attention to atShowingExceptionInfo and atShowedExceptionInfo action types.

To block error dialog you set Execute := False for atShowingExceptionInfo and Execute := True for atShowedExceptionInfo.

See also few examples.

If you application consists only of one main exe file and other 3rd party code do not use EL - then all you have to do is assign event handler in any place/unit of your application.

If your application consists of several DLLs/BPLs - then answer MAY vary, depending on how do you use them.

Q: I want to ignore particular exception. How can I do this?

A: Depends on type of this exception. Ideal case would be like this: target exception has his own class. For example, EIdCmdTCPClientConnectError. And you want to ignore all EIdCmdTCPClientConnectError exceptions. That's the simplest case – you can use so-called exception filters. Go to EurekaLog's options and open the “Exception filters” tab. Activate filters (well, it is on by default) and add new filter:

Enter “EIdCmdTCPClientConnectError” in “Class” field.

Options below define how do you want to override behavior for all exceptions of this class. For example, to deny EurekaLog's processing – switch “Handler” to “RTL”. If you do that, then every EIdCmdTCPClientConnectError will be processed as there was no EurekaLog installed (resulting in usual error message from VCL/RTL). You can also switch “Handler” to “none” to completely ignore this exception (well, usually silent ignoring is not a good practice, but can be useful sometimes).

Leaving “Handler” in “EurekaLog” state will allow you to customize EurekaLog's behavior for this type of exception. For example, you may want not to disable log creation/sending (for diagnostic reasons), but do not want to display an error dialog. In that case just switch “Dialog” to “None”.

See also other questions below to know what to do in more comlex cases.

Q: My application runs in environment without Internet access. I want to collect log manually in some sort of database to pick it up manually. How can I do this?

A: If you're satisfied with log-only (i.e. elf-file without screenshot) – then you can just don't enter any info on “Email and Web send” tab in EurekaLog's options. That way, all exceptions will be stored in single elf-file (which is specified in options). You can just pick it up at any time you want and do whatever you like with it. Note, that since you now storing entire database of all your bug-reports in single file – you may want to increase maximum errors count:

Q: But I actually prefer saving log-file and screenshot in, say, MySQL database. How can I do this?

A: Just use SaveScreenshot routine (or SaveScreenshotToStream) to save screenshot. And your log is already here – in log output path.

Q: How can I get log-file location at run-time?

A: Use this code:
  ExceptionLog, ECore;
  LogFileName := ExpandEnvVars(CurrentEurekaLogOptions.OutputLogFile(''));

Q: Where should I put my code for saving log into my own database (i.e. SaveScreenshot, etc)?

A: Probably, atSavedLogFile event in ExceptionActionNotify event handler would be a good place.

Q: I use XXX 3rd party component. I do not have source code. Sometimes it raises Access Violation. This is a bug in component. And there is no fix for it. I just want to ignore this particular exception. Is this possible?

A: Sure, it is possible. But only if you have some way to tell how this exception is different from any other Access Violation exception. For example, you can look at exception's address. If component resides in DLL (for example: ActiveX) – you can check if this address belongs to component's DLL. If component resides in your application (i.e. the usual component with set of dcu files) – you can check if this address belongs to component's unit. It is possible only if debug information is available for this address. For example:
procedure MyExceptionNofity(EurekaExceptionRecord: TEurekaExceptionRecord; var Handled: Boolean);
  Addr: Pointer;
  Module: HINST;
  DebugInfo: TEurekaDebugInfo;
  Addr := EurekaExceptionRecord.ExceptionAddress;

  Module := FindHInstance(Addr);

  Handled := True;
  if AnsiLowerCase(ExtractFileName(GetModuleName(Module))) = 'module.dll' then
    if GetSourceInfoByAddr(Cardinal(Addr), @DebugInfo) then
      Handled := (DebugInfo.ProcedureName <> 'RoutineName');

  ExceptionNotify := MyExceptionNofity;
Where: 'module.dll' is DLL's or exe's name, which contains your component. And 'RoutineName' (and may be ClassName field too) identifies location for code, which raised exception.

Note, if the debug information is not available for the components - you won't be able to get textual description for location. May be it would be a good idea to offload all problematic code to separate DLL library, so you can make checks by looking at module name only.

Of course, you can always do some dumb checks like:
Execute := (EurekaExceptionRecord.ExceptionAddress <> $12345678);
Where: $12345678 is address of your exception (for example, it's listed in “at address 12345678” part of message for Access Violation).

But this means that any single change in your application may break this check (if you change your code – this may change exception's address as well). Use it only if there is no other way. And don't forget to test/adjust address before releasing application.

Q: How can I localize EurekaLog's messages?

A: In the simplest case – just go to “Message Texts” tab. Translate messages to desired language. You may also save translation into collection to use in the another project. Scan our forum before doing your own translation – there are published translations done by our customers.

You can also switch languages in run-time by using LoadCustomizedTextsFromFile method of TEurekaModuleOptions class, like this:
  CurrentEurekaLogOptions.LoadCustomizedTextsFromFile(ExtractFilePath(Application.ExeName)  + 'Languages\SomeLang.etf');
etf-file is collection of messages, which you can save on “Message Texts” tab. You can find saved collections in a %AppData%\EurekaLog\ folder.

If you want to use some 3rd party translation tool (like TsiLang or dxGetText) or ITE – there is no direct support for this in EurekaLog v6 (it's planned for v7). But you can use manual workaround like this:
  CurrentEurekaLogOptions.CustomizedTexts[mtInformationMsgCaption] := 'Some  message from translation engine';
  … // other mtXXX constants here.