Here is my favourite JS logger format setup with winston.

import winston, { format, transports } from "winston"
import { inspect } from 'util';
 
const { combine, timestamp, json, simple, align, printf, prettyPrint, errors, colorize } = format
const devFormat = combine(
  timestamp({ format: 'HH:mm:ss.SS' }),
  errors({ stack: true }),
  printf((info) => {
    const level = info.level ? info.level.toUpperCase() : 'UNKNOWN';
    let msg = `[${info.timestamp}] [${level}]: ${info.message}`;
 
    // Access and append Symbol(splat) data if it exists
    const splatData = info[Symbol.for('splat')];
    if (splatData && Array.isArray(splatData) && splatData.length > 0) {
      const additionalData = splatData.map((item) => {
        return inspect(item, {
          colors: true,
          depth: null,
          compact: false
        })
      }).join(' ');
      msg += `\n${additionalData}`;
    }
 
    return msg;
  }),
  colorize({
    all: true,
  }))
 
const prodFormat = combine(
  timestamp(),
  json()
)
 
const logger = winston.createLogger({
  level: "silly",
  defaultMeta: { service: "random-service" },
  format: process.env.NODE_ENV === 'production' ? prodFormat : devFormat,
  transports: [
    new winston.transports.Console()
  ]
})
 
logger.info("Hello, world!")
logger.error("Hello, world!", new Error("test"))
logger.error(new Error("test"))
 
logger.info("Hello, world!", {
  tag: "test",
})
 
logger.info("Hello, world!", {
  tag: "test",
})
logger.warn("Hello, world!", {
  tag: "test",
})
logger.debug("Hello, world!", {
  tag: "test",
})
logger.silly("Hello, world!", {
  tag: "test",
})