Repository files navigation

react-logger-lib

ReactJS/ES6/ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Install

npm install react-logger-lib --save

Why one more library for logging?

This library is a tribute to simple logging facade from other languages and attempt to bring similar pattern into client side programming, ReactJS and other ES7 applications.

Benefits

  • it allows to manipulate filtration without build tools.
  • filtration is based on components hierarchy
  • it is lightweight 2KB gzipped (~8KB original)
  • no external dependencies, basically this is a module that you can just copy to you project
  • useful for automated unit tests, where it can trigger warnings according to expectations
  • no performance issues (if used properly)

Typical Usage in React Component

SidePicker.jsx:

importReact,{Component}from'react';import{Logger}from'react-logger-lib';classSidePickerextendsComponent{// Let's imagine some useful component for random side picking// One of 4 options should be chosen. And the same choice should not be repeated in the next attemptvalues=['left','right','top','bottom'];state={side: 'left'};componentDidMount(){Logger.of('App.SidePicker.componentDidMount').info('state=',this.state);}pickSide=()=>{// set of possible values to exclude current sideconstvalues=this.values.slice();values.splice(this.values.indexOf(this.state.side),1);Logger.of('App.SidePicker.pickSide.values').info('Set of new values',values);// new side is a random pick out of those valuesconstside=values[Math.floor(Math.random()*values.length)];Logger.of('App.SidePicker.pickSide.side').info('Another side chosen','side=',side);// save decision into the statethis.setState({ side });}pickWrongSide=()=>{// nothing happens. we produce just a warning, visible in ConsoleLogger.of('App.SidePicker.pickWrongSide').warn('Attempt to pick a wrong side');}render(){return(<div><div>Current Side: <strong>{this.state.side}</strong></div><buttonclassName='good-button'type='button'onClick={()=>(this.pickSide())}>
PICK ANOTHER SIDE
</button><buttonclassName='bad-button'type='button'onClick={()=>(this.pickWrongSide())}>
PICK WRONG SIDE
</button></div>);}}exportdefaultSidePicker;

Once you mount this component into your application, nothing is shown in Console tab when launched and when you click the first button, as it is declared under INFO level - and suppressed by default (this is why it is basically acceptable for production).

But when you click the second button, the wrong one, you will see console.warn

App.SidePicker.pickWrongSide | Attempt to pick a wrong side

stating that something wrong happened, though there was nothing critical for further application health.

What is the key feature of this approach, is that once written, it is compiled and running, and completely forgotten, you can manage logging level at anytime - and describe what level is required for what component tree in terms of your business logic. This management can be done just by putting localStorage variables in your browser.

For this particular example you can use one of the following to enable level at any depth:

localStorage.setItem('App','INFO');localStorage.setItem('App.SidePicker','INFO');localStorage.setItem('App.SidePicker.pickSide','INFO');localStorage.setItem('App.SidePicker.componentDidMount','INFO');

and then refresh the page to apply changes for these settings. When loaded and 2 buttons will be clicked, Console will show something like this:

App.SidePicker.componentDidMount | state= Object {side: "left"}
App.SidePicker.pickSide.values | Set of new values (3) ["right", "top", "bottom"]
App.SidePicker.pickSide.side | Another side chosen side= top
App.SidePicker.pickWrongSide | Attempt to pick a wrong side

Levels of logging

There are 4 levels of logging currently. From lowest to highest these are TRACE-INFO-WARN-ERROR:

  • OFF suppresses the logging completely
  • ERROR level will show only messages with ERROR level.
  • WARN level will show all messages with ERROR level as well.
  • INFO level will show all messages with ERROR and WARN levels as well.
  • TRACE level will show all messages with INFO, WARN and ERROR levels as well.

TRACE is not calling console.trace, it is just a logical level in this library

Using in Unit Tests

Now let's see what we can benefit from our logging in unit tests:

