Files
datacontroller.io/content/feed/support-diagnostics/index.md
T

8.0 KiB

title, description, date, author, authorLink, tags, previewImg
title description date author authorLink tags previewImg
Support Diagnostics: browser_info and browser_url_vars Data Controller sends two input tables with the startup service and with every service that runs customer code - the browser context and the page URL parameters. A support ticket can be answered from the job log, and a hook can read them. 2026-09-27 09:00:00 Data Controller https://www.linkedin.com/showcase/data-controller-for-sas
Announcements
./support-diagnostics.jpeg

Support Diagnostics: browser_info and browser_url_vars

A support ticket usually starts with a round of questions. Which browser? Which timezone? Which build? What was in the URL? Data Controller now answers them itself. The frontend sends two input tables - browser_info and browser_url_vars - with the startup service and with every service that runs customer-provided code, so the context of a session is in the job log before anyone asks for it, and a hook script can read it.

This ships with the next Data Controller release.

The two tables

Both are ordinary input tables on the request: the adapter turns each array in the payload into a table in the service's WORK library, so they arrive as work.browser_info and work.browser_url_vars.

browser_info

A single row, describing the client that made the request:

Column What it holds
url The URL of the Data Controller page itself (the iframe), not the document embedding it - so an embedded editor is distinguishable from a standalone one.
referrer The embedding document, from document.referrer. For an embedded report this is the report URL.
timezone The browser's IANA timezone, e.g. Europe/Berlin.
tz_offset The browser's UTC offset in minutes, as Date.getTimezoneOffset() returns it - positive west of UTC, so Europe/Berlin in summer is -120.
locale The browser locale, e.g. en-GB.
dc_version The Data Controller build, as appinfo() reports it.
adapter_version The @sasjs/adapter version the client was built with.
browser The browser family, parsed from the user agent.
browser_version The browser version.
platform The operating system.
user_agent The raw navigator.userAgent string.

browser_url_vars

One row per URL parameter:

Column What it holds
name The parameter name.
value The parameter value.

Parameters are read from both the search string and the hash query string, because Angular routes carry them after the #. Where the same name appears in both, the search string wins. The table is sent only when the page URL has at least one parameter.

Which services receive them

Service When
public/startupservice Once per session, so every job log carries the session context
editors/getdata The PRE_EDIT_HOOK
editors/stagedata The POST_EDIT_HOOK, through the loader
editors/restore The POST_EDIT_HOOK, through the loader
editors/getdynamiccolvals The dynamic cell dropdown programs
auditors/postdata The PRE_APPROVE_HOOK and POST_APPROVE_HOOK
editors/loadfile The POST_EDIT_HOOK - see the caveat below

Those are the services that run customer code. The viewer's viewlibs, viewtables and viewdata, the metadata services and usernav receive neither table, so the high-frequency calls stay lean.

Reading them in a hook

A hook script is %included into the running service, so it shares the service's WORK library and can read the tables directly. Take an embedded editor opened at:

https://datacontroller.io/#/editor/SASHELP.CLASS?labels=true&embed=va

browser_url_vars then holds a row per parameter - labels=true and embed=va - and a hook can read either by name:

%let labels=N;
%let embed=N;

%if %sysfunc(exist(work.browser_url_vars)) %then %do;
  proc sql noprint;
    select value into :labels
    from work.browser_url_vars
    where name = 'labels';
    select value into :embed
    from work.browser_url_vars
    where name = 'embed';
  quit;
%end;

Or branch on what the user's browser reports:

data _null_;
  set work.browser_info;
  call symputx('dc_timezone', timezone);
  call symputx('dc_locale', locale);
run;

Note the exist() guard. A service called directly, or by a frontend older than this feature, has no work.browser_info at all, so an unguarded set or select would fail the job. Guard first, then read.

In the job log

Turn debug on and the session initialisation writes both tables to the log, so a ticket can be diagnosed without a second round of questions:

NOTE: MPEINIT: work.browser_url_vars:
name=embed value=va
NOTE: MPEINIT: work.browser_info:
url=... referrer=... timezone=Europe/Berlin tz_offset=-120 locale=en-GB
dc_version=... adapter_version=... browser=Chrome browser_version=...
platform=Linux user_agent=...

The debug value depends on the path - &_debug=131 on the Compute API and SAS 9, &_debug=128 on the Viya web (JES) path with runAsTask enabled, which is what the frontend sends there. Either way, switching debug on in the app is enough.

Worth knowing

  • The values are supplied by the client. Treat them as diagnostics hints, never as a security boundary.
  • editors/loadfile is reached through the adapter's multipart file upload, which carries no input tables, so in normal use that service - and the hook it runs - does not receive them. It is on the list for completeness.
  • Nothing else pays for the tables: they travel only with the services above.

Full detail, including the column definitions and the log output, is in the troubleshooting documentation.