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

Application
Customers
Devices
Log Events

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
1
Create an Account

Register for free - no credit card required.

2
Create an Application

In the dashboard, click "New Application" and give it a name.

3
Get Your API Key

Click "API Keys" to see your key and download the integration code.

4
Integrate the SDK

Follow the guide below for your platform (Delphi or JavaScript).

5
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.

Compatibility: Works with Delphi 10 Seattle and later. Supports VCL, FMX, console apps, Windows services, and Linux servers.
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;
Customer ID: Use something that identifies your customer uniquely - company name, license key, or internal ID. You can set it at initialization or later with 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');
When no Customer ID is set, logs are associated with the Device ID (e.g., "john@PC-WORK"). This lets you track unlicensed installations.
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
Recommendation: Set 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
  1. Call StartTiming('operation_name') before the operation
  2. Call EndTiming('operation_name') after it completes
  3. View timing statistics in the ExeWatch dashboard (Timing tab)
  4. 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');
Tip: Set the slow threshold when creating your application. Operations exceeding this threshold are marked as "slow" in timing reports. Default is 1000ms.

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"
Tip: The ID should be stable (doesn't change) and unique per user. Don't use values that change (like session tokens) or might be shared (like "admin").
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
Privacy: Only collect user information you have consent to store. In many cases, just the user ID is enough to correlate issues without storing personal data.

Global Tags

Add key-value metadata that gets attached to all log events. Use tags for filtering and grouping.

Delphi
// Set tags
EW.SetTag('environment', 'production');
EW.SetTag('feature_flags', 'beta_ui');
EW.SetTag('subscription', 'enterprise');

// Remove a tag
EW.RemoveTag('feature_flags');

// Clear all tags
EW.ClearTags;
JavaScript
// Set tags
ew.setTag('environment', 'production');
ew.setTag('ab_test', 'variant_b');

// Remove a tag
ew.removeTag('ab_test');

// Clear all tags
ew.clearTags();
Common Tag Examples
Tag Values Use Case
environment production, staging, development Filter logs by environment
tenant customer-specific IDs Multi-tenant applications
feature checkout, reporting, import Track logs by feature area
subscription free, pro, enterprise Analyze by subscription tier

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
  1. Buffer - Logs are collected in memory
  2. Persist - When buffer is full or interval elapses, logs are written to disk
  3. Ship - Background thread sends files to server
  4. Retry - Failed sends are automatically retried
  5. 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)
Storage location: By default, logs are stored in %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
Important: Error and Fatal level events always bypass sampling and are sent 100% of the time. This ensures you never miss critical issues.

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

Ready to Get Started?

Create your free account and start monitoring in minutes.

Create Free Account