exewatch-delphi
Skill23 starsChanged 7 days agoApache-2.0View on GitHub
Use when adding, changing or debugging ExeWatch monitoring in a Delphi app - the ExeWatchSDKv1 unit, TExeWatch, InitializeExeWatch, remote logging, crash reporting, stack trace in production, application monitoring, telemetry (timings, counters, gauges, breadcrumbs), offline buffering, the map file, ExeWatchMapCLI. Triggers on "ExeWatch", "ExeWatchSDKv1", "TExeWatch", "application monitoring", "remote logging", "crash reporting", "stack trace in production", "telemetry in a Delphi app".
- Reads credentials
In this repository15 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- delphi-buildSkill
- delphi-code-smellsSkill
- delphi-firedacSkill
- delphi-securitySkill
- delphiSkill
- delphi-testingSkill
- dmvcframework-jsonrpcSkill
- dmvcframework-minimal-apiSkill
- dmvcframework-securitySkill
- dmvcframeworkSkill
- dmvcframework-testingSkill
- dmvcframework-uiSkill
- dmvcframework-webappSkill
- htmx-skillSkill
The file
Files
ExeWatch Delphi SDK
ExeWatch (bit Time Professionals) collects logs, crashes, timings and metrics from an application. The
Delphi SDK is plain source: ExeWatchSDKv1.pas plus ExeWatchSDKv1.VCL.pas / ExeWatchSDKv1.FMX.pas.
Verified against Delphi SDK 0.25.0 (EXEWATCH_SDK_VERSION in ExeWatchSDKv1.pas), ExeWatch product
1.90.1 (2026-11-05). Compiled on Delphi 13, 12 and XE8 (Win32), 13 (Win64, Linux64); run on 13 (Win32,
Win64) and XE8 (Win32), against a local HTTP listener only.
Before writing code, compare the project's version (grep EXEWATCH_SDK_VERSION ExeWatchSDKv1.pas in the
project or in %EXEWATCH_SDK%). If it is not 0.25.0, read the interface section of that copy and check
every name below against it. The shipped INTEGRATION_GUIDE.md and the docs are partly stale: where they
disagree with the code, the code wins (table in reference/api.md).
Critical rules - each one costs data or a day
EWraises beforeInitializeExeWatchand afterFinalizeExeWatch(Exception: ExeWatch not initialized). Code that can run at start-up or in afinalizationsection:if ExeWatchIsInitialized then.- An empty customer id keeps every event in memory. Nothing reaches disk, nothing is sent; if the
process ends before
EW.SetCustomerId('...'), those events are gone. Pass a non-empty id (even a placeholder) unless the real one arrives within seconds. - Initialize once per process, at start-up. Never per request, per form or per thread: a second
InitializeExeWatchdestroys the first instance (its shutdown makes one send attempt) and starts new threads. - The SDK owns every
TJSONObjectyou hand it (Logextra data,AddBreadcrumbdata,StartTimingmetadata,EndTimingend metadata), also when the call is filtered out. Do not free it, do not reuse it: freeing it is a double free (a leak test with and without the free confirms ownership). - GUI app: add exactly one hook unit (
ExeWatchSDKv1.VCLor.FMX) afterExeWatchSDKv1. Without it exceptions raised in event handlers are never reported; the SDK only logs a Warning (tagexewatch). Service, console, DLL: add neither. - A stack trace is only readable with symbols. Detailed map file + stack frames, linked into the exe.
Without any map a frame reads
MyApp.exe!__dbk_fcall_wrapper+0x1B7FFB: a real name, the wrong one. Details inreference/stacktraces.md. - Exceptions that escape a
TThreadare not reported (no hook sees them). Catch inExecuteand callEW.ErrorWithException. - Callbacks run on SDK threads:
OnError,OnLogsSent,OnDeviceInfoSent, the listener'sOnExeWatchLogsSent/OnExeWatchErrorand gauge callbacks. No VCL/FMX access;TThread.Queueinstead. - Breadcrumbs and timings are per thread.
EndTimingon another thread finds nothing and returns -1. On pooled server threads clear breadcrumbs at the start of each request. - Never log secrets or personal data (passwords, tokens, card numbers, full request bodies) in a message, tag, extra data, user, device info. Queued files are plain JSON on disk, and the device info carries the full command line, paths and local IPs. The API key is configuration: keep it out of public repos.
ReportMemoryLeaksOnShutdown := Trueprints SDK-internal leaks (TSTExportCache, thousands ofUnicodeString) once any exception was raised. Win32 and Win64. They are the SDK's symbol cache, not your code: do not "fix" them by freeing things.
Which unit declares what
| Identifier | Unit |
InitializeExeWatch, FinalizeExeWatch, EW, ExeWatch, ExeWatchIsInitialized, TExeWatch, TExeWatchConfig, TEWLogLevel, TBreadcrumbType, TUserIdentity, IExeWatchClientListener, TExeWatchHelper, EXEWATCH_SDK_VERSION | ExeWatchSDKv1 |
(no identifiers; installs the GUI exception hook in its initialization) | ExeWatchSDKv1.VCL or ExeWatchSDKv1.FMX |
TJSONObject for extra data | System.JSON |
TPair<string, string> for GlobalTags, InitialCustomDeviceInfo | System.Generics.Collections |
DLL route: EWInitialize, EWInfo, ew_Shutdown | ExeWatchSDKv1Imports |
Requirements: Delphi XE8 or later, Windows Vista or later, HTTPS (TLS 1.2) to the endpoint. EXEWATCH_SDK
is set by the RAD Studio setup and points at the SDK folder; add it to the Library Path, or copy the units
into the project.
The four questions
1. Start-up and shut-down (VCL; compiled on Delphi 13):
program MyApp;
uses
Vcl.Forms,
ExeWatchSDKv1,
ExeWatchSDKv1.VCL,
MainFormU in 'MainFormU.pas' {MainForm};
begin
InitializeExeWatch('ew_win_XXXXXXXX', 'ACME', '1.2.3'); // api key, customer id, release label
try
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
finally
FinalizeExeWatch; // ships what is left; the unit's finalization does it too
end;
end.The key must start ew_win_, ew_lin_, ew_mac_, ew_and_, ew_ios_ (or old ew_desk_); ew_web_ and any
other prefix raise Exception from InitializeExeWatch. For more settings build a TExeWatchConfig
(TExeWatchConfig.Create(Key, Customer), set fields, InitializeExeWatch(Cfg)): reference/api.md.
The RAD Studio menu Project > ExeWatch > Enable ExeWatch Telemetry generates ExeWatchConfig.pas
(StartExeWatch, SetExeWatchCustomer, FlushExeWatch; key constant EXEWATCH_API_KEY) and wires
StartExeWatch('customer-name') and FlushExeWatch into the .dpr. If a project has that unit, edit it; do
not add a second InitializeExeWatch.
2. Error log, exception, user identity:
EW.Error('Payment refused', 'billing'); // message, tag (default tag 'main')
EW.Error('Order %d failed for %s', [Id, Name], 'orders');
try
Save;
except
on E: Exception do
begin
EW.ErrorWithException(E, 'billing', 'Save failed'); // message becomes 'Save failed: <E.Message>'
raise;
end;
end;
EW.SetUser('u-42', 'jane@example.com', 'Jane Doe'); // on every later event; EW.ClearUser on logoutError and Fatal get a stack trace automatically, plus the thread's breadcrumbs. Levels:
Debug Info Warning Error Fatal; every one has a (Msg, Tag) and a (Format, Args, Tag) form.
ErrorWithException keeps the trace taken where the exception was raised, even if you catch it far away.
Identity, tags, customer: reference/api.md.
3. Unhandled exceptions with unit, function and line (32-bit VCL). Uses: ExeWatchSDKv1 +
ExeWatchSDKv1.VCL (above): the first catches what reaches ExceptProc (Fatal, Unhandled exception: ...),
the second what the VCL loop swallows (Error, GUI exception: ..., tag exception). Then produce and link
the symbols:
- Project Options > Delphi Compiler > Linking > Map file = Detailed (
dcc32 -GD), Stack frames on (-$W+), debug information on. Without stack frames a Win32 trace silently drops frames. - Link the map into the exe:
ExeWatchMapCLI MyApp.exe MyApp.map --delete-mapafter each build (before code signing), or the IDE menu ExeWatch > Link map file. Result in the event:symbol_source: "embedded". A.mapleft beside the exe works only where it sits (symbol_source: "map_file"). - Win64 needs no stack frames; Win32 does.
reference/stacktraces.mdhas the matrix and the CLI.
4. Network down. EW.Info never waits for the network. Events go to a memory buffer; a background
thread writes .ewlog JSON files to %LOCALAPPDATA%\ExeWatch\pending\<first 16 chars of the key>\ and
uploads them oldest first. On any failure except HTTP 422/429 the file is kept and retried after 30 s
(RetryIntervalMs), for ever, until it is 7 days old (MaxPendingAgeDays) and purged. After a restart the
leftovers are sent. An Error or Fatal is written to disk at once (at most one such write per second);
ordinary events within 5 s (FlushIntervalMs) or 100 events (BufferSize); a hard kill can lose what was
still in memory. Full rules:
reference/offline-and-config.md.
Reference index
| Open this | When |
reference/api.md | exact signatures: config fields and defaults, every TExeWatch method, tags, identity, breadcrumbs, timings and traces, metrics, device info, listener; the "guide vs code" table |
reference/stacktraces.md | empty or useless stack traces, 32 vs 64 bit, map file levels, ExeWatchMapCLI, IDE menu, symbol_source, no_stacktrace_reason |
reference/offline-and-config.md | buffering, file layout, retry by HTTP status, purge, sampling, server-provided config, shutdown time, StoragePath, GDPR |
reference/server-and-targets.md | web server, service, per-request pattern, one instance per tenant, DMVC, WebBroker, DLL route, packages, Linux, Android, FMX |
For DelphiMVCFramework servers, load the dmvcframework skill for controllers, filters and the exception
handler; this skill covers only the ExeWatch calls.
What not to do
InitializeExeWatchin a request handler, a formOnShowor a thread.- Logging in a tight loop "to see progress": every event costs quota and disk. Count with
IncrementCounterinstead. Cfg.Endpoint := ...left in committed code; definingLOCAL_EXEWATCH(it points the default endpoint at an internal address).- Reading
EW.Configto change settings: it returns a copy. Set the config before initializing. - Calling
EW.Shutdownyourself: it leavesExeWatchIsInitializedTrue. UseFinalizeExeWatch. - Trusting a trace with no map: ask for the map, do not read the offsets.
Not verified
Android, macOS and iOS targets (no compiler here), FMX at run time (it compiles on Win32), the DLL SDK at run time (its API has no endpoint override, so any run would call the real service), the IDE menus (read from the IDE source, not driven), the server's replace-or-merge behaviour for custom device info, the hourly purge, behaviour on a network that drops packets instead of refusing them.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

