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

Created on 29 Jul 2013  ·  60Comments  ·  Source: winstonjs/winston

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?

feature request

Most helpful comment

Man, got my hopes really high when I saw package and then noticed no browser support.
This would be really awesome for me in the browser to replace some home-grown stuff.

All 60 comments

I haven't tried it, but this might help https://github.com/farpoint/meteor-winston-client

There is 404 on that link @joshacheson !!

Do you have any progress with this issue ?

Based on feedback from #582, it seems that any future PR would need to focus on separating core logic and transports, rather than using shims such as brfs. This would be a large structural change and would almost certainly need guidance from core devs on style and approach, as they will ultimately be the ones left maintaining this.

The good news is that the code is mostly clean and well structured, but would need some guidance from core devs on style and approach. Could @indexzero / @pose share your thoughts?

Any progress with this issue?

Man, got my hopes really high when I saw package and then noticed no browser support.
This would be really awesome for me in the browser to replace some home-grown stuff.

+1

Same or me. Having the same logging lib for front and back also helps.

I was just browsing this and it seems a shame that even though it has a http transport there doesn't appear to be any standard client for the browser/other.

Sad that I will have to use bunyan

any news on this?

If you really want to use it, you could just write a custom transport for it, for example:

const Transport = require('winston-transport');

export default class BrowserConsole extends Transport {
  constructor(opts) {
    super(opts);

    this.name = 'BrowserConsole';
    this.levels = {
        error: 0,
        warn: 1,
        info: 2,
        debug: 4,
    };

    this.methods = {
        error: 'error',
        warn: 'warn',
        info: 'info',
        debug: 'log',
    };

    this.level = opts.level && this.levels.hasOwnProperty(opts.level)
                  ? opts.level : 'info';
  }

  log(method, message) {
    setImmediate(() => {
      this.emit('logged', method);
    });

    const val = this.levels[method];
    const mappedMethod = this.methods[method];

    if (val <= this.levels[this.level]) {
      // eslint-disable-next-line
      console[mappedMethod](message);
    }
  }
}

Then you can use it in this way:

import BrowserConsole from './BrowserConsole';

const { createLogger, transports } = require('winston');

const log = createLogger({
  level: 'info',
});

if (process.env.NODE_ENV !== 'production') {
  log.add(new BrowserConsole({
    level: 'info',
  }));
}

With this transport, you get a useless warning because it's considered as legacy code. It would also be beautiful to add the possibility to use other console methods (table, dir, trace, ...), but for simplicity sake it's like it is.

Thank you @chrisvoo, I've tried this solution but got error:

ReferenceError: Buffer is not defined
    replacer 
    json.js/module.exports< 
    _transform 
    _stream_transform.js/Transform.prototype._read
    _stream_transform.js/Transform.prototype._write
    doWrite
    writeOrBuffer
    _stream_writable.js/Writable.prototype.write
    log

@dmitry-salnikov I don't know, btw I cannot understand why this code is using streams. Maybe my use case was too simple. Try to share how you're using it.

@chrisvoo I've copypasted the BrowserConsole implementation to a separate file, then in another file pasted the second part of code, and after adding BrowserConsole transport code (the last line of your snippet) I've simply tried:

log.info('hello world');

Then I got error, and tried:

