6.3 Logging best practices

← 6. Debugging gondolkodásmód

6.3 Logging best practices – a jövőd önmagadnak

Áttekintő

Képzeld el: éjjel 2-kor production-ban elszáll az alkalmazás. A felhasználók nem tudnak bejelentkezni. A csapatvezető telefonon kérdez. És te ott ülsz a logok előtt, amelyekben csak ennyi van:

Error occurred
Something went wrong
null

Ez nem vicc – ezt élik meg minden nap juniorok a cégeknél. A logging az az eszköz, amit akkor ér a legtöbbet, amikor már minden elromlott. Ha jól csináltad, pontosan tudod, mi történt és mikor. Ha rosszul csináltad – vagy egyáltalán nem csináltad –, akkor csak találgatni tudsz.

Ez az alfejezet arról szól, hogyan loggolj úgy, hogy a jövőbeli önmagad hálás legyen érte. Nem kell több kód, nem kell összetett eszköz – csak néhány egyszerű szokás, amit minden senior fejlesztő ismer, és ami máris elkülönböztet a mezőnytől.


Részletes leírás

Mikor logolj – és mikor ne?

Az egyik leggyakoribb junior hiba: vagy mindent logolnak (és a log olvashatatlan), vagy semmit (és production-ban sötétben tapogatnak). A megoldás: tudatos szintezés.

Minden log eseménynek van egy kérdése: „Ezt tudni akarom-e production-ban?"

Ha igen → log. Ha csak fejlesztéshez kell → csak DEBUG szinten. Ha soha nem kell → ne logold.

Logold:

  • Alkalmazás indulása és leállása
  • Sikeres és sikertelen autentikáció
  • Fontos üzleti esemény (megrendelés rögzítve, fizetés sikeres/sikertelen)
  • Váratlan állapotok, hibák
  • Külső rendszerek elérhetősége (API, adatbázis)
  • Teljesítmény-kritikus műveletek időtartama

Ne logold:

  • Jelszavakat, tokeneket, session ID-ket, hitelkártya számokat
  • Minden egyes függvényhívást (csak fejlesztésben, DEBUG szinten)
  • Dolgokat, amelyek soha nem változnak (pl. statikus konfiguráció betöltése rutinszerűen)
  • Olyan részleteket, amik PII-t (személyes adatot) tartalmaznak, ha nem kell

Log szintek – a valódi különbség

A log szintek nem díszek. A szintek meghatározzák, hogyan reagálnak rájuk az emberek és a rendszerek.

SzintMire valóPélda
DEBUGFejlesztési diagnosztika, csak local/staging„Kérés paraméterei: user_id=42, page=3"
INFONormális működés eseményei„Felhasználó bejelentkezett: user@example.com"
WARNValami nem ideális, de az app fut„Cache miss, adatbázisból tölt: product_id=7"
ERRORValami elromlott, beavatkozás kell„Adatbázis kapcsolat időtúllépés: 30s"
CRITICAL / FATALAz egész rendszer veszélyben van„Out of memory – alkalmazás leáll"

Arany szabály: Ha production-ban fogod látni és nem fogsz rá reagálni – ne ERROR legyen. Az ERROR szint elveszti értékét, ha hemzseg tőle a log és mindenki megtanulja figyelmen kívül hagyni.

A senior fejlesztők egyik legfontosabb szokása: az ERROR szintet komolyan veszik. Ha ERROR logot látsz, az jelent valamit.


Strukturált logging – ne szövegbe írj, ha adatot akarsz

A hagyományos logging így néz ki:

[2026-04-30 14:23:01] ERROR: Failed to process order for user john@example.com, order ID 12345, amount 9990

Ez olvasható embernek. De ha 1000 ilyen sorból akarod kiszűrni az összes hibát egy adott felhasználóra, vagy meg akarod számolni a napi fizetési hibákat – kézzel kell parse-olni a szöveget. Ez fájdalmas.

Strukturált logging esetén az adatokat kulcs-érték párokban adod meg:

{
  "timestamp": "2026-04-30T14:23:01Z",
  "level": "ERROR",
  "message": "Failed to process order",
  "user_email": "john@example.com",
  "order_id": 12345,
  "amount": 9990,
  "error": "Payment gateway timeout"
}

