megahertz / electron-log

Simple logging module Electron/Node.js/NW.js application. No dependencies. No complicated configuration.
MIT License
1.3k stars 127 forks source link

How to use `addLevel` in renderer process? #337

Closed pouriamoosavi closed 1 year ago

pouriamoosavi commented 1 year ago

It seems that there is no addLevel in renderer process. Is there any way to add new level for logs in browser? Isn't it possible to sync main and renderer process so adding a new level in main, also adds a new level in renderer?

megahertz commented 1 year ago

Yes, I'm going to add it in the beginning of January.

pouriamoosavi commented 1 year ago

Great. Thanks :)

pouriamoosavi commented 1 year ago

For now (if anyone needs this feature) I solve my problem with this:

electron.js (main process):

const electronLog = require("electron-log")

electronLog.addLevel('alert', 0);
Object.assign(console, electronLog.functions);

preload.js (contextBridge):

electronLog: (data, level) => {
  if(!Array.isArray(data)) {
    data = [data]
  }
  ipcRenderer.send('__ELECTRON_LOG__', {
    data, level,
  });
},

index.js (renderer process):

window.electron.electronLog("Message", "alert");
megahertz commented 1 year ago

Will publish in an hour. Now levels are loaded from the main process

pouriamoosavi commented 1 year ago

Unfortunately I can't make it work. I just added the level in main process and tried to use it in renderer. Is it right?

My code: electron.js (main process):

electronLog.addLevel('alert', 0);
electronLog.errorHandler.startCatching()
Object.assign(console, electronLog.functions);

App.js (renderer process, React.js):

console.alert("App did mount");

Throws error:

Uncaught TypeError: console.alert is not a function

Output of console.log(console) in renderer:

assert: ƒ assert()
clear: ƒ clear()
context: ƒ context()
count: ƒ count()
countReset: ƒ countReset()
debug: ƒ debug()
dir: ƒ dir()
dirxml: ƒ dirxml()
error: ƒ ()
group: ƒ group()
groupCollapsed: ƒ groupCollapsed()
groupEnd: ƒ groupEnd()
info: ƒ info()
log: ƒ log()
memory: MemoryInfo {totalJSHeapSize: 72200000, usedJSHeapSize: 72200000, jsHeapSizeLimit: 2190000000}
profile: ƒ profile()
profileEnd: ƒ profileEnd()
reactStack: ƒ (e)
reactStackEnd: ƒ (e)
table: ƒ table()
time: ƒ time()
timeEnd: ƒ timeEnd()
timeLog: ƒ timeLog()
timeStamp: ƒ timeStamp()
trace: ƒ trace()
warn: ƒ warn()

My version: "electron-log": "^5.0.0-beta.11"

Do you know what is wrong?

Thanks in advance.

megahertz commented 1 year ago

You've assigned electronLog.functions to console in the main process, but not in a renderer

pouriamoosavi commented 1 year ago

Doing so didn't help neither. I think the alert function didn't add to electronLog on renderer process. Below is the result of console.log(window.electronLog.functions) in App.js

debug: ƒ ()
error: ƒ ()
info: ƒ ()
log: ƒ ()
silly: ƒ ()
verbose: ƒ ()
warn: ƒ ()

And so calling Object.assign(console, window.electronLog.functions); doesn't help.

megahertz commented 1 year ago

try to call this code in 50ms timeout. I think it could be a reason, since levels are synchronized in a tiny delay.

pouriamoosavi commented 1 year ago

Unfortunately no luck!

class App extends Component {
// ... 
  componentDidMount() {
    setTimeout(() => {
        console.log(window.electronLog.functions)
        Object.assign(console, window.electronLog.functions);
        console.log(console)
        console.alert("App did mount");
    }, 500);
  }
// ... 
}
debug: ƒ ()
error: ƒ ()
info: ƒ ()
log: ƒ ()
silly: ƒ ()
verbose: ƒ ()
warn: ƒ ()
[[Prototype]]: Object

Uncaught TypeError: console.alert is not a function

megahertz commented 1 year ago

You're right. I've accidentally tested a new code when context isolation is disabled. When enabled, it requires a more complex solution. WIll think about it.

pouriamoosavi commented 1 year ago

Meanwhile I did some testings. Not sure how much helpful it would be. It seems you know the problem already. Anyway: I've added a console.log in line 138 of renderer.js:

  logger.initializeLevels = () => {
    for (const level of logger.levels) {
      logger.functions[level] = (...args) => log(level, args);
      logger.functions.log = (...args) => log('info', args);
    }

    console.log("logger", logger) //THIS
    Object.assign(logger, logger.functions);
  };

And it has the alert function. Hope it would be helpful.

megahertz commented 1 year ago

electronLog instance in a renderer script is an immutable copy of the same instance in preload. So, it's not possible to extend the first at runtime according to context isolation restrictions. I exposed the logData function, so now it's the only solution. Maybe I'll have a better idea later.

Here's an example https://github.com/megahertz/electron-log/blob/master/e2e/custom-levels/index.html

megahertz commented 1 year ago

Works fine in beta 14 without any manipulation in a renderer

pouriamoosavi commented 1 year ago

It seems that window.electronLog is undefined in 5.0.0-beta.14 renderer process: Main process:

app.on('ready', () => {
  electronLog.initialize({preload: true})
...

App.js:

class App extends Component {
  componentDidMount() {
    if(window.electron) {
      setTimeout(() => {
        console.log(window.electronLog)
        Object.assign(console, window.electronLog.functions);
      }, 1000);
...

output:

undefined

Uncaught TypeError: Cannot read properties of undefined (reading 'functions')

Do you know what is wrong? Thanks

megahertz commented 1 year ago

This issue helped me understand that I'm heading in the wrong direction. So, I've changed a way how renderer is initialized in the latest beta build. https://github.com/megahertz/electron-log/blob/master/docs/initialize.md

pouriamoosavi commented 1 year ago

So you are saying that with my current setup (sandbox disable), I can't have my costume functions like alert, in renderer process? As I understand from the document and from console.log(__electronLog) my alert function did not come from main process to renderer process.

megahertz commented 1 year ago

Yes, custom functions are only available when you using a bundler or nodeIntegration. As a a workaround, you can call low-level __electronLog.sendToMain({ data: ['test'], level: 'alert' })

pouriamoosavi commented 1 year ago

Ok thanks

megahertz commented 1 year ago

There's a chance I'll rename this function before release to logData, but will save its signature.