log.log('info, 'hello world');

Both calls return same error.

My environment which makes possible using Node in browser is Meteor.js v1.6 (Node 8.8.1). Also I haven't modified any line of your code snippet.

BTW: I've started using winston not so long time ago, so may be doing something wrong.

@dmitry-salnikov what kind of error do you receive? Like "info is not a function"? Maybe for some reason, it's badly imported.

@chrisvoo please take a look:
screenshot 2017-11-08 20 35 31

The contents of BrowserConsole.js (you can see it in file tree) is exactly as in your snippet.

And I agree with you, I feel that something is wrong with import, but can't figure out why :( Could you please share your thoughts on this?

What's your Winston version? Mine is:

"winston": "^3.0.0-rc1",
"winston-transport": "^3.0.1"

Actually the same

$ npm ls | grep winston
├─┬ [email protected]
│ └── [email protected]
└── [email protected]
$ cat package.json | grep winston
    "winston": "^3.0.0-rc1",
    "winston-transport": "^3.0.1"

Trying to solve this issue I've made another test:

import winston from 'winston';
import BrowserConsole from '/imports/BrowserConsole.js';

const format = winston.format;
// next line throws exception, see below
const { combine, timestamp, label, printf, colorize, prettyPrint } = format;

const logger = winston.createLogger({
...

and got the next error: Cannot find module './combine'

Here is the stack trace and related code (browser screenshot):
screenshot 2017-11-10 04 01 04

Seems like something's really badly imported. @chrisvoo could you please take a look?

In Winston 3.0.0, transports are streams. However, in browserify-stream, readableStream instanceof Stream does not hold true. This makes Winston fall back to wrapping the transport in a Legacy wrapper and emitting a warning. I made a PR to use a different method for streaminess detection: #1145

@Jasu true i noticed this aswell, i have submitted an issue earlier in regards to this on winston-transport. I will also submit a pull request soon for the console transport to be isomorphic.

IGNOREME: I am commenting here so I can easily find this issue again in the future, [since I can't do that by subscribing alone](https://github.com/isaacs/github/issues/283).

I met the same problem, Error: Cannot find module './combine' .

+1

@chrisvoo : below is the error, when I try to run your snippet (winston and winston-transport are on 3+ versions )
ERROR in winston-transport/index.js
Module not found: Error: Can't resolve 'stream' in node_modules/winston-transport'

is there any chance that PR #1145 (opened in November 2017) will be merged this year? :wink:

@dmitry-salnikov #1145 has been merged to master. Not in a release yet though.

CLOSED CLOSED CLOSED.

Thanks for the support ya potatoes

5 years, at least its coming to fruition. Winston is still the best logging system for JavaScript IMO

Thanks!

This will remain open until there are tests in our test suite that verify browser support. Seems like most of the edge and corner cases around babel & webpack have been solved however.

In here we love winston and need for a client-side logging middleware.

A while have passed since browser was implemented and we'd like to use it from now.

Any rough idea of browser support, before waiting unit tests and full browser coverage ?

Tried to import winston to my project and failed with the following message:
ERROR in ./\~/winston/lib/winston/tail-file.js
Module not found: Error: Can't resolve 'fs' in '/Users/me/workspaces/app/node_modules/winston/lib/winston'
@ ./\~/winston/lib/winston/tail-file.js 10:11-24
@ ./\~/winston/lib/winston/transports/file.js
@ ./\~/winston/lib/winston/transports/index.js
@ ./\~/winston/lib/winston.js
@ ./src/app/app.module.ts
@ ./src/main.ts

winston's index.js import Transports which import '.file' which require 'fs'.

How do I unsubscribe from this fresh hell

  • Michael

On Tue, Aug 7, 2018 at 2:19 PM Kfir Erez notifications@github.com wrote:

Tried to import winston to my project and failed with the following
message:
ERROR in .//winston/lib/winston/tail-file.js
Module not found: Error: Can't resolve 'fs' in
'/Users/me/workspaces/app/node_modules/winston/lib/winston'
@ .//winston/lib/winston/tail-file.js 10:11-24
@ .//winston/lib/winston/transports/file.js
@ .//winston/lib/winston/transports/index.js
@ ./~/winston/lib/winston.js
@ ./src/app/app.module.ts
@ ./src/main.ts

winston's index.js import Transports which import '.file' which require
'fs'.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/winstonjs/winston/issues/287#issuecomment-410946148,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AE3lcdZ3aQKEVYvYB2TXjh0dnQ1FaBS2ks5uOTFhgaJpZM4A2vjK
.

@mjcd you're stuck here forever 😆 (j/k, you can unsub using the link on the email or via gh)

I've had the same error that @KErez had using webpack

A common way to solve that is put node: { fs: 'empty' } in your webpack config -- and obviously don't try to use the File transport from the browser, it won't work. Would be nice if we could make winston bundle in webpack without that extra config setting but idk if it's possible. Other popular packages recommend the same thing -- although https://github.com/pugjs/pug-loader/issues/8#issuecomment-328331541 suggests we could fix this in winston's package.json. Somebody want to try that and open a PR if it solves that error (i.e. without changing your app's webpack config, just changing winston's package.json)?

Me I'm getting an error due to the setImmediate... I don't understand why since @chrisvoo seems to succeed with. Maybe because you he use a polyfill?

My related issue: https://github.com/winstonjs/winston/issues/1489

Made a package based on @chrisvoo code (thx so much) here:
https://www.npmjs.com/package/winston-transport-browserconsole.

Also, there is a little sample there so you can compare against default winston console output.

A common way to solve that is put node: { fs: 'empty' } in your webpack config

Are there plans to support webpack browser bundles without the necessity to make this change to the webpack config?

Would be nice if we could make winston bundle in webpack without that extra config setting but idk if it's possible. Other popular packages recommend the same thing -- although pugjs/pug-loader#8 (comment) suggests we could fix this in winston's package.json. Somebody want to try that and open a PR if it solves that error (i.e. without changing your app's webpack config, just changing winston's package.json)?

@DABH Unfortunately, I don't think it's that simple. You guys use the browser field to define a different entry point. I believe it can either be used to define a different entry point OR replace certain modules, like described in that ticket - not both. But since you're already building your own browser version it seems, maybe it can just be removed from that. I'll take a peak if I have a chance this weekend.

Any progress on this? It's been 6 years and tomorrow it will be 2020 :-)

Maybe the solution would be to re package winston so transporters are they own module. Sounds like a betters solution

can we use Winston in angular ? how ?

@ArpithaGMGowda not with the standard angular CLI

Then what can we use for Angular 7 ??
Any idea

I used js-logger in my last project which worked very well and allow me to send logs to elk although it looks like it did not have much activity in the last year: https://github.com/jonnyreeves/js-logger
There are good logging services that may also work for you such as track.js

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

Problem with Winston the code based needs modernising without breaking core features.

The transport layer needs splitting out into it's own submouldes which in return will cause breaking changes I guess the team doesn't want to cause. To the point unless the team willing to adopt a new eco system. I'm unsure the PR thats being repaired will be approved.

are you people tried with NGX-Logger "https://www.npmjs.com/package/ngx-logger" ?

@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

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.

@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

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.

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 ?

@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.

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

@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...

@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.

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.

Was this page helpful?
0 / 5 - 0 ratings