Ezt már lehet szűrni, keresni, aggregálni. A modern loggyűjtő eszközök (pl. ELK stack, Datadog, Grafana Loki) strukturált logokat várnak.

Nem kell mindenhol azonnal bevzetni. De ha már belefogsz egy új projektbe, érdemes eleve így gondolkodni.


Mi kerüljön a logba – és mi ne (szenzitív adatok!)

Ez a rész kritikus, és egyben az a pont, ahol a juniorok a legtöbb biztonsági hibát elkövetik.

SOHA ne kerüljön a logba:

  • Jelszó (se plaintext, se hash formában)
  • API kulcs, secret token, access token
  • Session ID, cookie tartalom
  • Hitelkártya szám, bankszámlaszám
  • Teljes személy-azonosítható adat (TAJ szám, személyi igazolvány szám)
  • GDPR-érzékeny adatok (ha nem szükséges)

Miért? Mert a logok sokszor:

  • Elmentődnek fájlba, amit több ember lát
  • Elküldődnek log aggregátor rendszerekbe
  • Megjelennek monitoringban, dashboardokon
  • Esetleg rossz konfiguráció esetén nyilvánossá válnak

Helyes megközelítés: Ha logolni kell egy felhasználóra hivatkozva, logold az ID-jét, nem az email/nevét, ha az is elég. Ha logolni kell egy kérés tartalmát, szűrd ki a szenzitív mezőket.

# ROSSZ
logger.info(f"User login: email={user.email}, password={user.password}")

# JÓ
logger.info(f"User login attempt", extra={"user_id": user.id, "success": True})

Hogyan segíti a logging a production hibakeresést?

A jó logging nem debug eszköz – ez megfigyelőrendszer (observability). Amikor production-ban van hiba, általában nem tudsz debuggert csatlakoztatni. Csak a logokat látod.

Amit a jó logging megad:

  1. Mikor kezdődött a hiba? – Timestamp-ek alapján
  2. Melyik felhasználót érintette? – User ID a logban
  3. Mi volt a kérés? – Request ID, paraméterek (szenzitív nélkül)
  4. Hol romlott el? – Stack trace + a hiba előtti INFO logok
  5. Mennyire súlyos? – Log szint alapján gyorsan priorizálhatsz

Request ID / Correlation ID: Egy fontos minta: minden beérkező kéréshez generálj egy egyedi azonosítót, és ezt told végig az összes logon, ami ehhez a kéréshez tartozik. Így utólag össze tudod szedni az egy kérés teljes életútját.

INFO  [req-a7f3b] User authentication started: user_id=42
INFO  [req-a7f3b] Database query executed: 12ms
INFO  [req-a7f3b] Token generated, user logged in
ERROR [req-a7f3b] Session save failed: Redis timeout

Egyetlen pillanat alatt látható: bejelentkezett, lekérdezte az adatbázist, tokent generált, de a session mentés elbukott Redis timeout miatt.


Életszerű példák

Python (logging modul)

import logging

# Alapkonfiguráció (általában az app indulásakor egyszer)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(levelname)s %(name)s %(message)s'
)

logger = logging.getLogger(__name__)

def process_order(order_id: int, user_id: int):
    logger.info("Order processing started", extra={
        "order_id": order_id,
        "user_id": user_id
    })

    try:
        # üzleti logika...
        result = charge_payment(order_id)
        logger.info("Payment successful", extra={
            "order_id": order_id,
            "amount": result.amount
        })
        return result

    except PaymentGatewayTimeout as e:
        logger.error("Payment gateway timeout", extra={
            "order_id": order_id,
            "timeout_seconds": 30
        }, exc_info=True)
        raise

    except Exception as e:
        logger.critical("Unexpected error during payment", extra={
            "order_id": order_id
        }, exc_info=True)
        raise

Mi a helyes itt?

  • Kontextuális adatok (order_id, user_id) minden logban
  • Megfelelő szintek (INFO a normálhoz, ERROR a timeout-hoz, CRITICAL a váratlanhoz)
  • exc_info=True → stack trace bekerül a logba
  • Nincs szenzitív adat

JavaScript / Node.js (winston library)

const winston = require('winston');

