I don’t have much experience with nodejs/js, so I want to ask for help solving a problem with the nodejs, express framework and express logging middleware morgan. I import the logging from the middleware rather than placing it directly in the express app.js 'cause I need to get a custom log format etc. from middleware. My main express app.js:

import express from 'express';
import createError from 'http-errors';
import cookieParser from 'cookie-parser';

import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);


// Express Router
import { router as indexRouter } from './routes/index.js';
import { router as healthCheck } from './routes/healthz.js';
import { error, access } from './lib/logger.mjs';

// Main Express App
const app = express();
/*
    Log file settings
*/
app.set('logging', true);
app.set('logFileName', fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' }));

/*
   Middlewares
*/
app.use(error);     // Log only 4xx and 5xx responses to console
app.use(access);    // Log all requests to access.log

app.use(cookieParser());
app.use(express.json());
app.use(
    express.urlencoded({
        extended: true
    })
);

// View engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/healthz', healthCheck);

/*
    ...
    Part of the code has been excluded because it is not relevant to the question
    ...
*/


// module.exports = app;
export { app };

My logger middleware:

/*
   Logging
*/
"use strict";
import chalk from 'chalk';  // https://www.npmjs.com/package/chalk
import morgan from 'morgan';
const logger = morgan;
import { createStream } from 'rotating-file-stream';
const rfc = createStream;

import path from 'node:path';
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';



const logStreamName = (req, res) => {
    return req.app.get('logFileName');
};
// logStreamName.write('--- START LOG ---');

// 'morgan' original code part
function headersSent (res) {
    /*
        // istanbul ignore next: node.js 0.8 support

        res.headersSent >   true
        res._header     >   HTTP/1.1 400 Bad Request
        res.header      >   [Function: header]
    */
    return typeof res.headersSent !== 'boolean'
        ? Boolean(res._header)
        : res.headersSent
}

// Colored status token
logger.token('marked-status', (req, res) => {
    // let status = res.statusCode;
    let status = headersSent(res) ? res.statusCode : undefined

    function markedStatus (status) {
        // .toString()
        return status >= 500 ? chalk.bold.yellow(status.toString())
            : status >= 400 ? chalk.bold.red(status.toString())
                : status >= 300 ? chalk.bold.cyan(status.toString())
                    : status >= 200 ? chalk.bold.green(status.toString())
                        : chalk.reset(status)
    };

    return markedStatus(status);
});

// Custom format
logger.format('custom', [
    ':remote-addr', '-',
    ':remote-user',
    '[:date]',
    ':method', ':url', 'HTTP/:http-version',
    ':marked-status',
    ':res[content-length]',
    ':referrer',
    ':user-agent'].join(' ')
);

/*
    Log only 4xx and 5xx responses to console (process.stdout) and `error.log`.
    Standard Apache common log output.

    Format: ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version"
        :status :res[content-length]'
*/
const error = logger('custom', {
    skip: function (req, res) {
        return res.statusCode < 400;
    }
});

/*
    Log all requests to `access.log`. Standard Apache 'combined' log output.

    Format: ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version"
        :status :res[content-length] ":referrer" ":user-agent"'
*/
const access = logger('combined', {
    // stream: (req, res) => { return req.app.get('logFileName')},
    stream: logStreamName
    /*
    skip: function (req, res) {
        console.log(req.app.get('logFileName'));
        return false
    }
    */
});


export { error, access };

One of the goals I want is to create a log file, configure its rotation, and more in the main app.js. I pass the parameters of the write stream to the middleware level using the setting made through the method app.set:

app.set('logging', true);
app.set('logFileName', fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' }));

According to the express.js documentation I can access these values at the middleware level via req.app.get('setting'). I implemented this in a function the result of which is assigned to a variable logStreamName at the middleware level. But when executing the code and the first logging event occurs, I get the error:

/project/node_modules/morgan/index.js:130
      stream.write(line + '\n')
             ^

TypeError: stream.write is not a function
    at Array.logRequest (/project/node_modules/morgan/index.js:130:14)
    at listener (/project/node_modules/morgan/node_modules/on-finished/index.js:169:15)
    at onFinish (/project/node_modules/morgan/node_modules/on-finished/index.js:100:5)
    at callback (/project/node_modules/morgan/node_modules/ee-first/index.js:55:10)
    at ServerResponse.onevent (/project/node_modules/morgan/node_modules/ee-first/index.js:93:5)
    at ServerResponse.emit (node:events:530:35)
    at onFinish (node:_http_outgoing:1005:10)
    at afterWrite (node:internal/streams/writable:701:5)
    at afterWriteTick (node:internal/streams/writable:687:10)
    at process.processTicksAndRejections (node:internal/process/task_queues:81:21)

Node.js v20.11.1

I previously tested the operation of the function and the result returned by part of the test code in skip: option with output to the console (now commented out). The function returns the correct WriteStream value (I removed part of the output):

WriteStream {
  fd: 15,
  path: '/project/access.log',
  flags: 'a',
  mode: 438,
  flush: false,
  start: undefined,
  pos: undefined,
  bytesWritten: 0,
  _events: {
    close: undefined,
    error: undefined,
    prefinish: undefined,
    finish: undefined,
    drain: undefined,
    [Symbol(kConstruct)]: undefined
  },
  _writableState: WritableState {
    highWaterMark: 16384,
    length: 0,
    corked: 0,
    onwrite: [Function: bound onwrite],
    writelen: 0,
    bufferedIndex: 0,
    pendingcb: 0,
    [Symbol(kState)]: 17580812,
    [Symbol(kBufferedValue)]: null
  },
  _maxListeners: undefined,
  _eventsCount: 0,
  [Symbol(kFs)]: {
    },
    promises: [Getter]
  },
  [Symbol(kIsPerformingIO)]: false,
  [Symbol(shapeMode)]: true,
  [Symbol(kCapture)]: false
}

I ask you to help me understand why my function does not work in the stream: option, although it works without problems in the skip: option and in other parts of the middleware code? As I understand it, when used in the stream: option, the function does not return the correct value, although according to the Express.js documentation I have access to the variables req & res on the middleware level. Please help and thank you in advance!

0

There are 0 best solutions below