Core Concepts
Before integrating ExeWatch, it's important to understand how data is organized in the platform.
Application
An Application represents your software product. For each app you want to monitor, you create one Application in ExeWatch.
Examples:
- "MyDesktopApp"
- "CustomerPortal"
- "BackOffice"
Customer
A Customer is your end-user or client company. If you sell software to multiple businesses, each one is a separate Customer.
Examples:
- "ACME Corporation"
- "Smith & Associates"
- "john@example.com"
Device
A Device is a single installation of your app. Each computer, server, or browser where your app runs is a Device.
Examples:
- "john@WORKSTATION-01"
- "admin@SERVER-PROD"
- Browser fingerprint
Data Hierarchy
Each Application has multiple Customers. Each Customer has multiple Devices. Each Device sends Log Events.
Quick Start
Get up and running in 5 minutes with these simple steps.
Step-by-Step Setup
Create an Account
Register for free - no credit card required.
Create an Application
In the dashboard, click "New Application" and give it a name.
Get Your API Key
Click "API Keys" to see your key and download the integration code.
Integrate the SDK
Follow the guide below for your platform (Delphi or JavaScript). Check out the official sample projects for ready-to-run demos.
Start Monitoring!
Run your app and watch the logs appear in real-time.
What You'll Need
- An ExeWatch account (free tier available)
- Your application source code
- For Delphi: Delphi 10 Seattle or later
- For JavaScript: Any modern browser
- 5 minutes of your time
Delphi SDK Integration
Complete guide to integrating ExeWatch into your Delphi applications.
Step 1 Download SDK for Your App Type
Go to your Application's API Keys page to get your API Key and download the SDK. Choose the files based on your application type:
Console / Service / Web Server
Servers, CLI tools, background services
ExeWatchSDKv1.pas
VCL GUI Application (both files)
Windows desktop apps with VCL
ExeWatchSDKv1.pas
+ ExeWatchSDKv1.VCL.pas
For FMX/Mobile apps (Android, iOS), see the Mobile SDK section or create a mobile platform application.
Step 2 Initialize at Startup
Call InitializeExeWatch once when your application starts.
The SDK works with any type of Delphi application: VCL, FMX, console, web servers, Windows services.
Recommended approach: Initialize early with empty customer ID, then set it after reading from config/license file. This ensures you capture startup logs even before knowing the customer.
uses
ExeWatchSDKv1; // + ExeWatchSDKv1.VCL for VCL GUI apps
begin
// Initialize early with empty customer ID
InitializeExeWatch('ew_win_your_api_key', '');
EW.Info('Application starting...', 'Startup');
// Later, after reading license/config file:
CustomerId := LoadCustomerIdFromLicense(); // your function
EW.SetCustomerId(CustomerId);
EW.Info('License validated for: ' + CustomerId, 'License');
end.
For console applications, batch processors, or CLI tools.
program MyConsoleApp;
{$APPTYPE CONSOLE}
uses
SysUtils,
ExeWatchSDKv1;
begin
try
InitializeExeWatch('ew_win_your_api_key', '');
// Read customer from command line or config
if ParamCount >= 1 then
EW.SetCustomerId(ParamStr(1));
EW.Info('Processing started', 'Main');
// Your application logic here...
EW.Info('Processing completed', 'Main');
except
on E: Exception do
EW.Fatal(E.Message, 'Main');
end;
end.
For web applications (DMVCFramework, Horse, mORMot, etc.). Initialize once at server startup.
program MyWebServer;
{$APPTYPE CONSOLE}
uses
MVCFramework.Console,
Web.WebReq,
ExeWatchSDKv1,
WebModuleU in 'WebModuleU.pas';
begin
// Initialize ExeWatch - customer ID is your server/instance name
InitializeExeWatch('ew_lin_your_api_key', 'production-server-01');
EW.Info('Web server starting on port 8080', 'Startup');
RunServer(8080);
end.
// In your controller:
procedure TMyController.GetCustomers;
begin
EW.StartTiming('get_customers', 'api');
try
// Your logic...
Render(CustomerService.GetAll);
EW.EndTiming('get_customers');
except
on E: Exception do
begin
EW.EndTiming('get_customers',
TJSONObject.Create(TJSONPair.Create('error', E.Message)), False);
raise;
end;
end;
end;
For Windows services. Initialize in ServiceStart event.
procedure TMyService.ServiceStart(Sender: TService; var Started: Boolean);
begin
// Initialize ExeWatch - customer ID from registry or config
InitializeExeWatch('ew_win_your_api_key', '');
EW.SetCustomerId(ReadCustomerFromRegistry);
EW.Info('Service started', 'Service');
Started := True;
end;
procedure TMyService.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
EW.Info('Service stopping', 'Service');
Stopped := True;
// SDK shutdown is automatic
end;
procedure TMyService.Timer1Timer(Sender: TObject);
begin
EW.StartTiming('background_job');
try
ProcessPendingTasks;
EW.Info('Processed %d tasks', [TaskCount], 'Worker');
EW.EndTiming('background_job');
except
on E: Exception do
begin
EW.EndTiming('background_job',
TJSONObject.Create(TJSONPair.Create('error', E.Message)), False);
EW.Error('Job failed: %s', [E.Message], 'Worker');
end;
end;
end;
EW.SetCustomerId().
For server applications, use the server/instance name instead.
Step 3 Start Logging
Use the global EW object to log messages anywhere in your application.
uses
ExeWatchSDKv1;
procedure TMainForm.ButtonClick(Sender: TObject);
begin
// Log different severity levels
EW.Debug('Button clicked', 'UI');
EW.Info('Processing started', 'Core');
EW.Warning('Low memory detected', 'System');
EW.Error('Database connection failed', 'DB');
EW.Fatal('Critical error - shutting down', 'Core');
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
// Set user identity (optional but recommended)
EW.SetUser('user123', 'john@acme.com', 'John Doe');
EW.Info('Application started', 'Startup');
end;
That's it!
Your Delphi application is now sending logs to ExeWatch. Open the dashboard to see your logs in real-time. The SDK automatically handles offline storage, retries, hardware info collection, and exception capture.
Want more? Check out Performance Timing to measure operation durations and Custom Metrics to track counters and gauges.
.NET / C# SDK Integration
Add monitoring to your .NET applications — WinForms, WPF, console apps, and Windows services. Requires .NET 8.0+ on Windows.
Installation
Add the ExeWatch project reference or NuGet package to your solution. For WinForms apps, also add ExeWatch.WinForms.
Initialization
Initialize the SDK at application startup:
using ExeWatch;
// Console / WPF / Service
ExeWatchSdk.Initialize(new ExeWatchConfig
{
ApiKey = "ew_win_your_key",
CustomerId = "ACME-Corp"
});
// WinForms - also install the GUI exception hook
using ExeWatch.WinForms;
ExeWatchWinForms.Install(); // before Application.Run()
Usage
Use the EW shortcut class anywhere in your code:
using ExeWatch;
// Logging
EW.Info("User logged in", "auth");
EW.Error("Connection failed", "database");
// User identity
EW.SetUser("user123", "john@acme.com", "John Doe");
// Global tags
EW.SetTag("environment", "production");
// Breadcrumbs
EW.AddBreadcrumb("Opened Settings", "navigation");
// Performance timing with try/catch
EW.StartTiming("load_customers", "database");
try
{
var data = LoadCustomers();
EW.EndTiming("load_customers");
}
catch (Exception ex)
{
EW.EndTiming("load_customers",
new Dictionary<string, object> { ["error"] = ex.Message }, false);
throw;
}
// Custom metrics
EW.IncrementCounter("orders_processed");
EW.SetGauge("queue_length", 42);
Features
- Automatic Exception Capture — Unhandled exceptions, WinForms thread exceptions
- Offline Persistence — Logs saved to disk, sent when connection is available
- Hardware Info — CPU, RAM, disks, monitors, OS version
- User Identity — Track users across sessions
- Breadcrumbs — Trail of user actions before errors
- Performance Timing — Measure any operation with StartTiming/EndTiming
- Custom Metrics — Counters and gauges with periodic sampling
- Global Tags — Key-value context for all logs
- Server Config — Remotely adjust flush interval, batch size, log level, sampling
- Zero Dependencies — No NuGet packages required, uses System.Text.Json and System.Net.Http
Supported App Types
| App Type | Package | Exception Capture |
|---|---|---|
| Console | ExeWatch |
AppDomain.UnhandledException |
| Windows Service | ExeWatch |
AppDomain.UnhandledException |
| WinForms | ExeWatch + ExeWatch.WinForms |
Application.ThreadException + AppDomain |
| WPF | ExeWatch |
AppDomain.UnhandledException |
JavaScript SDK Integration
Add monitoring to your web applications in just a few lines of code.
Step 1 Add the Configuration
Add the configuration object to your HTML page, before the SDK script. You'll find your API key in the dashboard under API Keys > JavaScript.
<!-- Add this before </body> -->
<script>
window.ewConfig = {
apiKey: 'ew_web_xxxxxxxxxxxxxxxx',
customerId: 'ACME-Corporation'
};
</script>
Step 2 Include the SDK
Add the SDK script right after the configuration. Use the minified version for production.
<!-- Production (minified, 12KB) -->
<script src="https://exewatch.com/static/js/exewatch.v1.min.js"></script>
<!-- Development (readable, 35KB) -->
<script src="https://exewatch.com/static/js/exewatch.v1.js"></script>
Step 3 Start Logging
Use the global ew object anywhere in your JavaScript code (same as Delphi's EW).
// Set user identity (optional but recommended)
ew.setUser({ id: 'user123', email: 'john@acme.com', name: 'John Doe' });
// Log messages
ew.debug('Page loaded', 'init');
ew.info('User clicked checkout', 'ui');
ew.warning('Slow API response', 'api');
ew.error('Payment failed', 'checkout');
// Add breadcrumbs for debugging
ew.addBreadcrumb('Clicked Buy Now button', 'ui');
ew.addBreadcrumb('Navigated to /checkout', 'router');
// Set global tags
ew.setTag('environment', 'production');
ew.setTag('version', '2.5.0');
Complete Example
<!DOCTYPE html>
<html>
<head>
<title>My Web App</title>
</head>
<body>
<h1>Welcome</h1>
<button onclick="doSomething()">Click me</button>
<!-- ExeWatch Configuration -->
<script>
window.ewConfig = {
apiKey: 'ew_web_xxxxxxxxxxxxxxxx',
customerId: 'ACME-Corporation'
};
</script>
<script src="https://exewatch.com/static/js/exewatch.v1.min.js"></script>
<script>
// Initialize user
ew.setUser({ id: 'user123', email: 'john@acme.com' });
ew.info('Page loaded', 'init');
function doSomething() {
ew.addBreadcrumb('Button clicked', 'ui');
ew.info('User clicked the button', 'ui');
}
</script>
</body>
</html>
Filtering Third-Party Errors (ignoreUrls)
By default, the SDK captures all errors on the page, including failures from third-party scripts
(Google Ads, analytics, fonts, social widgets). Use ignoreUrls to filter out noise from URLs you don't control.
<script>
window.ewConfig = {
apiKey: 'ew_web_xxxxxxxxxxxxxxxx',
customerId: 'ACME-Corporation',
ignoreUrls: [
// Strings: match if URL contains the string
'googlesyndication.com',
'googleadservices.com',
'google-analytics.com',
'googletagmanager.com',
'fonts.googleapis.com',
'facebook.net',
'doubleclick.net',
// RegExp: for more complex patterns
/analytics\..*\.com/,
/cdn\.third-party\.io/
]
};
</script>
<script>, <link>, <img>),
Fetch/XHR network errors, and uncaught JavaScript errors originating from matching URLs.
Breadcrumbs from ignored URLs are also suppressed to keep your trail clean.
JavaScript SDK Configuration Reference
| Option | Type | Default | Description |
|---|---|---|---|
apiKey |
string | required | Browser API key (starts with ew_web_) |
customerId |
string | required | Customer identifier |
appVersion |
string | null |
App version for release tracking |
autoCapture |
boolean | true |
Auto-capture window errors, fetch/XHR failures, console.error |
ignoreUrls |
Array | [] |
URL patterns (strings or RegExp) to ignore in auto-capture. Matching resource, fetch, XHR, and uncaught errors are silently dropped. |
sampleRate |
number | 1.0 |
Sampling rate (0.0-1.0). Errors are always captured regardless. |
debug |
boolean | false |
Enable SDK debug output in browser console |
enabled |
boolean | true |
Enable/disable the SDK |
batchSize |
number | 10 |
Events per batch before auto-flush |
flushInterval |
number | 5000 |
Auto-flush interval in milliseconds |
That's it!
Your web application is now sending logs to ExeWatch. The SDK automatically captures browser info, screen size, and generates a persistent device ID.
Want more? Check out Performance Timing to measure operation durations and Custom Metrics to track counters and gauges.
Initialization Options
The SDK supports multiple initialization modes to fit different scenarios.
Standard Initialization
The simplest way to initialize - provide API key and Customer ID at startup.
// Delphi
Config := TExeWatchConfig.Create('ew_desk_xxx', 'ACME-Corp');
InitializeExeWatch(Config);
EW.SendDeviceInfo;
Deferred Customer ID
Initialize the SDK without knowing the customer ID upfront. This is useful when:
- License validation happens after app startup
- User authentication determines the customer
- You want to track unlicensed installations
// Initialize without customer ID
Config := TExeWatchConfig.Create('ew_desk_xxx', ''); // Empty customer_id
InitializeExeWatch(Config);
// Logs are tracked by device_id until customer is set
EW.Warning('No valid license found', 'license');
// Later, after license validation...
EW.SetCustomerId('ACME-Corp');
EW.Info('License validated successfully', 'license');
Configuration Options
The TExeWatchConfig record supports many options:
| Option | Default | Description |
|---|---|---|
Release |
(empty) | Version tag for release tracking |
AppVersion |
(auto-detect) | App version (auto-detected from executable) |
SampleRate |
1.0 | Sampling rate (0.0-1.0). 1.0 = 100% of events |
BufferSize |
100 | Max events before auto-flush |
FlushIntervalMs |
5000 | Auto-flush interval in milliseconds |
RetryIntervalMs |
30000 | Wait time between retry attempts |
StoragePath |
%LOCALAPPDATA%\ExeWatch | Path for offline log storage |
// Full configuration example
Config := TExeWatchConfig.Create('ew_desk_xxx', 'ACME-Corp');
Config.Release := '2.5.0';
Config.SampleRate := 0.5; // Send 50% of debug/info events
Config.BufferSize := 50;
Config.FlushIntervalMs := 3000;
InitializeExeWatch(Config);
Advanced Customer ID Scenarios
Real-world examples of handling customer identification in different application types.
Customer ID Change at Runtime
Use case: User switches between different accounts/licenses without restarting the application.
procedure TMainForm.FormCreate(Sender: TObject);
var
Config: TExeWatchConfig;
begin
Config := TExeWatchConfig.Create('ew_win_abc123...', 'CUSTOMER-A');
InitializeExeWatch(Config);
// Device info sent with customer_id = 'CUSTOMER-A'
end;
procedure TMainForm.OnUserSwitched(const NewCustomer: string);
begin
// User switches from CUSTOMER-A to CUSTOMER-B
EW.SetCustomerId(NewCustomer);
// Device info is RE-SENT with the new customer_id
// Backend updates the device to belong to the new customer
EW.Info('User switched to ' + NewCustomer, 'auth');
end;
SetCustomerId with a different customer_id,
the SDK automatically re-sends device info with the new customer. The device in the backend is moved to the new customer.
Multi-Tenant Desktop Application
Use case: Desktop SaaS application where customer_id is determined after user login.
var
FCurrentCustomer: string = '';
procedure TMainForm.FormCreate(Sender: TObject);
var
Config: TExeWatchConfig;
begin
// Initialize SDK WITHOUT customer_id
Config := TExeWatchConfig.Create('ew_win_abc123...', '');
InitializeExeWatch(Config);
// Log without customer_id (user not logged in yet)
EW.Info('Application started - waiting for login', 'startup');
end;
procedure TMainForm.OnLoginSuccess(const Username, Company: string);
begin
// After login, set customer_id
FCurrentCustomer := Company;
EW.SetCustomerId(FCurrentCustomer);
// From now on, all logs have customer_id
EW.Info('User logged in: ' + Username, 'auth');
end;
procedure TMainForm.OnLogout;
begin
EW.Info('User logged out: ' + FCurrentCustomer, 'auth');
// Optionally reset customer_id for next user
// EW.SetCustomerId('');
end;
SetCustomerId are associated with the device ID only
(e.g., "john@PC-WORK"). This helps track pre-login activity and unlicensed installations.
Custom Deployment Information
Use case: Track custom deployment metadata like environment (production/staging), database version, or feature flags.
procedure TMainForm.FormCreate(Sender: TObject);
var
Config: TExeWatchConfig;
Environment, DBVersion: string;
begin
// Read configuration from file/registry
Environment := ReadEnvironmentFromConfig; // 'production', 'staging', etc.
DBVersion := GetDatabaseVersion; // '5.2.1'
Config := TExeWatchConfig.Create('ew_win_abc123...', 'CUSTOMER-ID');
// Add custom device info
Config.InitialCustomDeviceInfo :=
reference to procedure(var Info: TDictionary<string, string>)
begin
Info.Add('environment', Environment);
Info.Add('db_version', DBVersion);
Info.Add('deployment_region', 'EU-West');
end;
InitializeExeWatch(Config);
EW.Info('Application started in ' + Environment, 'startup');
end;
// Update custom info at runtime
procedure TMainForm.OnFeatureFlagChanged(const Flags: string);
begin
EW.SetCustomDeviceInfo('feature_flags', Flags);
// Changes sent on next log batch
end;
environment: production, db_version: 5.2.1, deployment_region: EU-West.
Release Tracking
Track which version of your application generated each log event.
What is Release?
Release is a version identifier that tags every log event with the version of your app that generated it. This lets you correlate errors and issues with specific versions of your software.
Without Release
"We're seeing 50 errors today. Is this a new bug? Was it introduced in the last update? Did we already fix it?"
With Release
"50 errors - all from version 2.4.0. Version 2.5.0 has zero errors. The fix in 2.5.0 worked!"
Release vs AppVersion
The SDK tracks two version-related values. Understanding the difference helps you use them correctly:
| Release | AppVersion | |
|---|---|---|
| What is it | A label you choose to identify this deployment/build | The version number from the executable's metadata |
| How it's set | You set it manually via Config.Release |
Auto-detected from executable (Project > Options > Version Info) |
| Example | "2.5.0", "2024.01.15", "abc123" |
"2.5.0.1234" (FileVersion from EXE) |
| When to use | Track deployments, hotfixes, feature branches | Track exact build numbers |
Release to your semantic version (e.g., "2.5.0") and let AppVersion be auto-detected for the full build number.
How to Set the Release
Delphi
// Set release at initialization
Config := TExeWatchConfig.Create(
'ew_desk_xxx',
'ACME-Corp'
);
Config.Release := '2.5.0';
InitializeExeWatch(Config);
// All logs now tagged with release=2.5.0
EW.Info('App started', 'startup');
JavaScript
// Set release in config
window.ewConfig = {
apiKey: 'ew_web_xxx',
customerId: 'ACME-Corp',
release: '2.5.0' // Version tag
};
// All logs now tagged with release=2.5.0
ew.info('Page loaded', 'init');
What Value Should I Use?
Choose a format that works for your release process:
| Format | Example | Best for |
|---|---|---|
| Semantic Version | "2.5.0", "1.0.0-beta" |
Most applications - clear major.minor.patch numbering |
| Date-based | "2024.01.15", "24.1" |
Continuous deployment, monthly releases |
| Git commit | "abc123f", "main-abc123" |
Frequent deployments, when you need exact code reference |
| Build number | "build-1234", "CI-5678" |
CI/CD pipelines with incrementing build IDs |
Why Track Releases?
- Identify regressions - See if errors started after a specific release
- Verify fixes - Confirm that a bug fix actually reduced errors
- Rollback decisions - Know which version to rollback to if needed
- Gradual rollout - Monitor errors as you deploy to more users
- Compare versions - See if v2.5 is more stable than v2.4
- Support - Ask "what version are you on?" when helping users
Updates Page
Monitor which versions are deployed across your devices and track adoption.
How it Works
The Updates page shows the current version state of every device in your application. Every time a device sends data to ExeWatch (logs, device info), its version information is automatically updated. The page provides stats, filters, and a version distribution chart to help you understand your deployment landscape at a glance.
To inspect a specific device's version history (past upgrades, downgrades), click on the device name to open its detail modal.
AppVersion vs BinaryVersion
ExeWatch tracks two distinct version identifiers for each device. They serve different purposes and do not necessarily change together:
| AppVersion | BinaryVersion | |
|---|---|---|
| What is it | The product version name — a commercial or human-readable label chosen by the developer | The technical build number auto-detected from the executable's file metadata |
| How it's set | Manually via Config.AppVersion or Config.Release |
Auto-detected from executable (FileVersion / ProductVersion) |
| Format | Free-form: can be numeric ("2.5.0"), a codename ("oxygen"), or a mix ("2.0-oxygen") |
Always numeric: "X.Y.Z.W" (e.g. "10.0.0.0") |
| Changes with each build? | Not necessarily. A product version name may stay the same across multiple builds (e.g. "v2.0-oxygen" can ship as binary 2.0.0.1, 2.0.0.2, etc.) | Yes. Every recompile can produce a new build number |
| Example | "2.0-oxygen", "neon", "3.1.0" |
"2.0.0.1234", "10.0.0.0" |
How "Latest Version" is Determined
The Latest Version stat card shows the highest version currently deployed across all devices. Since version identifiers can vary in format, ExeWatch uses a gradual fallback strategy to determine which version is "the latest":
| Priority | Field used | How it compares | When it applies |
|---|---|---|---|
| 1st | BinaryVersion | Numeric comparison of each segment (e.g. 10.0.0.0 > 9.0.0.0) |
Always preferred when available, since it's auto-detected and always numeric |
| 2nd | AppVersion (numeric parts) | Extracts numbers from the string (e.g. "v3.0-neon" → 3.0) and compares numerically |
When BinaryVersion is missing or identical across versions |
| 3rd | Last activity date | The version used by the most recently active device wins | Last resort: when neither BinaryVersion nor AppVersion contain usable numbers (e.g. pure codenames like "oxygen" vs "neon") |
Examples
BinaryVersion differs
Device A: vOxygen (bin 1.0.0.0)
Device B: vNeon (bin 2.0.0.0)
Latest = vNeon (bin 2 > 1)
No binary, numbers in AppVersion
Device A: v2.0-oxygen (no binary)
Device B: v3.0-neon (no binary)
Latest = v3.0-neon (3.0 > 2.0)
Pure codenames, no numbers
Device A: oxygen (seen yesterday)
Device B: neon (seen today)
Latest = neon (more recent activity)
Adoption Percentage
The Adoption stat shows what percentage of your devices are running the latest version (as determined above). A low adoption percentage means many devices are still on older versions — you may want to encourage your customers to update.
Use the Version Distribution chart and the version filter to drill down into which specific versions are still in use and by which customers.
Usage Analytics
Understand how your software is being used. The Usage page provides session counts, device activity, customer rankings, usage patterns, and error rate trends — all computed automatically from your existing log data. No additional SDK configuration needed.
How it Works
Every time your SDK initializes, it generates a unique session ID and sends an "Application started" log. ExeWatch uses this data — along with device IDs, customer IDs, and timestamps — to compute usage analytics automatically.
Navigate to Usage in the app toolbar and select a time period (1d, 7d, 30d, 90d, or All) to explore your application's usage data.
Period Selector
All widgets on the Usage page respect the selected period. The time bucket granularity adapts automatically:
| Period | Time Range | Bucket Size |
|---|---|---|
| 1d | Last 24 hours | 1 hour |
| 7d | Last 7 days | 1 day |
| 30d | Last 30 days | 1 day |
| 90d | Last 90 days | 1 week |
| All | All available data | 1 month |
Each stat card also shows a delta percentage comparing the current period to the previous period of equal length (e.g., last 7 days vs the 7 days before that).
Widgets
Summary Cards
Four stat cards at the top: Sessions (unique app runs), Devices (unique machines), Customers (unique end-users), and Avg Session (average time between first and last log of each session, excluding single-log sessions).
Sessions Over Time
Line chart showing sessions and devices per time bucket. Empty buckets (days/hours with no activity) are shown as zero, so you can see gaps clearly.
Usage Heatmap
A 7×24 grid showing session activity by day of week and hour of day (UTC). Brighter cells mean more sessions. Hover on any cell to see the exact count.
Top Customers
Table ranking your top 10 customers by session count, with device count and last active timestamp. Useful for identifying your most engaged users and spotting customers that have gone silent.
OS Distribution
Doughnut chart showing the top 10 OS versions across active devices. Full OS strings are displayed in a legend below the chart, with device counts and percentages.
Error Rate Trend
Line chart showing the error rate (percentage of ERROR + FATAL logs out of all logs) per time bucket. Use the version dropdown to filter by one or more app versions. Buckets with no traffic show as gaps (not 0%).
Health Monitoring
Each application gets an automatic health status — Healthy, Degraded, or Critical — visible in the dashboard and sidebar. When the status changes you receive an email notification (and a Discord message, if configured).
What is Monitored
Three signals are evaluated over the last 24 hours. The overall status is the worst of the three.
| Signal | What it measures | Degraded (default) | Critical (default) |
|---|---|---|---|
| Error rate | % of error + fatal events over total | ≥ 1% | ≥ 5% |
| Crashes | Number of fatal events | ≥ 1 | ≥ 3 |
| Slow operations Pro & Business | % of timing measurements above the slow threshold | ≥ 5% | ≥ 20% |
If there are fewer than 100 events in 24h, the error rate shows as Insufficient data instead of a potentially misleading percentage. All thresholds are customizable per application in App Settings → Health Thresholds.
Stable Notifications
ExeWatch won't flood your inbox when a metric hovers near a threshold. Three safeguards keep notifications meaningful:
- Hysteresis — once a signal enters a worse state, it needs to improve by a 30% margin before it can recover. For example, an error rate that crossed 1% (Degraded) must drop below 0.7% to return to Healthy.
- Consecutive confirmation — a new status must hold for 3 consecutive checks before it is accepted. A single spike won't change the status.
- Cooldown — after a notification is sent, the next one is held for at least 60 minutes (configurable in App Settings).
Performance Timing
Measure how long operations take in your application. Identify slow queries, API calls, and bottlenecks.
How It Works
- Call
StartTiming('operation_name')before the operation - Call
EndTiming('operation_name')after it completes - View timing statistics in the ExeWatch dashboard (Timing tab)
- Operations exceeding the slow threshold are highlighted
Delphi Example
// Basic timing with try..except
EW.StartTiming('database_query');
try
RunMyDatabaseQuery;
EW.EndTiming('database_query'); // success
except
on E: Exception do
begin
EW.EndTiming('database_query',
TJSONObject.Create(TJSONPair.Create('error', E.Message)), False);
raise;
end;
end;
// With category for grouping
EW.StartTiming('load_customers', 'database');
try
LoadCustomersFromDB;
EW.EndTiming('load_customers');
except
on E: Exception do
begin
EW.EndTiming('load_customers',
TJSONObject.Create(TJSONPair.Create('error', E.Message)), False);
raise;
end;
end;
JavaScript Example
// Basic timing
ew.startTiming('api_call');
await fetch('/api/data');
ew.endTiming('api_call');
// With category
ew.startTiming('render_chart', 'ui');
renderComplexChart();
ew.endTiming('render_chart');
Metrics (Counters & Gauges)
Track numeric values over time with counters and gauges. Monitor API call rates, memory usage, active connections, and more.
What are Metrics?
ExeWatch supports two types of metrics, inspired by Prometheus:
Counter
A value that only increases. Use counters to track how many times something happens.
Examples: API calls, cache hits, orders processed, errors encountered.
Gauge
A value that can go up or down. Use gauges to track the current state of something.
Examples: memory usage, active connections, queue depth, CPU temperature.
How It Works
- Your application records metrics using
IncrementCounterorRecordGauge - The SDK aggregates values locally (min, max, avg, count)
- Every 60 seconds, aggregated data is flushed to the ExeWatch server
- For periodic gauges, the SDK automatically samples registered callbacks at a configurable interval (default 30s)
- View metrics in the Metrics tab of your application dashboard
Delphi Examples
Counters
// Increment by 1 (default)
EW.IncrementCounter('api_calls');
// Increment by a specific value
EW.IncrementCounter('bytes_processed', FileSize);
// With a tag for grouping
EW.IncrementCounter('api_calls', 1, 'network');
EW.IncrementCounter('cache_hits', 1, 'database');
Gauges (manual)
// Record a point-in-time value
EW.RecordGauge('active_connections', ActiveCount);
EW.RecordGauge('queue_depth', Queue.Count, 'jobs');
Gauges (periodic / automatic)
// Register a callback that the SDK samples every 30s automatically
EW.RegisterPeriodicGauge('memory_mb',
function: Double
begin
Result := GetProcessMemoryMB;
end,
'system');
// Unregister when no longer needed
EW.UnregisterPeriodicGauge('memory_mb');
Configuration
// Set gauge sampling interval (default 30s, min 10s)
Config.GaugeSamplingIntervalSec := 15;
JavaScript Examples
// Counters
ew.incrementCounter('page_views');
ew.incrementCounter('api_calls', 1, 'network');
// Manual gauge
ew.recordGauge('items_in_cart', cart.length);
// Periodic gauge (auto-sampled every 30s)
ew.registerPeriodicGauge('dom_nodes', function() {
return document.querySelectorAll('*').length;
}, 'performance');
// Unregister
ew.unregisterPeriodicGauge('dom_nodes');
// Force flush metrics immediately
ew.flushMetrics();
Configuration
// Set gauge sampling interval in config (default 30, min 10)
window.ewConfig = {
apiKey: 'ew_web_xxx',
customerId: 'customer123',
gaugeSamplingIntervalSec: 15
};
'system', 'network', 'database').
Tags appear as filters in the Metrics dashboard, making it easy to focus on specific areas.
User Identity
Associate logs with specific users to understand who is affected by issues.
What is User Identity?
User Identity represents the person currently using your application. This is different from Customer (which represents the company/organization) and Device (which represents the computer).
Customer
The company or organization that purchased/uses your software. Example: "ACME Corporation"
Device
A specific computer or installation. Example: "john@WORKSTATION-01"
User
The individual person logged in. Example: "John Doe (john@acme.com)"
Example scenario: ACME Corporation (Customer) has 50 employees.
Each employee has their own computer (Device). When John Doe logs into the app on his computer,
you call SetUser so all his actions are tagged with his identity.
How to Use SetUser
Call SetUser after the user logs into your application. All subsequent log events will include this user information.
Delphi
// After user logs in successfully
EW.SetUser(
'user-12345', // ID - unique identifier
'john@acme.com', // Email (optional)
'John Doe' // Display name (optional)
);
// Now all logs include user info
EW.Info('Opened invoice #123', 'invoices');
EW.Warning('Slow query detected', 'db');
EW.Error('Failed to save', 'core');
// All these logs will show: user_id=user-12345
// When user logs out
EW.ClearUser;
EW.Info('User logged out', 'auth');
JavaScript
// After user logs in (pass an object)
ew.setUser({
id: 'user-12345',
email: 'john@acme.com',
name: 'John Doe'
});
// All logs now tagged with user
ew.info('Added item to cart', 'cart');
ew.error('Payment failed', 'checkout');
// On logout
ew.clearUser();
What Values Should I Use?
| Parameter | Required | What to use | Examples |
|---|---|---|---|
ID |
Required | A unique, stable identifier for the user. Use your internal user ID from your database. | "user-12345", "USR_abc123", "42" |
Email |
Optional | User's email address. Useful for contacting affected users. | "john@acme.com" |
Name |
Optional | Display name for easier identification in the dashboard. | "John Doe", "J. Smith" |
Why Use User Identity?
- Find affected users - When an error occurs, see who was affected
- Reproduce issues - Follow a specific user's actions to understand what went wrong
- Contact users - Reach out to users experiencing problems
- Usage patterns - Understand how different users use your app
- Support tickets - Link support requests to specific log trails
- Impact analysis - Know how many unique users are affected by a bug
Hardware Info
The SDK automatically collects device hardware information to help you understand the environment where issues occur.
Collected Information (Delphi SDK)
System
- OS type and version
- Hostname and username
- Timezone and locale
- System language
- System boot time
- Local IP addresses
Hardware
- CPU name and architecture
- Physical and logical cores
- Total and available memory
- Disk drives (size, free space, type)
- Monitors (resolution, color depth)
Application
- Executable path
- Working directory
- Command line
- File version (auto-detected)
- Product name
- Company name
Custom Device Info
Add your own key-value data to device info for application-specific context.
// Set custom info at initialization
Config := TExeWatchConfig.Create('ew_desk_xxx', 'ACME-Corp');
Config.InitialCustomDeviceInfo := [
TPair.Create('license_type', 'enterprise'),
TPair.Create('database', 'PostgreSQL 15')
];
InitializeExeWatch(Config);
// Or set/update custom info at runtime
EW.SetCustomDeviceInfo('active_modules', 'accounting,inventory');
EW.SendCustomDeviceInfo; // Send accumulated custom info
// Or set and send in one call
EW.SendCustomDeviceInfo('last_sync', '2024-01-15T10:30:00');
Offline Mode & Persistence
The SDK is designed to never lose logs, even when the network is unavailable or the app crashes.
How It Works
- Buffer - Logs are collected in memory
- Persist - When buffer is full or interval elapses, logs are written to disk
- Ship - Background thread sends files to server
- Retry - Failed sends are automatically retried
- Cleanup - Successfully sent files are deleted
Benefits
- Logs survive app crashes
- Works offline (syncs when online)
- Non-blocking (async background sending)
- Automatic retries on failure
- Oldest logs sent first (FIFO)
%LOCALAPPDATA%\ExeWatch\pending.
You can customize this with Config.StoragePath.
Sampling
Reduce event volume by sampling a percentage of logs. Useful for high-traffic applications.
Configuration
// Send only 10% of debug/info/warning logs
Config := TExeWatchConfig.Create('ew_desk_xxx', 'ACME-Corp');
Config.SampleRate := 0.1; // 10%
InitializeExeWatch(Config);
// These have a 10% chance of being sent
EW.Debug('Trace message');
EW.Info('User action');
// These are ALWAYS sent (100%)
EW.Error('Something failed');
EW.Fatal('Critical error');
Sample Rate Values
| Value | % Sent | Use Case |
|---|---|---|
1.0 |
100% | Default - send everything |
0.5 |
50% | Moderate reduction |
0.1 |
10% | High-volume apps |
0.01 |
1% | Very high volume |
Log Levels
ExeWatch supports 5 severity levels to help you categorize and filter your logs.
| Level | Method | When to Use |
|---|---|---|
| Debug | EW.Debug() / ew.debug() |
Detailed information for debugging. Usually disabled in production. |
| Info | EW.Info() / ew.info() |
General information about app execution. User actions, state changes. |
| Warning | EW.Warning() / ew.warning() |
Something unexpected but not critical. Deprecation notices, slow operations. |
| Error | EW.Error() / ew.error() |
Errors that affect functionality but don't crash the app. Failed API calls, validation errors. |
| Fatal | EW.Fatal() / ew.fatal() |
Critical errors that crash or severely impact the app. Unhandled exceptions. |
Best Practices
Do
- Use meaningful tags to categorize logs (e.g., "DB", "API", "UI")
- Set user identity when available for easier debugging
- Log important user actions as Info level
- Include context in error messages
- Use breadcrumbs to track user flow
Don't
- Log sensitive data (passwords, credit cards, PII)
- Use Debug level for everything in production
- Log in tight loops (can generate excessive events)
- Ignore the tag parameter - it helps filtering
- Use Fatal for non-critical errors
Privacy & Terms of Service Guide
When you integrate ExeWatch into your application, you collect operational data from your end users. Depending on your jurisdiction, you may need to disclose this in your Terms of Service or Privacy Policy. Below you'll find exactly what data the SDK collects and a ready-to-use template you can adapt.
What Data Does the SDK Collect?
Delphi Native SDK — Desktop, Server, Mobile
Collected automatically:
- Device ID —
username@hostname(e.g.,jack@MYWORKLAPTOP), where username is the logged-in OS user and hostname is the machine name - System info — OS version, timezone, locale, language
- Hardware — CPU, RAM, disk space, screen resolution
- Application — binary version, optional app version, executable path
- Session ID — random identifier per app run (not tied to user identity)
- Log events — level, message, tag, thread, timestamps
- Performance timing — operation names and durations (if used)
Collected only if you set it:
- Customer ID — identifier you assign (e.g., company name, license key)
- User identity — only if you call
SetUser()with ID, email, or name - Breadcrumbs — user actions trail, only if you call
AddBreadcrumb() - Global tags — key-value pairs you explicitly set with
SetTag() - Custom log messages — only what your code explicitly logs
JavaScript SDK — Browser
Collected automatically:
- Device ID — random 8-character string stored in
localStorage(e.g.,a3f8b2c1). No OS username or hostname is collected. - Browser info — user agent, screen size, language
- Session ID — random identifier per page load
- Log events — level, message, tag, timestamps
- Performance timing — operation names and durations (if used)
Collected only if you set it:
- Customer ID — identifier you assign
- User identity — only if you call
setUser() - Breadcrumbs — only if you call
addBreadcrumb() - Global tags — key-value pairs you explicitly set
- Custom log messages — only what your code explicitly logs
Where Is Data Stored?
- Servers: ExeWatch data is stored on servers located in the European Union (EU)
- Encryption: All data is transmitted over HTTPS (TLS 1.2+)
- Retention: Data is retained according to your ExeWatch plan (7, 30, or 90 days) and automatically deleted after that period
- Sub-processors: See our Data Processing Agreement (DPA) for the full list of sub-processors
Privacy Policy / ToS Template
Copy and adapt the text below for your application's Privacy Policy or Terms of Service. Replace the placeholders in [brackets] with your own information.
Data controller
[Your Company Name]
[Your registered address]
Email: [your contact email]
[If applicable:] Data Protection Officer: [DPO name and contact]
Overview
[Your Application Name] uses ExeWatch, a third-party application monitoring service provided by bit Time Professionals S.r.l. (Italy), to monitor application performance, detect errors, and improve software quality.
Legal basis for processing
[Choose the legal basis that applies to your situation:]
Option A — Legitimate interest (Art. 6(1)(f) GDPR): The processing of technical diagnostic data is necessary for our legitimate interest in ensuring the reliability, security, and quality of our software. We have conducted a balancing test and determined that this interest is not overridden by your rights and freedoms, as the data collected is limited to technical information and does not include personal content.
Option B — Contractual necessity (Art. 6(1)(b) GDPR): The processing of diagnostic data is necessary for the performance of the contract between you and [Your Company Name], specifically to provide software support, maintain product quality, and fulfill our obligation to deliver a reliable application.
Option C — Consent (Art. 6(1)(a) GDPR): Diagnostic data is collected only with your explicit consent, which you can withdraw at any time. See the "Opting out" section below.
What data is collected
When you use [Your Application Name], the following technical data is automatically collected and sent to ExeWatch servers:
• Device information: computer name (hostname), operating system username (the currently logged-in user, e.g. "jack"), operating system version, CPU model, RAM amount, available disk space, screen resolution. On desktop, server, and mobile platforms, each device is identified as username@hostname (e.g., "jack@MYWORKLAPTOP").
• Application data: application version, session identifier (random, not tied to your identity), timestamps of events
• Diagnostic data: error messages, performance measurements, and operational log entries generated by the application
[If you use SetUser() or Customer ID, add the following:]
Additionally, the following information may be associated with the collected data:
• Account identifier: your license key or company name, used to associate diagnostic data with your organization
• User identifier: [describe what you pass — e.g., username, email, or internal ID]
What data is NOT collected
The monitoring system does not collect: keystrokes, screen content or screenshots, file contents, browsing history, GPS location, passwords, or any personal documents.
Purpose of data collection
This data is collected solely for the following purposes:
• Detecting and diagnosing software errors and crashes
• Monitoring application performance and reliability
• Understanding software usage patterns to improve the product
• Tracking software version adoption across installations
Data storage and retention
Collected data is transmitted over encrypted connections (HTTPS/TLS 1.2+) to ExeWatch servers located in the European Union. Data is retained for a maximum of [7/30/90] days, after which it is automatically and permanently deleted.
Third-party data processor
ExeWatch (bit Time Professionals S.r.l., Italy) acts as a data processor on behalf of [Your Company Name] (data controller). A Data Processing Agreement (DPA) governs the relationship and ensures GDPR compliance. For details, see: https://exewatch.com/ui/dpa
Your rights
Under the General Data Protection Regulation (GDPR) and other applicable data protection laws, you have the following rights regarding your personal data:
• Right of access (Art. 15) — request a copy of the data we hold about you
• Right to rectification (Art. 16) — request correction of inaccurate data
• Right to erasure (Art. 17) — request deletion of your data
• Right to restriction (Art. 18) — request that we limit how your data is processed
• Right to data portability (Art. 20) — request your data in a machine-readable format
• Right to object (Art. 21) — object to processing based on legitimate interest
To exercise any of these rights, contact: [your contact email]
You also have the right to lodge a complaint with your local data protection supervisory authority if you believe your data is being processed unlawfully.
Opting out
[Choose the option that matches your chosen legal basis:]
Option A (for Legitimate interest or Contractual necessity): You may object to the processing of diagnostic data by contacting us at [your contact email]. We will evaluate your request and cease processing unless we can demonstrate compelling legitimate grounds. The monitoring cannot be disabled individually within the application, but all data is handled in strict accordance with this policy and applicable data protection laws.
Option B (for Consent): You may withdraw your consent and disable diagnostic data collection at any time from [Settings > Privacy] in the application, or by contacting us at [your contact email]. Withdrawal of consent does not affect the lawfulness of processing carried out before the withdrawal.
Tips for Compliance
- Avoid logging personal data: Don't pass usernames, emails, or personal info in log messages unless strictly necessary
- Be transparent: If you use
SetUser(), mention it in your Privacy Policy - Minimize data: Only log what you need for debugging. Use sampling to reduce volume
- Consider opt-out: If required by your jurisdiction, provide users with a way to disable monitoring
- Sign the DPA: If you process EU personal data, review and sign our Data Processing Agreement
- Update regularly: Review your Privacy Policy when you add new SDK features (e.g., User Identity, Breadcrumbs)
Tutorials & Resources
Official SDK Samples
Ready-to-run demo projects for Delphi, JavaScript, and more. Clone, configure your API key, and start exploring.
View on GitHubUpdates & Usage Analytics
Blog post covering the Updates page, Usage Analytics, and how to track your application adoption.
Read Blog Post