Structured Logging to podejście do zapisywania logów, w którym każda wiadomość jest przedstawiona w maszynowo czytelnym formacie z parami klucz-wartość, a nie jako nieustrukturyzowany tekst. W przeciwieństwie do płaskich ciągów znaków, logi strukturyzowane zawierają metadane: znacznik czasu, poziom, moduł, identyfikator zapytania — i mogą być indeksowane przez systemy analityczne. Według danych O'Reilly Effective Logging, przejście na formaty strukturyzowane skraca czas poszukiwania incydentu z godzin do minut dzięki możliwości filtrowania po polach. To standard de facto w nowoczesnym programowaniu mobilnym i serwerowym: JSON i logfmt pozwalają przetwarzać logi programami, a nie wzrokiem.
Najważniejsze
Structured Logging to metoda zapisywania logów, w której każda wiadomość zawiera nazwane pola z typowanymi wartościami. Zamiast ciągu znaków typu User 42 logged in from device ABC log strukturyzowany wygląda jak zestaw pól: user_id=42, event=login, device_id=ABC, timestamp=2026-07-04T10:30:00Z.
Główną zaletą logów strukturyzowanych w porównaniu z tekstowymi jest możliwość programowego przetwarzania. Parsowanie logów tekstowych wymaga wyrażeń regularnych i założeń dotyczących formatu ciągu. Logi strukturyzowane są parsowane bezstratnie: każde pole ma znany typ i nazwę, co pozwala na budowanie zapytań typu znajdź wszystkie błędy autoryzacji z ostatniej godziny dla użytkownika 42 bez dodatkowego przetwarzania.
Według danych Honeycomb.io (2023), zespoły używające structured logging w produkcji wykrywają incydenty średnio 4 razy szybciej w porównaniu z zespołami polegającymi na tekstowych logach i grepie.
Structured Logging obsługuje kilka formatów serializacji. Wybór formatu zależy od infrastruktury: JSON jest wygodny do integracji z Elasticsearch i systemami chmurowymi, logfmt — do podglądu konsolowego przez tail i grep, Protocol Buffers — dla systemów wysokiej wydajności z ograniczeniem przepustowości.
| Format | Przykład | Kiedy używać |
|---|---|---|
| JSON | {"event":"login","user_id":42} | ELK Stack, zbieracze chmurowe, mikrousługi |
| Logfmt | event=login user_id=42 duration_ms=150 | Konsola, tail, heroku logs |
| MessagePack | Binarny odpowiednik JSON | Systemy o wysokim obciążeniu, IoT |
JSON to najpopularniejszy format dla logów strukturyzowanych. Jest natywnie obsługiwany przez wszystkie systemy zbierania: Logstash, Fluentd, Amazon CloudWatch, Google Cloud Logging. Logi JSON są łatwo czytelne dla człowieka i parsowane przez każdy język programowania bez dodatkowych bibliotek. Główną wadą jest nadmiarowość: każda para klucz-wartość wymaga cudzysłowów i dwukropków, co zwiększa objętość przechowywanych danych o 30–50% w porównaniu z logfmt.
Logfmt został opracowany w Heroku do widoczności konsolowej. Jest bardziej kompaktowy niż JSON, zachowuje czytelność dla człowieka i łatwo się go przycina przez cut i awk. Przykład: ts=2026-07-04T10:30:00Z level=error module=api status=500. Logfmt nie wymaga eskapowania większości znaków i dobrze nadaje się do logowania stdout w kontenerach.
W aplikacjach mobilnych Structured Logging rozwiązuje trzy kluczowe zadania: wyszukiwanie przyczyn awarii bez odtwarzania na urządzeniu, śledzenie sesji użytkowników i analizę wydajności według wersji aplikacji.
Logi tekstowe na urządzeniach mobilnych są prawie bezużyteczne — programista nie może grepować logów na urządzeniu użytkownika. Logi strukturyzowane są wysyłane do systemów chmurowych (Firebase, Sentry, Datadog) i tam indeksowane. Można zbudować zapytanie: pokaż wszystkie crashe na iOS 17.4, wersja aplikacji 3.2, w module checkouts — i uzyskać dokładny wybór w kilka sekund.
Według danych Sentry (2024), aplikacje używające structured breadcrumbs mają o 60% więcej kontekstu w każdym raporcie awarii w porównaniu z aplikacjami logującymi tylko tekst błędu. To bezpośrednio wpływa na szybkość naprawy błędu.
ELK Stack — Elasticsearch, Logstash, Kibana — pozostaje standardową infrastrukturą do pracy z logami strukturyzowanymi. Logstash przyjmuje logi w JSON, przekształca i wysyła do Elasticsearch w celu indeksacji, Kibana zapewnia interfejs wizualny do zapytań i dashboardów.
Dla aplikacji mobilnych popularne są rozwiązania chmurowe: Firebase Crashlytics z niestandardowymi logami, Sentry z breadcrumbs, Datadog z APM-śledzeniem. Przyjmują one logi strukturyzowane bezpośrednio z mobilnego SDK i nie wymagają wdrażania własnego backendu. Firebase oferuje darmowy pakiet do raportowania awarii, Sentry dodaje distributed tracing, a Datadog integruje się z APM do śledzenia wydajności zapytań na kliencie i serwerze jednocześnie.
Grafana Loki — alternatywa dla Elasticsearch, zoptymalizowana dla logów. Loki nie indeksuje zawartości wiadomości domyślnie, lecz używa etykiet (labels) do filtrowania. Jest to znacznie tańsze w przechowywaniu i szybsze przy zapytaniach według ustalonego zestawu pól.
// Structured logging przez Swift Logger w JSON
struct StructuredLog {
let event: String
let attributes: [String: Any]
let level: String
func serialize() -> String {
var base = "event=\(event) level=\(level)"
for (key, value) in attributes {
base += " \(key)=\(value)"
}
return base
}
}
Pierwsza zasada logowania strukturyzowanego: każda wiadomość musi zawierać identyfikator zapytania lub sesji. Bez kontekstu pojedynczy log jest bezużyteczny — nie można zrozumieć, do którego użytkownika lub zapytania się odnosi. Dodawaj correlation ID na starcie sesji i przekazuj go przez wszystkie warstwy aplikacji.
Druga zasada: typowanie pól. Pola numeryczne (duration_ms, status_code, retry_count) powinny być przekazywane jako liczby, a nie jako ciągi znaków. Elasticsearch i podobne systemy indeksują liczby i ciągi znaków inaczej: do liczb można stosować agregacje (średnia, mediana, percentyl), do ciągów — wyszukiwanie pełnotekstowe. Fałszywe typowanie pozbawia możliwości budowania analitycznych dashboardów.
Trzecia zasada: unikaj zagnieżdżonych obiektów. Logi JSON o głębokości zagnieżdżenia większej niż 2 poziomy są trudne do filtrowania i wizualizacji. Zamiast {"user": {"name": "Alice", "role": "admin"}} używaj płaskich kluczy: user_name=Alice user_role=admin.
// Structured logging na Android przez Timber + logfmt
class StructuredTree : Timber.Tree() {
override fun log(priority: Int, tag: String?,
message: String?, t: Throwable?) {
val level = priorityToLevel(priority)
val logfmt = "level=$level tag=$tag message=$message"
sendToRemote(logfmt)
}
}
Minimalny zestaw pól dla każdej strukturyzowanej wiadomości: timestamp w ISO 8601, level (debug/info/warn/error/fatal), logger (nazwa modułu lub klasy), message (czytelny dla człowieka opis zdarzenia). Dodatkowo: correlation_id, user_id (jeśli znany), version (wersja aplikacji), platform (iOS/Android), environment (dev/staging/prod).
Bez correlation_id logi strukturyzowane zamieniają się w zbiór rozproszonych rekordów, których nie można połączyć w jeden scenariusz użytkownika. Generuj UUID przy każdym uruchomieniu aplikacji i dodawaj go do wszystkich logów sesji. W praktyce correlation_id powinien być przekazywany przez wszystkie warstwy: od zdarzeń UI po zapytania sieciowe i zadania w tle — w przeciwnym razie część logów pozostanie bez kontekstu i nie będzie uczestniczyć w analityce. Przy kompleksowym śledzeniu jeden UUID pozwala zebrać pełny obraz ścieżki użytkownika.
Na iOS logowanie strukturyzowane można zrealizować przez opakowanie os_log, które serializuje pola do formatu logfmt. Na Android — przez Timber z niestandardowym Tree, który przekształca wiadomości w JSON lub logfmt przed wysłaniem na serwer.
import OSLog
struct StructuredLogger {
let subsystem: String
let category: String
func log(level: OSLogType,
event: String,
context: [String: Any]) {
let oslogger = Logger(
subsystem: subsystem,
category: category
)
let fields = context.map {
"\($0.key)=\($0.value)"
}.joined(separator: " ")
oslogger.log(level: level,
"\(event) \(fields)")
}
}
Wybór między logami strukturyzowanymi a tekstowymi zależy od etapu projektu. Na wczesnych etapach rozwoju logi tekstowe są prostsze i szybsze — programista pisze message bezpośrednio bez dodatkowych opakowań. Ale gdy tylko projekt wykracza poza jedną drużynę lub jeden serwer, logi strukturyzowane stają się obowiązkowe.
| Kryterium | Logi tekstowe | Logi strukturyzowane |
|---|---|---|
| Czytelność | Wysoka w konsoli | Średnia (wymaga pretty-print) |
| Wyszukiwanie | grep po podciągu | Zapytania po polach i wartościach |
| Agregacja | Nieobsługiwana | Średnia, mediana, percentyle |
| Integracja | Wymaga parsowania | Natywna w ELK/Loki/Datadog |
| Objętość przechowywania | Mniejsza (bez metadanych) | Większa (pola + wartości) |
Często zadawane pytania
Do wysyłania na serwer używaj JSON — jest natywnie obsługiwany przez Firebase Crashlytics, Sentry i Datadog. Do lokalnego podglądu w logach Xcode lub Android Studio używaj logfmt — jest bardziej kompaktowy i czytelny bez formatowania.
Tak, logi strukturyzowane na kliencie pozwalają dodać kontekst do każdego raportu awarii: wersję OS, stan sieci, ostatnie działania użytkownika. Bez strukturyzowanych breadcrumbs raport awarii zawiera tylko stos wywołań bez scenariusza użytkownika.
Logfmt jest bardziej kompaktowy (o 30–50% mniejsza objętość) i łatwiej czyta się go w terminalu. JSON obsługuje zagnieżdżone obiekty i tablice, ale wymaga eskapowania cudzysłowów. Wybór zależy od infrastruktury: dla ELK — JSON, do podglądu konsolowego — logfmt.
Utwórz jedną instancję UUID przy uruchomieniu aplikacji, zapisz ją w singletonie lub kontenerze DI i przekazuj do wszystkich loggerów przez konstruktor. Alternatywą jest użycie threading-local lub Continuation Local Storage w Kotlin coroutines.
Można, ale nie jest zalecane — mieszanie powoduje utratę możliwości automatycznej indeksacji. Jeśli część logów jest tekstowa, trzeba je parsować wyrażeniami regularnymi, co obniża wydajność i niezawodność wyszukiwania. Lepiej migrować wszystkie logi na format strukturyzowany.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również