Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
dcbartlett opened this issue Jul 29, 2013 · 68 comments
Open

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

dcbartlett opened this issue Jul 29, 2013 · 68 comments
Labels
Feature Request Request for new functionality to support use cases not already covered

Comments

@dcbartlett
Copy link

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?

@gihrig
Copy link

gihrig commented Jul 29, 2013

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

@pose
Copy link
Member

pose commented Sep 30, 2014

This is related to: #180

@pose pose mentioned this issue Oct 17, 2014
@indexzero indexzero added the Feature Request Request for new functionality to support use cases not already covered label Nov 4, 2014
@indexzero indexzero changed the title Can winston be used on the front-end for logging? Can winston be used on the front-end (i.e. browser) for logging? Mar 17, 2015
@bikramjs
Copy link

There is 404 on that link @joshacheson !!

@SiarheiMelnik
Copy link

Do you have any progress with this issue ?

@foxx
Copy link
Contributor

foxx commented Nov 27, 2015

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?

@sashakru
Copy link

Any progress with this issue?

@tommck
Copy link

tommck commented Feb 28, 2017

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.

@ronanquillevere
Copy link

+1

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

@MJCD
Copy link

MJCD commented Oct 21, 2017

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.

@MJCD
Copy link

MJCD commented Oct 21, 2017

Sad that I will have to use bunyan

@Bamieh
Copy link
Contributor

Bamieh commented Nov 7, 2017

any news on this?

@chrisvoo
Copy link

chrisvoo commented Nov 7, 2017

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.

@dmitry-salnikov
Copy link

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

@chrisvoo
Copy link

chrisvoo commented Nov 8, 2017

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

@dmitry-salnikov
Copy link

dmitry-salnikov commented Nov 8, 2017

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

@chrisvoo
Copy link

chrisvoo commented Nov 8, 2017

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

@dmitry-salnikov
Copy link

dmitry-salnikov commented Nov 8, 2017

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

@chrisvoo
Copy link

chrisvoo commented Nov 9, 2017

What's your Winston version? Mine is:

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

@dmitry-salnikov
Copy link

Actually the same

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

@dmitry-salnikov
Copy link

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?

@Jasu
Copy link
Contributor

Jasu commented Nov 15, 2017

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

@MarcoMedrano
Copy link

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
Copy link

@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
Copy link

Keimeno commented Jun 21, 2020

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
Copy link

z00m1n commented Sep 3, 2020

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
Copy link

Keimeno commented Sep 3, 2020

@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
Copy link

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
Copy link

@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
Copy link

Keimeno commented Oct 5, 2020

@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
Copy link

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
Copy link

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
Copy link

Hooked74 commented Jul 1, 2022

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');

lnist added a commit to mangrovedao/mangrove.js that referenced this issue Aug 26, 2022
* fix(webapp): Make browsers use browser bundle
  * Otherwise, they try to import fs, os, etc. only available in node.
* fix(bundling): Remove direct and indirect node exports
  * Otherwise, browsers will fail to import node dependencies.
* fix(bundling): Add shims for select functionality for browsers
  * fs cannot be imported in browser, readJsonWallet is not relevant in
browser, so we add a shim for it.
  * added shim for logging since our logging framework depends on node (winstonjs/winston#287)
* fix(bundling): Bundle js output instead of ts
  * Also switch from iife to cjs format. Otherwise, vite cannot find exports - this may be due to esbuild not understanding all of tsconfig.
* fix(bundling): Don't bundle js in parallel with building it
@DMiradakis
Copy link

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
Copy link

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
Copy link

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
Copy link
Contributor

wbt commented Feb 6, 2023

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
Copy link

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
Copy link
Contributor

wbt commented May 18, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature Request Request for new functionality to support use cases not already covered
Projects
None yet
Development

No branches or pull requests