const logger = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()  // strukturált log JSON-ben
    ),
    transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'app.log' })
    ]
});

// Middleware: minden kéréshez request ID
app.use((req, res, next) => {
    req.requestId = crypto.randomUUID();
    next();
});

// Használat egy route-ban
app.post('/api/login', async (req, res) => {
    const { email } = req.body;  // jelszót NEM logoljuk!

    logger.info('Login attempt', {
        requestId: req.requestId,
        email: email  // ha GDPR megengedi, egyébként csak user_id
    });

    try {
        const user = await authenticateUser(email, req.body.password);
        logger.info('Login successful', {
            requestId: req.requestId,
            userId: user.id
        });
        res.json({ token: user.generateToken() });

    } catch (err) {
        logger.warn('Login failed', {
            requestId: req.requestId,
            reason: err.message  // 'Invalid credentials' – nem részletezzük jobban
        });
        res.status(401).json({ error: 'Invalid credentials' });
    }
});

Mi a WARN itt és miért nem ERROR? A sikertelen bejelentkezés normális esemény – felhasználók elgépelik a jelszót. Csak akkor legyen ERROR, ha sok ilyen jön egy IP-ről (brute force gyanú).


PHP (Monolog – Laravel-ben is ez fut a háttérben)

use Psr\Log\LoggerInterface;

class OrderService
{
    public function __construct(private LoggerInterface $logger) {}

    public function cancelOrder(int $orderId, int $userId): void
    {
        $this->logger->info('Order cancellation requested', [
            'order_id' => $orderId,
            'user_id'  => $userId,
        ]);

        $order = Order::findOrFail($orderId);

        if ($order->status === 'shipped') {
            $this->logger->warning('Cannot cancel shipped order', [
                'order_id' => $orderId,
                'status'   => $order->status,
            ]);
            throw new OrderCancellationException('Order already shipped');
        }

        $order->cancel();

        $this->logger->info('Order cancelled successfully', [
            'order_id' => $orderId,
        ]);
    }
}

Laravel-ben a Log facade ugyanezt biztosítja (Log::info(...), Log::error(...)), a Monolog a háttérben fut.


Rossz logging minták – mire figyelj

# ❌ ROSSZ: semmitmondó üzenet
logger.error("Error")

# ❌ ROSSZ: jelszó a logban
logger.debug(f"User {username} logged in with password {password}")

# ❌ ROSSZ: DEBUG az ERROR helyett
logger.debug("Database connection failed – this is serious!")

# ❌ ROSSZ: túl részletes minden lépésnél INFO szinten
for item in cart_items:
    logger.info(f"Processing item {item.id}")  # ezret logol egy checkout során

# ✅ JÓ: kontextus + megfelelő szint
logger.error("Database connection failed", extra={
    "host": db_host,
    "port": db_port,
    "error": str(e)
})

# ✅ JÓ: összesített log a ciklus helyett
logger.info("Cart processing started", extra={
    "item_count": len(cart_items),
    "user_id": user.id
})

Példafeladat

Feladat 1: Log szint hozzárendelése

Rendeld hozzá a megfelelő log szintet (DEBUG / INFO / WARN / ERROR / CRITICAL) az alábbi eseményekhez, és indokold meg röviden a döntésed:

  1. Felhasználó sikeresen feltöltött egy profilképet
  2. Az alkalmazás elindul és megnyitja az adatbázis-kapcsolatot
  3. Egy API kérés 3 másodpercet vett igénybe (a normál 200ms helyett)
  4. A fizetési átjáró 5 egymást követő kérésnél is időtúllépett
  5. Egy felhasználó rossz jelszóval próbált belépni
  6. A szerver elfogy a szabad memóriából, az alkalmazás nem tud új kérést kiszolgálni
  7. Egy ritkán használt, opcionális cache miss történt
  8. Egy belső admin endpoint 403-as hibát adott vissza egy normál felhasználónak

Feladat 2: Rossz log üzenetek javítása

Az alábbi log sorok valódi kódból kerültek ki. Azonosítsd a problémát és írj jobb változatot!

a)

logger.error("Something went wrong")

b)

logger.info(`User ${user.email} logged in with password ${user.password}`);

c)

