From 6469ad00a39b623815a8f40060a3845ced0b1db6 Mon Sep 17 00:00:00 2001 From: blog-dev Date: Sun, 27 Sep 2026 20:31:42 +0000 Subject: [PATCH] feed: add a support diagnostics post (browser_info, browser_url_vars) --- content/feed/support-diagnostics/index.md | 148 ++++++++++++++++++ .../support-diagnostics.jpeg | Bin 0 -> 30302 bytes 2 files changed, 148 insertions(+) create mode 100644 content/feed/support-diagnostics/index.md create mode 100644 content/feed/support-diagnostics/support-diagnostics.jpeg diff --git a/content/feed/support-diagnostics/index.md b/content/feed/support-diagnostics/index.md new file mode 100644 index 0000000..0731b1f --- /dev/null +++ b/content/feed/support-diagnostics/index.md @@ -0,0 +1,148 @@ +--- +title: 'Support Diagnostics: browser_info and browser_url_vars' +description: 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. +date: '2026-09-27 09:00:00' +author: 'Data Controller' +authorLink: https://www.linkedin.com/showcase/data-controller-for-sas +tags: + - Announcements +previewImg: './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 `%include`d into the running service, so it shares the service's WORK library and can read the tables directly. A URL parameter by name: + +```sas +%let labels=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'; + quit; +%end; +``` + +Or branch on what the user's browser reports: + +```sas +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](https://docs.datacontroller.io/dci-troubleshooting/). + + + + diff --git a/content/feed/support-diagnostics/support-diagnostics.jpeg b/content/feed/support-diagnostics/support-diagnostics.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..822f5f2c75759839448a57c2777d5c45254e1d2e GIT binary patch literal 30302 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

g zzR<#8y+wACC8E)|P(+s3A@?psn7`z&{=RaK4^ynrho8J;h8eQ1!I7%)0~$jgCKy28 zRaj&qt@}XianehE^`&Kf1#$aN59g4f?N0S=oryU2VMo<=Nhi=fFO5IrJ0g&$oyV4K zm|t|UM)HK9h?apwuak>P9Gi3im<@&BGpm2eJ!3Ww<$GGoY^5C?BC(qU zv%iY(1PX(nC8pmNljqf+F-e0NFy|nxdyv#~CWGXB1VpYB5erV$Hyi)%<6ujJ>8JC$ zv+~Lh)({aNmSSE20k^6NWIuL-EJXemi*Z^q)LIWG9t3&R7L)Y(Adeb5cB#hZ67GHy zLGz6S2>xA9QhNllBZ2uhU%Ieyhyg5-nMgu5GHsx7oY|Gc2p?og>|jx}NA-n0Fawdk zDB=xZXL^LqBF3~0KeUL0$c}7ZeDFE9doR9O$)S#P9zV8)5jY%`X7!F#^Av3R04MNa zo~^qdvX-mii^0Io>e^&dASFQ^*|(Jp1s$~CY9N|Do4i?_n?OiAjXVUZoBRe4`O`u=LK*%zr*uEZJ0@GW7ms`}wWmAjE8N8~On=yUtAO*? z*nqB`njzY_-k>~?X!1>)D0;oO3L1fI5XPFDIJtL|wi$sKH8Qjs>Y(1E##B(_lM40^ z((tL5k16uL+Pj}i+WNd|e(jsBXERCWC=qi#PUlBh@Q~jGvJpKZi$rBS*jOx{l5bLh z9Xq+t5;k)5e(t-FmWLcSG!JZjzq;s1?2(X!X9>2e664QSDV{k_hL}IN;X4hh@O@G! zN@}wD84%8{uq**EWCa=O*BMh9_izI_yZsZ_uce%LovK%e>bpg_LAtyRyf}0qjE$0T zd~x(~9nnvxO;P zb-8^HBP{qr(M+E@`awed>E_UJ zo}o}8J4lOh51YVT!3U!V^q8>)?LMtwFNmjPXc+Xb6<7miVEYjaDH28WCPO`G-Ebfg zP5_A@3?u>wVErT!@{vOu-ysS7U}fe~8X-fR7%_Yw6B!ydKB@IUM)irWr>RF9K+ubHhWG;jc?bt zUn9}LNjoIN9g);S{-dv&<|O_xs&qY(7D>D^_nJZJ|E||e)|PgGQW2#ig=n+mdR{O+Hs5@K zWYMb+uM6{?=*XVbb4}K-H)7B!Dy+Dl9+>^XVt+zljkt%I2>^-CZj`5X+-n}h6va38 z!rQbeKd&?zjFO5l-JN%+ANv!||F;n$3-sq@D>nNC(SeeevspJzG5JP4uzJ7lOcs6d zkd3(2rKq;_@wJMcmnS8}Uc@YKUwhxg<@LF}el>TyeR>*!ZL#Q+;S&lBpCq4WnW1z6 zZ>bQq@DTYr|=qJ053<1M#R*RDk zPca?!q)xEF$Un#Wbtc6lfu$q-(Go*WSz`1+@>2}Oj!%Z-p-qirqsVL|(c31hbzHQY z1DIdTpDe?&&z9jMAu*NfkbhfM)NMj+sIV%46TpUvGPifY{x(KFM4FwL^ zj0^vYd$BRxHzI@|3Ohy`qisy+c<=QhWg&) zt7r|CsTqg^mNIr9z8(nzOQ^GnrbG!_}Q>x=H!y#LgS1(#)GRwltkET~6w&caY)p zwtBP-Xq#-p&7oucRrn9jP(bKrs&G-%lbIQ1;|(uSEbo*E*@~8i}57?(zT4k#W3M5bCkEpQh>Fy$e0I4m_%H zo3Iip8+Nuo+6pa%7_)@$qNH&5`woa-J1iQnh{#x(*3i?@B@S~2xw4eKcYlk$uNF-r z4Bwc{C&n63NeTc;;oP58eyzYSz4pD((hT18&-n=llg?2Z+y=kF^@3iFr0nuUBgrMg z?5?Kl7j^xYJcluB_%gmZ1lDJRV`3(n+Q{_7r1zn~Fa*19d_J?^p0d82a;K;P6Uhj6 zo=9OFJw|jTl-qJ2A7AY{@(kYKHvk9wUQC@RzSwuIGZtRA0b{5B1OxWA*kIU;e@RC) z&Su#($)zx1#L-(k#sRkPm^#6Au6a77!_gCjyf55}1nE;#Cmy%w+WohT^RT*h=7Kt| z=llddyAK*q6A