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.
| Szint | Mire való | Példa |
|---|---|---|
| DEBUG | Fejlesztési diagnosztika, csak local/staging | „Kérés paraméterei: user_id=42, page=3" |
| INFO | Normális működés eseményei | „Felhasználó bejelentkezett: user@example.com" |
| WARN | Valami nem ideális, de az app fut | „Cache miss, adatbázisból tölt: product_id=7" |
| ERROR | Valami elromlott, beavatkozás kell | „Adatbázis kapcsolat időtúllépés: 30s" |
| CRITICAL / FATAL | Az 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:
- Mikor kezdődött a hiba? – Timestamp-ek alapján
- Melyik felhasználót érintette? – User ID a logban
- Mi volt a kérés? – Request ID, paraméterek (szenzitív nélkül)
- Hol romlott el? – Stack trace + a hiba előtti INFO logok
- 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:
- Felhasználó sikeresen feltöltött egy profilképet
- Az alkalmazás elindul és megnyitja az adatbázis-kapcsolatot
- Egy API kérés 3 másodpercet vett igénybe (a normál 200ms helyett)
- A fizetési átjáró 5 egymást követő kérésnél is időtúllépett
- Egy felhasználó rossz jelszóval próbált belépni
- A szerver elfogy a szabad memóriából, az alkalmazás nem tud új kérést kiszolgálni
- Egy ritkán használt, opcionális cache miss történt
- 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.