$this->logger->debug('CRITICAL ERROR: payment failed completely!!!');

d)

for i in range(1000):
    logger.info(f"Processing record {i}")

e)

logger.error("Error", err);  // err egy Error objektum

Tesztfeladatok

1. feladat

Melyik log szintet érdemes használni, ha egy felhasználó érvénytelen formátumú email-címet adott meg regisztrációkor?

  • A) DEBUG
  • B) INFO
  • C) WARN
  • D) ERROR
Megoldás

C) WARN – Érvénytelen felhasználói input nem szerver-oldali hiba, és várható eset. Nem ERROR, mert az alkalmazás megfelelően kezeli (validációs üzenet küld vissza). INFO sem lenne rossz, de WARN jobban jelzi, hogy valami nem a várt úton haladt.


2. feladat

Miért veszélyes ez a log sor?

logger.debug(f"Login: user={user.email}, pass={password}")
  • A) Mert DEBUG szinten van, de ERROR-nak kellene lennie
  • B) Mert a jelszó bekerül a logba, ahol illetéktelenek is láthatják
  • C) Mert az email cím is szenzitív adat és soha nem logolható
  • D) Mert a debug log lassítja az alkalmazást
Megoldás

B) A jelszó a logban komoly biztonsági kockázat – a logfájlok sokszor több ember számára hozzáférhetők, elküldődnek log aggregátorokba, és hosszú ideig megmaradnak. Soha ne kerüljön jelszó a logba. (Az A és D is lehet igaz részben, de a fő probléma a B.)


3. feladat

Mi a strukturált logging fő előnye a hagyományos szöveges loggal szemben?

  • A) Kevesebb helyet foglal el
  • B) Gyorsabban írja ki a log-ot
  • C) Az adatokat gépi úton könnyebb keresni, szűrni és aggregálni
  • D) Olvashatóbb az emberi szem számára
Megoldás

C) A strukturált log (JSON, kulcs-érték párok) célja éppen az, hogy log aggregáló rendszerek (ELK, Datadog stb.) hatékonyan tudják feldolgozni. Az emberi olvashatóság (D) általában kicsit romlik, cserébe a gépi feldolgozhatóság sokat nyer.


4. feladat

Egy webshopban minden egyes termék megtekintésekor ez fut:

logger.info(f"Product viewed: id={product_id}")

A webshop napi 100 000 oldallekérést kap. Mi a probléma?

  • A) Semmi, ez teljesen helyes megközelítés
  • B) Ez a log szint túl alacsony, ERROR-nak kellene lennie
  • C) 100 000 INFO log naponta megnehezíti a valódi problémák megtalálását és rengeteg helyet foglal
  • D) A product_id szenzitív adat, nem kerülhet a logba
Megoldás

C) A "log noise" – a felesleges, magas volumenű logok – az egyik leggyakoribb probléma. Ha minden termékmegtekintés INFO szintű log, a valódi hibák és fontos események eltűnnek a zajban. Ez DEBUG szinten maradjon, ami production-ban ki van kapcsolva. Az analytics adatokhoz (termékmegtekintések száma) ne logot használj, hanem dedikált analytics rendszert.


5. feladat

Mi a Request ID / Correlation ID fő célja a loggolásban?

  • A) Biztonsági azonosítás – megakadályozza az illetéktelen hozzáférést
  • B) Összekapcsolja egy kérés összes log bejegyzését, hogy az egész folyamat nyomon követhető legyen
  • C) Meggyorsítja a log írást azáltal, hogy azonosítja a log forrását
  • D) Helyettesíti a session ID-t a felhasználó azonosítására
Megoldás

B) A Request ID minden log bejegyzésnél megjelenik, így utólag össze lehet szedni egy adott kérés teljes életútját – hasznos, ha egy komplex folyamatban kell megtalálni, hol romlott el valami.


6. feladat

Melyik szituációban helyes WARN szintet használni?

  • A) Az adatbázis nem elérhető
  • B) A felhasználó sikeres fizetést indított
  • C) A cache-ből hiányzik egy adat és az alkalmazás az adatbázisból tölt be helyette (fallback)
  • D) Az alkalmazás elindul
Megoldás

