Display browser console in HTML page

Viewed 1662

Edit: I found the code that Stack Overflow uses: https://github.com/gh-canon/stack-snippet-console

I've found a bunch of answers that show how to output the console to a webpage, but I'm trying to make it so that the messages are also logged to the console. Ironically, if you run snippets on Stack Overflow, they do what I'm trying to.

// This causes a stack overflow
var native = window;
native.console = {
    log: function(message){
        $('ul.messages').append('<li>Log: ' + message + '</li>');
        console.log(message);
    },
    error: function(message){
        $('ul.messages').append('<li>Error: ' + message + '</li>');
        console.error(message);
    },
    warn: function(message){
        $('ul.messages').append('<li>Warn: ' + message + '</li>');
        console.warn(message);
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="messages"></ul>

3 Answers

I think you just need to cache the original console methods and call them from the cache-- the way you have it now calls your stubbed log, which causes infinite recursion (and thus a stackoverflow):

$(document).ready(function(){
    console.log('You should know...');
    console.error('Something went wrong...');
    console.warn('Look out for this...');
})


// This should work
var native = window;
var originalConsole = native.console;
native.console = {
    log: function(message){
        $('ul.messages').append('<li>Log: ' + message + '</li>');
        originalConsole.log(message);
    },
    error: function(message){
        $('ul.messages').append('<li>Error: ' + message + '</li>');
        originalConsole.error(message);
    },
    warn: function(message){
        $('ul.messages').append('<li>Warn: ' + message + '</li>');
        originalConsole.warn(message);
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h3>messages</h3>
<ul class="messages"></ul>

You can create a wrapper function that takes in a function and outputs your modified function.

const wrapper = (fn, name) => {
  return function(msg) {
    $('ul.messages').append(`<li>${name}: ${msg}</li>`);
    fn(msg);
  };
}

$(document).ready(() => {
  window.console.log = wrapper(console.log, "Log");
  window.console.warn = wrapper(console.warn, "Warn");
  window.console.error = wrapper(console.error, "Error");
});

I do not recommend you modify the original function, you can create a new one, and that can show the message on both console and your node.

Blow is pure javascript code.

Example 1

function decorator(wrap, func) {
  return (...para) => {
    func(para)
    return wrap(para)
  }
}

const mylog = decorator(window.console.log, (...para)=>{
  const ul = document.querySelector(`ul[class=msg]`)
  const range = document.createRange()
  const frag = range.createContextualFragment(`<li>${para}</li>`)
  ul.append(frag)
})

mylog("Hello world!")
<h3>messages</h3>
<ul class="msg"></ul>

Example 2

window.onload = () => {
    const decoratorFunc = (methodName, msg) => {
      const symbol = methodName === "info" ? "" :
        methodName === "error" ? "❌" :
          methodName === "warn" ? "⚠" : ""

      const ul = document.querySelector(`ul[class=msg]`)
      const range = document.createRange()
      const frag = range.createContextualFragment(`<li>${symbol} ${msg}</li>`)
      ul.append(frag)
    }
    const myConsole = new MethodDecorator(window.console, decoratorFunc)
    myConsole.Apply(["log", "info", "error", "warn", // those will run {decoratorFunc + ``window.console.xxx``}
      "others" //  I created, it doesn't belong method of window.console so it only run ``decoratorFunc``
    ])
    myConsole.log("log test")
    myConsole.info("info test")
    console.info("not influenced") // not influenced
    myConsole.error("error test")
    myConsole.warn("warn test")
    myConsole.others("others test")
    myConsole.Reset()
    // myConsole.warn("warn test") // error
}

class MethodDecorator {
  constructor(obj, func) {
    this.obj = obj
    this.func = func
  }

  Apply(nameArray) {
    const orgMethodNameArray = Object.getOwnPropertyNames(this.obj)
    for (const methodName of nameArray) {
      const orgMethodName = orgMethodNameArray.find(e => e === methodName) // if not found return undefined
      const prop = {}
      prop[methodName] = {
        "configurable": true,
        "value": (...args) => {
          this.func(methodName, args) // decorator function
          if (orgMethodName) {
            this.obj[orgMethodName](args) // for example, console.log(args)
          }
        }
      }
      Object.defineProperties(this, prop)
    }
  }

  Reset() {
    const extraMethodNameArray = Object.getOwnPropertyNames(this).filter(name => name !== "obj" || name !== "func")
    for (const extraMethodName of extraMethodNameArray) {
      const prop = {}
      prop[extraMethodName] = {
        value: undefined
      }
      Object.defineProperties(this, prop)
    }
  }
}
<h3>messages</h3>
<ul class="msg"></ul>

The console of Run code snippet will block some contents. Click the full page after you click run code snippet to see all contents.

Reference

Related