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). Check out the official sample projects for ready-to-run demos.

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

Compatibility: Delphi 10 Seattle and later. The VCL/FMX units enable automatic unhandled exception capture for GUI apps.
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;
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, 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>
What gets filtered: Resource loading errors (<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');
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);

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;
How it works: When you call 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;
Tip: Logs sent before 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;
Result: In the backend device modal, you'll see your custom info alongside hardware details: environment: production, db_version: 5.2.1, deployment_region: EU-West.
More details: For complete integration patterns, troubleshooting, and best practices, see the Delphi SDK Integration Guide

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

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"
Tip: Both values are always displayed together on the Updates page so you get the full picture. AppVersion tells you what product release the customer has; BinaryVersion tells you what exact build is running.
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)

Recommendation: For the most reliable version tracking, make sure your build process sets a FileVersion in the executable metadata. This gives ExeWatch a numeric BinaryVersion to work with, ensuring accurate version ordering regardless of your AppVersion naming convention.
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
1dLast 24 hours1 hour
7dLast 7 days1 day
30dLast 30 days1 day
90dLast 90 days1 week
AllAll available data1 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%).

No SDK changes needed. Usage Analytics works automatically with any version of the ExeWatch SDK. All metrics are derived from the session IDs, device IDs, and customer IDs already sent with every log event.

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.

The Slow operations signal is available on Pro and Business plans. On the free plan, health is based on error rate and crashes.
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).
No SDK changes needed. Health monitoring works automatically from the data your SDK already sends. Tune thresholds and cooldown in App Settings.


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 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');
Tip: Set the slow threshold when creating your application. Operations exceeding this threshold are marked as "slow" in timing reports. Default is 1000ms.

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
  1. Your application records metrics using IncrementCounter or RecordGauge
  2. The SDK aggregates values locally (min, max, avg, count)
  3. Every 60 seconds, aggregated data is flushed to the ExeWatch server
  4. For periodic gauges, the SDK automatically samples registered callbacks at a configurable interval (default 30s)
  5. View metrics in the Metrics tab of your application dashboard
Quota: Metric data points count toward your monthly event quota (same as logs). Each flush interval produces one data point per unique metric name+tag combination.
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
};
Tip: Use tags to group related metrics (e.g., '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"
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

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 IDusername@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
Important: The SDK does not collect keystrokes, screen content, file contents, browsing history, GPS location, or any data beyond what is listed above. You have full control over what personal information is sent.
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.

Disclaimer: This template is provided as a starting point only and does not constitute legal advice. ExeWatch (bit Time Professionals S.r.l.) acts exclusively as a data processor and is not responsible for the data that you, as the data controller, choose to collect, transmit, or store through the SDK. It is your sole responsibility to ensure that your use of ExeWatch complies with all applicable data protection laws in your jurisdiction. We recommend consulting a qualified legal professional to review your Privacy Policy before publication.
Application Monitoring and Data Collection

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 GitHub
Video Tutorial

Getting started with ExeWatch — Delphi SDK integration walkthrough.

Watch on YouTube
Updates & Usage Analytics

Blog post covering the Updates page, Usage Analytics, and how to track your application adoption.

Read Blog Post

Ready to Get Started?

Create your free account and start monitoring in minutes.

Create Free Account