How to catch errors on Winston Http Transport - node.js

I'm creating a logging framework using winston. Basically using built in winston http transport. My question is, how should I catch any http exceptions that might have occurred while making http post request due to invalid host/path/auth. Below is my code structure-
const { createLogger, format, transports } = require('winston');
const { combine, timestamp, label, printf } = format;
const transportOptions = {
host: 'host',
port: 'port',
path: '/',
auth: '',
};
const myFormat = printf(debug => {
var msg = {
timestamp: debug.timestamp,
label: debug.label,
level: debug.level,
message: debug.message,
};
return JSON.stringify(msg);
});
const logger = createLogger({
level: 'debug',
format: combine(label({ label: 'label' }), timestamp(), myFormat),
transports: [
new transports.Console(),
new transports.Http(transportOptions),
],
json: false,
});
logger.info('Test');
If I provide wrong value in transportOptions it just does nothing. I want my framework through some sort of exception so that I would know when any error/exception occurs. Can anyone please give me any suggestion?

According to https://github.com/winstonjs/winston/blob/master/lib/winston/transports/http.js#L57 , it looks like the HTTP transport emits a 'warn' event if the HTTP response code isn't 200. So as long as your server returns non-200 when there's an error, then you could listen to the 'warn' event from your transport and process the error accordingly (throw an exception or whatever you want). Here is example code (I tested and it works):
// Declare your transport outside the logger definition
const t = new transports.Http(transportOptions);
...
t.on('warn', (e) => console.log('warning! ' + e));
logger.info('Test'); // 'warning! ...'
Note that if the server gives no response, then the transport doesn't report a warning; whether that is a bug or feature I'm not sure... :)

Related

How to use fluentbit with Nestjs?

I have a fluentbit running in the Docker container 192.168.1.201:24224 which is connected to the elasticsearch.
Now, I am trying to connect my nestjs logger to the fluentbit:
logger.ts
import * as winston from 'winston';
var config = {
host: '192.168.1.201',
port: 24224,
timeout: 3.0,
requireAckResponse: true, // Add this option to wait response from Fluentd certainly
};
var fluentTransport = require('fluent-logger').support.winstonTransport();
var fluent = new fluentTransport('test', config);
// Initialize Winston Logger
const logger = winston.createLogger({
level: 'verbose',
format: winston.format.json(),
defaultMeta: { service: 'test-service' },
transports: [
fluent,
new winston.transports.Console({
format: winston.format.json(),
}),
],
});
export default logger;
I am using the logger in this way:
loggservice.ts:
async log_info(
tracking_id: any,
) {
logger.log({
tracking_id: tracking_id,
});
}
then, in the function :
test.ts:
log_info(tracking_id)
It seems that it can not send the log to the fluentbit(so that I can get the log in ES), am I missing something here?

Express logs resource usage after everything happened for that operation

I'm using Express 4.17.1 — and Winston 3.3.3 / Morgan 1.10.0 for logging purposes, which I've managed to configure in this way:
import winston from "winston";
const options = {
console: {
colorize: true,
format: winston.format.combine(
winston.format.colorize(),
winston.format.timestamp(),
winston.format.printf((msg) => {
return `${msg.timestamp} [${msg.level}] - ${msg.message}`;
})
),
handleExceptions: true,
json: true,
level: "debug",
},
};
const logger = winston.createLogger({
exitOnError: false,
transports: [new winston.transports.Console(options.console)], // alert > error > warning > notice > info > debug
});
logger.stream = {
write: (message) => {
logger.info(message.slice(0, -1)); // ...use lowest log level so the output will be picked up by any transports
},
};
export { logger };
I then import the Winston config inside application.js where all the Express setup is done like this:
import express from "express";
import morgan from "morgan";
import { logger } from "./config/winston.js";
const app = express();
app.use(express.json());
// ...some more settings
app.use(morgan("dev", { stream: logger.stream })); // combined, dev
// ...route Definitions
export { app };
The issue I have is with some log statements that looks like are automatically handled by Winston (I guess) based on the Express route that was processed (which I like), but they are being logged after all the usual statements for the corresponding workflow. This is an example:
2021-03-03T16:25:22.199Z [info] - Searching all customers...
{
method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [],
__knexQueryUid: 'dFKYVOlaQyJswbxoex7Y6',
sql: 'select `customers`.* from `customers`'
}
2021-03-03T16:25:22.203Z [info] - Done with all customers workflow...
2021-03-03T16:25:22.215Z [info] - GET /api/customers 200 11.727 ms - 395
I would expect 2021-03-03T16:25:22.215Z [info] - GET /api/customers 200 11.727 ms - 395 to be the very first piece in that workflow because it's the start of it, but it's being logged at the end — which is kind of confusing.
Is there a way to fix this or is this like that by design? If it can be fixed, what should I add/remove/tweak in the Winston config or anywhere else.
According to the morgan-documentation, there's a setting which allows you to write the access-log on request instead of on response (with the latter being the default):
Write log line on request instead of response. This means that a
requests will be logged even if the server crashes, but data from the
response (like the response code, content length, etc.) cannot be
logged.
So passing { immediate: true } to morgan should make the log appear at the beginning of the workflow, but the log will obviously only contain request related data.

Transport winston logs to a server

