docs

Developer Mode and Diagnostics

Turn on developer mode to catch API and data-binding mistakes during development.

On this page

You can turn on developer mode to catch mistakes when using the JavaScript API, the DOM API, and data bindings.

By default, these mistakes fail safely and silently— misspelled binding names, markup path typos, and calls on missing elements do nothing. Developer mode reports each mistake as it happens so you can fix it during development.

Turning On Developer Mode

To turn on developer mode, set Config::diagnostics.developer_mode to true before creating the renderer (or pass the config to App::Create() when using AppCore).

Config config;
#ifndef NDEBUG
config.diagnostics.developer_mode = true;  // development builds only
#endif

Platform::instance().set_config(config);
RefPtr<Renderer> renderer = Renderer::Create();
ULConfig config = ulCreateConfig();
#ifndef NDEBUG
ulConfigSetDeveloperMode(config, true);  // development builds only
#endif

ULRenderer renderer = ulCreateRenderer(config);
ulDestroyConfig(config);

🚧 Turn Off in Shipped Builds

Leave developer mode off in shipped builds. Running with diagnostics on slows down property reads on JavaScript API namespace objects. Shipped builds also ignore diagnostics environment variables when developer mode is off.

Reading Warnings

Diagnostics are sent to the Logger at LogLevel::Warning. Each message begins with an API tag such as [js] for the JavaScript API, [dom] for the DOM API, or [data] for data bindings.

If a script on the page accesses an undefined property on an exposed API:

JavaScript
console.log(myApp.verison);  // a typo for myApp.version

The library logs a warning with the suggested spelling:

[js] 'myApp.verison' is not defined. Did you mean 'version'?

When markup binds to a property that does not exist on a bound type:

HTML
<p>{{player.nmae}}</p>

The library logs the unmatched path:

[data] {{player.nmae}}: 'Player' has no field 'nmae'.

Where Warnings Appear

The library sends every warning to the logger— warnings tied to a specific page also appear in that page's console.

Destination Details
ultralight.log Written by AppCore for applications that call App::Create(). The log is saved in the per-user app data folder configured by Settings::developer_name and app_name rather than beside the executable. See Logging and Console Messages.
Logger Used by applications that call Renderer::Create(). If you don't register a Logger, the library produces no log output.
Page console View messages in the Web Inspector or receive them through ViewListener::OnAddConsoleMessage() with source kMessageSource_NativeAPI. Warnings tied to a specific page (such as misspelled exposed properties, markup path typos, or rejected style values) appear here across all API families. Setup and handle warnings (such as a failed attach, a malformed selector, or a call on a handle after a page navigates away) go to the logger only.

Setting Diagnostics Levels

You can configure diagnostics per API family using the DiagnosticsLevel enum. Both javascript and dom in Config::diagnostics default to DiagnosticsLevel::Auto.

Level Reports
DiagnosticsLevel::Off No per-operation warnings. Setup mistakes, such as a failed attach or a malformed listener selector, still warn at every level.
DiagnosticsLevel::Warn Reports each mistake as it happens.
DiagnosticsLevel::Strict Everything reported by Warn, plus stricter checks. Passing extra arguments to an exposed function raises a TypeError on the page, and every DOM call on an empty handle logs a warning.
DiagnosticsLevel::Auto Uses Warn when developer mode is on, and Off when developer mode is off (the default).

The dom field configures checks for both the DOM API and data bindings, while javascript configures the JavaScript API.

To set the DOM and data-binding diagnostics level to strict:

config.diagnostics.dom = DiagnosticsLevel::Strict;
ulConfigSetDOMDiagnostics(config, kULDiagnosticsLevel_Strict);

👍 Catch Calls That Have No Effect

If a DOM call has no effect, set the DOM level to DiagnosticsLevel::Strict. The library logs a warning for each call on an empty handle (eg, from a misspelled selector or an element that isn't on the page). This check applies only to the DOM API— data bindings already report markup mistakes at DiagnosticsLevel::Warn, so DiagnosticsLevel::Strict adds nothing for them.

Overriding Levels from the Environment

You can override diagnostics levels at launch without recompiling by setting environment variables.

Set UL_JS_DIAGNOSTICS for the JavaScript API and UL_DOM_DIAGNOSTICS for the DOM API and data bindings. Each variable accepts off, warn, or strict (case-insensitive).

To launch an application with strict DOM diagnostics from the command line:

Shell
# macOS and Linux:
UL_DOM_DIAGNOSTICS=strict ./MyApp

# Windows (PowerShell):
$env:UL_DOM_DIAGNOSTICS = "strict"; .\MyApp.exe

📘 Active Only in Developer Mode

The library reads diagnostics environment variables once at startup. These variables are ignored unless developer mode is on.

Checks Reported by Each API

Each API family checks for common mistakes specific to how native code interacts with the page.

Filtering Log Messages

The Config::diagnostics.min_log_level setting specifies the lowest severity the library sends to the Logger. It defaults to LogLevel::Info, which includes warnings and errors while hiding LogLevel::Debug messages.

Because diagnostics warnings are logged at LogLevel::Warning, setting the minimum level to LogLevel::Error or LogLevel::Fatal hides them.

To lower the threshold so debug messages also reach the log:

config.diagnostics.min_log_level = LogLevel::Debug;
ulConfigSetMinLogLevel(config, kLogLevel_Debug);