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).
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 and Get API Key
In the ExeWatch dashboard, go to your Application's API Keys section. Here you'll find:
- Your API Key - Copy this to use in your code
- Download SDK button - Downloads
ExeWatchSDKv1.pas
Add ExeWatchSDKv1.pas to your Delphi project.
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;
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);
finally
EW.EndTiming('get_customers');
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');
except
on E: Exception do
EW.Error('Job failed: %s', [E.Message], 'Worker');
end;
EW.EndTiming('background_job');
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, and hardware info collection.
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',
endpoint: 'https://exewatch.com'
};
</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>
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.
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);
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
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
EW.StartTiming('database_query');
RunMyDatabaseQuery;
EW.EndTiming('database_query');
// With category for grouping
EW.StartTiming('load_customers', 'database');
LoadCustomersFromDB;
EW.EndTiming('load_customers');
// Nested timing (uses stack - LIFO)
EW.StartTiming('process_order');
EW.StartTiming('validate_items');
ValidateItems;
EW.EndTiming; // ends 'validate_items'
EW.StartTiming('calculate_total');
CalculateTotal;
EW.EndTiming; // ends 'calculate_total'
EW.EndTiming; // ends 'process_order'
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');
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