C) A cache miss egy fallback helyzet – az alkalmazás kezelni tudja, de valami nem az optimális úton haladt. Ez tipikus WARN eset: nem hiba, de érdemes tudni róla. Az A) ERROR, a B) és D) INFO szint.


7. feladat

Az alábbi kódrészletben mi a hibás loggolási szokás?

try {
    await sendEmail(user.email, subject, body);
    logger.info('Email sent');
} catch (err) {
    logger.error('Email failed');
}
  • A) Az INFO szint helytelen, DEBUG kellene
  • B) A catch blokkban nem kerül be a hiba részlete (err) a logba, így nem tudható meg, mi történt
  • C) Az email cím szenzitív adat, nem kerülhet a logba
  • D) A try-catch nem szükséges, ha van logger
Megoldás

B) Az err objektum nem kerül a logba, így ha hiba van, csak annyit tudunk: „Email failed" – de nem tudjuk, miért. Helyes: logger.error('Email failed', { error: err.message, userId: user.id }). (A C) megfontolandó, de az email küldésnél az email cím logolása általában szükséges és elfogadott.)


8. feladat

Melyik állítás igaz a log szintekről?

  • A) ERROR szintet kell használni minden kivétel (exception) elkapásakor
  • B) A log szintek csak dokumentációs célúak, a rendszer nem szűr rájuk
  • C) Az ERROR szint elveszti értékét, ha olyan eseményekre is használják, amelyek rendszeresen előfordulnak és nem igényelnek beavatkozást
  • D) DEBUG logot production-ban is mindig be kell kapcsolni, hogy legyen elég információ
Megoldás

C) Ha az ERROR szint „zajossá" válik (pl. minden validation error ERROR), a csapat megtanulja figyelmen kívül hagyni. A log szinteknek az a céljuk, hogy figyelmünket a megfelelő helyre irányítsák. Az A) hamis – egy sikertelen bejelentkezés exception, de WARN szint. A B) hamis – log aggregátorok és alertek szintre szűrnek. A D) hamis és veszélyes – production-ban a DEBUG teljesítmény-problémát és biztonsági kockázatot jelent.


9. feladat

Melyik a helyes módszer érzékeny adatok kezelésére loggoláskor?

  • A) Mindent logolj, de titkosítsd a logfájlt
  • B) Soha ne logolj felhasználói adatot
  • C) Logold a nem-érzékeny azonosítókat (pl. user_id), és szűrd ki a szenzitív mezőket (jelszó, token, kártya)
  • D) Érzékeny adatokat csak DEBUG szinten logold, mert azt kikapcsolják production-ban
Megoldás

C) A helyes egyensúly: logolj eleget a hibakereséshez (user_id, order_id, timestamps), de ne logolj szenzitív adatot semmilyen szinten. A D) veszélyes tévedés – nem garantálható, hogy DEBUG tényleg ki van kapcsolva, és egy konfiguráció-hiba komoly adatszivárgást okozhat.


10. feladat

Egy új junior fejlesztő ezzel a megközelítéssel adja hozzá a loggolást a kódjához:

def calculate_discount(user_id, cart_total, promo_code):
    print(f"DEBUG: user={user_id}, total={cart_total}, promo={promo_code}")
    # ... logika ...
    print(f"DEBUG: discount calculated: {discount}")
    return discount

Miért problémás ez, és mi a jobb megoldás?

  • A) A print() lassabb, mint a logger – cseréljük le logger.debug()-ra, minden más jó
  • B) Több probléma is van: print() nem irányítható (nincs szintezés, nincs kikapcsolás production-ban, nincs strukturált adat), és a promo_code esetleg szenzitív. Megoldás: proper logger használata megfelelő szinttel
  • C) A függvény nem kellene, hogy logoljon – a logging kizárólag a controller réteg dolga
  • D) A függvény neve nem jó, és ezért rossz a logging is
Megoldás

B) A print() alapú „logging" a juniorok leggyakoribb hibája. Problémák: nem szintezett, production-ban nem kapcsolható ki, nem megy log aggregátorba, nincs timestamp, nincs strukturált formátum. A helyes megoldás: logger.debug(...) a dedikált logger objektummal. A promo kód logolása is kérdéses – ez üzleti logikatól függ, de érdemes megfontolni.

Scroll to Top