EurekaLog
Menu

The EurekaLog Developer Guide

25. Working with EurekaLog's settings

EurekaLog's options are not something your program reads at startup from a file next to the executable. They are decided while the project is built: they live in an .eof settings file, the build's post-processing step loads them and adds them to the executable, and from then on they are part of the binary. What a developer can do with them afterwards is narrower than it looks - read them, change a documented subset of them at run time, and read back custom keys of your own - and knowing which file a given build actually used is often the whole answer to "why did it behave differently on the build machine".

If IDE and build-machine executables behave differently, follow the file-selection rules in section 25.3 before changing runtime code. If you want reproducible shared settings, use section 25.4; if project files must stay read-only, use section 25.6. Chapter 2 connects these choices to compiler prerequisites, post-processing and deployment. Chapter 1 provides the first-report walkthrough.

25.1 Where the options live

EurekaLog options are stored in a UTF-8 text file in INI format, under [Exception Log]. For example, this deliberately short file changes one tooling option and adds an application key; it is not a complete enabled-project configuration:

[Exception Log]
DeleteMapAfterCompile=0
Custom_ReportLabel="Desktop"

Several facts about that file matter in practice:

  • The file that belongs to a project is named after the project. EurekaLog stores the project's base settings in an .eof file with the same name as the project file: a Project1.dpr gets a Project1.eof. That file is used by default in all cases - compiling from the IDE and compiling from the command line alike.
  • Base settings are a delta. When EurekaLog saves the base settings of a project it writes only the options whose values differ from the defaults. It writes the full set of options only when you export options to external settings. So a hand-edited Project1.eof is a list of departures from the defaults, not a complete picture of what the executable will do.
  • Some options never leave the IDE. Options whose names start with _ are not saved into the executable: they are design-time only, stored in the project options but not injected into the final binary. They are the settings you use to steer the tooling, not the running program.
  • Some options are only for the tooling in another sense: the DoNotTouch family described in section 25.6, which tells EurekaLog how much it is allowed to modify in your project.

.eof files are just renamed text ini-files, and they can be obtained in several ways: by using the "Export" button in the project options dialog, by saving settings to a new file in the external settings editor, by creating and editing one manually in any text editor, or by copying another .eof file. When you export, EurekaLog writes the complete set of options, which is what makes an exported file useful as a profile you can re-import as a starting point.

Exported profiles have a default home: %AppData%\Neos Eureka S.r.l\EurekaLog\Profiles\ (for example C:\Users\UserName\AppData\Roaming\Neos Eureka S.r.l\EurekaLog\Profiles). Any .eof file placed there appears as a "custom" project type in EurekaLog's project-type list; .eof files outside that location do not appear there at all. Apart from that folder, a typical place to keep an external .eof is the project's own folder or a sub-folder of it. That freedom belongs to external files, which are named explicitly; the per-build configuration files of section 25.3 have to sit in the same folder as the project file.

Use the project options or standalone settings editor for ordinary changes. Reserve manual edits for custom keys and settings with no UI, such as DoNotTouch. Export a configured project rather than trying to build a complete enabled configuration by guessing keys and numeric enum values.

25.2 The syntax of a settings file

The rules the documentation gives for a settings file are short but complete, and they are what you need if you generate or parse one:

  • Each option has the form Name=Value.
  • Each option occupies a single line only; line breaks inside text parameters are allowed through encoding.
  • Integer values are stored directly.
  • Boolean values are stored as 0 (False) or 1 (True).
  • Float values are stored as fixed-point values, and the decimal delimiter is always . - for example 12.345.
  • Enumeration values are stored as integers, not as text.
  • String values are enclosed in double quotes, which is what allows spaces and line breaks inside them. An empty string may be stored as two double quotes ("") or as an empty parameter.

Inside strings, four characters need escaping, and non-ASCII text has a recommended encoding:

