winstonjs / winston

A logger for just about everything.
http://github.com/winstonjs/winston
MIT License
22.82k stars 1.81k forks source link

Can winston be used on the front-end (i.e. browser) for logging? #287

Open dcbartlett opened 11 years ago

dcbartlett commented 11 years ago

Can winston be used on the front-end for logging? I'd like to use Winston for greater front-end logging ability. Can this be done?

Jordan-Hall commented 4 years ago

@ArpithaGMGowda I guess for me, i don't like writing a code base that ties you into a set framework. Code should be agnostic as possible from UI to service calls. I also like the idea of a unified mechanism. Why have one way for the backend and another for the frontend

MarcoMedrano commented 4 years ago

I'm rewriting this library into a new structure which removes the dependency on node. Should have it finish in coming week

@Jordan-Hall Wonder if we are having a rc any soon (and thanks). Having to modify third party webpacks in order to do not break when they use our project/lib which uses winston is struggling.

Jordan-Hall commented 4 years ago

@MarcoMedrano Its a fundamental change which would in theory be a new library time i finished it.

@pose Whats your view on splitting out the transport layers from the core package? I like Winston but the eco system does need changing

Keimeno commented 4 years ago

Took a while to write this, but I've come up with a somewhat reasonable solution to this. The following will allow you to use the error, warn and info levels in the browser (with custom prefixes!), and they will look like this: gHLo47GZ0bvMAsiqhxRfSV3TIWyXn9NO

For this to work, you need to install winston, logform and winston-transport as dependencies.

Here's the code you need to implement this. Notice that this is written in typescript, the javascript example is below.

import * as winston from 'winston';
import {TransformableInfo} from 'logform';
import TransportStream = require('winston-transport');

// enumeration to assign color values to
enum LevelColors {
  INFO = 'darkturquoise',
  WARN = 'khaki',
  ERROR = 'tomato',
}

// type levels used for setting color and shutting typescript up
type Levels = 'INFO' | 'WARN' | 'ERROR';

const defaultColor = 'color: inherit';

//! Overriding winston console transporter
class Console extends TransportStream {
  constructor(options = {}) {
    super(options);

    this.setMaxListeners(30);
  }

  log(info: TransformableInfo, next: () => void) {
    // styles a console log statement accordingly to the log level
    // log level colors are taken from levelcolors enum
    console.log(
      `%c[%c${info.level.toUpperCase()}%c]:`,
      defaultColor,
      `color: ${LevelColors[info.level.toUpperCase() as Levels]};`,
      defaultColor,
      // message will be included after stylings
      // through this objects and arrays will be expandable
      info.message
    );

    // must call the next function here
    // or otherwise you'll only be able to send one message
    next();
  }
}

// creating silent loggers with according levels
// silent by default to be automatically deactivated
// in production mode
export const logger = winston.createLogger({
  transports: [
    new Console({
      silent: true,
      level: 'info',
    }),
  ],
});

// don't log anything in production mode
// probably should go further and return non
// working logger function to reduce
// execution time and improve speed results
// on application
if (process.env.NODE_ENV !== 'production') {
  logger.transports.forEach(transport => (transport.silent = false));
}

and here's the javascript example

import * as winston from 'winston';

import {TransformableInfo} from 'logform';
import TransportStream = require('winston-transport');

// enumeration to assign color values to
const LevelColors = {
  INFO: 'darkturquoise',
  WARN: 'khaki',
  ERROR: 'tomato',
}

const defaultColor = 'color: inherit';

//! Overriding winston console transporter
class Console extends TransportStream {
  constructor(options = {}) {
    super(options);

    this.setMaxListeners(30);
  }

  log(info, next) {
    // styles a console log statement accordingly to the log level
    // log level colors are taken from levelcolors enum
    console.log(
      `%c[%c${info.level.toUpperCase()}%c]:`,
      defaultColor,
      `color: ${LevelColors[info.level.toUpperCase()]};`,
      defaultColor,
      // message will be included after stylings
      // through this objects and arrays will be expandable
      info.message
    );

    // must call the next function here
    // or otherwise you'll only be able to send one message
    next();
  }
}

// creating silent loggers with according levels
// silent by default to be automatically deactivated
// in production mode
export const logger = winston.createLogger({
  transports: [
    new Console({
      silent: true,
      level: 'info',
    }),
  ],
});

// don't log anything in production mode
// probably should go further and return non
// working logger function to reduce
// execution time and improve speed results
// on application
if (process.env.NODE_ENV !== 'production') {
  logger.transports.forEach(transport => (transport.silent = false));
}

You can change the colors in the LevelColors enum. If you want to change the formatting, take a look at line 29.

To add support for the debug level. set the level in the console options to 'debug'. It is also possible to add support for all standard winston levels, meaning: emerg, alert, crit, error, warn, info and debug. If you want to use these as well you need to add assign this object to the levels config in the createLogger root

{
   emerg: 0,
   alert: 1,
   crit: 2,
   error: 3,
   warn: 4,
   info: 5,
   debug: 6,
}

and then add the color values in the LevelColors enum.

z00m1n commented 4 years ago

I'm struggling to get a clear picture of the status of this issue. Can I use Winston in my React app ?

I'm actually not so much interested in logging to the browser console - and quite honestly, I don't understand the point in overriding a winston console transporter when the built-in console serves the same purpose; maybe someone could kindly enlighten me.

