---
title: "Logowanie strukturalne z formatem logów i poziomami, które przeszukasz po polu"
description: "Logowanie strukturalne umieszcza każdą wartość w osobnym polu zamiast w zdaniu. Wysłaliśmy 1 zamówienie na 3 sposoby do LogBrew i pokazujemy, który format logów znajdziesz ponownie po order_id."
canonical: "https://logbrew.co/pl/guides/structured-logging"
last_updated: "2026-11-10"
---

# Logowanie strukturalne z formatem logów i poziomami, które przeszukasz po polu

Logowanie strukturalne umieszcza każdą wartość w osobnym polu zamiast w zdaniu. Wysłaliśmy 1 zamówienie na 3 sposoby do LogBrew i pokazujemy, który format logów znajdziesz ponownie po `order_id`.

Przetestowane 10 listopada 2026 z użyciem Python 3.13.5 i logbrew-sdk 0.1.29

Autor [Furkan Erday](https://github.com/furkanerday). Furkan Erday jest założycielem LogBrew. Codziennie rozwija LogBrew z pomocą Claude Code i Codex.

Canonical: https://logbrew.co/pl/guides/structured-logging

## Którą linię logu znajdziesz ponownie po `order_id`

| Gdzie trafił id | Wyszukiwanie tekstu | Filtr po polu |
| --- | --- | --- |
| W treści komunikatu | Znaleziona | Nieznaleziona |
| W polach `extra={}` | Nieznaleziona | Znaleziona |
| W słowniku wewnątrz `extra={}` | Nieznaleziona | Nieznaleziona |

Wysłaliśmy 1 zamówienie na 3 sposoby przez handler Pythona, a potem go szukaliśmy. Pełny kubek oznacza, że wyszukiwanie znalazło linię.

## Wybierz format logów, który da się filtrować

### Trzymaj stały komunikat, wartości umieszczaj w polach

Pole zachowuje nazwę i typ. Wartość w treści komunikatu to tylko podciąg. W Pythonie przekaż pola w `extra={}`, a komunikat zostaje ten sam dla każdego zamówienia.

```bash
log.info("order paid", extra={"order_id": "ord_42", "amount_cents": 1999})
```

### Pisz linie JSON, gdy log czyta program

Podklasa `Formatter` zamienia każdy rekord w 1 obiekt JSON na linię, ze standardowymi polami i każdą wartością z `extra`. Kolektor logów parsuje linię bez regexa.

```bash
import json
import logging

RECORD_FIELDS = set(vars(logging.LogRecord("", 0, "", 0, "", (), None))) | {"message", "asctime"}


class JsonFormatter(logging.Formatter):
    def format(self, record: logging.LogRecord) -> str:
        line = {
            "time": self.formatTime(record, "%Y-%m-%dT%H:%M:%S%z"),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getMessage(),
        }
        line.update({k: v for k, v in vars(record).items() if k not in RECORD_FIELDS})
        if record.exc_info:
            line["exception"] = self.formatException(record.exc_info)
        return json.dumps(line)


handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logging.basicConfig(level=logging.INFO, handlers=[handler])
log = logging.getLogger("taproom")

log.info("order paid", extra={"order_id": "ord_42", "amount_cents": 1999})
log.warning("keg below 10%", extra={"keg": "ipa", "percent": 8})
```

Wynik

```text
{"time": "2026-11-10T08:34:04+0300", "level": "INFO", "logger": "taproom", "message": "order paid", "order_id": "ord_42", "amount_cents": 1999}
{"time": "2026-11-10T08:34:04+0300", "level": "WARNING", "logger": "taproom", "message": "keg below 10%", "keg": "ipa", "percent": 8}
```

### Wyślij pola do LogBrew

`LogBrewLoggingHandler` wysyła każdy rekord z wartościami `extra` jako polami. To samo zamówienie wysłaliśmy na 3 sposoby, w treści komunikatu, jako pola `extra` i jako słownik wewnątrz `extra`.

```bash
import logging
import os

from logbrew_sdk import HttpTransport, LogBrewClient, LogBrewLoggingHandler

client = LogBrewClient.create(api_key=os.environ["LOGBREW_SERVER_API_KEY"])
transport = HttpTransport()
log = logging.getLogger("taproom")
log.addHandler(LogBrewLoggingHandler(client, transport, metadata={"service": "taproom"}))
log.setLevel(logging.INFO)

logging.getLogger("taproom.text").info("order %s paid, %d cents", "ord_42", 1999)
logging.getLogger("taproom.fields").info(
    "order paid", extra={"order_id": "ord_42", "amount_cents": 1999}
)
logging.getLogger("taproom.nested").info(
    "order paid", extra={"order": {"id": "ord_42", "amount_cents": 1999}}
)

response = client.flush(transport)
print("HTTP", response.status_code, "accepted", response.accepted_events, "logs")
```

Wynik

```text
HTTP 200 accepted 3 logs
```

### Znajdź linię ponownie po polu

`GET /api/logs` przyjmuje `attribute_filters` ze ścieżką pola, typem, operatorem i wartością. Filtr po `order_id` znalazł tylko linię wysłaną z `extra`. Słownik wewnątrz `extra` został odrzucony, bo handler zachowuje tylko teksty, liczby, wartości logiczne i `None`.

```bash
curl -sS -G "https://api.logbrew.co/api/logs" \
  -H "Authorization: Bearer $LOGBREW_TOKEN" \
  --data-urlencode "project_id=$LOGBREW_PROJECT" \
  --data-urlencode "since=2026-11-10T05:31:33Z" \
  --data-urlencode 'attribute_filters=[{"path":"/metadata/order_id","type":"string","operator":"eq","value":"ord_42"}]' \
  | jq -c '.[] | {message, order_id: .attributes.metadata.order_id}'
```

Wynik

```text
{"message":"order paid","order_id":"ord_42"}
```

## Używaj poziomów logowania tak samo w każdej usłudze

### Na produkcji używaj 4 poziomów

`info` dla tego, co się wydarzyło, `warning` dla tego, co poszło źle, ale zostało obsłużone, `error` dla nieudanego żądania albo zadania, a `critical` dla tego, co zatrzymuje usługę. `debug` zostaw na lokalne uruchomienia. Handler LogBrew wysyła rekordy DEBUG jako `info`.

### Ustaw poziom 1 raz, przy starcie

Czytaj poziom ze środowiska, żeby 1 host mógł działać na DEBUG, a reszta zostać na INFO. W Pythonie robi to `logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO"))`.

### Loguj liczby jako liczby

`amount_cents: 1999` zachowuje typ, więc filtr `amount_cents >= 1000` go dopasowuje. Wysłaliśmy też `amount_cents: "1999"` jako tekst i filtr liczbowy pominął tę linię.

```bash
curl -sS -G "https://api.logbrew.co/api/logs" \
  -H "Authorization: Bearer $LOGBREW_TOKEN" \
  --data-urlencode "project_id=$LOGBREW_PROJECT" \
  --data-urlencode "since=2026-11-10T05:31:33Z" \
  --data-urlencode 'attribute_filters=[{"path":"/metadata/amount_cents","type":"number","operator":"gte","value":1000}]' \
  | jq -c '.[] | {message, amount_cents: .attributes.metadata.amount_cents}'
```

Wynik

```text
{"message":"order paid","amount_cents":1999}
```

## Co zobaczyliśmy podczas testu

Wysłaliśmy 1 zamówienie na 3 sposoby przez handler Pythona, a potem szukaliśmy go przez API LogBrew. Wyszukiwanie tekstu `search=ord_42` dopasowało 1 z 3 linii, tę z id w komunikacie.

Filtr `order_id = ord_42` dopasował 1 linię, tę wysłaną z `extra={"order_id": "ord_42"}`. Filtr `amount_cents >= 1000` dopasował tę samą linię, bo handler zachował 1999 jako liczbę.

Linia wysłana z `extra={"order": {...}}` nie pasowała do niczego. Handler wysyła z `extra` teksty, liczby, wartości logiczne i `None`, a słownik albo listę pomija.

```bash
curl -sS -G "https://api.logbrew.co/api/logs" \
  -H "Authorization: Bearer $LOGBREW_TOKEN" \
  --data-urlencode "project_id=$LOGBREW_PROJECT" \
  --data-urlencode "since=2026-11-10T05:31:33Z" \
  --data-urlencode "search=ord_42" \
  | jq -c '.[] | {message, order_id: .attributes.metadata.order_id}'
```

Wynik

```text
{"message":"order ord_42 paid, 1999 cents","order_id":null}
```

## Zwykły tekst a format JSON

Zwykły format `%(message)s` odrzuca każdą wartość z `extra`. Linia poniżej kończy się na `INFO order paid` bez id zamówienia, więc nikt później nie znajdzie tego zamówienia.

Linia JSON zachowuje pola i ich typy, a kolektor logów parsuje ją bez regexa. Człowiek czyta ją trudniej, więc trzymaj krótki komunikat i stałe nazwy pól.

Python nie ma wbudowanego formattera JSON. python/cpython#77691 poprosił o niego w 2018 roku i został zamknięty, bo dokumentacja już pokazuje, jak go napisać. 15-liniowy `Formatter` powyżej to właśnie ten przepis.

```bash
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("taproom")

log.info("order paid", extra={"order_id": "ord_42", "amount_cents": 1999})
```

Wynik

```text
2026-11-10 08:34:04,955 INFO order paid
```

## Co zapisuje LogBrew

Każdy log zachowuje poziom, nazwę loggera i każde proste pole z `extra`, plus pola z `metadata={}` na handlerze. API filtruje po ścieżce pola, typie i operatorze. Tekst przyjmuje `eq`, `ne`, `contains` i `starts_with`, a liczby `gt`, `gte`, `lt` i `lte`.

LogBrew nie indeksuje pola, którego nazwa zawiera `password`, `secret`, `token`, `cookie` albo `authorization`, więc filtr nigdy go nie dopasuje. I tak trzymaj sekrety z dala od linii logów.

Log wysłany wewnątrz śladu niesie identyfikatory śladu i spanu, więc filtr po `order_id` prowadzi do żądania, które wytworzyło tę linię.

## Najczęściej zadawane pytania

### Czym jest logowanie strukturalne?

Logowanie, w którym każda wartość to nazwane pole z typem, jako JSON albo jako pola rekordu logu, zamiast słów w zdaniu. Narzędzie wyszukiwania filtruje wtedy po polu, nie po podciągu.

### Jakie są poziomy logowania po kolei?

DEBUG, INFO, WARNING, ERROR, CRITICAL, z numerami od 10 do 50 w Pythonie. Logger ustawiony na INFO przepuszcza INFO i wyżej, a DEBUG odrzuca.

### Logować w JSON czy w zwykłym tekście?

W JSON, gdy linie czyta program, taki jak kolektor, plik albo narzędzie wyszukiwania. W zwykłym tekście, gdy terminal czyta tylko człowiek. W obu trzymaj te same pola.

### Jak dodać pola do linii logu w Pythonie?

Przekaż `extra={"order_id": "ord_42"}` do wywołania logu. Standardowy formatter ignoruje pola extra. Formatter JSON albo handler LogBrew je zachowuje.

### Czy w LogBrew mogę szukać logów po polu?

Tak. `GET /api/logs` przyjmuje do 4 `attribute_filters`, każdy ze ścieżką pola, typem i operatorem takim jak `eq` albo `gte`.

## Filtruj logi po dowolnym polu

LogBrew zachowuje każde pole, które logujesz, i znajduje linię ponownie po polu, obok śladu i problemu, do których należy. Zacznij od hojnego darmowego planu, bez karty.

[Zacznij za darmo](https://logbrew.co/pl)

## Źródła

- [Python docs, Logging Cookbook](https://docs.python.org/3/howto/logging-cookbook.html)
- [Python docs, LogRecord attributes](https://docs.python.org/3/library/logging.html#logrecord-attributes)
- [python/cpython#77691](https://github.com/python/cpython/issues/77691)

## Więcej poradników o agentach i Pythonie

- [Dodaj serwer MCP do Claude Code i Codex](https://logbrew.co/pl/guides/mcp-server-claude-code-codex)
- [Jak używać AGENTS.md w Claude Code i Codex](https://logbrew.co/pl/guides/agents-md)
- [Napraw błąd Claude 529 overloaded i błąd 429 rate limit](https://logbrew.co/pl/guides/claude-overloaded-rate-limit-error)
- [Skonfiguruj logowanie w Pythonie z właściwymi poziomami i przechwytywaniem wyjątków](https://logbrew.co/pl/guides/python-logging)
- [Jak naprawić błędy w Claude Code, Codex, JavaScript i Pythonie](https://logbrew.co/pl/guides)