importReactfrom'react';import{shallow}from'enzyme';import{enableLogger,Logger}from'react-logger-lib';importSidePickerfrom'./SidePicker';constexpectNoErrors=()=>{expect(Logger.calls.error).toEqual(0);};constexpectNoWarn=()=>{expect(Logger.calls.warn).toEqual(0);expectNoErrors();};constexpectWarn=()=>{expect(Logger.calls.warn).toBeGreaterThan(0);};describe('App.SidePicker',()=>{it('was mounted without warnings',()=>{enableLogger(()=>{shallow(<SidePicker/>);expectNoWarn();});});it('good button works without warnings',()=>{enableLogger(()=>{constelem=shallow(<SidePicker/>);// expect good button to be foundconstgoodButton=elem.find('button').at(0);expect(goodButton.text()).toEqual('PICK ANOTHER SIDE');// clicking on itgoodButton.simulate('click');expectNoWarn();});});it('bad button triggers warnings',()=>{enableLogger(false);// we do not want to see the warning when it happensconstelem=shallow(<SidePicker/>);// expect bad button to be foundconstbadButton=elem.find('button').at(1);expect(badButton.text()).toEqual('PICK WRONG SIDE');// clicking on itbadButton.simulate('click');expectWarn();});});

Jest tests above are checking that there was no warnings during components mounting, that there was no warning when the good button is clicked, and some warning was expected on the bad button.

  • All logging is suppressed when running with Jest. Therefore enabledLogger() function is a way to display actual warnings and errors when they are happening during tests.
  • Typical case, as described above - we enable warnings when we do not expect them to happen. And we disable them when we actually expect a warning.
  • enableLogger() supports 2 ways of calling it. It's parameter could be a synchronous call with boolean flag or callback to be executed when warnings/errors logging is enabled.

Eliminating Logger using WebPack Loader

There is a natural desire to eliminate logging completely on certain production environments to get as small build size as possible. One of the ways it could be achieved - is by adding extra loader for your webpack configuration

  1. Create new file no-logger-loader.js with the following contents:
module.exports=function(source,map){if(source.indexOf('react-logger-lib')!==-1){constcleanSource=source.replace(/Logger\.of\([^\)]+\)\.(trace|info|warn|error)/g,'');this.callback(null,cleanSource,map);}else{this.callback(null,source,map);}}
  1. Modify your production configuration webpack.config.prod.js. It is important to add this loader beforebabel-loader.
 ...
module: {
...
rules: [
...
{test: /\.(js|jsx)$/,include: paths.appSrc,loader: require.resolve('./no-logger-loader')},
...
]}
  1. Please make sure UglifyJsPlugin is also enabled in that webpack configuration.

This way you will get rid of all logging on this environment.

Disclaimer

  • This library is more a pattern to be cloned and configured for your own needs. This is why there are no plugins and extensions like in other similar facades.
  • We did it just because we love Slf4J approach in Java, but there was no similar thing in React applications.
  • Unlike Java, where component path is taken to logs automatically, here naming is left on responsibility of a writer. Our advice is to keep it as hierarchy of your business logic, as clear and standard as possible.
  • Please use logging wisely on render() methods, as extensive logging during rendering is an easy way to downgrade your application performance.
  • There is no slowdown noticed when logs are suppressed or not matching their level.
  • Feel free to discuss this project under Github Issues.

About

ReactJS / ES6 / ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

react-logger-lib

ReactJS/ES6/ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Install

npm install react-logger-lib --save

Why one more library for logging?

This library is a tribute to simple logging facade from other languages and attempt to bring similar pattern into client side programming, ReactJS and other ES7 applications.

Benefits

  • it allows to manipulate filtration without build tools.
  • filtration is based on components hierarchy
  • it is lightweight 2KB gzipped (~8KB original)
  • no external dependencies, basically this is a module that you can just copy to you project
  • useful for automated unit tests, where it can trigger warnings according to expectations
  • no performance issues (if used properly)

Typical Usage in React Component

SidePicker.jsx:

importReact,{Component}from'react';import{Logger}from'react-logger-lib';classSidePickerextendsComponent{// Let's imagine some useful component for random side picking// One of 4 options should be chosen. And the same choice should not be repeated in the next attemptvalues=['left','right','top','bottom'];state={side: 'left'};componentDidMount(){Logger.of('App.SidePicker.componentDidMount').info('state=',this.state);}pickSide=()=>{// set of possible values to exclude current sideconstvalues=this.values.slice();values.splice(this.values.indexOf(this.state.side),1);Logger.of('App.SidePicker.pickSide.values').info('Set of new values',values);// new side is a random pick out of those valuesconstside=values[Math.floor(Math.random()*values.length)];Logger.of('App.SidePicker.pickSide.side').info('Another side chosen','side=',side);// save decision into the statethis.setState({ side });}pickWrongSide=()=>{// nothing happens. we produce just a warning, visible in ConsoleLogger.of('App.SidePicker.pickWrongSide').warn('Attempt to pick a wrong side');}render(){return(<div><div>Current Side: <strong>{this.state.side}</strong></div><buttonclassName='good-button'type='button'onClick={()=>(this.pickSide())}>
PICK ANOTHER SIDE
</button><buttonclassName='bad-button'type='button'onClick={()=>(this.pickWrongSide())}>
PICK WRONG SIDE
</button></div>);}}exportdefaultSidePicker;

Once you mount this component into your application, nothing is shown in Console tab when launched and when you click the first button, as it is declared under INFO level - and suppressed by default (this is why it is basically acceptable for production).

But when you click the second button, the wrong one, you will see console.warn

App.SidePicker.pickWrongSide | Attempt to pick a wrong side

stating that something wrong happened, though there was nothing critical for further application health.

What is the key feature of this approach, is that once written, it is compiled and running, and completely forgotten, you can manage logging level at anytime - and describe what level is required for what component tree in terms of your business logic. This management can be done just by putting localStorage variables in your browser.

For this particular example you can use one of the following to enable level at any depth:

localStorage.setItem('App','INFO');localStorage.setItem('App.SidePicker','INFO');localStorage.setItem('App.SidePicker.pickSide','INFO');localStorage.setItem('App.SidePicker.componentDidMount','INFO');

and then refresh the page to apply changes for these settings. When loaded and 2 buttons will be clicked, Console will show something like this:

App.SidePicker.componentDidMount | state= Object {side: "left"}
App.SidePicker.pickSide.values | Set of new values (3) ["right", "top", "bottom"]
App.SidePicker.pickSide.side | Another side chosen side= top
App.SidePicker.pickWrongSide | Attempt to pick a wrong side

Levels of logging

There are 4 levels of logging currently. From lowest to highest these are TRACE-INFO-WARN-ERROR:

  • OFF suppresses the logging completely
  • ERROR level will show only messages with ERROR level.
  • WARN level will show all messages with ERROR level as well.
  • INFO level will show all messages with ERROR and WARN levels as well.
  • TRACE level will show all messages with INFO, WARN and ERROR levels as well.

TRACE is not calling console.trace, it is just a logical level in this library

Using in Unit Tests

Now let's see what we can benefit from our logging in unit tests:

importReactfrom'react';import{shallow}from'enzyme';import{enableLogger,Logger}from'react-logger-lib';importSidePickerfrom'./SidePicker';constexpectNoErrors=()=>{expect(Logger.calls.error).toEqual(0);};constexpectNoWarn=()=>{expect(Logger.calls.warn).toEqual(0);expectNoErrors();};constexpectWarn=()=>{expect(Logger.calls.warn).toBeGreaterThan(0);};describe('App.SidePicker',()=>{it('was mounted without warnings',()=>{enableLogger(()=>{shallow(<SidePicker/>);expectNoWarn();});});it('good button works without warnings',()=>{enableLogger(()=>{constelem=shallow(<SidePicker/>);// expect good button to be foundconstgoodButton=elem.find('button').at(0);expect(goodButton.text()).toEqual('PICK ANOTHER SIDE');// clicking on itgoodButton.simulate('click');expectNoWarn();});});it('bad button triggers warnings',()=>{enableLogger(false);// we do not want to see the warning when it happensconstelem=shallow(<SidePicker/>);// expect bad button to be foundconstbadButton=elem.find('button').at(1);expect(badButton.text()).toEqual('PICK WRONG SIDE');// clicking on itbadButton.simulate('click');expectWarn();});});

Jest tests above are checking that there was no warnings during components mounting, that there was no warning when the good button is clicked, and some warning was expected on the bad button.

  • All logging is suppressed when running with Jest. Therefore enabledLogger() function is a way to display actual warnings and errors when they are happening during tests.
  • Typical case, as described above - we enable warnings when we do not expect them to happen. And we disable them when we actually expect a warning.
  • enableLogger() supports 2 ways of calling it. It's parameter could be a synchronous call with boolean flag or callback to be executed when warnings/errors logging is enabled.

Eliminating Logger using WebPack Loader

There is a natural desire to eliminate logging completely on certain production environments to get as small build size as possible. One of the ways it could be achieved - is by adding extra loader for your webpack configuration

  1. Create new file no-logger-loader.js with the following contents:
module.exports=function(source,map){if(source.indexOf('react-logger-lib')!==-1){constcleanSource=source.replace(/Logger\.of\([^\)]+\)\.(trace|info|warn|error)/g,'');this.callback(null,cleanSource,map);}else{this.callback(null,source,map);}}
  1. Modify your production configuration webpack.config.prod.js. It is important to add this loader beforebabel-loader.
 ...
module: {
...
rules: [
...
{test: /\.(js|jsx)$/,include: paths.appSrc,loader: require.resolve('./no-logger-loader')},
...
]}
  1. Please make sure UglifyJsPlugin is also enabled in that webpack configuration.

This way you will get rid of all logging on this environment.

Disclaimer

  • This library is more a pattern to be cloned and configured for your own needs. This is why there are no plugins and extensions like in other similar facades.
  • We did it just because we love Slf4J approach in Java, but there was no similar thing in React applications.
  • Unlike Java, where component path is taken to logs automatically, here naming is left on responsibility of a writer. Our advice is to keep it as hierarchy of your business logic, as clear and standard as possible.
  • Please use logging wisely on render() methods, as extensive logging during rendering is an easy way to downgrade your application performance.
  • There is no slowdown noticed when logs are suppressed or not matching their level.
  • Feel free to discuss this project under Github Issues.

About

ReactJS / ES6 / ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

react-logger-lib

ReactJS/ES6/ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Install

npm install react-logger-lib --save

Why one more library for logging?

This library is a tribute to simple logging facade from other languages and attempt to bring similar pattern into client side programming, ReactJS and other ES7 applications.

Benefits

  • it allows to manipulate filtration without build tools.
  • filtration is based on components hierarchy
  • it is lightweight 2KB gzipped (~8KB original)
  • no external dependencies, basically this is a module that you can just copy to you project
  • useful for automated unit tests, where it can trigger warnings according to expectations
  • no performance issues (if used properly)

Typical Usage in React Component

SidePicker.jsx:

importReact,{Component}from'react';import{Logger}from'react-logger-lib';classSidePickerextendsComponent{// Let's imagine some useful component for random side picking// One of 4 options should be chosen. And the same choice should not be repeated in the next attemptvalues=['left','right','top','bottom'];state={side: 'left'};componentDidMount(){Logger.of('App.SidePicker.componentDidMount').info('state=',this.state);}pickSide=()=>{// set of possible values to exclude current sideconstvalues=this.values.slice();values.splice(this.values.indexOf(this.state.side),1);Logger.of('App.SidePicker.pickSide.values').info('Set of new values',values);// new side is a random pick out of those valuesconstside=values[Math.floor(Math.random()*values.length)];Logger.of('App.SidePicker.pickSide.side').info('Another side chosen','side=',side);// save decision into the statethis.setState({ side });}pickWrongSide=()=>{// nothing happens. we produce just a warning, visible in ConsoleLogger.of('App.SidePicker.pickWrongSide').warn('Attempt to pick a wrong side');}render(){return(<div><div>Current Side: <strong>{this.state.side}</strong></div><buttonclassName='good-button'type='button'onClick={()=>(this.pickSide())}>
PICK ANOTHER SIDE
</button><buttonclassName='bad-button'type='button'onClick={()=>(this.pickWrongSide())}>
PICK WRONG SIDE
</button></div>);}}exportdefaultSidePicker;

Once you mount this component into your application, nothing is shown in Console tab when launched and when you click the first button, as it is declared under INFO level - and suppressed by default (this is why it is basically acceptable for production).

But when you click the second button, the wrong one, you will see console.warn

App.SidePicker.pickWrongSide | Attempt to pick a wrong side

stating that something wrong happened, though there was nothing critical for further application health.

What is the key feature of this approach, is that once written, it is compiled and running, and completely forgotten, you can manage logging level at anytime - and describe what level is required for what component tree in terms of your business logic. This management can be done just by putting localStorage variables in your browser.

For this particular example you can use one of the following to enable level at any depth:

localStorage.setItem('App','INFO');localStorage.setItem('App.SidePicker','INFO');localStorage.setItem('App.SidePicker.pickSide','INFO');localStorage.setItem('App.SidePicker.componentDidMount','INFO');

and then refresh the page to apply changes for these settings. When loaded and 2 buttons will be clicked, Console will show something like this:

App.SidePicker.componentDidMount | state= Object {side: "left"}
App.SidePicker.pickSide.values | Set of new values (3) ["right", "top", "bottom"]
App.SidePicker.pickSide.side | Another side chosen side= top
App.SidePicker.pickWrongSide | Attempt to pick a wrong side

Levels of logging

There are 4 levels of logging currently. From lowest to highest these are TRACE-INFO-WARN-ERROR:

  • OFF suppresses the logging completely
  • ERROR level will show only messages with ERROR level.
  • WARN level will show all messages with ERROR level as well.
  • INFO level will show all messages with ERROR and WARN levels as well.
  • TRACE level will show all messages with INFO, WARN and ERROR levels as well.

TRACE is not calling console.trace, it is just a logical level in this library

Using in Unit Tests

Now let's see what we can benefit from our logging in unit tests:

importReactfrom'react';import{shallow}from'enzyme';import{enableLogger,Logger}from'react-logger-lib';importSidePickerfrom'./SidePicker';constexpectNoErrors=()=>{expect(Logger.calls.error).toEqual(0);};constexpectNoWarn=()=>{expect(Logger.calls.warn).toEqual(0);expectNoErrors();};constexpectWarn=()=>{expect(Logger.calls.warn).toBeGreaterThan(0);};describe('App.SidePicker',()=>{it('was mounted without warnings',()=>{enableLogger(()=>{shallow(<SidePicker/>);expectNoWarn();});});it('good button works without warnings',()=>{enableLogger(()=>{constelem=shallow(<SidePicker/>);// expect good button to be foundconstgoodButton=elem.find('button').at(0);expect(goodButton.text()).toEqual('PICK ANOTHER SIDE');// clicking on itgoodButton.simulate('click');expectNoWarn();});});it('bad button triggers warnings',()=>{enableLogger(false);// we do not want to see the warning when it happensconstelem=shallow(<SidePicker/>);// expect bad button to be foundconstbadButton=elem.find('button').at(1);expect(badButton.text()).toEqual('PICK WRONG SIDE');// clicking on itbadButton.simulate('click');expectWarn();});});

Jest tests above are checking that there was no warnings during components mounting, that there was no warning when the good button is clicked, and some warning was expected on the bad button.

  • All logging is suppressed when running with Jest. Therefore enabledLogger() function is a way to display actual warnings and errors when they are happening during tests.
  • Typical case, as described above - we enable warnings when we do not expect them to happen. And we disable them when we actually expect a warning.
  • enableLogger() supports 2 ways of calling it. It's parameter could be a synchronous call with boolean flag or callback to be executed when warnings/errors logging is enabled.

Eliminating Logger using WebPack Loader

There is a natural desire to eliminate logging completely on certain production environments to get as small build size as possible. One of the ways it could be achieved - is by adding extra loader for your webpack configuration

  1. Create new file no-logger-loader.js with the following contents:
module.exports=function(source,map){if(source.indexOf('react-logger-lib')!==-1){constcleanSource=source.replace(/Logger\.of\([^\)]+\)\.(trace|info|warn|error)/g,'');this.callback(null,cleanSource,map);}else{this.callback(null,source,map);}}
  1. Modify your production configuration webpack.config.prod.js. It is important to add this loader beforebabel-loader.
 ...
module: {
...
rules: [
...
{test: /\.(js|jsx)$/,include: paths.appSrc,loader: require.resolve('./no-logger-loader')},
...
]}
  1. Please make sure UglifyJsPlugin is also enabled in that webpack configuration.

This way you will get rid of all logging on this environment.

Disclaimer

  • This library is more a pattern to be cloned and configured for your own needs. This is why there are no plugins and extensions like in other similar facades.
  • We did it just because we love Slf4J approach in Java, but there was no similar thing in React applications.
  • Unlike Java, where component path is taken to logs automatically, here naming is left on responsibility of a writer. Our advice is to keep it as hierarchy of your business logic, as clear and standard as possible.
  • Please use logging wisely on render() methods, as extensive logging during rendering is an easy way to downgrade your application performance.
  • There is no slowdown noticed when logs are suppressed or not matching their level.
  • Feel free to discuss this project under Github Issues.

About

ReactJS / ES6 / ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

react-logger-lib

ReactJS/ES6/ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Install

npm install react-logger-lib --save

Why one more library for logging?

This library is a tribute to simple logging facade from other languages and attempt to bring similar pattern into client side programming, ReactJS and other ES7 applications.

Benefits

  • it allows to manipulate filtration without build tools.
  • filtration is based on components hierarchy
  • it is lightweight 2KB gzipped (~8KB original)
  • no external dependencies, basically this is a module that you can just copy to you project
  • useful for automated unit tests, where it can trigger warnings according to expectations
  • no performance issues (if used properly)

Typical Usage in React Component

SidePicker.jsx:

importReact,{Component}from'react';import{Logger}from'react-logger-lib';classSidePickerextendsComponent{// Let's imagine some useful component for random side picking// One of 4 options should be chosen. And the same choice should not be repeated in the next attemptvalues=['left','right','top','bottom'];state={side: 'left'};componentDidMount(){Logger.of('App.SidePicker.componentDidMount').info('state=',this.state);}pickSide=()=>{// set of possible values to exclude current sideconstvalues=this.values.slice();values.splice(this.values.indexOf(this.state.side),1);Logger.of('App.SidePicker.pickSide.values').info('Set of new values',values);// new side is a random pick out of those valuesconstside=values[Math.floor(Math.random()*values.length)];Logger.of('App.SidePicker.pickSide.side').info('Another side chosen','side=',side);// save decision into the statethis.setState({ side });}pickWrongSide=()=>{// nothing happens. we produce just a warning, visible in ConsoleLogger.of('App.SidePicker.pickWrongSide').warn('Attempt to pick a wrong side');}render(){return(<div><div>Current Side: <strong>{this.state.side}</strong></div><buttonclassName='good-button'type='button'onClick={()=>(this.pickSide())}>
PICK ANOTHER SIDE
</button><buttonclassName='bad-button'type='button'onClick={()=>(this.pickWrongSide())}>
PICK WRONG SIDE
</button></div>);}}exportdefaultSidePicker;

Once you mount this component into your application, nothing is shown in Console tab when launched and when you click the first button, as it is declared under INFO level - and suppressed by default (this is why it is basically acceptable for production).

But when you click the second button, the wrong one, you will see console.warn

App.SidePicker.pickWrongSide | Attempt to pick a wrong side

stating that something wrong happened, though there was nothing critical for further application health.

What is the key feature of this approach, is that once written, it is compiled and running, and completely forgotten, you can manage logging level at anytime - and describe what level is required for what component tree in terms of your business logic. This management can be done just by putting localStorage variables in your browser.

For this particular example you can use one of the following to enable level at any depth:

localStorage.setItem('App','INFO');localStorage.setItem('App.SidePicker','INFO');localStorage.setItem('App.SidePicker.pickSide','INFO');localStorage.setItem('App.SidePicker.componentDidMount','INFO');

and then refresh the page to apply changes for these settings. When loaded and 2 buttons will be clicked, Console will show something like this:

App.SidePicker.componentDidMount | state= Object {side: "left"}
App.SidePicker.pickSide.values | Set of new values (3) ["right", "top", "bottom"]
App.SidePicker.pickSide.side | Another side chosen side= top
App.SidePicker.pickWrongSide | Attempt to pick a wrong side

Levels of logging

There are 4 levels of logging currently. From lowest to highest these are TRACE-INFO-WARN-ERROR:

  • OFF suppresses the logging completely
  • ERROR level will show only messages with ERROR level.
  • WARN level will show all messages with ERROR level as well.
  • INFO level will show all messages with ERROR and WARN levels as well.
  • TRACE level will show all messages with INFO, WARN and ERROR levels as well.

TRACE is not calling console.trace, it is just a logical level in this library

Using in Unit Tests

Now let's see what we can benefit from our logging in unit tests:

importReactfrom'react';import{shallow}from'enzyme';import{enableLogger,Logger}from'react-logger-lib';importSidePickerfrom'./SidePicker';constexpectNoErrors=()=>{expect(Logger.calls.error).toEqual(0);};constexpectNoWarn=()=>{expect(Logger.calls.warn).toEqual(0);expectNoErrors();};constexpectWarn=()=>{expect(Logger.calls.warn).toBeGreaterThan(0);};describe('App.SidePicker',()=>{it('was mounted without warnings',()=>{enableLogger(()=>{shallow(<SidePicker/>);expectNoWarn();});});it('good button works without warnings',()=>{enableLogger(()=>{constelem=shallow(<SidePicker/>);// expect good button to be foundconstgoodButton=elem.find('button').at(0);expect(goodButton.text()).toEqual('PICK ANOTHER SIDE');// clicking on itgoodButton.simulate('click');expectNoWarn();});});it('bad button triggers warnings',()=>{enableLogger(false);// we do not want to see the warning when it happensconstelem=shallow(<SidePicker/>);// expect bad button to be foundconstbadButton=elem.find('button').at(1);expect(badButton.text()).toEqual('PICK WRONG SIDE');// clicking on itbadButton.simulate('click');expectWarn();});});

Jest tests above are checking that there was no warnings during components mounting, that there was no warning when the good button is clicked, and some warning was expected on the bad button.

  • All logging is suppressed when running with Jest. Therefore enabledLogger() function is a way to display actual warnings and errors when they are happening during tests.
  • Typical case, as described above - we enable warnings when we do not expect them to happen. And we disable them when we actually expect a warning.
  • enableLogger() supports 2 ways of calling it. It's parameter could be a synchronous call with boolean flag or callback to be executed when warnings/errors logging is enabled.

Eliminating Logger using WebPack Loader

There is a natural desire to eliminate logging completely on certain production environments to get as small build size as possible. One of the ways it could be achieved - is by adding extra loader for your webpack configuration

  1. Create new file no-logger-loader.js with the following contents:
module.exports=function(source,map){if(source.indexOf('react-logger-lib')!==-1){constcleanSource=source.replace(/Logger\.of\([^\)]+\)\.(trace|info|warn|error)/g,'');this.callback(null,cleanSource,map);}else{this.callback(null,source,map);}}
  1. Modify your production configuration webpack.config.prod.js. It is important to add this loader beforebabel-loader.
 ...
module: {
...
rules: [
...
{test: /\.(js|jsx)$/,include: paths.appSrc,loader: require.resolve('./no-logger-loader')},
...
]}
  1. Please make sure UglifyJsPlugin is also enabled in that webpack configuration.

This way you will get rid of all logging on this environment.

Disclaimer

  • This library is more a pattern to be cloned and configured for your own needs. This is why there are no plugins and extensions like in other similar facades.
  • We did it just because we love Slf4J approach in Java, but there was no similar thing in React applications.
  • Unlike Java, where component path is taken to logs automatically, here naming is left on responsibility of a writer. Our advice is to keep it as hierarchy of your business logic, as clear and standard as possible.
  • Please use logging wisely on render() methods, as extensive logging during rendering is an easy way to downgrade your application performance.
  • There is no slowdown noticed when logs are suppressed or not matching their level.
  • Feel free to discuss this project under Github Issues.

About

ReactJS / ES6 / ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

react-logger-lib

ReactJS/ES6/ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Install

npm install react-logger-lib --save

Why one more library for logging?

This library is a tribute to simple logging facade from other languages and attempt to bring similar pattern into client side programming, ReactJS and other ES7 applications.

Benefits

  • it allows to manipulate filtration without build tools.
  • filtration is based on components hierarchy
  • it is lightweight 2KB gzipped (~8KB original)
  • no external dependencies, basically this is a module that you can just copy to you project
  • useful for automated unit tests, where it can trigger warnings according to expectations
  • no performance issues (if used properly)

Typical Usage in React Component

SidePicker.jsx:

importReact,{Component}from'react';import{Logger}from'react-logger-lib';classSidePickerextendsComponent{// Let's imagine some useful component for random side picking// One of 4 options should be chosen. And the same choice should not be repeated in the next attemptvalues=['left','right','top','bottom'];state={side: 'left'};componentDidMount(){Logger.of('App.SidePicker.componentDidMount').info('state=',this.state);}pickSide=()=>{// set of possible values to exclude current sideconstvalues=this.values.slice();values.splice(this.values.indexOf(this.state.side),1);Logger.of('App.SidePicker.pickSide.values').info('Set of new values',values);// new side is a random pick out of those valuesconstside=values[Math.floor(Math.random()*values.length)];Logger.of('App.SidePicker.pickSide.side').info('Another side chosen','side=',side);// save decision into the statethis.setState({ side });}pickWrongSide=()=>{// nothing happens. we produce just a warning, visible in ConsoleLogger.of('App.SidePicker.pickWrongSide').warn('Attempt to pick a wrong side');}render(){return(<div><div>Current Side: <strong>{this.state.side}</strong></div><buttonclassName='good-button'type='button'onClick={()=>(this.pickSide())}>
PICK ANOTHER SIDE
</button><buttonclassName='bad-button'type='button'onClick={()=>(this.pickWrongSide())}>
PICK WRONG SIDE
</button></div>);}}exportdefaultSidePicker;

Once you mount this component into your application, nothing is shown in Console tab when launched and when you click the first button, as it is declared under INFO level - and suppressed by default (this is why it is basically acceptable for production).

But when you click the second button, the wrong one, you will see console.warn

App.SidePicker.pickWrongSide | Attempt to pick a wrong side

stating that something wrong happened, though there was nothing critical for further application health.

What is the key feature of this approach, is that once written, it is compiled and running, and completely forgotten, you can manage logging level at anytime - and describe what level is required for what component tree in terms of your business logic. This management can be done just by putting localStorage variables in your browser.

For this particular example you can use one of the following to enable level at any depth:

localStorage.setItem('App','INFO');localStorage.setItem('App.SidePicker','INFO');localStorage.setItem('App.SidePicker.pickSide','INFO');localStorage.setItem('App.SidePicker.componentDidMount','INFO');

and then refresh the page to apply changes for these settings. When loaded and 2 buttons will be clicked, Console will show something like this:

App.SidePicker.componentDidMount | state= Object {side: "left"}
App.SidePicker.pickSide.values | Set of new values (3) ["right", "top", "bottom"]
App.SidePicker.pickSide.side | Another side chosen side= top
App.SidePicker.pickWrongSide | Attempt to pick a wrong side

Levels of logging

There are 4 levels of logging currently. From lowest to highest these are TRACE-INFO-WARN-ERROR:

  • OFF suppresses the logging completely
  • ERROR level will show only messages with ERROR level.
  • WARN level will show all messages with ERROR level as well.
  • INFO level will show all messages with ERROR and WARN levels as well.
  • TRACE level will show all messages with INFO, WARN and ERROR levels as well.

TRACE is not calling console.trace, it is just a logical level in this library

Using in Unit Tests

Now let's see what we can benefit from our logging in unit tests:

importReactfrom'react';import{shallow}from'enzyme';import{enableLogger,Logger}from'react-logger-lib';importSidePickerfrom'./SidePicker';constexpectNoErrors=()=>{expect(Logger.calls.error).toEqual(0);};constexpectNoWarn=()=>{expect(Logger.calls.warn).toEqual(0);expectNoErrors();};constexpectWarn=()=>{expect(Logger.calls.warn).toBeGreaterThan(0);};describe('App.SidePicker',()=>{it('was mounted without warnings',()=>{enableLogger(()=>{shallow(<SidePicker/>);expectNoWarn();});});it('good button works without warnings',()=>{enableLogger(()=>{constelem=shallow(<SidePicker/>);// expect good button to be foundconstgoodButton=elem.find('button').at(0);expect(goodButton.text()).toEqual('PICK ANOTHER SIDE');// clicking on itgoodButton.simulate('click');expectNoWarn();});});it('bad button triggers warnings',()=>{enableLogger(false);// we do not want to see the warning when it happensconstelem=shallow(<SidePicker/>);// expect bad button to be foundconstbadButton=elem.find('button').at(1);expect(badButton.text()).toEqual('PICK WRONG SIDE');// clicking on itbadButton.simulate('click');expectWarn();});});

Jest tests above are checking that there was no warnings during components mounting, that there was no warning when the good button is clicked, and some warning was expected on the bad button.

  • All logging is suppressed when running with Jest. Therefore enabledLogger() function is a way to display actual warnings and errors when they are happening during tests.
  • Typical case, as described above - we enable warnings when we do not expect them to happen. And we disable them when we actually expect a warning.
  • enableLogger() supports 2 ways of calling it. It's parameter could be a synchronous call with boolean flag or callback to be executed when warnings/errors logging is enabled.

Eliminating Logger using WebPack Loader

There is a natural desire to eliminate logging completely on certain production environments to get as small build size as possible. One of the ways it could be achieved - is by adding extra loader for your webpack configuration

  1. Create new file no-logger-loader.js with the following contents:
module.exports=function(source,map){if(source.indexOf('react-logger-lib')!==-1){constcleanSource=source.replace(/Logger\.of\([^\)]+\)\.(trace|info|warn|error)/g,'');this.callback(null,cleanSource,map);}else{this.callback(null,source,map);}}
  1. Modify your production configuration webpack.config.prod.js. It is important to add this loader beforebabel-loader.
 ...
module: {
...
rules: [
...
{test: /\.(js|jsx)$/,include: paths.appSrc,loader: require.resolve('./no-logger-loader')},
...
]}
  1. Please make sure UglifyJsPlugin is also enabled in that webpack configuration.

This way you will get rid of all logging on this environment.

Disclaimer

  • This library is more a pattern to be cloned and configured for your own needs. This is why there are no plugins and extensions like in other similar facades.
  • We did it just because we love Slf4J approach in Java, but there was no similar thing in React applications.
  • Unlike Java, where component path is taken to logs automatically, here naming is left on responsibility of a writer. Our advice is to keep it as hierarchy of your business logic, as clear and standard as possible.
  • Please use logging wisely on render() methods, as extensive logging during rendering is an easy way to downgrade your application performance.
  • There is no slowdown noticed when logs are suppressed or not matching their level.
  • Feel free to discuss this project under Github Issues.

About

ReactJS / ES6 / ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

react-logger-lib

ReactJS/ES6/ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Install

npm install react-logger-lib --save

Why one more library for logging?

This library is a tribute to simple logging facade from other languages and attempt to bring similar pattern into client side programming, ReactJS and other ES7 applications.

Benefits

  • it allows to manipulate filtration without build tools.
  • filtration is based on components hierarchy
  • it is lightweight 2KB gzipped (~8KB original)
  • no external dependencies, basically this is a module that you can just copy to you project
  • useful for automated unit tests, where it can trigger warnings according to expectations
  • no performance issues (if used properly)

Typical Usage in React Component

SidePicker.jsx:

importReact,{Component}from'react';import{Logger}from'react-logger-lib';classSidePickerextendsComponent{// Let's imagine some useful component for random side picking// One of 4 options should be chosen. And the same choice should not be repeated in the next attemptvalues=['left','right','top','bottom'];state={side: 'left'};componentDidMount(){Logger.of('App.SidePicker.componentDidMount').info('state=',this.state);}pickSide=()=>{// set of possible values to exclude current sideconstvalues=this.values.slice();values.splice(this.values.indexOf(this.state.side),1);Logger.of('App.SidePicker.pickSide.values').info('Set of new values',values);// new side is a random pick out of those valuesconstside=values[Math.floor(Math.random()*values.length)];Logger.of('App.SidePicker.pickSide.side').info('Another side chosen','side=',side);// save decision into the statethis.setState({ side });}pickWrongSide=()=>{// nothing happens. we produce just a warning, visible in ConsoleLogger.of('App.SidePicker.pickWrongSide').warn('Attempt to pick a wrong side');}render(){return(<div><div>Current Side: <strong>{this.state.side}</strong></div><buttonclassName='good-button'type='button'onClick={()=>(this.pickSide())}>
PICK ANOTHER SIDE
</button><buttonclassName='bad-button'type='button'onClick={()=>(this.pickWrongSide())}>
PICK WRONG SIDE
</button></div>);}}exportdefaultSidePicker;

Once you mount this component into your application, nothing is shown in Console tab when launched and when you click the first button, as it is declared under INFO level - and suppressed by default (this is why it is basically acceptable for production).

But when you click the second button, the wrong one, you will see console.warn

App.SidePicker.pickWrongSide | Attempt to pick a wrong side

stating that something wrong happened, though there was nothing critical for further application health.

What is the key feature of this approach, is that once written, it is compiled and running, and completely forgotten, you can manage logging level at anytime - and describe what level is required for what component tree in terms of your business logic. This management can be done just by putting localStorage variables in your browser.

For this particular example you can use one of the following to enable level at any depth:

localStorage.setItem('App','INFO');localStorage.setItem('App.SidePicker','INFO');localStorage.setItem('App.SidePicker.pickSide','INFO');localStorage.setItem('App.SidePicker.componentDidMount','INFO');

and then refresh the page to apply changes for these settings. When loaded and 2 buttons will be clicked, Console will show something like this:

App.SidePicker.componentDidMount | state= Object {side: "left"}
App.SidePicker.pickSide.values | Set of new values (3) ["right", "top", "bottom"]
App.SidePicker.pickSide.side | Another side chosen side= top
App.SidePicker.pickWrongSide | Attempt to pick a wrong side

Levels of logging

There are 4 levels of logging currently. From lowest to highest these are TRACE-INFO-WARN-ERROR:

  • OFF suppresses the logging completely
  • ERROR level will show only messages with ERROR level.
  • WARN level will show all messages with ERROR level as well.
  • INFO level will show all messages with ERROR and WARN levels as well.
  • TRACE level will show all messages with INFO, WARN and ERROR levels as well.

TRACE is not calling console.trace, it is just a logical level in this library

Using in Unit Tests

Now let's see what we can benefit from our logging in unit tests:

importReactfrom'react';import{shallow}from'enzyme';import{enableLogger,Logger}from'react-logger-lib';importSidePickerfrom'./SidePicker';constexpectNoErrors=()=>{expect(Logger.calls.error).toEqual(0);};constexpectNoWarn=()=>{expect(Logger.calls.warn).toEqual(0);expectNoErrors();};constexpectWarn=()=>{expect(Logger.calls.warn).toBeGreaterThan(0);};describe('App.SidePicker',()=>{it('was mounted without warnings',()=>{enableLogger(()=>{shallow(<SidePicker/>);expectNoWarn();});});it('good button works without warnings',()=>{enableLogger(()=>{constelem=shallow(<SidePicker/>);// expect good button to be foundconstgoodButton=elem.find('button').at(0);expect(goodButton.text()).toEqual('PICK ANOTHER SIDE');// clicking on itgoodButton.simulate('click');expectNoWarn();});});it('bad button triggers warnings',()=>{enableLogger(false);// we do not want to see the warning when it happensconstelem=shallow(<SidePicker/>);// expect bad button to be foundconstbadButton=elem.find('button').at(1);expect(badButton.text()).toEqual('PICK WRONG SIDE');// clicking on itbadButton.simulate('click');expectWarn();});});

Jest tests above are checking that there was no warnings during components mounting, that there was no warning when the good button is clicked, and some warning was expected on the bad button.

  • All logging is suppressed when running with Jest. Therefore enabledLogger() function is a way to display actual warnings and errors when they are happening during tests.
  • Typical case, as described above - we enable warnings when we do not expect them to happen. And we disable them when we actually expect a warning.
  • enableLogger() supports 2 ways of calling it. It's parameter could be a synchronous call with boolean flag or callback to be executed when warnings/errors logging is enabled.

Eliminating Logger using WebPack Loader

There is a natural desire to eliminate logging completely on certain production environments to get as small build size as possible. One of the ways it could be achieved - is by adding extra loader for your webpack configuration

  1. Create new file no-logger-loader.js with the following contents:
module.exports=function(source,map){if(source.indexOf('react-logger-lib')!==-1){constcleanSource=source.replace(/Logger\.of\([^\)]+\)\.(trace|info|warn|error)/g,'');this.callback(null,cleanSource,map);}else{this.callback(null,source,map);}}
  1. Modify your production configuration webpack.config.prod.js. It is important to add this loader beforebabel-loader.
 ...
module: {
...
rules: [
...
{test: /\.(js|jsx)$/,include: paths.appSrc,loader: require.resolve('./no-logger-loader')},
...
]}
  1. Please make sure UglifyJsPlugin is also enabled in that webpack configuration.

This way you will get rid of all logging on this environment.

Disclaimer

  • This library is more a pattern to be cloned and configured for your own needs. This is why there are no plugins and extensions like in other similar facades.
  • We did it just because we love Slf4J approach in Java, but there was no similar thing in React applications.
  • Unlike Java, where component path is taken to logs automatically, here naming is left on responsibility of a writer. Our advice is to keep it as hierarchy of your business logic, as clear and standard as possible.
  • Please use logging wisely on render() methods, as extensive logging during rendering is an easy way to downgrade your application performance.
  • There is no slowdown noticed when logs are suppressed or not matching their level.
  • Feel free to discuss this project under Github Issues.

About

ReactJS / ES6 / ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

react-logger-lib

ReactJS/ES6/ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Install

npm install react-logger-lib --save

Why one more library for logging?

This library is a tribute to simple logging facade from other languages and attempt to bring similar pattern into client side programming, ReactJS and other ES7 applications.

Benefits

  • it allows to manipulate filtration without build tools.
  • filtration is based on components hierarchy
  • it is lightweight 2KB gzipped (~8KB original)
  • no external dependencies, basically this is a module that you can just copy to you project
  • useful for automated unit tests, where it can trigger warnings according to expectations
  • no performance issues (if used properly)

Typical Usage in React Component

SidePicker.jsx:

importReact,{Component}from'react';import{Logger}from'react-logger-lib';classSidePickerextendsComponent{// Let's imagine some useful component for random side picking// One of 4 options should be chosen. And the same choice should not be repeated in the next attemptvalues=['left','right','top','bottom'];state={side: 'left'};componentDidMount(){Logger.of('App.SidePicker.componentDidMount').info('state=',this.state);}pickSide=()=>{// set of possible values to exclude current sideconstvalues=this.values.slice();values.splice(this.values.indexOf(this.state.side),1);Logger.of('App.SidePicker.pickSide.values').info('Set of new values',values);// new side is a random pick out of those valuesconstside=values[Math.floor(Math.random()*values.length)];Logger.of('App.SidePicker.pickSide.side').info('Another side chosen','side=',side);// save decision into the statethis.setState({ side });}pickWrongSide=()=>{// nothing happens. we produce just a warning, visible in ConsoleLogger.of('App.SidePicker.pickWrongSide').warn('Attempt to pick a wrong side');}render(){return(<div><div>Current Side: <strong>{this.state.side}</strong></div><buttonclassName='good-button'type='button'onClick={()=>(this.pickSide())}>
PICK ANOTHER SIDE
</button><buttonclassName='bad-button'type='button'onClick={()=>(this.pickWrongSide())}>
PICK WRONG SIDE
</button></div>);}}exportdefaultSidePicker;

Once you mount this component into your application, nothing is shown in Console tab when launched and when you click the first button, as it is declared under INFO level - and suppressed by default (this is why it is basically acceptable for production).

But when you click the second button, the wrong one, you will see console.warn

App.SidePicker.pickWrongSide | Attempt to pick a wrong side

stating that something wrong happened, though there was nothing critical for further application health.

What is the key feature of this approach, is that once written, it is compiled and running, and completely forgotten, you can manage logging level at anytime - and describe what level is required for what component tree in terms of your business logic. This management can be done just by putting localStorage variables in your browser.

For this particular example you can use one of the following to enable level at any depth:

localStorage.setItem('App','INFO');localStorage.setItem('App.SidePicker','INFO');localStorage.setItem('App.SidePicker.pickSide','INFO');localStorage.setItem('App.SidePicker.componentDidMount','INFO');

and then refresh the page to apply changes for these settings. When loaded and 2 buttons will be clicked, Console will show something like this:

App.SidePicker.componentDidMount | state= Object {side: "left"}
App.SidePicker.pickSide.values | Set of new values (3) ["right", "top", "bottom"]
App.SidePicker.pickSide.side | Another side chosen side= top
App.SidePicker.pickWrongSide | Attempt to pick a wrong side

Levels of logging

There are 4 levels of logging currently. From lowest to highest these are TRACE-INFO-WARN-ERROR:

  • OFF suppresses the logging completely
  • ERROR level will show only messages with ERROR level.
  • WARN level will show all messages with ERROR level as well.
  • INFO level will show all messages with ERROR and WARN levels as well.
  • TRACE level will show all messages with INFO, WARN and ERROR levels as well.

TRACE is not calling console.trace, it is just a logical level in this library

Using in Unit Tests

Now let's see what we can benefit from our logging in unit tests:

importReactfrom'react';import{shallow}from'enzyme';import{enableLogger,Logger}from'react-logger-lib';importSidePickerfrom'./SidePicker';constexpectNoErrors=()=>{expect(Logger.calls.error).toEqual(0);};constexpectNoWarn=()=>{expect(Logger.calls.warn).toEqual(0);expectNoErrors();};constexpectWarn=()=>{expect(Logger.calls.warn).toBeGreaterThan(0);};describe('App.SidePicker',()=>{it('was mounted without warnings',()=>{enableLogger(()=>{shallow(<SidePicker/>);expectNoWarn();});});it('good button works without warnings',()=>{enableLogger(()=>{constelem=shallow(<SidePicker/>);// expect good button to be foundconstgoodButton=elem.find('button').at(0);expect(goodButton.text()).toEqual('PICK ANOTHER SIDE');// clicking on itgoodButton.simulate('click');expectNoWarn();});});it('bad button triggers warnings',()=>{enableLogger(false);// we do not want to see the warning when it happensconstelem=shallow(<SidePicker/>);// expect bad button to be foundconstbadButton=elem.find('button').at(1);expect(badButton.text()).toEqual('PICK WRONG SIDE');// clicking on itbadButton.simulate('click');expectWarn();});});

Jest tests above are checking that there was no warnings during components mounting, that there was no warning when the good button is clicked, and some warning was expected on the bad button.

  • All logging is suppressed when running with Jest. Therefore enabledLogger() function is a way to display actual warnings and errors when they are happening during tests.
  • Typical case, as described above - we enable warnings when we do not expect them to happen. And we disable them when we actually expect a warning.
  • enableLogger() supports 2 ways of calling it. It's parameter could be a synchronous call with boolean flag or callback to be executed when warnings/errors logging is enabled.

Eliminating Logger using WebPack Loader

There is a natural desire to eliminate logging completely on certain production environments to get as small build size as possible. One of the ways it could be achieved - is by adding extra loader for your webpack configuration

  1. Create new file no-logger-loader.js with the following contents:
module.exports=function(source,map){if(source.indexOf('react-logger-lib')!==-1){constcleanSource=source.replace(/Logger\.of\([^\)]+\)\.(trace|info|warn|error)/g,'');this.callback(null,cleanSource,map);}else{this.callback(null,source,map);}}
  1. Modify your production configuration webpack.config.prod.js. It is important to add this loader beforebabel-loader.
 ...
module: {
...
rules: [
...
{test: /\.(js|jsx)$/,include: paths.appSrc,loader: require.resolve('./no-logger-loader')},
...
]}
  1. Please make sure UglifyJsPlugin is also enabled in that webpack configuration.

This way you will get rid of all logging on this environment.

Disclaimer

  • This library is more a pattern to be cloned and configured for your own needs. This is why there are no plugins and extensions like in other similar facades.
  • We did it just because we love Slf4J approach in Java, but there was no similar thing in React applications.
  • Unlike Java, where component path is taken to logs automatically, here naming is left on responsibility of a writer. Our advice is to keep it as hierarchy of your business logic, as clear and standard as possible.
  • Please use logging wisely on render() methods, as extensive logging during rendering is an easy way to downgrade your application performance.
  • There is no slowdown noticed when logs are suppressed or not matching their level.
  • Feel free to discuss this project under Github Issues.

About

ReactJS / ES6 / ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

react-logger-lib

ReactJS/ES6/ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Install

npm install react-logger-lib --save

Why one more library for logging?

This library is a tribute to simple logging facade from other languages and attempt to bring similar pattern into client side programming, ReactJS and other ES7 applications.

Benefits

  • it allows to manipulate filtration without build tools.
  • filtration is based on components hierarchy
  • it is lightweight 2KB gzipped (~8KB original)
  • no external dependencies, basically this is a module that you can just copy to you project
  • useful for automated unit tests, where it can trigger warnings according to expectations
  • no performance issues (if used properly)

Typical Usage in React Component

SidePicker.jsx:

importReact,{Component}from'react';import{Logger}from'react-logger-lib';classSidePickerextendsComponent{// Let's imagine some useful component for random side picking// One of 4 options should be chosen. And the same choice should not be repeated in the next attemptvalues=['left','right','top','bottom'];state={side: 'left'};componentDidMount(){Logger.of('App.SidePicker.componentDidMount').info('state=',this.state);}pickSide=()=>{// set of possible values to exclude current sideconstvalues=this.values.slice();values.splice(this.values.indexOf(this.state.side),1);Logger.of('App.SidePicker.pickSide.values').info('Set of new values',values);// new side is a random pick out of those valuesconstside=values[Math.floor(Math.random()*values.length)];Logger.of('App.SidePicker.pickSide.side').info('Another side chosen','side=',side);// save decision into the statethis.setState({ side });}pickWrongSide=()=>{// nothing happens. we produce just a warning, visible in ConsoleLogger.of('App.SidePicker.pickWrongSide').warn('Attempt to pick a wrong side');}render(){return(<div><div>Current Side: <strong>{this.state.side}</strong></div><buttonclassName='good-button'type='button'onClick={()=>(this.pickSide())}>
PICK ANOTHER SIDE
</button><buttonclassName='bad-button'type='button'onClick={()=>(this.pickWrongSide())}>
PICK WRONG SIDE
</button></div>);}}exportdefaultSidePicker;

Once you mount this component into your application, nothing is shown in Console tab when launched and when you click the first button, as it is declared under INFO level - and suppressed by default (this is why it is basically acceptable for production).

But when you click the second button, the wrong one, you will see console.warn

App.SidePicker.pickWrongSide | Attempt to pick a wrong side

stating that something wrong happened, though there was nothing critical for further application health.

What is the key feature of this approach, is that once written, it is compiled and running, and completely forgotten, you can manage logging level at anytime - and describe what level is required for what component tree in terms of your business logic. This management can be done just by putting localStorage variables in your browser.

For this particular example you can use one of the following to enable level at any depth:

localStorage.setItem('App','INFO');localStorage.setItem('App.SidePicker','INFO');localStorage.setItem('App.SidePicker.pickSide','INFO');localStorage.setItem('App.SidePicker.componentDidMount','INFO');

and then refresh the page to apply changes for these settings. When loaded and 2 buttons will be clicked, Console will show something like this:

App.SidePicker.componentDidMount | state= Object {side: "left"}
App.SidePicker.pickSide.values | Set of new values (3) ["right", "top", "bottom"]
App.SidePicker.pickSide.side | Another side chosen side= top
App.SidePicker.pickWrongSide | Attempt to pick a wrong side

Levels of logging

There are 4 levels of logging currently. From lowest to highest these are TRACE-INFO-WARN-ERROR:

  • OFF suppresses the logging completely
  • ERROR level will show only messages with ERROR level.
  • WARN level will show all messages with ERROR level as well.
  • INFO level will show all messages with ERROR and WARN levels as well.
  • TRACE level will show all messages with INFO, WARN and ERROR levels as well.

TRACE is not calling console.trace, it is just a logical level in this library

Using in Unit Tests

Now let's see what we can benefit from our logging in unit tests:

importReactfrom'react';import{shallow}from'enzyme';import{enableLogger,Logger}from'react-logger-lib';importSidePickerfrom'./SidePicker';constexpectNoErrors=()=>{expect(Logger.calls.error).toEqual(0);};constexpectNoWarn=()=>{expect(Logger.calls.warn).toEqual(0);expectNoErrors();};constexpectWarn=()=>{expect(Logger.calls.warn).toBeGreaterThan(0);};describe('App.SidePicker',()=>{it('was mounted without warnings',()=>{enableLogger(()=>{shallow(<SidePicker/>);expectNoWarn();});});it('good button works without warnings',()=>{enableLogger(()=>{constelem=shallow(<SidePicker/>);// expect good button to be foundconstgoodButton=elem.find('button').at(0);expect(goodButton.text()).toEqual('PICK ANOTHER SIDE');// clicking on itgoodButton.simulate('click');expectNoWarn();});});it('bad button triggers warnings',()=>{enableLogger(false);// we do not want to see the warning when it happensconstelem=shallow(<SidePicker/>);// expect bad button to be foundconstbadButton=elem.find('button').at(1);expect(badButton.text()).toEqual('PICK WRONG SIDE');// clicking on itbadButton.simulate('click');expectWarn();});});

Jest tests above are checking that there was no warnings during components mounting, that there was no warning when the good button is clicked, and some warning was expected on the bad button.

  • All logging is suppressed when running with Jest. Therefore enabledLogger() function is a way to display actual warnings and errors when they are happening during tests.
  • Typical case, as described above - we enable warnings when we do not expect them to happen. And we disable them when we actually expect a warning.
  • enableLogger() supports 2 ways of calling it. It's parameter could be a synchronous call with boolean flag or callback to be executed when warnings/errors logging is enabled.

Eliminating Logger using WebPack Loader

There is a natural desire to eliminate logging completely on certain production environments to get as small build size as possible. One of the ways it could be achieved - is by adding extra loader for your webpack configuration

  1. Create new file no-logger-loader.js with the following contents:
module.exports=function(source,map){if(source.indexOf('react-logger-lib')!==-1){constcleanSource=source.replace(/Logger\.of\([^\)]+\)\.(trace|info|warn|error)/g,'');this.callback(null,cleanSource,map);}else{this.callback(null,source,map);}}
  1. Modify your production configuration webpack.config.prod.js. It is important to add this loader beforebabel-loader.
 ...
module: {
...
rules: [
...
{test: /\.(js|jsx)$/,include: paths.appSrc,loader: require.resolve('./no-logger-loader')},
...
]}
  1. Please make sure UglifyJsPlugin is also enabled in that webpack configuration.

This way you will get rid of all logging on this environment.

Disclaimer

  • This library is more a pattern to be cloned and configured for your own needs. This is why there are no plugins and extensions like in other similar facades.
  • We did it just because we love Slf4J approach in Java, but there was no similar thing in React applications.
  • Unlike Java, where component path is taken to logs automatically, here naming is left on responsibility of a writer. Our advice is to keep it as hierarchy of your business logic, as clear and standard as possible.
  • Please use logging wisely on render() methods, as extensive logging during rendering is an easy way to downgrade your application performance.
  • There is no slowdown noticed when logs are suppressed or not matching their level.
  • Feel free to discuss this project under Github Issues.

About

ReactJS / ES6 / ES7 logging - lightweight library with managed logging levels, similar to simple logging facades in other languages

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages