winstonjs / winston

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

Attempt to write logs with no transports. #1645

Open lukaswilkeer opened 5 years ago

lukaswilkeer commented 5 years ago

Please tell us about your environment:

What is the problem?

Attempt to write logs with no transports on console.

[winston] Attempt to write logs with no transports {"message":"Hey man, I am here!","level":"debug"}
[winston] Attempt to write logs with no transports {"message":"App running on 3000","level":"info"}

What do you expect to happen instead?

Expect to write on console and combined.log and error.log files.

Other information

logger.js

import winston from 'winston'

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    //
    // - Write to all logs with level `info` and below to `combined.log` 
    // - Write all logs error (and below) to `error.log`.
    //
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
    new winston.transports.Console({ format: winston.format.json() })
  ]
})

winston.info('Hey man, I am here!')

export default logger

Usage on index.js

require('./lib/logger.js')
winston.info(`App running on ${port}`)

Obeservation error and combined.log has permission 777 event through is not the ideal.

HR commented 5 years ago

@lukaswilkeer have you found a work around?

lukaswilkeer commented 5 years ago

Ain't no.

lukaswilkeer commented 5 years ago

Up.

DiukarevSergii commented 5 years ago

did you find the solution?

awatson1978 commented 5 years ago

Managed a workaround by attaching the winston logger to the global window object. If you're upgrading from console.log this offers a nice refactor path that allows you to globally search/replace console.log( with logger.info( or logger.log("info",. The following needs to be done in the top most file of your client. Something like main.js. Works in submodules, imported packages, etc. etc. Not ideally functional and might introduce side effects, but seems to be working and at least it replaces console.log.

import { createLogger, format, transports } from 'winston';
import 'setimmediate';

// lets create a global logger
const logger = createLogger({
  level: 'info',
  format: format.json(),
  defaultMeta: { service: 'user-service' },
  transports: [
    new transports.Console({
      level: 'info',
      format: format.combine(
        format.colorize(),
        format.simple()
      )
    })
  ]
});

window.logger = global.logger = logger;
lukaswilkeer commented 5 years ago

Happy that you encounter a solution to your context. On node setting a global variable doesn't solved my problem.

Chukstart commented 4 years ago

What is exactly causing this error haven't logged anything too

lukaswilkeer commented 4 years ago

Sounds like winston are trying to create the transports, but is not created.

lkashef commented 4 years ago

@lukaswilkeer, Assuming that createLogger doesn't already add the logger/stream to winston, just merely construct the logger.

Based on your code snippet, adding winston.add(logger) after you create your logger should solve the problem.

You could also add more streams, loggers and once you require winston in any part of the application, it should return the configured logger.

lukaswilkeer commented 4 years ago

@lkashef in that case works fine, except that the errors doens't get logged on terminal or to the files. Only a message:

{"level":"error"}

PS: The code running the error is a catch block, like:

await promise.catch(winston.error)
lkashef commented 4 years ago

@lukaswilkeer if the error doesn't get logged into neither files nor terminals, where do you see the {"level":"error"}? Also without getting much into the specifications, could try logging something basic like winston.error("error", "error message") and winston.info("info", "just info")?

Try it in the same file that you require winston and configure winston the first time, make sure that you are getting the different logs as expected and that the message is showing as expected, the next step if this works, to move the two test log statements to a file that is being executed after winston has been called and configured the first time.

If both tests work then you are good to go and it's just a code problem that would be present anyway using any logging tool including console and it's not winston related.


Here is working snippet, it doesn't even use createLogger and it doesn't need to export the logger.

    if (process.env.LOG_CONSOLE_TRANSPORT === 'true') {
        const myFormat = printf(formatLogMessage);

        winston.add(new transports.Console({
            level: process.env.LOG_CONSOLE_TRANSPORT_LEVEL,
            format: combine(colorize(), myFormat)
        }));
    }
    if(process.env.LOG_FILE_TRANSPORT === 'true') {
        winston.add(new transports.File({
            level: process.env.LOG_FILE_TRANSPORT_LEVEL,
            format: format.json(),
            filename: 'combined.log'
        }));

        winston.add(new transports.File({
            level: process.env.LOG_FILE_TRANSPORT_LEVEL,
            format: format.json(),
            filename: 'combined.log'
        }));    
    }
