📋 Introduction
A monitoring rule is a JSON document that tells the Agent/Worker/Profiler which
application to watch and exactly what to collect from it (window/click activity, CPU and
memory, HTTP calls, session replay, and so on). This document is the full reference for
that JSON format.
⚙️ How a rule reaches the Agent
You will normally author these rules in the GermainUX server's admin UI, not hand-edit
JSON files — but understanding where the JSON comes from helps when diagnosing why a
setting doesn't seem to be taking effect:
-
The Agent polls the server for the list of target applications and their rule names.
-
For each rule, it fetches the rule's JSON from the server by name.
-
If the server request fails, it falls back to a file named
{Agent install folder}\\config\\{ruleName}.json. -
A local rule (registry
Targets\\{exe}\\ConfigFile) bypasses all of the above and is read directly from the
file path you specify — this is the only path that doesn't need a server at all.
In every case, the resulting JSON has the same shape described below, and you can always
confirm exactly what's currently applied via the tray icon's Configurations submenu
(see Agent Settings).
A minimal starting point is shown in full at the bottom of this document. Every field
below is optional unless stated otherwise — omitting a block leaves that feature at its
default (generally: off).
📁 Top-level fields
{
"application": "My Application",
"version": "2026.1",
"agentName": "MyApplication-Prod",
"profileName": "Default",
"correlationId": ""
}
|
Field |
Purpose |
|---|---|
|
|
Free-text application name attached to every fact this rule produces. |
|
|
Informational agent/rule version string. |
|
|
Unique name identifying this monitoring rule instance; used for provenance and is the name the Agent uses when fetching this rule's config by name. |
|
|
Name of the monitoring profile this rule belongs to; informational/provenance only. |
|
|
Normally left unset — the Agent mints one per monitored process automatically so the Worker and Profiler's facts for the same running instance can be joined together. |
🔧 monitorConfig
"monitorConfig": {
"winTitle": "My Application",
"useImageSibling": false
}
|
Field |
Purpose |
|---|---|
|
|
Partial window title used to help identify the application's main window. |
|
|
When a click lands on an image control, also look for a sibling element to attribute the click to (useful for icon-only buttons where the clickable label lives in a neighboring element). |
📦 queue — how facts are sent
"queue": {
"pushInterval": 30,
"bufferSize": 1000,
"batchSize": 100,
"outputPath": "",
"prettyOutput": false,
"httpTimeoutMs": 5000
}
|
Field |
Default |
Purpose |
|---|---|---|
|
|
|
Seconds between pushes to GermainUX. |
|
|
|
Max facts held in memory before older ones are dropped. |
|
|
|
Max facts sent per HTTP request. |
|
|
(none) |
When set, facts are written to this local file instead of sent over HTTP — useful for offline capture/testing. Leave unset for normal operation. |
|
|
|
Indent the JSON when writing to |
|
|
|
Timeout for each data-upload HTTP request. Keep this well below the Agent's worker shutdown grace period so a final flush at shutdown has time to complete or fail cleanly rather than being force-killed mid-send. |
🛡️ proxy
"proxy": {
"url": "http://localhost:",
"delayInit": false,
"excludedPorts": [ 443, 4225, 4226 ],
"minPort": 10000
}
Configures an outbound proxy used for HTTP monitoring interception. excludedPorts keeps
the proxy from intercepting traffic on ports you don't want captured (commonly the
GermainUX ports themselves, to avoid the agent monitoring its own uploads).
🐛 debug
"debug": {
"walkSiblings": false,
"walkTree": false,
"dumpTree": false,
"dumpSnapshot": false,
"printCreateWinInfo": false
}
Verbose diagnostic logging switches for the UI Worker's own troubleshooting (dumping
control trees, sibling walks, snapshot contents, etc. to the log). All should stay false
in normal production use — they add log volume and are only useful when working with
support to diagnose a UI-capture issue.
💡 logging
"logging": {
"level": "INFO",
"name": "myapplication",
"path": "C:/germainagent/"
}
Informational metadata carried with the rule. This does not control where the Worker or
Profiler actually write their log files — that's governed entirely by the
LogPath/WorkerLogLevel/CLRLogLevel registry settings described in
Agent Settings. Leave this block as-is or omit it.
🌐 globalVariables — context attached to every fact
"globalVariables": [
{
"name": "username",
"filter": { "type": "text", "name": "UsernameControlAutomationId" },
"lowerCase": true
}
]
Each entry describes a value to read from the UI once and attach as context to every
fact emitted afterward (e.g. the logged-in username shown somewhere in the app's chrome).
See Context variable fields below for the full field list —
userClicks selector variables use the exact same shape.
🔒 Data privacy: masking and exclusions
Two related, independent settings control what raw values are allowed to leave the
machine:
"maskAll": false,
"exclusions": [
{
"name": "Suppress username from facts",
"fieldName": "user.name",
"type": 1,
"factType": "",
"pattern": "",
"preserveLength": true,
"preserveWhitespace": false
}
]
|
Field |
Purpose |
|---|---|
|
|
When |
|
|
Descriptive label for the rule (not sent anywhere, just for your own reference). |
|
|
Dot-path of the fact field this rule applies to (e.g. |
|
|
Optional — restrict the rule to a specific fact |
|
|
Which sanitization to apply — see table below. |
|
|
Optional regular expression. When set, only the matched portion of the value is sanitized; when omitted, the entire value is sanitized. |
|
|
Mask-only: replace each character with |
|
|
Mask-only: leave whitespace characters unmasked so the text still "looks like" separate words. Default |
type values:
|
|
Behavior |
|---|---|
|
|
Replace the value (or matched portion) with |
|
|
Replace the value with a one-way hash — irreversible, but two occurrences of the same original value hash identically, so equality-based analysis still works. |
|
|
Remove the value entirely. |
✨ features — everything else is opt-in
All monitoring capability beyond basic identification lives under features, one block
per capability, each independently enabled. This section is required to be present in
the rule JSON (even if every sub-feature inside it is left off).
📊 processMetrics
"processMetrics": {
"enabled": true,
"interval": 60,
"clrFallbackEnabled": false,
"threadDetailEnabled": true,
"threadPoolEnabled": false,
"resourceCountersEnabled": false
}
Periodic OS-level process metrics: CPU %, working-set memory, thread count/detail, and
(optionally) handle/GDI/USER object counts and disk I/O deltas — the classic native
resource-leak indicators for a long-running desktop app.
|
Field |
Purpose |
|---|---|
|
|
Turn on collection and set its interval (seconds). |
|
|
Only relevant for a headless .NET process with no UI Worker — makes the CLR Profiler collect these metrics itself instead. Leave |
|
|
Emit a per-thread CPU/state sample (via WMI), not just a total thread count. |
|
|
Also report a best-effort count of |
|
|
Also report process handle count, GDI object count, USER object count, and disk read/write byte deltas since the last sample. |
♻️ gcMetrics
"gcMetrics": { "enabled": false, "interval": 60 }
.NET garbage-collection metrics: a fact per GC pause as it happens (with generation,
whether it was induced, and heap size before/after), plus periodic cumulative gen0/1/2
collection counts every interval seconds. Requires the CLR Profiler to be attached.
⏱️ freezeDetection
"freezeDetection": { "enabled": false, "pollIntervalMs": 2000, "hangTimeoutMs": 2000 }
Periodically pings each monitored window (pollIntervalMs) to see if it's still
responding to messages, waiting up to hangTimeoutMs before considering it hung. A fact
is emitted when a previously-hung window recovers, reporting how long it was
unresponsive. This runs its own background thread independent of other UI monitoring, so
it has a real (small) always-on cost — weigh that against how important freeze detection
is for the target application.
🔬 profiler — CPU sampling
"profiler": { "enabled": false, "interval": 1000, "inclusions": [], "exclusions": [] }
Stack-sampling CPU profiler (requires the CLR Profiler attached). interval is how often
(ms) a stack sample is taken. inclusions/exclusions are executable-name filters for
which modules' code should appear in samples (both empty = everything).
📡 http — outbound HTTP monitoring
"http": {
"enabled": false,
"sslEnabled": true,
"sslPorts": [ 443 ],
"excludedPorts": [ 4225, 4226 ],
"collectRequestBody": true,
"requestBodyExclusions": "",
"collectResponseBody": true,
"responseBodyExclusions": ".*(css|js|png|jpg|jpeg|gif|svg|mpg)",
"collectHeaders": false
}
Captures outbound HTTP(S) calls made by the target process, broken into connect/wait/
download timing phases.
|
Field |
Purpose |
|---|---|
|
|
Enable TLS interception on the listed ports, needed to see HTTPS request/response detail (not just timing). |
|
|
Ports never monitored — typically your own GermainUX ports, to avoid capturing the agent's own uploads. |
|
|
Whether to capture request bodies, and a regex of URLs/content to skip (e.g. large binary uploads). |
|
|
Same, for responses — the sample regex above skips static assets (CSS/JS/images) as noise. |
|
|
Also capture request/response headers, including parsed |
❌ exception
"exception": { "enabled": false }
Reports managed (.NET) exceptions as they're thrown, with a captured stack trace.
Requires the CLR Profiler.
👁️ monitor — core UI activity
"monitor": {
"enabled": true,
"windowEventsEnabled": true,
"mouseEnabled": false,
"changeEnabled": false,
"keyboardEnabled": false
}
The baseline UI Worker monitoring feature; everything below it in this section
(clickMaps, userClicks, userSearches, userFacingErrors) requires enabled: true
here as well.
|
Field |
Purpose |
|---|---|
|
|
Master switch — when |
|
|
Report windows being created/destroyed, and the app's own startup. |
|
|
Report every mouse click on a control (raw telemetry — not filtered to specific business actions; see |
|
|
Report when an input control's value changes (text edits, checkbox/radio toggles). |
|
|
Report command-style key presses (Enter, Escape, function keys, Ctrl/Alt/Win combinations) — not general text typed into fields. |
🔥 clickMaps / mouseMaps — heatmap overlays
"clickMaps": { "enabled": false },
"mouseMaps": { "enabled": false }
clickMaps reports one point per click (control + screen position) for click-heatmap
visualizations. mouseMaps buffers raw mouse movement and flushes it periodically (every
5 minutes, on view change, or at shutdown) for movement heatmaps. Both are tagged with
whichever "view" was most recently identified by a userClicks selector — see below.
☝️ userClicks — business-level click tracking
"userClicks": {
"enabled": false,
"typeValue": "Native:User Click",
"selectors": {
"Button Click": {
"prefix": "Click on",
"filter": { "type": "button" },
"variables": [
{
"name": "argument1",
"contextScan": false,
"filter": { "name": "hierarchy" },
"childIndex": 0
}
],
"requireDoubleClick": false
}
}
}
Unlike monitor.mouseEnabled (which reports every click), userClicks only reports a
click when it matches one of your named selectors — intended for tracking specific
business actions ("clicked Submit", "opened a customer record") rather than raw UI noise.
|
Field |
Purpose |
|---|---|
|
|
The fact |
|
|
Which control this selector matches — see Filter fields. |
|
|
Text prefix used when building a description of the click (default: "Click on \<control\>"). |
|
|
Optional regexes further constraining the match against the control's label/name. |
|
|
Only match when the click was paired as a double-click (e.g. a DataGrid row drill-down), not a single click. |
|
|
Extra context variables captured from elsewhere in the UI and attached to this specific click's fact. |
🔍 userSearches
"userSearches": {
"enabled": false,
"selectors": {
"Search Button": {
"trigger": { "type": "button", "name": "SearchButton" },
"valueControl": { "name": "SearchBox" }
}
}
}
When the trigger control is clicked, the current value of the separate valueControl
is captured as the search term. Use this for search boxes where the meaningful value
lives in a different control than the one that was clicked.
userFacingErrors
"userFacingErrors": {
"enabled": false,
"rules": [
{ "label": "Error Dialog", "pattern": "[Ee]rror|[Ff]ailed|[Ee]xception" }
]
}
Reports when a newly created window's title matches one of the given regular
expressions — a lightweight way to detect error/failure dialogs without needing to
identify their exact controls. label becomes the fact's name field on a match.
🎞️ replay — session replay
"replay": {
"enabled": false,
"pointerEnabled": true,
"controlMutationEnabled": true,
"structuralChangesEnabled": true,
"windowLifecycleEnabled": true,
"focusMonitoringEnabled": true,
"scrollMonitoringEnabled": true,
"scrollbarMonitoringEnabled": true,
"fragmentMaxEvents": 1000,
"fragmentMaxAge": 15,
"snapshotInterval": 30,
"iconWin32Enabled": false,
"thumbnailRules": [],
"colorSamplingEnabled": false,
"colorSamplingTypes": [ "pane", "window", "group", "button" ],
"selfHealEnabled": false,
"selfHealBurstThreshold": 3
}
Captures enough of the UI tree over time to reconstruct a visual replay of the session,
independent of the plain monitoring facts above.
|
Field |
Purpose |
|---|---|
|
|
Master switch for all replay capture. |
|
|
Record mouse clicks/moves. |
|
|
Record control property changes (label, bounds, visibility). |
|
|
Record controls being added/removed from the UI tree. |
|
|
Record windows being created/destroyed. |
|
|
Record focus changes between controls. |
|
|
Record scroll position/size and scrollbar thumb/track position changes. |
|
|
Flush the current batch of replay events after this many events, or this many seconds, whichever comes first. |
|
|
Emit a full state snapshot at least this often (seconds); |
|
|
Capture each monitored window's application icon (via |
|
|
Ordered rules selecting which child controls get a captured visual thumbnail, and when to re-capture one — see Thumbnail rule criteria. |
|
|
Sample background/border colors of controls (restricted to the listed UIA control type names; empty = all types) so the replay viewer can approximate styling without a full screenshot. |
|
|
Periodically re-scan the live UI tree to correct any drift between it and the Worker's in-memory copy; a rescan is also triggered after this many consecutive forced/deadline flushes in a row. Leave off unless replay data has been observed to drift from the real UI. |
🏷️ Filter fields
Used by userClicks/userSearches selectors, globalVariables, and context variables,
to identify a specific control:
|
Field |
Matches on |
|---|---|
|
|
UIA control type, as a lowercase name ( |
|
|
The control's UIA AutomationId. |
|
|
The control's visible label/name text. |
|
|
The Win32 window class name. |
Any combination of these can be set; all specified fields must match (AND).
📑 Context variable fields
Used by both globalVariables and a userClicks selector's variables:
|
Field |
Purpose |
|---|---|
|
|
The name this value is attached to the fact under. |
|
|
Which control to read the value from — see Filter fields above. |
|
|
If ≥ 0, read from the Nth child of the matched element instead of the element itself. |
|
|
If non-zero, read from a sibling offset by this many positions instead. |
|
|
Optional regex applied to the raw value; if present, only the matched portion is kept. |
|
|
Whether to actively search the UI tree for this value ( |
|
|
If |
|
|
Lowercase the captured value. |
🖼️ Thumbnail rule criteria
Each replay.thumbnailRules[] entry has a method ("win32" or "screen" capture) and
a criteria object of AND'd match conditions, evaluated against each control the Worker
encounters:
|
Criteria field |
Matches |
|---|---|
|
|
UIA control type name(s) that must / must not match (see Filter fields for the naming convention). |
|
|
AutomationId(s) that must / must not match. |
|
|
Exact Win32 class name(s) that must / must not match. |
|
|
Win32 class name, split on whitespace into tokens: "any of these tokens present" / "all of these tokens present" / "none of these tokens present". |
|
|
Visible label(s) that must / must not match (use |
|
|
|
|
|
|
Each rule also takes a top-level recaptureOn array listing which attribute changes
(label, visibility, bounds, value, selected) should trigger re-capturing the
thumbnail for a matched control after its first capture; omit it (or leave it empty) to
capture once and never recapture.
📄 Full example
See Config/agent-config-sample.json in the agent source tree for a complete, valid
starting-point file combining every block above with realistic default values and a
"description" field explaining it's meant to be copied and customized per application.
🖼️ Thumbnail rule criteria
Each replay.thumbnailRules[] entry has a method ("win32" or "screen" capture) and
a criteria object of AND'd match conditions, evaluated against each control the Worker
encounters:
|
Criteria field |
Matches |
|---|---|
|
|
UIA control type name(s) that must / must not match (see Filter fields for the naming convention). |
|
|
AutomationId(s) that must / must not match. |
|
|
Exact Win32 class name(s) that must / must not match. |
|
|
Win32 class name, split on whitespace into tokens: "any of these tokens present" / "all of these tokens present" / "none of these tokens present". |
|
|
Visible label(s) that must / must not match (use |
|
|
|
|
|
|
Each rule also takes a top-level recaptureOn array listing which attribute changes
(label, visibility, bounds, value, selected) should trigger re-capturing the
thumbnail for a matched control after its first capture; omit it (or leave it empty) to
capture once and never recapture.
📄 Full example
See Config/agent-config-sample.json in the agent source tree for a complete, valid
starting-point file combining every block above with realistic default values and a
"description" field explaining it's meant to be copied and customized per application.
🖼️ Thumbnail rule criteria
Each replay.thumbnailRules[] entry has a method ("win32" or "screen" capture) and
a criteria object of AND'd match conditions, evaluated against each control the Worker
encounters:
|
Criteria field |
Matches |
|---|---|
|
|
UIA control type name(s) that must / must not match (see Filter fields for the naming convention). |
|
|
AutomationId(s) that must / must not match. |
|
|
Exact Win32 class name(s) that must / must not match. |
|
|
Win32 class name, split on whitespace into tokens: "any of these tokens present" / "all of these tokens present" / "none of these tokens present". |
|
|
Visible label(s) that must / must not match (use |
|
|
|
|
|
|
Each rule also takes a top-level recaptureOn array listing which attribute changes
(label, visibility, bounds, value, selected) should trigger re-capturing the
thumbnail for a matched control after its first capture; omit it (or leave it empty) to
capture once and never recapture.
📄 Full example
See Config/agent-config-sample.json in the agent source tree for a complete, valid
starting-point file combining every block above with realistic default values and a
"description" field explaining it's meant to be copied and customized per application.
🖼️ Thumbnail rule criteria
Each replay.thumbnailRules[] entry has a method ("win32" or "screen" capture) and
a criteria object of AND'd match conditions, evaluated against each control the Worker
encounters:
|
Criteria field |
Matches |
|---|---|
|
|
UIA control type name(s) that must / must not match (see Filter fields for the naming convention). |
|
|
AutomationId(s) that must / must not match. |
|
|
Exact Win32 class name(s) that must / must not match. |
|
|
Win32 class name, split on whitespace into tokens: "any of these tokens present" / "all of these tokens present" / "none of these tokens present". |
|
|
Visible label(s) that must / must not match (use |
|
|
|
|
|
|
Each rule also takes a top-level recaptureOn array listing which attribute changes
(label, visibility, bounds, value, selected) should trigger re-capturing the
thumbnail for a matched control after its first capture; omit it (or leave it empty) to
capture once and never recapture.
📄 Full example
See Config/agent-config-sample.json in the agent source tree for a complete, valid
starting-point file combining every block above with realistic default values and a
"description" field explaining it's meant to be copied and customized per application.
🖼️ Thumbnail rule criteria
Each replay.thumbnailRules[] entry has a method ("win32" or "screen" capture) and
a criteria object of AND'd match conditions, evaluated against each control the Worker
encounters:
|
Criteria field |
Matches |
|---|---|
|
|
UIA control type name(s) that must / must not match (see Filter fields for the naming convention). |
|
|
AutomationId(s) that must / must not match. |
|
|
Exact Win32 class name(s) that must / must not match. |
|
|
Win32 class name, split on whitespace into tokens: "any of these tokens present" / "all of these tokens present" / "none of these tokens present". |
|
|
Visible label(s) that must / must not match (use |
|
|
|
|
|
|
Each rule also takes a top-level recaptureOn array listing which attribute changes
(label, visibility, bounds, value, selected) should trigger re-capturing the
thumbnail for a matched control after its first capture; omit it (or leave it empty) to
capture once and never recapture.
📄 Full example
See Config/agent-config-sample.json in the agent source tree for a complete, valid
starting-point file combining every block above with realistic default values and a
"description" field explaining it's meant to be copied and customized per application.