i was reading through the winston documentation and I came across the following statement
A transport is essentially a storage device for your logs
So I assumed if i set up a http transport i would be able to aggregate the logs somewhere else. In my case in an application running in localhost:3210
Does anybody know why I'm not receiving the log I'm trying to send ?
Here is my code:
import { createLogger, format, transports } from 'winston';
const { combine, timestamp, label, printf} = format;
const myFormat = printf(({ level, message, label, timestamp }) => {
return `${timestamp} [${label}] ${level}: ${message}`;
});
export const logger = createLogger({
format: combine(label({ label: 'Test Service' }), timestamp(), myFormat),
transports: [
new transports.Http({
host: 'localhost',
port: 3210,
path: '/'
})
]
});
logger.log({
level: 'info',
message: `Hello there`
});
Try logger.info('Hello there');. Also, no idea how your server looks like, but make sure it has a defined POST method with the required path.

Winston not Logging to console in typescript

I am confused by winston. I am using the following typescript code to log onto the console in my *.ts file:
import { Logger, LoggerInstance } from "winston";
const logger:LoggerInstance = new Logger();
logger.info('Now my debug messages are written to the console!');
the console remains empty. There are no compile errors or other issues.
At the same time the following works fine:
const wnstn = require("winston");
wnstn.info('Finally my messages are written to the console!');
Does anyone have a clue why that is the case? Do I have to configure the Logger differently? How would I use the defaults I get from the second example?
This is the Typescript way to import Winston.
First, be sure you have installed the typing :
npm i -D #types/winston
Then, in your script.ts
import { Logger, transports } from 'winston';
var logger = new Logger({
transports: [
new transports.Console(),
new transports.File ({ filename: 'somefile.log' })
]
});
In genral, you can import all constants and types without using winston.<...> before.
When you instantiate a new Logger instance you need to provide it a list of transports so it knows where to send the logs:
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)(),
new (winston.transports.File)({ filename: 'somefile.log' })
]
});
Well,
thanks to the hint of #idbehold , I found that a plain and easy:
import * as winston from "winston";
winston.info('Now my debug messages are written to the console!');
works for the default logger..
Well, I am following below file as winston.ts
import * as appRoot from 'app-root-path';
import * as winston from 'winston';
// define the custom settings for each transport (file, console)
const options = {
file: {
level: 'info',
filename: `${appRoot}/logs/app.log`,
handleExceptions: true,
json: true,
maxsize: 5242880, // 5MB
maxFiles: 5,
colorize: false,
},
console: {
level: 'debug',
handleExceptions: true,
json: false,
colorize: true,
},
};
// instantiate a new Winston Logger with the settings defined above
const logger: winston.Logger = winston.createLogger({
transports: [
new winston.transports.File(options.file),
new winston.transports.Console(options.console)
],
exitOnError: false, // do not exit on handled exceptions
});
// create a stream object with a 'write' function that will be used by `morgan`
const myStream = {
write: (message: string) => {
// use the 'info' log level so the output will be picked up by both transports (file and console)
logger.info(message);
}
};
export default logger;
as using inside app.ts as this.app.use(morgan('combined', { stream: winston.stream }));

How to flush winston logs?

I want to flush the winston logger before process.exit.
process.on('uncaughtException', function(err){
logger.error('Fatal uncaught exception crashed cluster', err);
logger.flush(function(){ // <-
process.exit(1);
});
});
Is there anything like logger.flush available? I couldn't find anything about it, other than people complaining about winston not being very actively maintained.
As an alternative, is there any popular (actively maintained) multi-transport logging framework that provides a flushing capability?
Winston actually allows you to pass in a callback which is executed when all transports have been logged:
process.on('uncaughtException', function(err) {
logger.log('error', 'Fatal uncaught exception crashed cluster', err, function(err, level, msg, meta) {
process.exit(1);
});
});
Docs: https://github.com/flatiron/winston#events-and-callbacks-in-winston
Unfortuantely, Winston will sometimes call the logging callback before the transport has had a chance to flush, so the accepted answer can still lead to un-saved log messages (especially on the first turn of the event loop). A better solution is implemented in the winston-log-and-exit package / patch.
Calling process.exit inside the log-callback like Thomas Heymann suggested will not ensure that the logs are actually flushed, especially when using a File-transport.
Instead of calling process.exit directly I would let the logger call process.exit after the log was flushed:
logger.js :
var winston = require('winston');
winston.loggers.add('my-logger', {
console: {
level: 'debug',
colorize: true,
timestamp: true,
handleExceptions: true
},
file: {
level: 'info',
colorize: false,
timestamp: true,
filename: file,
handleExceptions: true
}
});
var logger = winston.loggers.get('my-logger');
/* ******* *
* EXPORTS
* ******* */
exports.exitAfterFlush = function(code) {
logger.transports.file.on('flush', function() {
process.exit(code);
});
};
exports.info = function() {
logger.info.apply(this, arguments);
};
exports.warn = function() {
logger.info.apply(this, arguments);
};
exports.error = function() {
logger.info.apply(this, arguments);
};
And in your code:
var logger = require('./logger.js');
logger.exitAfterFlush(0);
info('Done!');
Tested on NodeJS v4.1.2 and winston 1.1.0
This may help someone. logger is an instance of winston.createLogger which defines two transports (console & file). The exit code is reflected in the shell.
const logger = require('../library/log');
function exitAfterFlush() {
logger.on('finish', function () {
logger.end();
});
};
process.on('exit', (code) => {
console.log(`About to exit with code: ${code}`);
});
logger.info(`Started with PID:${process.pid}`);
logger.error(`*****`);
process.exitCode = 11;
exitAfterFlush();
Winston has a better way to deal with this exceptions.
var logger = new (winston.Logger)({
transports: [
new winston.transports.File({ filename: 'path/to/all-logs.log' })
],
handleExceptions: true,
exceptionHandlers: [
new winston.transports.File({ filename: 'path/to/exceptions.log' })
]
});

Resources