lukaswilkeer commented 4 years ago

Using the previous version, with some modifications on name functions, doesn't work.

Winston throws an error on Console transports.

No transform function found on format. Did you create a format instance?. On that case is for console transport.

Removing it,

Error: Cannot log to file without filename or stream.

Code:

winston.add(new transports.Console({
  level: 'info',
  format: winston.format.combine(winston.format.colorize(), winston.format.json)
}))

winston.add(new transports.File({
  level: 'info',
  format: winston.format.json,
  file: 'combined.log'
}))

winston.add(new transports.File({
  level: 'error',
  format: winston.format.json,
  filename: 'error.log'
}))
lkashef commented 4 years ago

@lukaswilkeer

This was just an example for a simple implementation, it was correct to remove the myFormat custom function.

1) Try creating these files manually, it might be a permission issue.

2) For context, this is the important statements, to make sure that you are importing the correct modules for winston and transports.

const winston = require('winston');
const { format, transports} = winston;
const { combine, printf, colorize } = format;

3) Also I am using "winston": "3.2.1", so make sure you have the same version, at least to understand if this is working or not.

4) If non of the above is working, I would suggest you make sure you install the latest winston and follow the official example, then do test and refractor to reach to the current implementation you need, when it breaks from one test to another, you will be able to see where does it break exactly and maybe this will help us figure why it's behaving that way.

lukaswilkeer commented 4 years ago
  1. It's not a permission issue, actually I give the 777 to combined and error.log to be sure.
  2. Imports are correctly.
  3. Winston is up-to-date.
  4. I'm following the official example.

I created a repro script and winston works correctly. The problem where Uncaught errors inside the application. (Not reported or proper winston error). Don't know the root cause. In this case, winston.add(logger) solved the problem of transports.

Closed issue.

matale commented 4 years ago

I get this error occasionally on a freshly pulled project, I do a combination of multiple npm i, deleting node modules folder and once I opened up task manager and there were 7 or so node processes running which I killed .

lukaswilkeer commented 4 years ago

@matale this is not a best pratice to maintain a project on a webhost or vm. You i'll need to ssh every time to clean up the node_modules and this will surprise you, therefore there's no notification about it. If you see it. On dev on i'll see a lot and and will ramble what's the cause.

On that case, the winston.add(logger), solved my problem for a long running application and further projects. This is not an error to see.

@lkashef what solved my issue is that I'm creating transports and not an logger. So, my actual code looks pretty simple from the previous version with a major change.

import winston from 'winston'
import { format } from 'winston'

const loggerFormat = format.combine(
  format.errors({ stack: true }),
  format.timestamp(),
  format.metadata(),
  format.json()
)

const consoleFormat = {
  format: format.combine(
    format.splat(),
    format.simple(),
    format.errors({ stack: true })
  ),
  level: 'info',
  handleExceptions: true,
  json: false,
  colorize: true
}

const logger = winston.createLogger({
  level: 'info',
  format: loggerFormat,
  transports: [
    //
    // - Write to all logs with level `info` and below to `combined.log` 
    // - Write all logs error (and below) to `error.log`.
    //
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
    new winston.transports.Console(consoleFormat)
  ]
})

winston.add(logger)
winston.debug('Hey man, I am here!')

export default logger
mscottnelson commented 3 years ago

For future readers, since I spent too much time on this this morning.

I ran into this error, and at least in my case, it related to my module imports. For context, I am currently working on incrementally converting a CommonJS project to TypeScript (eg ECMAScript module imports).

A formatter was defined in a CommonJS export like so:

// myFormat.js
const { format } = require('winston');
module.exports = { myFormat }

and in the primary logger definition, was defined like so:

// logger.ts
import { format } from 'winston';
import myFormat from './myFormat.js'

I alternately also tried

