> ## Documentation Index
> Fetch the complete documentation index at: https://docs.logbrew.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Logowanie w Node.js i Express z bibliotekami Winston i Pino

> Skonfiguruj logger Winston albo Pino w Node.js i Express, wybierz poziomy logowania i wysyłaj do LogBrew logi strukturalne z ID śladu.

LogBrew to platforma obserwowalności dla programistów i agentów kodujących. Ma
hojny darmowy plan i nie wymaga karty.

Ten przewodnik konfiguruje logowanie z bibliotekami Winston i Pino w Node.js,
dodaje logger dla Express i wysyła każdy log do LogBrew razem ze śladem żądania.
Każdy przykład uruchomiono w Node.js 22 z Winston 3.19, Pino 10.4, Express 5.3,
`@logbrew/sdk` 0.1.40, `@logbrew/node` 0.1.24 i `@logbrew/express` 0.1.10. Każdy
blok wyniku pokazuje prawdziwy wynik.

## Wybierz Winston albo Pino

Obie biblioteki zapisują logi strukturalne w JSON. Zostań przy bibliotece,
której twoja aplikacja już używa. Konfiguracja LogBrew jest inna dla każdej z
nich.

| | Winston | Pino |
| - | - | - |
| Domyślny wynik | Format wybierasz sam, na przykład `winston.format.json()`. | 1 wiersz JSON na każdy rekord. |
| Argument z komunikatem | `logger.info("message", { fields })` | `logger.info({ fields }, "message")` |
| Miejsca docelowe | Transporty w procesie aplikacji. | Transporty w wątku roboczym. |
| Loggery potomne | `logger.child({ fields })` | `logger.child({ fields })` |
| Maskowanie | Własny format. | Wbudowana opcja `redact`. |
| Konfiguracja LogBrew | Dodaj `createLogBrewWinstonTransport()` do `transports`. | Wywołaj raz `installLogBrewPinoInstrumentation()`. |

## Przygotuj projekt LogBrew

**1. Utwórz klucz projektu**

