Skip to content

Send Winston Logs with OpenTelemetry (Node.js)

This guide shows how to forward logs from an existing Winston setup to KloudMate with OpenTelemetry.

  • A Node.js application that already uses Winston
  • A KloudMate private key
  • The ability to install additional npm packages

Assume your Express application already writes logs with Winston:

index.ts

import express from 'express'
import { createLogger, transports } from 'winston'

const logger = createLogger({
  transports: [new transports.Console()],
})

const app = express()
const port = 5000

app.get('/', (req: any, res: any) => {
  logger.info({ msg: 'received request', query: req.query })
  res.send('hello world')
})

app.listen(port, () => {
  logger.info(`Server is running at http://localhost:${port}`)
})

Install the packages required to emit OpenTelemetry logs:

npm i @opentelemetry/exporter-logs-otlp-http \
@opentelemetry/sdk-logs \
@opentelemetry/resources \
@opentelemetry/semantic-conventions

Create a customLogger.ts file that forwards Winston logs to KloudMate and still writes them to the console.

import { createLogger, transports } from 'winston'
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'
import {
  LoggerProvider,
  BatchLogRecordProcessor,
} from '@opentelemetry/sdk-logs'
import { Resource } from '@opentelemetry/resources'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { SeverityNumber } from '@opentelemetry/api-logs'

const resource = Resource.default().merge(
  new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'my-app',
    [SemanticResourceAttributes.SERVICE_VERSION]: '0.1.0',
  })
)

const loggerProvider = new LoggerProvider({
  resource: resource,
})
const logExporter = new OTLPLogExporter({
  url: `https://otel.kloudmate.com:4318/v1/logs`,
  headers: {
    Authorization: 'KM_PRIVATE_KEY',
  },
})
const logProcessor = new BatchLogRecordProcessor(logExporter)
loggerProvider.addLogRecordProcessor(logProcessor)

const formatLog = (args: any) =>
  typeof args === 'string' ? args : JSON.stringify(args)

const consoleTransport = new transports.Console()
const logger = createLogger({
  transports: [consoleTransport],
})

export const customLogger = {
  ...logger,
  info: (args: any) => {
    loggerProvider
      .getLogger('otel-logger')
      .emit({ body: formatLog(args), severityNumber: SeverityNumber.INFO })
    return logger.info(args)
  },

  error: (args: any) => {
    loggerProvider
      .getLogger('otel-logger')
      .emit({ body: formatLog(args), severityNumber: SeverityNumber.ERROR })
    return logger.error(args)
  },
}

Replace KM_PRIVATE_KEY with your KloudMate workspace private key.

Update index.ts to use the wrapped logger:

import { customLogger as logger } from './customLogger'
// ...same code as before

Start the application and generate a few requests. You should then see the emitted Winston logs in Log Explorer.