Problem/Motivation
JSerror doesn't work on any supported version of Drupal.
The 8.x-1.x branch was never actually ported. It sits on the same commit as 7.x-1.x (f860d62) and the code in it is still Drupal 7. jserror.info says core = 7.x, and the module uses .tpl.php templates, hook_menu(), l(), format_date(), format_interval() and truncate_utf8(). All of those went away in Drupal 8.
You can't install it on Drupal 11. Drupal doesn't even see the module, because there is no jserror.info.yml. Running PHPStan over it produces 84 errors, nearly all of them calls to functions that no longer exist.
I'm reviving the module. The branch name is part of the problem here: anyone who finds the project assumes a Drupal 8 version exists when it doesn't.
Steps to reproduce
- Install Drupal 11.
- Clone the module into
web/modules/contrib/jserrorand check out8.x-1.x. - Run
drush en jserror.
You get Unable to install modules jserror due to missing modules jserror.
Proposed resolution
Rewrite for Drupal 11 on a 1.0.x branch, keeping the part of the module that's actually worth keeping: a small inline script in the head that installs an error handler before any other script runs, buffers whatever it catches, and only fetches the reporting script once the page has loaded. That's what lets it catch errors thrown while the site's own JavaScript is still loading, which is when the interesting breakage tends to happen.
Structure
- Routing and controllers instead of
hook_menu()and page callbacks. ConfigFormBaseand ajserror.settingsconfig object instead ofvariable_get().- Twig templates instead of
.tpl.php. - Two services,
JserrorStorageandUserAgentParser, holding the query and parsing logic. - Unit, Kernel and Functional tests.
Behaviour
- Browscap is no longer a dependency. It ships a large data file that needs regular updates, which is a lot to ask for browser name, major version and OS. There's a small parser in the module now.
- Unhandled promise rejections are captured too.
window.onerrornever sees those. - Sampling is decided in the browser rather than on the server. A server-side decision gets baked into the page cache and served to everyone, so the percentage you configure wouldn't mean much on a cached site.
- Report filters live in the query string instead of
$_SESSION, so a filtered report is a link you can paste into an issue. - New
administer jserrorpermission for the settings form and for clearing the log. Viewing the report still usesaccess site reports.
Schema fixes
lineandcolwereTINYINT, so anything past line 255 was destroyed on the way in. Most errors in bundled JavaScript are past line 255. They're plain integers now.browser_versionandplatform_versionwere integers and couldn't hold a value like10.15.7. They're strings now.
Reporting endpoint
The /jserror endpoint accepts anonymous writes, which is the whole point of it, so it needed tightening:
- It now requires a JSON content type and a same origin
Originheader. Form-encoded,text/plainand multipart POSTs are CORS simple requests, so any site could have made its visitors post here with no preflight and no consent. Those were accepted before. - Flood control counts stored rows instead of requests. One request can carry fifty errors, so counting requests understated the real number by a factor of fifty. Rejected submissions cost one unit as well, otherwise sending junk is free.
- Each field has a byte budget applied before storage. The page URL is copied into every row of a batch, so a generous limit gets multiplied by the batch size.
- Integers are clamped to the column range and the language value is stripped to ASCII, so a submission can't fail an insert and hand back a 500.
- The response reports how much of the batch was accepted, so the client re-sends the remainder instead of assuming it all landed.
Report screens
- Page URLs only become links when they use
httporhttps. Anything else is printed as text, so a storedjavascript:URL can't be clicked on a page only admins can reach. - Grouped errors are described using a real occurrence. Aggregating each column on its own produced browser and platform combinations that never actually happened together.
- The summary on the detail page reads a bounded sample of occurrences rather than only the current pager page.
Remaining tasks
- Rewrite (done)
- Unit, Kernel and Functional tests (done)
- PHPCS, PHPStan, ESLint and Stylelint passing (done)
- GitLab CI (done)
- Review
- Tag
1.0.0-alpha1 - Retire the
8.x-1.xbranch - Update the project page, it still lists Browscap as a requirement
User interface changes
- Settings are at
/admin/config/development/jserror, with rewritten labels and descriptions. - The report at
/admin/reports/jserrorlooks different, filtering happens through the query string, and clearing the log goes through a confirm form. - New permission: Administer JSerror.
API changes
All of it. Nothing from 7.x-1.x survives: no function, hook, template or theme function.
The reporting endpoint kept its field names (m, f, l, c, s, w) but takes a JSON body now instead of a form-encoded payload= parameter. Responses include accepted and stored counts.
Data model changes
The jserror table keeps its name and its column names. The types changed as described above. Settings moved from variable_get() to the jserror.settings config object.
There is no upgrade path from 7.x-1.x. No migration, and hook_update_N() doesn't apply because there was never a Drupal 8 or later release to update from. Treat any old error rows as throwaway.
Comments
Comment #3
abhishek-anand commentedComment #6
abhishek-anand commentedComment #8
abhishek-anand commented