Logging Handler & UI Overlay

Logger that optionally mirror to a on-page UI overlay shared by every page match-common userscript

Este script não deve ser instalado diretamente. É uma biblioteca destinada a ser incluída por outros scripts através da diretiva de metadados // @require https://update.greasyfork.org/scripts/588114/1893327/Logging%20Handler%20%20UI%20Overlay.js

Terá de instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Terá de instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Terá de instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Terá de instalar uma extensão como Tampermonkey para instalar este script.

Terá de instalar uma extensão de gestão de scripts de utilizador para instalar este script.

(Já tenho um gestor de scripts de utilizador, deixe-me instalá-lo!)

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

(Já tenho um gestor de estilos de utilizador, deixe-me instalá-lo!)

Autor
SassyRhombus
Versão
1.4.0
Criado
22/07/2026
Atualizado
04/08/2026
Tamanho
20 KB
Licença
BSD-0

Overview

One logging call, two sinks: the browser console and a translucent, click-through log panel pinned to the top-right of the page.

  • exposed as a global (LoggingUI.create) for @require
  • the panel is a page-level singleton, keyed off its DOM id. Userscripts run in separate sandboxes and cannot see each other's variables, but they do share the document -- so the first instance to need the panel builds it and every later instance adopts the node that is already there. n scripts on a page therefore produce one panel, not n stacked copies.
  • every line carries a timestamp and the caller's short tag; the tag is coloured from a hash of itself, so each participating script is distinguishable at a glance without any central registry of colours
  • severities are debug/info/warn/error, coloured white/blue/yellow/red, and map onto console.debug/info/warn/error
  • the panel body is dimmed until hovered, so it never competes with the page

Toggle scoping:

  • the panel is shared page state. Flipping it from any instance shows/hides the one panel for all of them. Since 1.1.0 the state lives in data-logui-panel on documentElement rather than on the panel node: it is the one surface that exists before any logger does, so a controller with no logger of its own can drive it. Instances observe the attribute, follow it, and persist it as their own preference.
  • the menu command is owned page-wide by the Logging UI Controls shim (logging-ui-controls.shim.user.js), which matches every site, stays inert until a logger announces itself via data-logui-consumers, and stamps the remembered choice at document-start so it wins over each script's default instead of racing it. Consumers should no longer call registerMenuCommands(); doing so just duplicates the entry.
  • the console mirror is per-instance: each script decides independently whether it also talks to devtools, and there is no menu command for it. A script that wants the mirror user-controllable exposes it in its own settings UI.

Persistence is best-effort. GM_getValue and GM_setValue are used when the calling script granted them; without the grants the toggles simply live for the lifetime of the page.

Compatibility: 1.1.0 still mirrors data-logui-visible onto the panel node, so a 1.0.x instance pinned by some other script on the same page keeps following flips. The reverse does not hold — a 1.0.x instance writes only the panel node, which nothing in 1.1.0 observes — so read a mixed-version page as "1.1.0 leads, 1.0.x follows".

Usage

// @require https://update.greasyfork.org/...
// @grant   GM_getValue
// @grant   GM_setValue
// @grant   GM_registerMenuCommand

const logger = LoggingUI.create({
    name: '$NAME',             // console prefix
    tag: 'gcr',                // panel prefix
    console: true,             // default console mirror
    panel: true,               // default panel visibility
    panelSink: !IS_PROFILE,    // this instance may write
});

logger.info('feed seeded with %d comments', n);
logger.warn('poll failed:', err);
logger.consoleOnly.debug('response body', dump);  // devtools, never the panel

Options

LoggingUI.create(cfg):

key default note
name falls back to tag console prefix, bracketed
tag name, else 'log' panel prefix, and the identity announced in data-logui-consumers
console true initial console mirror; a stored preference outranks it
panel true initial panel visibility; both the page-wide state and a stored preference outrank it
panelSink true false makes this instance console-only — it never builds or writes the panel, but still tracks panelEnabled for a settings UI
prefsKey 'logui_prefs' GM storage key; give each script its own to keep preferences separate
maxLines 500 panel scrollback, oldest lines dropped
minLevel 'debug' severity floor, dropped before either sink

The floor here is the library's; a script wanting a user-facing verbosity control layers its own on top and gates its call sites (the mail helper's Advanced "Log level" slider is the worked example). Keep the two distinct: minLevel is how severe a line must be, consoleOnly is where it is allowed to go.

Module exports

export note
LoggingUI.create the factory
LoggingUI.LEVELS ['debug','info','warn','error'], in ascending severity
LoggingUI.ATTRS 1.1.0 — { state, consumers }, the documentElement attribute names, so a controller can reason about the page's loggers without owning one

The controls shim

logging-ui-controls.shim.user.js is a standalone userscript, not part of the library. It owns the "Toggle log panel" menu command for the whole page.

  • @match *://*/*, @run-at document-start. Stamping data-logui-panel before any consumer constructs means the remembered choice wins over each script's panel default rather than racing it.
  • it deliberately does not @require the library. It runs on every page and must stay inert on the overwhelming majority that have no logger; pulling in the overlay CSS and instance machinery just to conclude "not here" is the wrong trade. Its contract is the two attributes, reimplemented in miniature.
  • self-suppressing: no menu entry until something announces itself in data-logui-consumers. Consumers announce as they construct, which can be well after document-start, so the shim watches the attribute rather than sampling once.
  • it follows as well as leads. A consumer flipping the panel from its own settings UI is still the authority; the shim observes and re-persists so its remembered value does not drift.
  • its own preference lives under logui_controls, separate from any consumer's prefsKey. Default is off.

Consumer migration to 1.1.0:

  • drop the registerMenuCommands() call; the shim provides the entry, and a second registration is a duplicate row that moves only that one script.
  • a script that had exposed "Toggle console logging" through the library loses it — that command is gone. Console mirroring is per-script state, so surface it in the script's own settings UI if users need it.
  • an in-script panel toggle is still fine and is now a view onto the shared state, not a private flag. The mail helper keeps its Advanced "Toggle logging UI" row on exactly these terms.
  • route high-volume dumps through logger.consoleOnly.* so they reach devtools without flooding the panel every other script shares.

API

method(s) note
logger.debug info
logger.log(...args) alias of info
logger.consoleEnabled getter/setter (boolean)
logger.panelEnabled getter/setter (boolean, page-wide)
logger.consoleOnly.debug info
logger.toggleConsole() / togglePanel() flip and persist, returns new state
logger.registerMenuCommands(opts) deprecated 1.1.0 — the shim owns the panel command
logger.clear() empty the shared panel
logger.element the panel node, or null