My situation is that my React app runs in a Docker container behind an nginx / Let's Encrypt proxy, so I have no access to any JavaScript console output; I would therefore like to forward any log output to a syslog server.

I have successfully set up a syslog-ng Docker container which consolidates log output from the database, backend and some other containers my project is made of, but I can't seem to find a straight-forward / canonical approach to syslogging output from the React frontend.

Before I go about hacking some dumb home-made solution, does anyone have some better advice for me ? Maybe take the code above and replace console.log by some code that sends the message over the network to the syslog server ?

Keimeno commented 4 years ago

@z00m1n It mostly depends on your use case. I use winston in the browser to log all requests and function calls I make. And if I'm in a production environment I limit the output to only print errors.

And using my code and exchanging the console.log statement with something else would work.

However, before you write a hacky solution to make this work, I'd propose using sentry.

Jordan-Hall commented 4 years ago

It also depends on if you have control over webpack. Sadly this amazing package is out of date architecturally which makes it impossible to truly solve

gcperrin commented 4 years ago

@Keimeno have you noticed any odd behavior or performance issues? Really want to use but it has to be uber-stable as some logging will happen in production for my use case...

Keimeno commented 4 years ago

@gcperrin Not sure if you can call it a performance issue, but I've been running some benchmarks and gotten the following results: dev environment: it logs something to the console prod environment: it doesn't log something but it does call the log function

console.info (dev environment); 1.863s for 10.000 logs. (0,1893ms each) logger.info (dev environment): 7.980s for 10.000 logs. (0.7980ms each)

logger.info (prod environment); 3.731s for 10.000 logs. (0.3731ms each)

This means if you use my function to silence the loggers in production, you will still have synchronous code running for 0.3731ms (potentially even higher). It might not be a performance issue, but if you have a few hundred logs that are silent in production, it might cause your webapp to lag.

shihaosun2013 commented 4 years ago

Any way to use winston to persist log into file system in the browser side?

I have a React application and want to store the client side logs into file system. Please kindly suggest some thoughts.

Thanks in advance.

rpatrick00 commented 3 years ago

In my electron-based application, I am using winston on the node.js (backend) side but would love to be able to use it on the chromium (web) side to stream logs to the same backend file. Not sure if there is an existing strategy/transport to achieve this or not...

Hooked74 commented 2 years ago

In an isomorphic application, it is possible to use the basic functions of winston if you import it in chunks. And you also need a polyfill for setImmediate if you use basic transports:

import createLogger from 'winston/lib/winston/create-logger';
import ConsoleTransport from 'winston/lib/winston/transports/console';
import logform from 'logform';

let logger = createLogger({
  transports: [
    new ConsoleTransport({
      format: logform.format.simple(),
    }),
  ],
});

if (typeof window !== 'undefined') {
  if (!window.setImmediate) {
    let uniqImmediateId = 0;
    const immediateMap = new Map();
    const runImmediate = (id) => {
      if (immediateMap.has(id)) {
        const [callback, args] = immediateMap.get(id);
        Reflect.apply(callback, undefined, args);
        clearImmediate(id);
      }
    };

    const channel = new MessageChannel();
    const registerImmediate = (id) => channel.port2.postMessage(id);

    channel.port1.onmessage = (event) => runImmediate(event.data);

    window.setImmediate = (callback, ...args) => {
      uniqImmediateId++;
      immediateMap.set(uniqImmediateId, [callback, args]);
      registerImmediate(uniqImmediateId);

      return uniqImmediateId;
    };
    window.clearImmediate = (id) => immediateMap.delete(id);
  }
}

logger.info('message');
DMiradakis commented 1 year ago

In my electron-based application, I am using winston on the node.js (backend) side but would love to be able to use it on the chromium (web) side to stream logs to the same backend file. Not sure if there is an existing strategy/transport to achieve this or not...

Exact same situation here, I want to use a common logger library file for both the frontend and "backend" Electron processes. The only option that I can think of to use it browser-side in Electron is to make a wrapper library for the .info, .error, etc. methods and execute them via IPC calls that are handled in the main BrowserWindow with .on() or .handle() event handlers.

I think another option in Electron browser-side is to enable nodeIntegration for your BrowserWindow, but the docs suggest trying not to use that flag as much as possible for safety concerns.

robertpatrick commented 1 year ago

We are wrapping the logger on the browser side so that it looks like winston and uses invoke()/handle() to make IPC calls that call the real winston logger on the Electron side.

DMiradakis commented 1 year ago

We are wrapping the logger on the browser side so that it looks like winston and uses invoke()/handle() to make IPC calls that call the real winston logger on the Electron side.

Yep, exactly what I've done, too.

wbt commented 1 year ago

The main obstacle to making this easier is that the File transport is basically baked into Winston core and needs to be separated out, but the Winston project has zero funding to support that work.

Red-Quill commented 1 year ago

What is the situation on this issue? I would like to work on this problem. By "separating out" do you mean separating into an external package? I researched this a bit and was able to separate the file transport and related tests to a separate package without affecting other unit or integration tests. Also found a minor bug (related to the soon to be legacy file streams). I could finish those and post PRs.

wbt commented 1 year ago

Yes, splitting packages between winston (core) and winston-file similar to how we have winston-syslog, winston-mongo, winston-logzio, etc.