Warning

**Deprecation Notice**: The legacy Coralogix Node.js Winston integration will be deprecated in favor of the [OpenTelemetry SDK](https://opentelemetry.io/docs/languages/js/) and will no longer be supported after **June 30, 2026**. See the [end-of-life notice](https://coralogix.com/docs/user-guides/latest-updates/deprecations/nodejs-winston-sdk/index.md) for migration details.

# Node.js Winston

This guide shows how to send [Winston](https://github.com/winstonjs/winston) logs from a Node.js application to Coralogix using the [OpenTelemetry Node.js SDK](https://opentelemetry.io/docs/languages/js/) with the [Winston instrumentation](https://www.npmjs.com/package/@opentelemetry/instrumentation-winston), exporting logs over OTLP/gRPC.

For more background on Winston itself, see the [Winston repository](https://github.com/winstonjs/winston) and the Coralogix blog post [Complete Winston Logger Guide](https://coralogix.com/blog/complete-winston-logger-guide-with-hands-on-examples/).

## Package dependencies setup

Add the following packages to your project (versions are examples; prefer the latest compatible releases from the JavaScript package registry):

- `@opentelemetry/api`
- `@opentelemetry/sdk-node`
- `@opentelemetry/sdk-logs`
- `@opentelemetry/exporter-logs-otlp-grpc`
- `@opentelemetry/instrumentation-winston`
- `@opentelemetry/resources`
- `@opentelemetry/semantic-conventions`
- `winston`

Install them with the Node package manager:

```bash
npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-logs \
  @opentelemetry/exporter-logs-otlp-grpc @opentelemetry/instrumentation-winston \
  @opentelemetry/resources @opentelemetry/semantic-conventions winston
```

Here is a minimal `package.json` you can use as a starting point:

```json
{
  "name": "nodejs-winston-otel-app",
  "version": "1.0.0",
  "main": "index.js",
  "type": "commonjs",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "@opentelemetry/api": "^1.9.1",
    "@opentelemetry/exporter-logs-otlp-grpc": "^0.214.0",
    "@opentelemetry/instrumentation-winston": "^0.58.0",
    "@opentelemetry/resources": "^2.6.1",
    "@opentelemetry/sdk-logs": "^0.214.0",
    "@opentelemetry/sdk-node": "^0.214.0",
    "@opentelemetry/semantic-conventions": "^1.40.0",
    "@opentelemetry/winston-transport": "^0.24.0",
    "winston": "^3.19.0"
  }
}
```

Select the https://ingress.\[[DOMAIN_VALUE]\] endpoint that corresponds to your Coralogix [domain](https://coralogix.com/docs/user-guides/account-management/account-settings/coralogix-domain/index.md) using the domain selector at the top of the page.

Pin or update packages to the latest versions when you use this example for testing.

## Application implementation

Start the OpenTelemetry `NodeSDK` with a log record processor and `WinstonInstrumentation` **before** you `require("winston")`, so the instrumentation can patch Winston. Map Coralogix application and subsystem names on the resource using `cx.application.name` and `cx.subsystem.name`.

The following example sends logs to a local OpenTelemetry Collector on OTLP gRPC port `4317` by default. Override the endpoint with `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` or `OTEL_EXPORTER_OTLP_ENDPOINT`.

```javascript
"use strict";

const { trace } = require("@opentelemetry/api");
const { NodeSDK } = require("@opentelemetry/sdk-node");
const { BatchLogRecordProcessor } = require("@opentelemetry/sdk-logs");
const { OTLPLogExporter } = require("@opentelemetry/exporter-logs-otlp-grpc");
const { WinstonInstrumentation } = require("@opentelemetry/instrumentation-winston");
const { resourceFromAttributes } = require("@opentelemetry/resources");
const { ATTR_SERVICE_NAME } = require("@opentelemetry/semantic-conventions");

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const endpoint =
  process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ||
  process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||
  "http://localhost:4317";

async function main() {
  const sdk = new NodeSDK({
    resource: resourceFromAttributes({
      [ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || "winston-otel-sample",
      "cx.application.name": process.env.CORALOGIX_APPLICATION || "winston-otel-app",
      "cx.subsystem.name": process.env.CORALOGIX_SUBSYSTEM || "worker",
    }),
    logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter({ url: endpoint }))],
    instrumentations: [new WinstonInstrumentation()],
  });

  await sdk.start();

  // Keep winston require after SDK start so instrumentation patches it.
  const winston = require("winston");
  const logger = winston.createLogger({
    level: process.env.LOG_LEVEL || "info",
    format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
    defaultMeta: {
      component: "winston-otel-example",
      environment: process.env.NODE_ENV || "local",
    },
    transports: [new winston.transports.Console()],
  });

  logger.info("hello winston with OpenTelemetry");

  const tracer = trace.getTracer("winston-otel-example");
  tracer.startActiveSpan("manual-span", (span) => {
    logger.warn("winston log with trace correlation");
    span.end();
  });

  // Give batch processor a moment before shutdown flush.
  await sleep(1500);
  try {
    await sdk.shutdown();
  } catch (err) {
    console.error("Shutdown completed with exporter error:", err.message || err);
  }
  console.log("Done: flush + shutdown completed");
}

main().catch((err) => {
  console.error("Application failed:", err);
  process.exit(1);
});
```

**Notes**

- The log exporter batches records. On shutdown, call `sdk.shutdown()` so pending logs are exported.

Note

If using TypeScript, set the `esModuleInterop` option to `true` in your `tsconfig.json` when you combine CommonJS packages with ESM-style imports.

### Logging output

With the OpenTelemetry SDK, you can send logs either to a local [OpenTelemetry Collector](https://coralogix.com/docs/opentelemetry/kubernetes-observability/kubernetes-observability-using-opentelemetry/index.md) or directly to Coralogix using an OTLP endpoint.

#### OpenTelemetry Collector

Point `OTEL_EXPORTER_OTLP_ENDPOINT` (or `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`) at your collector’s OTLP/gRPC address (often port `4317`).

#### Coralogix OpenTelemetry endpoint

Configure the exporter to send to your Coralogix OTLP endpoint and authenticate with your [Send-Your-Data API key](https://coralogix.com/docs/user-guides/account-management/api-keys/send-your-data-api-key/index.md).

```bash
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingress.[[DOMAIN_VALUE]]:443
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer send_your_data_key"
OTEL_RESOURCE_ATTRIBUTES="service.name=winston-otel-sample,cx.application.name=AppName,cx.subsystem.name=SubName"
```

### Additional resources

|                          |                                                                                                                   |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| Blog                     | [Complete Winston Logger Guide](https://coralogix.com/blog/complete-winston-logger-guide-with-hands-on-examples/) |
| OpenTelemetry JavaScript | [OpenTelemetry JS docs](https://opentelemetry.io/docs/languages/js/)                                              |
| Coralogix Endpoints      | [Coralogix Endpoints](https://coralogix.com/docs/integrations/coralogix-endpoints/index.md)                       |

### Support

For help, use in-app chat or email [support@coralogix.com](mailto:support@coralogix.com).