// logger.ts
import * as winston from 'winston';
const { format } = winston;

and

// logger.ts
import winston from 'winston';
const { format } = winston;

and

// logger.ts
import winston from 'winston';
winston.format.combine(/*etc*/);

I didn't get any of these to work as you would expect.

Note that this wasn't a problem consistently, and I think it ultimately depended on the order in which modules were resolved. I made my problem go away by converting myFormat.js to myFormat.ts

Sajjon commented 3 years ago

I too wasted two hours on this issue in a TypeScript codebasw without any success.

If anyone manages to fix it please let me know. I tried just Console and just File transports or various combinations of both.

Tries both 3.3.3 and 3.3.2.

vaughandroid commented 3 years ago

Since this issue has become a bit of a grab-bag of "reasons you might see the 'Attempt to write logs with no transports'" error, I'll add one I encountered: You will see the error if you don't pass at least one transport when you create a logger, even if you specify some transports before actually attempting to log.

We had code which amounted to this:

const transports = [];

export const logger = winston.createLogger({
  format: winston.format.json(),
  transports
});

export function init(options?: { path: string }): void {
  if (options?.path) {
    transports.push(new winston.transports.File({ filename: options.path }));
  } else {
    transports.push(new winston.transports.Console());
  }
}

init();

logger.info('Foo');

Replacing the init with a factory method, and passing in a non-empty list of transports resolved the issue.

TimUnderhay commented 3 years ago

I encountered this issue when converting a Node app from CommonJS (CJS) to ES Modules (ESM), and wanted to share my solution. The problem ended up not being winston at all, but rather a difference in how ESM modules are imported in Node vs CJS. With legacy CJS, we use require(), which is synchronous, and we can run other code in between require() statements. But ESM modules are loaded with import(), which are inherently async. What this also means is from the main entry point, all modules import and execute their outer code blocks before the main entry point's outer block ever executes -- the imports are all 'hoisted'.

(I'm using Typescript, so you will see 'import' rather than require, but the import() statements were compiled into require() statements in the js output.)

CJS index.ts:

import { makeLogger, getLogger } from './logging';
makeLogger('myServer');
import * as utils from './utils';
const log = getLogger();

Pre-ESM, I had a logging module with a function that created the logger, and another function for retrieving the logger (getLogger() ). Every project file would then require this logger module, and call getLogger() from the outer scope and save the logger into an outer log variable. I.e.

CJS every other module (outer scope):

import { getLogger } from './logging';
const log = getLogger();

The problem was that once I converted main app to ESM modules, getLogger() would get called in all of the sub-modules before the entry point module ever called makeLogger(), because of the above change in import order / execution behaviour.

My solution (there's probably a better way) was to use an RxJS BehaviorSubject in the logging module, and subscribe to it in each of the application modules. Once the main app entry point calls makeLogger(), makeLogger() will 'broadcast' the logger to every module which is subscribed to the BehaviorSubject, thus each respective module will store the logger in its outer scope.

ESM index.ts:

import { makeLogger, LoggerSubject } from './logging';
makeLogger('myServer');
let log: winston.Logger;
LoggerSubject.subscribe( logger => log = logger as winston.Logger);

ESM logging.ts:

export const LoggerSubject = new BehaviorSubject<winston.Logger | undefined>(undefined);
export const makeLogger = (appLabel: string) => {
  ...
  LoggerSubject.next(winston.loggers.get('myLoggerName'));
};

ESM every other app module.ts:

import { LoggerSubject } from './logging';
let log: winston.Logger;
LoggerSubject.subscribe( logger => log = logger as winston.Logger);

Now no more "attempt to write logs with no transports" error!

andymel123 commented 2 years ago

I had that error because I did not know that winston.Container.get() creates and returns a new logger instance if no logger with that id already exists.

I thought I can use the container as a logger cache of dynamically added loggers. My plan was to call container.get and create my logger if this get returns no logger. But as it always returns a logger my logger (with transports) where never created. Instead everything used that DerivedLogger that writes Attempt to write logs with no transports

I have just replaced the container with a simple Map