Zaloguj się w [CLI](https://docs.logbrew.co/pl/guides/cli.md). Utwórz projekt i zapisz jego klucz
wysyłania w pliku, który może czytać tylko właściciel.

```bash theme={null}
install -d -m 700 "$HOME/.logbrew"
logbrew projects create checkout-api \
  --runtime node \
  --environment development \
  --ingest-key-file "$HOME/.logbrew/checkout-api.ingest" \
  --json
```

CLI nie wyświetla klucza. Na produkcji używaj menedżera sekretów swojej
platformy.

**2. Zainstaluj pakiety**

Pakiety wymagają Node.js 18.19 lub nowszego. Zainstaluj Winston, Pino lub
obie biblioteki.

```bash theme={null}
npm install @logbrew/sdk @logbrew/node @logbrew/express express winston pino
```

**3. Ustaw klucz i tożsamość wdrożenia**

```bash theme={null}
export LOGBREW_SERVER_API_KEY="$(tr -d '\r\n' < "$HOME/.logbrew/checkout-api.ingest")"
export LOGBREW_ENVIRONMENT="development"
export LOGBREW_RELEASE="checkout-api@1.0.0"
```

`createLogBrewNodeClient()` odczytuje klucz z `LOGBREW_SERVER_API_KEY`.
Nazwa usługi to pole `name` z `package.json`. Ustaw `LOGBREW_SERVICE_NAME`,
aby użyć innej nazwy. Przykłady używają modułów ES, więc ustaw
`"type": "module"` w `package.json`.

## Poziomy logowania w Node.js

Każda biblioteka ma własne nazwy i liczby poziomów. LogBrew zapisuje 4 poziomy,
a pierwotny poziom zachowuje w metadanych.

| Poziom w LogBrew | Poziom w Winstonie (priorytet) | Poziom w Pino (liczba) |
| - | - | - |
| `info` | `info` (2), `http` (3), `verbose` (4), `debug` (5), `silly` (6) | `info` (30), `debug` (20), `trace` (10) |
| `warning` | `warn` (1) | `warn` (40) |
| `error` | `error` (0) | `error` (50) |
| `critical` | `fatal` lub `critical`, jeśli je zdefiniujesz | `fatal` (60) |

W Winstonie poważniejszy poziom ma niższą liczbę. W Pino poważniejszy poziom ma
wyższą liczbę. Na produkcji ustaw `info`, a `debug` włączaj tylko na czas
krótkiej diagnozy.

## Logger Winston

Zapisz ten plik jako `winston-app.js`. Pierwszy format odrzuca rekordy poniżej
poziomu loggera, zanim dostanie je jakikolwiek transport. Transport LogBrew
dostaje każdy rekord, który zapisuje logger, więc potrzebuje tego filtra.

```js theme={null}
import winston from "winston";
import { createLogBrewWinstonTransport } from "@logbrew/sdk";
import { createLogBrewNodeClient } from "@logbrew/node";

const client = createLogBrewNodeClient();

const logger = winston.createLogger({
  level: "info",
  format: winston.format.combine(
    winston.format((info) => (logger.isLevelEnabled(info.level) ? info : false))(),
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: { component: "checkout" },
  transports: [
    new winston.transports.Console(),
    createLogBrewWinstonTransport({ client })
  ]
});

logger.debug("cart loaded");
logger.info("order created", { orderId: "ord_1001", totalCents: 4200 });
logger.warn("payment retry", { attempt: 2 });
logger.error(new Error("card declined"));

await client.shutdown();
```

Uruchom `node winston-app.js`. Konsola dostaje 1 wiersz JSON na każdy rekord z
poziomem `info` lub wyższym. Stos jest tu skrócony.

```text theme={null}
{"component":"checkout","level":"info","message":"order created","orderId":"ord_1001","timestamp":"2026-11-07T14:19:53.863Z","totalCents":4200}
{"attempt":2,"component":"checkout","level":"warn","message":"payment retry","timestamp":"2026-11-07T14:19:53.864Z"}
{"component":"checkout","level":"error","message":"card declined","stack":"Error: card declined\n    at file:///srv/app/winston-app.js:25:14\n ...","timestamp":"2026-11-07T14:19:53.864Z"}
```

LogBrew dostaje te same 3 rekordy. Pola trafiają do metadanych `context.*`.

| `message` | `level` | `metadata` |
| - | - | - |
| `order created` | `info` | `winstonLevel` `info`, `context.component` `checkout`, `context.orderId` `ord_1001`, `context.totalCents` 4200 |
| `payment retry` | `warning` | `winstonLevel` `warn`, `context.component` `checkout`, `context.attempt` 2 |
| `card declined` | `error` | `winstonLevel` `error`, `context.component` `checkout`, `errorName` `Error`, `errorMessage` `card declined` |

Transport wysyła nazwę i komunikat błędu, ale nie stos. Aby wysłać stos, ustaw
`includeErrorStack: true` w `createLogBrewWinstonTransport()`. Każdy komunikat
staje się też breadcrumbem następnego problemu. Aby to wyłączyć, ustaw
`breadcrumbs: false`.

## Logger Pino

Zapisz ten plik jako `pino-app.js`. Wywołaj
`installLogBrewPinoInstrumentation()` jeden raz, przed pierwszym logiem. Ta
funkcja odczytuje każdy gotowy rekord Pino. Pino nadal zapisuje logi do własnego
miejsca docelowego.

```js theme={null}
import pino from "pino";
import {
  createLogBrewNodeClient,
  installLogBrewPinoInstrumentation
} from "@logbrew/node";

const client = createLogBrewNodeClient();
installLogBrewPinoInstrumentation({ client });

const logger = pino({
  level: "info",
  redact: ["password", "token", "req.headers.authorization"]
});
const orderLogger = logger.child({ component: "checkout" });

orderLogger.debug("cart loaded");
orderLogger.info({ orderId: "ord_1001", totalCents: 4200 }, "order created");
orderLogger.warn({ attempt: 2, token: "tok_example" }, "payment retry");
orderLogger.error(new Error("card declined"), "payment failed");

await client.shutdown();
```

Uruchom `node pino-app.js`. Nazwa hosta i stos są tu skrócone.

```text theme={null}
{"level":30,"time":1794061059490,"pid":13985,"hostname":"app-host","component":"checkout","orderId":"ord_1001","totalCents":4200,"msg":"order created"}
{"level":40,"time":1794061059493,"pid":13985,"hostname":"app-host","component":"checkout","attempt":2,"token":"[Redacted]","msg":"payment retry"}
{"level":50,"time":1794061059494,"pid":13985,"hostname":"app-host","component":"checkout","err":{"type":"Error","message":"card declined","stack":"Error: card declined\n ..."},"msg":"payment failed"}
```

LogBrew dostaje 3 rekordy. Pino odrzuca rekord `debug`, zanim zobaczy go
LogBrew.

| `message` | `level` | `metadata` |
| - | - | - |
| `order created` | `info` | `pinoLevel` `info`, `pinoLevelNumber` 30, `context.component` `checkout`, `context.orderId` `ord_1001`, `context.totalCents` 4200 |
| `payment retry` | `warning` | `pinoLevel` `warn`, `pinoLevelNumber` 40, `context.component` `checkout`, `context.attempt` 2 |
| `payment failed` | `error` | `pinoLevel` `error`, `pinoLevelNumber` 50, `context.component` `checkout`, `errorName` `Error`, `errorMessage` `card declined` |

Pola `token` nie ma w LogBrew. LogBrew odrzuca pola z poświadczeniami, plikami
cookie, treścią żądania, parametrami zapytania, adresami URL i stosem. Ścieżki
`redact` z Pino działają jako druga warstwa ochrony. `pid`, `hostname` i `time`
to domyślne pola Pino, więc LogBrew ich nie kopiuje.

Automatyczne przechwytywanie wymaga Pino 9.11 lub nowszego z linii 9.x albo Pino
10.1 lub nowszego. Dla starszych wersji Pino użyj
`createLogBrewPinoDestination()` z `@logbrew/sdk` jako miejsca docelowego Pino.

Aby czytelnie przeglądać wynik Pino w środowisku deweloperskim, przekaż go do
`pino-pretty`.

```bash theme={null}
node pino-app.js | npx pino-pretty
```

## Logger dla Express

`logbrewMiddleware()` tworzy 1 span żądania na każde żądanie.
`logbrewErrorHandler()` zgłasza błędy jako problemy. Log Pino wewnątrz żądania
dostaje ID śladu i ID spanu tego żądania. Zapisz ten plik jako `express-app.js`.

```js theme={null}
import express from "express";
import pino from "pino";
import { logbrewErrorHandler, logbrewMiddleware } from "@logbrew/express";
import {
  createLogBrewNodeClient,
  installLogBrewPinoInstrumentation
} from "@logbrew/node";

const client = createLogBrewNodeClient();
installLogBrewPinoInstrumentation({ client });
const logger = pino({ level: "info" });

const app = express();
app.use(logbrewMiddleware());

app.get("/orders/:orderId", (req, res) => {
  logger.info({ orderId: req.params.orderId }, "order loaded");
  res.json({ ok: true });
});

app.get("/fail", () => {
  throw new Error("inventory service timeout");
});

app.use(logbrewErrorHandler());
app.use((err, req, res, next) => {
  logger.error(err, "request failed");
  res.status(500).json({ error: "internal" });
});

const server = app.listen(3000, () => logger.info("listening on 3000"));

process.on("SIGTERM", () => {
  server.close(async () => {
    await client.shutdown();
    process.exit(0);
  });
});
```

Uruchom aplikację. Potem wyślij 2 żądania z drugiego terminala.

```bash theme={null}
curl -s http://127.0.0.1:3000/orders/ord_1001
curl -s http://127.0.0.1:3000/fail
```

LogBrew dostaje te zdarzenia. Log i span każdego żądania mają to samo ID śladu.

| Zdarzenie | Nazwa lub komunikat | Ślad |
| - | - | - |
| span | `GET /orders/:orderId`, status `ok` | `4f18970f494d633c2164588fee9c692a` |
| log | `order loaded` | `4f18970f494d633c2164588fee9c692a` |
| problem | `GET /fail failed`, komunikat `inventory service timeout` | `d273e106d73ce6c5a5955e5a8c74d2d0` |
| span | `GET /fail`, status `error` | `d273e106d73ce6c5a5955e5a8c74d2d0` |
| log | `request failed` | `d273e106d73ce6c5a5955e5a8c74d2d0` |

Nazwa spanu używa szablonu trasy, a nie konkretnej ścieżki. Log
`listening on 3000` nie ma śladu, bo powstaje poza żądaniem.

Pozwól, aby middleware tworzył własnych klientów żądań. Nie przekazuj do
`logbrewMiddleware()` ani do `logbrewErrorHandler()` klienta `client`, który
jest wspólny dla całego procesu. Middleware zamyka swojego klienta żądania po
każdej odpowiedzi.

### Dodaj ślad do logów Winstona

W Winstonie przekaż transportowi aktywny ślad.

```js theme={null}
import { createLogBrewWinstonTransport } from "@logbrew/sdk";
import { createLogBrewNodeClient, getActiveLogBrewTrace } from "@logbrew/node";

const client = createLogBrewNodeClient();
const logbrewTransport = createLogBrewWinstonTransport({
  client,
  traceProvider: getActiveLogBrewTrace
});
```

Dodaj `logbrewTransport` do listy `transports` loggera. Log Winstona wewnątrz
żądania Express ma wtedy ID śladu i ID spanu tego żądania.

## Odczytaj logi

```bash theme={null}
logbrew read logs --service checkout-api --since 1h --json
logbrew read logs --severity error --search "request failed" --since 1h --json
logbrew read logs --trace "<trace_id>" --since 1h --json
```

Ostatnie polecenie odczytuje wszystkie logi 1 żądania. Skopiuj `<trace_id>` ze
śladu lub z problemu. Aby czytać logi z CI lub z agenta kodującego, użyj klucza
odczytu projektu. Szczegóły znajdziesz w przewodniku [Sprawdź
konfigurację](https://docs.logbrew.co/pl/guides/verify-setup.md).

## Zamknij aplikację bez utraty logów

Klient wysyła zdarzenia z kolejki 1 sekundę po pierwszym zdarzeniu albo po
zebraniu 50 zdarzeń. Gdy pętla zdarzeń jest pusta, klient przez najwyżej 2
sekundy wysyła pozostałe zdarzenia. `process.exit()` i sygnały pomijają ten
krok. Przed wyjściem wywołaj `await client.shutdown()`, tak jak robi to handler
`SIGTERM` w przykładzie dla Express.

## Rozwiązywanie problemów

| Objaw | Przyczyna | Rozwiązanie |
| - | - | - |
| `createLogBrewNodeClient requires serverApiKey, apiKey, LOGBREW_SERVER_API_KEY, or LOGBREW_API_KEY` | Klucz nie jest ustawiony w tej powłoce. | Ponownie wykonaj krok z `export`. |
| Logi Winstona z poziomem `debug` są w LogBrew, ale nie w konsoli | Transport LogBrew nie stosuje poziomu loggera. | Dodaj format z filtrem poziomu z przykładu dla Winstona. |
| Logi Pino nie pojawiają się w LogBrew | Wersja Pino nie ma kanału śledzenia albo logi powstały przed `installLogBrewPinoInstrumentation()`. | Użyj Pino 9.11 lub nowszego albo 10.1 lub nowszego. Włącz przechwytywanie przed pierwszym logiem. |
| Logi znikają po pierwszym żądaniu, a `client.shutdown()` zgłasza `client is already shut down` | Do middleware Express przekazano klienta wspólnego dla całego procesu. | Usuń opcję `client` z `logbrewMiddleware()` i `logbrewErrorHandler()`. |
| Po wdrożeniu brakuje ostatnich logów | Proces zakończył się przed dostarczeniem zdarzeń. | Wywołaj `await client.shutdown()` w handlerze `SIGTERM`. |

Wybór pól logów opisuje przewodnik [Logowanie
strukturalne](https://docs.logbrew.co/pl/concepts/structured-logging.md). Inne frameworki opisuje
przewodnik [Integracja SDK](https://docs.logbrew.co/pl/guides/sdk-ingestion.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.