Log alkalmazás
A Log alkalmazás lehetővé teszi a rendszer naplóinak megtekintését, szűrését és elemzését. Jelenleg a hibanaplók (error logs) megjelenítését támogatja, az aktivitási naplók (activity logs) fejlesztés alatt állnak.
Áttekintés
Szekció neve “Áttekintés”A Log alkalmazás két fő részből áll:
- Hibanaplók (Error Log) - Rendszer hibák, figyelmeztetések és debug üzenetek
- Aktivitási naplók (Activity Log) - Felhasználói műveletek naplózása (fejlesztés alatt)
Főbb funkciók
Szekció neve “Főbb funkciók”- Hibanaplók megjelenítése táblázatos formában
- Szűrés log szint szerint (debug, info, warn, error, fatal)
- Szűrés forrás (source) szerint
- Rendezés oszloponként
- Lapozás nagy adatmennyiség esetén
- Színkódolt log szintek
- Jogosultság alapú hozzáférés
Fájl struktúra
Szekció neve “Fájl struktúra”apps/log/├── index.svelte # Fő layout (AppLayout + menü)├── menu.json # Menü definíció (Error, Activity)├── error-logs.remote.ts # Server action hibanaplók lekéréséhez└── components/ ├── ErrorLog.svelte # Hibanaplók táblázat szűrőkkel ├── ActivityLog.svelte # Aktivitási naplók (placeholder) └── errorLogColumns.ts # Táblázat oszlopdefiníciókMenü struktúra
Szekció neve “Menü struktúra”A menu.json definiálja az alkalmazás menüpontjait:
[ { "labelKey": "menu.error", "href": "#error", "icon": "ShieldAlert", "component": "ErrorLog", "requiredPermission": "log.error.view" }, { "labelKey": "menu.activity", "href": "#activity", "icon": "ShieldAlert", "component": "ActivityLog" }]Jogosultságok:
log.error.view- Hibanaplók megtekintése (csak admin)- Activity Log jelenleg nincs jogosultsághoz kötve
Server Actions
Szekció neve “Server Actions”error-logs.remote.ts
Szekció neve “error-logs.remote.ts”fetchErrorLogs (command)
Szekció neve “fetchErrorLogs (command)”Hibanaplók lekérése szűrési és lapozási paraméterekkel.
const result = await fetchErrorLogs({ page: 1, pageSize: 20, level: ['error', 'fatal'], // opcionális, string vagy string[] source: 'server', // opcionális search: 'database', // opcionális (jelenleg nem használt) sortBy: 'timestamp', // opcionális, alapértelmezett: 'timestamp' sortOrder: 'desc' // opcionális, 'asc' vagy 'desc'});Validáció:
page: minimum 1, alapértelmezett: 1pageSize: 1-100 között, alapértelmezett: 20level: ‘debug’ | ‘info’ | ‘warn’ | ‘error’ | ‘fatal’ (egyedi vagy tömb)source: stringsortBy: stringsortOrder: ‘asc’ | ‘desc’
Visszatérési érték:
{ success: true, data: LogEntry[], pagination: { page: number, pageSize: number, totalCount: number, totalPages: number }}Működés:
- Validálja a bemeneti paramétereket
- Meghívja a
logRepository.findMany()metódust - Lekéri az összes találat számát (
logRepository.count()) - Visszaadja az adatokat és a lapozási információkat
Komponensek
Szekció neve “Komponensek”ErrorLog.svelte
Szekció neve “ErrorLog.svelte”Hibanaplók megjelenítése DataTable komponenssel, szűrőkkel és lapozással.
Állapot:
let data = $state<LogEntry[]>([]);let loading = $state(false);let paginationInfo = $state<PaginationInfo>({ page: 1, pageSize: 20, totalCount: 0, totalPages: 0});let levelFilter = $state<string[]>([]);let sourceFilter = $state('');let tableState = $state<DataTableState>({ page: 1, pageSize: 20, sortBy: 'timestamp', sortOrder: 'desc'});Szűrők:
-
Level filter - Faceted filter komponens
- Többszörös kiválasztás (debug, info, warn, error, fatal)
- Színkódolt badge-ek
-
Source filter - Input mező
- 300ms debounce
- Részleges egyezés (ILIKE)
Reaktivitás:
$effect(() => { tableState; levelFilter; sourceFilter; untrack(() => loadData());});Bármely szűrő vagy táblázat állapot változásakor automatikusan újratölti az adatokat.
Toolbar snippet:
{#snippet toolbar({ table, handleSort })} <Input placeholder={t('log.error.filters.sourcePlaceholder')} value={sourceFilter} oninput={handleSourceInput} class="h-8 w-[150px] lg:w-[250px]" /> <DataTableFacetedFilter title={t('log.error.columns.level')} options={levels} selectedValues={levelFilter} onValuesChange={handleLevelChange} /> {#if isFiltered} <Button variant="ghost" onclick={handleReset} class="h-8 px-2 lg:px-3"> {t('log.error.filters.reset')} <X class="ml-2 size-4" /> </Button> {/if}{/snippet}ActivityLog.svelte
Szekció neve “ActivityLog.svelte”Placeholder komponens az aktivitási naplókhoz (fejlesztés alatt).
<h2>{t('log.activity.title')}</h2>errorLogColumns.ts
Szekció neve “errorLogColumns.ts”Táblázat oszlopdefiníciók a TanStack Table-hez.
Oszlopok:
-
Level - Log szint színkódolással
- Rendezés támogatott
- Színek: debug (szürke), info (kék), warn (sárga), error (piros), fatal (sötét piros, félkövér)
-
Message - Hibaüzenet
- Rendezés nem támogatott
- Max szélesség: 500px, truncate
- Tooltip teljes üzenettel
-
Source - Forrás (pl. ‘server’, ‘client’, ‘database’)
- Rendezés támogatott
- Monospace font, badge stílussal
-
Timestamp - Időbélyeg
- Rendezés támogatott
- Lokalizált formátum (
toLocaleString())
Példa oszlop definíció:
{ accessorKey: 'level', enableHiding: true, header: ({ column }) => renderComponent(DataTableColumnHeader, { get column() { return column; }, get title() { return t('log.error.columns.level'); }, onSort }), cell: ({ row }) => { const level = String(row.original.level); const colorMap: Record<string, string> = { debug: 'text-gray-500', info: 'text-blue-500', warn: 'text-yellow-500', error: 'text-red-500', fatal: 'text-red-700 font-bold' }; const snippet = createRawSnippet(() => ({ render: () => `<span class="${colorMap[level] ?? ''} uppercase text-xs font-medium">${level}</span>` })); return renderSnippet(snippet, {}); }}Logging rendszer
Szekció neve “Logging rendszer”Logger osztály
Szekció neve “Logger osztály”A logger.ts fájl tartalmazza a központi Logger osztályt.
Inicializálás:
const config = createLogConfig( env.LOG_TARGETS, // 'console' | 'file' | 'database' env.LOG_LEVEL, // 'debug' | 'info' | 'warn' | 'error' | 'fatal' env.LOG_DIR // Fájl naplók könyvtára);export const logger = new Logger(config);Használat:
import { logger } from '$lib/server/logging/logger';
// Debug üzenetawait logger.debug('Debug message', { source: 'myModule' });
// Info üzenetawait logger.info('User logged in', { source: 'auth', userId: '123', context: { email: 'user@example.com' }});
// Figyelmeztetésawait logger.warn('Deprecated API used', { source: 'api' });
// Hibaawait logger.error('Database connection failed', { source: 'database', stack: error.stack, context: { connectionString: 'postgres://...' }});
// Kritikus hibaawait logger.fatal('System crash', { source: 'server', stack: error.stack});Log szintek prioritása:
const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3, fatal: 4};Csak a beállított szint vagy magasabb prioritású üzenetek kerülnek naplózásra.
Transports
Szekció neve “Transports”A Logger három transport-ot támogat:
- ConsoleTransport - Konzolra írás (fejlesztéshez)
- FileTransport - Fájlba írás (production)
- DatabaseTransport - Adatbázisba írás (UI megjelenítéshez)
Konfiguráció:
LOG_TARGETS=console,database # Vesszővel elválasztvaLOG_LEVEL=info # Minimum log szintLOG_DIR=./logs # Fájl naplók könyvtáraLogRepository
Szekció neve “LogRepository”Az logRepository.ts kezeli az adatbázis műveleteket.
findMany(filters: LogFilters)
Szekció neve “findMany(filters: LogFilters)”Hibanaplók lekérése szűrőkkel.
const logs = await logRepository.findMany({ level: ['error', 'fatal'], source: 'database', userId: '123', from: new Date('2024-01-01'), to: new Date('2024-12-31'), limit: 50, offset: 0, sortBy: 'timestamp', sortOrder: 'desc'});Szűrők:
level: LogLevel vagy LogLevel[] (inArray vagy eq)source: string (ILIKE, részleges egyezés)userId: string (eq)from: Date (gte)to: Date (lte)limit: number (alapértelmezett: 100)offset: number (alapértelmezett: 0)sortBy: ‘level’ | ‘source’ | ‘timestamp’sortOrder: ‘asc’ | ‘desc’
count(filters: LogFilters)
Szekció neve “count(filters: LogFilters)”Találatok számának lekérése (lapozáshoz).
const totalCount = await logRepository.count({ level: ['error', 'fatal'], source: 'database'});countByLevel(timeRange?: TimeRange)
Szekció neve “countByLevel(timeRange?: TimeRange)”Log bejegyzések száma szintenként.
const counts = await logRepository.countByLevel({ from: new Date('2024-01-01'), to: new Date('2024-12-31')});// { debug: 10, info: 50, warn: 20, error: 5, fatal: 1 }Típusok
Szekció neve “Típusok”LogEntry
Szekció neve “LogEntry”interface LogEntry { id: string; level: LogLevel; message: string; source: string; timestamp: string; stack?: string; context?: Record<string, unknown>; userId?: string; url?: string; method?: string; routeId?: string; userAgent?: string;}LogLevel
Szekció neve “LogLevel”type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';LogFilters
Szekció neve “LogFilters”interface LogFilters { level?: LogLevel | LogLevel[]; source?: string; userId?: string; from?: Date; to?: Date; limit?: number; offset?: number; sortBy?: string; sortOrder?: 'asc' | 'desc';}Adatbázis séma
Szekció neve “Adatbázis séma”error_logs tábla
Szekció neve “error_logs tábla”{ id: serial('id').primaryKey(), level: varchar('level', { length: 10 }).notNull(), message: text('message').notNull(), source: varchar('source', { length: 100 }).notNull(), stack: text('stack'), context: jsonb('context'), userId: varchar('user_id', { length: 255 }), url: text('url'), method: varchar('method', { length: 10 }), routeId: varchar('route_id', { length: 255 }), userAgent: text('user_agent'), createdAt: timestamp('created_at').defaultNow().notNull()}Indexek:
level- Gyors szűrés log szint szerintsource- Gyors szűrés forrás szerintcreatedAt- Gyors rendezés időbélyeg szerintuserId- Felhasználó specifikus naplók
Használati példák
Szekció neve “Használati példák”Hibanaplók megtekintése
Szekció neve “Hibanaplók megtekintése”// Komponensbenimport { fetchErrorLogs } from '$apps/log/error-logs.remote';
const result = await fetchErrorLogs({ page: 1, pageSize: 20, level: ['error', 'fatal'], sortBy: 'timestamp', sortOrder: 'desc'});
if (result.success) { console.log('Logs:', result.data); console.log('Total:', result.pagination.totalCount);}Hiba naplózása
Szekció neve “Hiba naplózása”// Server-side kódbanimport { logger } from '$lib/server/logging/logger';
try { // Valamilyen művelet await riskyOperation();} catch (error) { await logger.error('Operation failed', { source: 'myModule', stack: error.stack, context: { operation: 'riskyOperation', params: { /* ... */ } } }); throw error;}HTTP kérés naplózása
Szekció neve “HTTP kérés naplózása”import { logger } from '$lib/server/logging/logger';
export async function handle({ event, resolve }) { try { const response = await resolve(event);
// Sikeres kérés naplózása await logger.info('HTTP request', { source: 'http', url: event.url.pathname, method: event.request.method, routeId: event.route.id, userId: event.locals.user?.id, context: { status: response.status } });
return response; } catch (error) { // Hiba naplózása await logger.error('HTTP request failed', { source: 'http', url: event.url.pathname, method: event.request.method, routeId: event.route.id, userId: event.locals.user?.id, stack: error.stack, userAgent: event.request.headers.get('user-agent') });
throw error; }}Egyedi transport létrehozása
Szekció neve “Egyedi transport létrehozása”import type { LogTransport, LogEntry } from '$lib/server/logging/types';
export class SlackTransport implements LogTransport { name = 'slack';
async write(entry: LogEntry): Promise<void> { // Csak error és fatal szintű üzenetek if (entry.level !== 'error' && entry.level !== 'fatal') { return; }
// Slack webhook hívás await fetch(process.env.SLACK_WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: `[${entry.level.toUpperCase()}] ${entry.message}`, attachments: [{ color: entry.level === 'fatal' ? 'danger' : 'warning', fields: [ { title: 'Source', value: entry.source, short: true }, { title: 'Timestamp', value: entry.timestamp, short: true } ] }] }) }); }}Fordítások
Szekció neve “Fordítások”A Log alkalmazás fordításai a translations.log namespace-ben találhatók:
-- packages/database/src/seeds/translations/log.tsINSERT INTO translations (namespace, key, locale, value) VALUES ('log', 'title', 'hu', 'Naplók'), ('log', 'title', 'en', 'Logs'), ('log', 'menu.error', 'hu', 'Hibanaplók'), ('log', 'menu.error', 'en', 'Error Logs'), ('log', 'menu.activity', 'hu', 'Aktivitási naplók'), ('log', 'menu.activity', 'en', 'Activity Logs'), ('log', 'error.columns.level', 'hu', 'Szint'), ('log', 'error.columns.level', 'en', 'Level'), -- ...Best practice-ek
Szekció neve “Best practice-ek”-
Log szintek használata: Használd a megfelelő log szintet (debug fejlesztéshez, info normál működéshez, warn figyelmeztetésekhez, error hibákhoz, fatal kritikus hibákhoz)
-
Kontextus hozzáadása: Mindig adj hozzá releváns kontextust (userId, url, method, stb.) a naplóbejegyzésekhez
-
Stack trace: Hibák esetén mindig add hozzá a stack trace-t a könnyebb hibakereséshez
-
Érzékeny adatok: Ne naplózz jelszavakat, API kulcsokat vagy más érzékeny adatokat
-
Performance: A naplózás aszinkron, de nagy mennyiségű naplózás lassíthatja a rendszert - használd megfelelően a log szinteket
-
Adatbázis méret: Rendszeresen tisztítsd az régi naplóbejegyzéseket (pl. 30 napnál régebbiek törlése)
-
Forrás megadása: Mindig add meg a forrást (source) a könnyebb szűréshez és hibakereséshez
-
Transport konfiguráció: Production környezetben használj database és file transport-ot, fejlesztésben console-t
Hibaelhárítás
Szekció neve “Hibaelhárítás”Naplók nem jelennek meg
Szekció neve “Naplók nem jelennek meg”Probléma: A Log alkalmazásban nem látszanak a naplóbejegyzések.
Megoldás:
- Ellenőrizd a
LOG_TARGETSkörnyezeti változót - tartalmazza-e adatabase-t - Ellenőrizd a
LOG_LEVELbeállítást - lehet, hogy túl magas (pl.error, deinfoszintű üzeneteket keresel) - Nézd meg az
error_logstáblát közvetlenül az adatbázisban - Ellenőrizd a jogosultságokat (
log.error.view)
Szűrés nem működik
Szekció neve “Szűrés nem működik”Probléma: A szűrők nem szűrik az adatokat.
Megoldás:
- Ellenőrizd a böngésző konzolt hibákért
- Nézd meg a network tab-ot - meghívódik-e a
fetchErrorLogsaction - Ellenőrizd a
$effectreaktivitást - minden szűrő változó szerepel-e benne
Lapozás hibás
Szekció neve “Lapozás hibás”Probléma: A lapozás nem működik megfelelően.
Megoldás:
- Ellenőrizd a
paginationInfoállapotot - Nézd meg a
totalCountértékét - helyes-e - Ellenőrizd a
validatePaginationParamsfüggvényt a server action-ben
Performance problémák
Szekció neve “Performance problémák”Probléma: A Log alkalmazás lassan tölt be.
Megoldás:
- Csökkentsd a
pageSizeértékét (alapértelmezett: 20) - Használj szűrőket a találatok számának csökkentéséhez
- Ellenőrizd az adatbázis indexeket
- Tisztítsd a régi naplóbejegyzéseket