console-filter-js
v1.1.0
Published
Make native console.log() (and other console methods) proxied and filtered by text string
Maintainers
Readme
consoleFilter.js
version: 1.1
integrity check sha256: zXEmiXnwYJz3RMhOIi4VbhKOT1v3nvgs7k3MW+yK0z8=
Make native console.log() (and other console methods) proxied and filtered by text string.
Only pure javascript with TypeScript annotations, no other dependencies, libraries, frameworks or anything like that. TypeScript what? It's only about annotations, automatic tools can mark this class as a TypeScript library, but it's not true, just an autodetection failure, the script itself is pure javascript, only the annotations, interfaces, variable types described by TypeScript, ...
Use
Include file consoleFilter.mjs into document. Including should look like:
<script src="./consoleFilter.mjs?v=1.1" type="module" crossorigin="anonymous" integrity="sha256-zXEmiXnwYJz3RMhOIi4VbhKOT1v3nvgs7k3MW+yK0z8="></script>or inside javascript:
const { ConsoleFilter, methods, AsyncLogger } = await import( './consoleFilter.mjs?v=1.1' );After import all console commands are proxied!
How to set what to filter?
There are 2 options, and for both the same applies: you only fill in the parts of the settings you want to change; if you do not mention them, the default settings are used. You can find the default settings from the static read-only property ConsoleFilter.DEFAULT_SETTINGS. For clarity, that variable contains the default settings, not the current instance settings.
1. Configure via a JSON element.
What JSON element? This is a simplified term for script type="application/json", or script type="text/json" (even though this syntax is deprecated, it still works). It is important to know that script type="application/json" is treated by the browser as ordinary text, not as executable script! That means it does not block page rendering while the script runs; on the contrary, it has no effect on page rendering.
The important attribute here is id with the value console-filter-settings. The script looks for the element with that id. Since the script itself is a module (type="module"), it does not matter where in the page the JSON element is placed, whether in the header or at the end of the body.
<script type="application/json" id="console-filter-settings">
{
"allowlist": [ "items", "cart" ],
"blocklist": [ "items debug" ]
}
</script>The content must be valid JSON (e.g. no comma after the last item).
2. Inject settings via an HTTP GET parameter.
The second option is to place them in the HTTP GET parameter named settings. (You can find the parameter name from the static read-only property ConsoleFilter.SETTINGS_URL_PARAMETER.) The value is JSON encoded for use in URL, for example with encodeURIComponent( JSON.stringify( … ) ). Without encodeURIComponent() characters like &, # or + in the settings would break the URL.
Dynamic import() is asynchronous, so you must wait for the result using await or Promise.
Example:
<script type="module">
const { ConsoleFilter, methods, AsyncLogger } = await import( './consoleFilter.mjs?v=1.1&settings=' + encodeURIComponent( JSON.stringify( {
allowlist: [ 'items', 'cart' ],
blocklist: [ 'items debug' ],
} ) ) );
</script>Option 1 is slightly less resource-intensive, but the difference is minimal. Both options can be combined: settings from the URL parameter are applied first, settings from the JSON element override them. Invalid settings (e.g. invalid JSON) are reported by console.error() and ignored, the script works with the rest of the settings.
Re-load settings
You can load new settings and the functions will work with it immediately.
Using the ConsoleFilter.methods.readSettings() (for static methods) command or the instanceOfConsoleFilter.readSettings() command (e.g. consoleA.readSettings() for one AsyncLogger), the script will again search for <script type="application/json" id="console-filter-settings"> and merge the settings from it into the current settings.
So what exactly does it do?
As described above, after including the script (in any way), all console commands are now proxied and respond to settings (allowlist and/or blocklist).
So for example console.log( 'exact string' ) will be wiped out if settings.blocklist is set to ['exact string', /* … more possible strings … */]… or logs into Browser's console normally, if exact string is not blocked.
How filtering works
A message is identified by its beginning: the first argument, when it is a string (%c style directives and repeated spaces are ignored; for console.assert() it is the first argument after the condition). So it is good to start messages with some identifier – a class name, a file name, … – for example console.log( 'ItemsList loaded', items ).
An entry of allowlist or blocklist matches a message when the message starts with the entry as whole words: entry 'items' matches 'items' and 'items loaded', but not 'itemsLoaded'; entry 'exact string' matches 'exact string' and 'exact string with more words'.
blocklist– matching messages are hidden. It has priority overallowlist.allowlist– when it is not empty, only matching messages are written.'*'(or'all') – inallowlistallows everything, inblocklisthides everything except messages matchingallowlist.- groups (
console.group(),console.groupCollapsed()) – whole content of a group matchingallowlistis written (except messages matchingblocklist), whole content of a hidden group is hidden. - messages without text at the beginning (e.g.
console.log( { data } )) are hidden only when the output is restricted by a non-emptyallowlistor byblocklist'*'(inside a group matchingallowlistthey are written). console.clear()andconsole.groupEnd()are not compared withallowlistandblocklist.
Native console and methods (see below) write messages immediately, also inside groups.
But that's not all
When dynamically importing, you can use the methods object, it contains all static console methods with the same parameters as in the console object. So you can use, for example, const specialConsole = methods; specialConsole.log('some string'); what is it for? This is in case you want to keep console unchanged and have the filtered commands in another object, in this case it was the object in the specialConsole variable. However, in this case, it is necessary to tell ConsoleFilter during import that it should not modify the native console object in any way. This can be done using the bool setting autoAppendConsole, which you can change from the default true to false. Both settings work (as described above). Then you have (for example) the standard console.log() and the filtered specialConsole.log() available.
And finally, the AsyncLogger class, which is used for logging inside asynchronous methods called immediately after each other. Otherwise, commands inside groups (console.group() and console.groupCollapsed()) could be mixed up. How to do that? For example, const consoleA = new AsyncLogger(); and const consoleB = new AsyncLogger(). Now, if consoleA and consoleB have an open group (.group()), their output will not mix and will be output only after each group is closed (using .groupEnd()). Because of that, .time() / .timeEnd() and .trace() inside a group of AsyncLogger are evaluated when the group is written out, not when they are called.
In addition, consoleA and consoleB now each have their own settings. A new AsyncLogger starts with the settings of the page (JSON element and URL parameter) and changes of its settings do not affect the native console or other loggers. If I want to prefix all records in the console view with a string, this is possible by changing the settings of each logger. For example: consoleA.settings.texts.prefix = 'a: ', now all records in consoleA will be prefixed with 'a: ', if I write the command consoleA.log('some text'), the record in the console will be: 'a: some text'. At the same time, consoleB.log('some text') will only log 'some text', the setting was for consoleA, not for consoleB.
All settings:
The settings are described by this annotation:
type Settings = {
/**
* Beginnings of messages (whole words) that are allowed, when not empty only they are written.
* '*' or 'all' allows everything
*/
allowlist?: string | string[],
/**
* Beginnings of messages (whole words) that are hidden, it has priority over allowlist.
* '*' or 'all' hides everything except allowlist
*/
blocklist?: string | string[],
/** Automatically appends the console to the document */
autoAppendConsole: boolean,
/** Write console logs also into document.body */
appendConsoleIntoBody: boolean,
/** Possible to change some console function to another. Null means not convert. */
forceConvertFunctions: {
/** Rewrite console.log() into… */
log: null
/** Rewrite console.info() into… */
info: null
/** Rewrite console.warn() into… */
warn: null
/** Rewrite console.error() into… */
error: null
},
/** All written text in this class */
texts: {
/** Text added before each console message */
prefix: string,
/** Text added after each console message */
suffix: string,
},
}To set a property, it is not necessary to insert the entire settings object, just the value you want to change and the rest will remain in the default state.
What are the other files for?
consoleFilter.globals.d.ts, interface with all types and annotations for script. This will help your editor (or your AI agent) to understand the script, know what each method does, what the input parameters are, what data types the variables have, ... However, it is not needed for the script to function properly. If you delete the file, everything will work as before (just your IDE or AI agent may not work as well as it could).consoleFilter.spec.mjs, Unit tests for main script. It is not needed for the script's functionality itself. If you delete this file, nothing will happen, everything will work. For programmers or AI agents, however, unit tests will help to find out if their changes broke something.modules/ictest.mjs, Tests runtime. Used only for the above mentioned unit test file. Not needed for the script itself.tests-runner.htmlHTML file used for run tests in Browser. It is also not needed for the script itself.scripts/run-browser-tests.mjsrunstests-runner.htmlin headless browser (Playwright) and fails when any test fails. Used bynpm testand CI. Not needed for the script itself.scripts/check-integrity.mjschecks that SRIintegrityhashes intests-runner.htmlandREADME.mdmatch the current files. Not needed for the script itself.eslint.config.mjslint rules derived from the code style inAGENTS.md..github/workflows/GitHub Actions (CI) running all checks after every push and pull request.package.jsoncommand for NPM ( npm.js ) catalog.AGENTS.mdcommands for AI agents, description how to work with this repository. Something like Readme for AI.README.mdclass description in Markdown.
Development checks
After every push and pull request, GitHub Actions runs:
- syntax check of all JavaScript files (
node --check) - ESLint (
npm run lint) - TypeScript type check of JSDoc annotations against
consoleFilter.globals.d.ts(npm run typecheck) - check of SRI
integrityhashes intests-runner.htmlandREADME.md(npm run check:integrity) npm pack --dry-runandnpm audit- unit tests in headless Chromium, Firefox and WebKit (
npm test) - CodeQL security analysis
Locally: npm ci, npx playwright install chromium and then npm run check runs everything.
After changing consoleFilter.mjs or consoleFilter.spec.mjs run npm run fix:integrity to update their hashes in README.md and tests-runner.html.
Common gotchas
- The
.mjsfile must be served with a JavaScript MIME type. If imports fail, check your server configuration.
Services
Unpkg: https://unpkg.com/console-filter-js
NPM: https://www.npmjs.com/package/console-filter-js
Licence
CC BY-SA 4.0
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
More info at https://iiic.dev/console-filter-js