\q        a double quote character ("), inside a quoted string
\r\n      a line break
\\        a backslash character
%%        a percent character
%U<hex>   a Unicode character, e.g. %U044F for #$44F (Cyrillic small letter Ya)

While non-ASCII symbols can be stored "as is", the documentation strongly recommends encoding them as %U<hex-code>.

Three more rules that are easy to get wrong:

  • Sort order is a convenience, not a requirement. Options are sorted in the files EurekaLog writes, but new options can be entered and placed in any order, and there is no need to preserve the sort order.
  • There is no predefined list of allowed option names. The file is a key/value store as far as the syntax is concerned.
  • There is a reserved convention for your own keys. Use a Custom_ (or My_) prefix: EurekaLog will never have an option name starting with Custom_, so your names cannot collide with its settings.

Read your custom keys with CurrentEurekaLogOptions:

uses
  EBase, EModules;

function ReportLabel: String;
begin
  Result := '';
  if IsEurekaLogActive then
    Result := CurrentEurekaLogOptions.CustomField['Custom_ReportLabel'];
end;

This function belongs in the reporting executable or standalone module, not a lightweight DLL. Use CurrentEurekaLogOptions, declared in EModules; do not substitute the spelling CurrentEurekaModuleOptions.

25.3 Which file a build actually uses

There is a documented resolution order, and it is the part of this chapter you are most likely to need when two builds of "the same" project behave differently.

  1. Base settings. <ProjectName>.eof next to the project file - Project1.eof for Project1.dpr. Used by default in all cases.
  2. Per-build-configuration settings. You may create individual settings for each build configuration, and the expected naming convention is <ProjectName>_<BuildConfigurationName>.eof. For Project1.dpr, building in the "Debug" build configuration makes EurekaLog use Project1_Debug.eof; switching to "Release" and building again makes it use Project1_Release.eof.
  3. Fallback. If the specific configuration file does not exist for the current build configuration, EurekaLog falls back to the base settings in <ProjectName>.eof.

The documented way to produce a per-configuration file is to configure EurekaLog for that configuration, save it with Export into <ProjectName>_<BuildConfigurationName>.eof in the project's folder, and then close the dialog with Cancel instead of OK: saving would write those temporary changes into the project's own .eof and turn them into the base settings. If you did not create a file for some configuration, the base settings have to be checked afterwards.

Two rules qualify that sequence:

  • There is no inheritance support in EurekaLog's settings. A Project1_Debug.eof is not a patch applied on top of Project1.eof: it is the whole settings file for that build. The fallback in step 3 is all-or-nothing. (The documentation states this flatly in more than one place, including for project groups.)
  • A full rebuild is required after switching the active build configuration - not just a compile. For a change of target platform the same advice is given as a recommendation rather than a requirement.

When you drive the build yourself, the IDE variables that name the configuration and the platform are the documented way to pick the right file, since they expand to the build configuration name (such as Debug and Release) and to the short platform name (such as Win32, Win64, OSX). A file name like Project1_Win32_Debug.eof can therefore be selected with a switch such as --el_config=Project1_$(Platform)_$(Config).eof.

The symbol that decides whether anything happens at all

Before any of that file resolution matters, post-processing checks one thing: whether the EUREKALOG conditional symbol is defined in the project. EurekaLog's own units are included in the program's uses clause under that symbol - the documented example guards EMemLeaks, EResLeaks, the dialogs, EDebugExports, EFixSafeCallException, EMapWin32, EAppVCL and ExceptionLog7 with {$IFDEF EUREKALOG}. If the symbol is not defined, post-processing is skipped.

Do not remove EUREKALOG_VER7 - only EUREKALOG. And mind whose inheritance is whose: build configuration settings in the IDE are inherited, so a symbol enabled in the "Base" configuration reaches every configuration below it. Where EurekaLog is wanted in some configurations and not in others, the documented procedure is to remove EUREKALOG from the base configuration first and then enable it in the specific configurations you want, enabling EurekaLog only on "leaf" configurations, or to disable the inheritance for the symbol manually. If you build with a configuration in which the symbol is not defined, EurekaLog detects that and notifies you - either switch to a configuration that has it, or define the symbol for the current one.

This, then, is the difference between the two kinds of "inheritance" you meet here: the symbol is inherited through the IDE's build configurations like any other define; the settings are not inherited at all.

25.4 External settings, and sharing them

Sometimes the settings should not live next to the project. The documented reasons: sharing common settings between several projects, or having no Delphi/C++ Builder project at all - for example when you need EurekaLog settings for a DLL built by another compiler. That is what external settings are: a standalone .eof file referenced from the project instead of the project's own.

How the reference is made, in the order the documentation presents the methods:

  • From the project options: the path is specified as relative to the project's folder. The .eof file name can be arbitrary - it need not match the project's name, and it need not sit in the same folder.
  • From the command-line compiler, with the --el_config switch. Note the interaction the documentation points out: ecc32/ecc32speed respect the "use external settings" option recorded in the project, so specifying the file once in the project options is enough; you can still pass --el_config yourself if you want to.
  • Without the IDE expert installed, you can still work: no post-processing happens from the IDE, but the project's own .eof file is left untouched rather than deleted, so you can edit it with the external settings editor or a plain text editor, and it will be used automatically if you make manual post-processing.

Taking control of the pre-build and post-build steps

First establish a working report using chapter 1, then export the working options to a separately named file such as Project1_Custom_Config.eof. Add DoNotTouch=1 to the project's own options on Advanced / Custom/Manual and save; do not add it to that exported configuration. You now own the integration that the IDE expert previously maintained.

For an IDE/MSBuild build, enter this as the pre-build event, on one physical line:

"C:\Tools\EurekaLog\Packages\Studio16\ecc32.exe" "--el_prepare=$(PROJECTPATH)" "--el_config=Project1_Custom_Config.eof"

Enter this as the post-build event, after successful compilation, also on one physical line:

"C:\Tools\EurekaLog\Packages\Studio16\ecc32.exe" "--el_alter_exe=$(PROJECTPATH);$(OUTPUTPATH)" "--el_config=Project1_Custom_Config.eof"

Replace C:\Tools\EurekaLog with the installation directory and Studio16 with the package directory matching the IDE that builds this project; it is an example, not a universal compiler. Use the IDE mapping and tool selection in chapter 2. Replace the configuration filename with your exported file. Keep the quoted switches intact: the semicolon separates the project path from the actual output executable path. $(OUTPUTPATH) matters when configurations or platforms have different output directories. Do not replace it with a guessed project-root EXE.

Do not prefix these commands with IF EXIST: a missing postprocessor must be a build failure, not a silently unprocessed release. Make the build fail on a nonzero event exit code. Inspect the build output for both commands and the completed post-processing stage, not just the Delphi compiler's success.

--el_prepare handles required units, but you must set the required compiler/debug-information options yourself for every enabled configuration (chapter 2). Ensure the events are inherited by, and not overridden in, each applicable configuration. For per-platform files you can use "--el_config=Project1_$(Platform)_$(Config).eof" in both commands.

These are IDE/MSBuild events: invoking dcc32 directly does not run them. A direct-compiler script must perform preparation and post-processing explicitly, or use the EurekaLog command-line build route in chapter 2. Rebuild and exercise the produced executable for each supported configuration; a successful compile alone does not establish that it contains the intended settings.

Sharing inside a project group: two options, one conclusion

EurekaLog offers two documented ways to give a group of projects the same settings, and they differ in whether later changes propagate.

  • By value. Configure one project, then import its .eof into every other project of the logical group (or copy Project1.eof over Project2.eof and so on). All the projects end up with exactly the same settings, but they are isolated: a later change in one project does not affect the others, and to change the group you must edit one project and repeat the exercise.
  • By reference. Configure one project as the master, save its settings to an .eof, and switch "Use external settings" on in every other project of the group, pointing at that file. All projects now read the common file, so editing the master's options adjusts the whole group. The documented price is that the .eof files must not be deleted - they are required. A project that should differ from the group is taken out of the arrangement by unchecking "Use external settings", importing the .eof, and adjusting it locally. The documented shortcut is to set up "Use external settings" in one project and copy that project's .eof - the file that carries the reference - to the others; it works only when all of the projects are in the same folder.

Both procedures come with the same design advice: sharing settings blindly between projects is not a good idea, because projects of different types - VCL application, DLL, packages, service - need slightly or significantly different settings. Group your projects logically (one group for the main executable, one for DLLs, one for packages), and create one settings file per type. The same advice appears for external settings: using the same settings for different project types is usually not a good idea; if you want to copy settings between projects, use import/export.

And the conclusion that matters when you are debugging a settings puzzle: there is no inheritance.

When the resolution goes wrong

Three documented failure modes, all worth remembering because they look like EurekaLog misbehaving when they are really a settings file being wrong:

  • Batch builds and external build tools. The warnings in these articles repeat one sentence: be extra careful when doing batch compilation of projects in project groups or when using external build tools, because IDE bugs and other issues may pass incorrect information to EurekaLog (via the OpenTools API, in the IDE's case). Pay close attention to which settings file the build tool hands to the compilers, and double-check the final executables. A per-configuration .eof that was never picked up, or the wrong one being picked up, is exactly the kind of thing this warning is about.
  • Removing and re-adding EurekaLog to the project. The documentation calls this out as a wrong way to switch EurekaLog off for a build: the reset removes EurekaLog from the project, and in doing so erases all of its existing options, so the next "add" starts from the defaults. If the project's options were customised at all, they are lost. Use the EUREKALOG symbol or run-time switching instead.
  • Read-only project files. Setting .dpr/.dproj to read-only - a common marker for version control - prevents EurekaLog from working properly, because it needs to modify the project to include its units and to adjust options. Worth knowing: EurekaLog only tries to modify your project files when the project does not match its configuration (map file generation disabled, uses clause missing the EurekaLog units, and so on); if there is no need to change anything it will not write. That is why a "file is read-only" error is a useful signal in itself: the configuration changed and the project needs adjusting.

25.5 What can be changed at run time

Everything above is build-time. At run time the documentation offers these levers, and it makes the cost of using them plain: run-time customisation still pays the compilation and startup penalty of having EurekaLog in the build. If performance is the reason you are switching things off, turning them off at run time does not remove that cost.

For example, a diagnostic launch mode may disable EurekaLog at startup:

uses
  SysUtils, EBase;

procedure ConfigureDiagnosticLaunch;
begin
  if FindCmdLineSwitch('disable-reporting', True) then
    SetEurekaLogState(False);
end;

Call this once after startup has selected the launch mode and before starting normal work. Treat this switch as an application policy decision: it prevents reporting while disabled. SetEurekaLogState, declared in EBase, returns the previous global state. When temporarily changing the state, save that return value and restore it in a finally block instead of unconditionally enabling reporting. SetEurekaLogStateInThread is the per-thread control; IsEurekaLogActive reports the global state (chapter 10). Note the asymmetry with the build-time approach: switching the symbol off removes the code and its cost, switching EurekaLog off at run time does not.

To change just the reporting destination, dialog, saving or sending policy, change the live options at startup. Section 17.3 gives the service/GUI setup, including the feature-in-build prerequisites. For a local diagnostic build that must save reports without sending them:

uses
  EBase, ETypes, EModules;

procedure KeepReportsLocal;
begin
  if not IsEurekaLogActive then
    Exit;
  CurrentEurekaLogOptions.SaveLogFile := True;
  CurrentEurekaLogOptions.SenderClasses := esmNoSend;
end;

The configured output folder still needs to be writable by the process account. This changes defaults for future exceptions, not already-raised ones; use the current exception's Options inside an event handler (section 17.4 and section 24.3). Sender destination setup and real-send verification are in chapter 3. Memory and leak control use EMemLeaks rather than these properties (chapter 12).

If performance is the reason for changing settings, measure the enabled features rather than disabling the entire extended memory manager by reflex. Allocation stack collection for leak tracking can be a substantial cost. A documented configuration to try is to keep Enable extended memory manager enabled while disabling Catch memory leaks and, if appropriate, other memory-debugging features. This is not a universal performance guarantee: workload, bitness and enabled checks matter (chapters 22 and 5).

The distinction has a correctness consequence too: EurekaLog uses its memory-manager filter to track exception-object lifetimes. In the documented worst case, Delphi 2007 or earlier with both the extended memory manager and low-level hooks disabled cannot track those lifetimes.

For the question "is EurekaLog present in this build at all?", the documentation points at one unit as the deliberate answer: the EBase unit is described as specially designed to be included in any application without including the full EurekaLog code, and as the way to test whether EurekaLog was enabled for your application. Its answer is a run-time one - IsEurekaLogInstalled - with the {$IFDEF EUREKALOG} conditional of section 25.3 as the compilation-time alternative.

25.6 Leaving the project alone: the DoNotTouch family

The settings file also carries the switches that decide how much EurekaLog is allowed to modify your project. They are ordinary options, written under [Exception Log]. The trio that keeps automatic post-processing going while EurekaLog leaves the project files alone is DoNotTouchOptions, DoNotTouchUnits and DoNotTouchPackages; DoNotTouch disables all EurekaLog assist for the project, and DoNotTouchCompilation disables the post-processing assist:

Option Documented effect
DoNotTouch Completely disables EurekaLog assist for the project.
DoNotTouchCompilation Disables post-processing assist. Documented for when you intend to invoke ecc32/ecc32speed manually.
DoNotTouchOptions Disables modification of project options and defines. For when you manage project options manually.
DoNotTouchUnits Disables modification of units in the uses clause. For when you manage your units manually.
DoNotTouchPackages Disables modifications in the run-time packages list.

The documented use for the whole family is an initial setup followed by a lock-down:

  1. Configure EurekaLog in the project once, with the project files writable. This step sets the project options, includes the EurekaLog units in the application, and stores EurekaLog's options with the project.
  2. Turn the assist off - DoNotTouchOptions=1, DoNotTouchUnits=1, DoNotTouchPackages=1 - and save. EurekaLog will no longer try to modify project options or the list of units.
  3. Restore the read-only attribute on the project files. The project can be read-only and EurekaLog works; it post-processes the project on each compilation using the currently set options.

The options are entered as plain lines in the [Exception Log] section:

[Exception Log]

DoNotTouchOptions=1
DoNotTouchUnits=1
DoNotTouchPackages=1

The same mechanism has an inverted use. If you keep EurekaLog's IDE expert installed but have non-EurekaLog projects whose files are read-only, EurekaLog may try to remove itself from them and fail because the files cannot be written. The documented fix is to create an empty .eof text file with the same name as the project, containing:

[Exception Log]

DoNotTouch=1

Save it, and the project is skipped.

25.7 The units that expose the configuration

Chapter 17 supplies the callable contracts and examples for these units; appendices A and D provide the lookup reference. Use this section to choose the right layer, not as a list of statements to paste into your program.

EConfig - the configuration storage

EConfig is the layer that reads and writes EurekaLog's configuration and knows whether that configuration lives in the registry or in an .ini file. By default it is the registry, under the general EurekaLog key (\Software\EurekaLab\EurekaLog\7.0); SetupConfig(AUseRegistry, AKeyPathOrFileName) changes that, and it is meant to be called once at application startup, preferably in a unit's initialization section. The typed readers are wrappers around ConfigReadString, and the typed writers around ConfigWriteString, so the string routines carry the real work.

// location and identity of the configuration store
ConfigFileName       // returns .ini-file name to store EurekaLog configuration
ConfigIsRegistry     // indicates if EurekaLog configuration is stored in the registry or in .ini-file
ConfigKeyPath        // returns registry key name to store EurekaLog configuration
SetupConfig          // changes EurekaLog configuration storage

// typed access to a key in a section
ConfigReadBool       // reads boolean value from specified key and section
ConfigReadFloat      // reads float value from specified key and section
ConfigReadInteger    // reads integer value from specified key and section
ConfigReadString     // reads string value from specified key and section
ConfigWriteBool      // writes boolean value to specified key and section
ConfigWriteFloat     // writes float value to specified key and section
ConfigWriteInteger   // writes integer value to specified key and section
ConfigWriteString    // writes string value to specified key and section
ConfigDelete         // deletes a key from the specified section

// encoding of values in the store
EncodeConfigValue    // low-level routine to encode data before saving to configuration storage
DecodeConfigValue    // low-level routine to decode data after reading from configuration storage

// raw access to the two backing stores
IniReadString        // reads string from .ini file
IniWriteString       // writes string to .ini file
RegKeyCheck          // checks registry key for existing
RegKeyRead           // reads string from registry key
RegKeyWrite          // writes string to registry key
ReadRawStrings       // backward-compatibility routine for the raw store
WriteRawStrings      // backward-compatibility routine for the raw store

The pair EncodeConfigValue/DecodeConfigValue is the code-side counterpart of the escaping rules in section 25.2: it escapes the special characters a value contains before it is stored - the documentation names #0, line breaks and trailing spaces - and reverses that on the way back, with the exact encoded form left unspecified. The typed writers already perform that encoding, so this pair is there for implementing your own high-level wrapper; use the type specific readers (ConfigReadBool and friends) rather than the raw ones unless you are working with the store as text.

EModules - options and module facts

Need Routine
Change defaults for future exceptions in the current reporting module CurrentEurekaLogOptions
Describe the executable GetApplicationName, GetApplicationDescription
Identify the module containing an exception address GetExceptionModuleName, GetExceptionModuleVersion
Obtain the module's post-processing/compilation time GetCompilationDate, GetCurrentModuleCompilationDate
Format the project's compilation date or time as text __DATE__, __TIME__

Call the options accessor only when EurekaLog is active; an invalid call displays an error and terminates the process. The module-information routines are a different group: they can be useful without an active tracer. Their empty-result and date-fallback rules are in section 17.6.