Structured Logging је приступ бележењу дневника где је свака порука представљена у машински читљивом формату са паровима кључ-вредност, уместо као неструктуриран текст. За разлику од равних ниски, структурирани дневници садрже метаподатке: timestamp, ниво, модул, идентификатор захтева — и могу бити индексирани од стране аналитичких система. Према подацима O'Reilly Effective Logging, прелазак на структуриране формате смањује време проналажења инцидента са сати на минуте захваљујући могућности филтрирања по пољима. Ово је де факто стандард у савременом мобилном и серверском развоју: JSON и logfmt омогућавају обраду дневника програмима, а не очима.
Главно
Structured Logging је метода бележења дневника где свака порука садржи именована поља са типизованим вредностима. Уместо ниске попут User 42 logged in from device ABC структурирани дневник изгледа као скуп поља: user_id=42, event=login, device_id=ABC, timestamp=2026-07-04T10:30:00Z.
Главна предност структурираних дневника над текстованим је могућност програмске обраде. Парсирање текстованих дневника захтева регуларне изразе и претпоставке о формату ниске. Структурирани дневници се парсирају без губитака: свако поље има познати тип и назив, што омогућава изградњу упита попут пронађи све грешке ауторизације у последњем сату за корисника 42 без додатне обраде.
Према подацима Honeycomb.io (2023), тимови који користе structured logging у продукцији откривају инциденте у просеку 4 пута брже у поређењу са тимовима који се ослањају на текстоване дневнике и grep.
Structured Logging подржава неколико формата серијализације. Избор формата зависи од инфраструктуре: JSON је погодан за интеграцију са Elasticsearch и облачним системима, logfmt — за конзолни преглед кроз tail и grep, Protocol Buffers — за високоперформантне системе са ограничењима пропусног опсега.
| Формат | Пример | Када користити |
|---|---|---|
| JSON | {"event":"login","user_id":42} | ELK Stack, облачни сакупљачи, микросервиси |
| Logfmt | event=login user_id=42 duration_ms=150 | Конзола, tail, heroku logs |
| MessagePack | Бинарни еквивалент JSON | Високооптерећени системи, IoT |
JSON је најраспрострањенији формат за структуриране дневнике. Подржан је нативно од свих система за прикупљање: Logstash, Fluentd, Amazon CloudWatch, Google Cloud Logging. JSON дневници се лако читају од стране човека и парсирају у било ком програмском језику без додатних библиотека. Главни недостатак — сувишност: сваки пар кључ-вредност захтева наводнике и двотачке, што повећава обим ускладиштених података за 30–50% у поређењу са logfmt.
Logfmt је развијен у Heroku за конзолну видљивост. Компактнији је од JSON, задржава читљивост за човека и лако се скраћује кроз cut и awk. Пример: ts=2026-07-04T10:30:00Z level=error module=api status=500. Logfmt не захтева екранизацију већине знакова и добро је прилагођен за stdout-логовање у контејнерима.
У мобилним апликацијама Structured Logging решава три кључна задатка: проналажење узрока падова без репродукције на уређају, праћење корисничких сесија и анализу перформанси по верзијама апликације.
Текстовани дневници на мобилним уређајима су готово бескорисни — програмер не може да grep-ује дневнике на уређају корисника. Структурирани дневници се шаљу у облачне системе (Firebase, Sentry, Datadog) и тамо се индексирају. Може се направити упит: прикажи све crash-е на iOS 17.4, верзија апликације 3.2, у модулу checkouts — и добити прецизан избор за неколико секунди.
Према подацима Sentry (2024), апликације које користе structured breadcrumbs имају 60% више контекста у сваком извештају о паду у поређењу са апликацијама које логују само текст грешке. То директно утиче на брзину исправке грешке.
ELK Stack — Elasticsearch, Logstash, Kibana — остаје стандардна инфраструктура за рад са структурираним дневницима. Logstash прима дневнике у JSON, претвара их и шаље у Elasticsearch за индексацију, Kibana пружа визуелни интерфејс за упите и контролне табле.
За мобилне апликације популарна су облачна решења: Firebase Crashlytics са прилагођеним дневницима, Sentry са breadcrumbs, Datadog са APM праћењем. Они примају структуриране дневнике директно из мобилног SDK и не захтевају постављање сопственог позадинског система. Firebase нуди бесплатни пакет за извештаје о падовима, Sentry додаје distributed tracing, а Datadog се интегрише са APM за истовремено праћење перформанси захтева на клијенту и серверу.
Grafana Loki — алтернатива Elasticsearch, оптимизована за дневнике. Loki не индексира садржај порука подразумевано, већ користи ознаке (labels) за филтрирање. То је значајно јефтиније за складиштење и брже при упитима по фиксном скупу поља.
// Structured logging кроз Swift Logger у 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
}
}
Прво правило структурираног логовања: свака порука мора да садржи идентификатор захтева или сесије. Без контекста, појединачна порука је бескорисна — немогуће је разумети на ког корисника или захтев се односи. Додајте correlation ID на почетку сесије и преносите га кроз све слојеве апликације.
Друго правило: типизација поља. Нумеричка поља (duration_ms, status_code, retry_count) треба преносити као бројеве, а не као ниске. Elasticsearch и слични системи индексирају бројеве и ниске различито: на бројеве се могу применити агрегације (просек, медијана, перцентил), на ниске — претрага пуног текста. Лажна типизација лишава могућности изградње аналитичких контролних табли.
Треће правило: избегавајте угнежђене објекте. JSON дневници са дубином угнежђења већом од 2 нивоа су тешки за филтрирање и визуелизацију. Уместо {"user": {"name": "Alice", "role": "admin"}} користите равне кључеве: user_name=Alice user_role=admin.
// Structured logging на Android кроз 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)
}
}
Минимални скуп поља за сваку структурирану поруку: timestamp у ISO 8601, level (debug/info/warn/error/fatal), logger (назив модула или класе), message (опис догађаја читљив за човека). Додатно: correlation_id, user_id (ако је познат), version (верзија апликације), platform (iOS/Android), environment (dev/staging/prod).
Без correlation_id структурирани дневници се претварају у скуп разбацаних записа које је немогуће повезати у један кориснички сценарио. Генеришите UUID при сваком покретању апликације и додајте га у све дневнике сесије. У пракси, correlation_id мора да се преноси кроз све слојеве: од UI догађаја до мрежних захтева и позадинских задатака — у супротном део дневника остаје без контекста и не учествује у аналитици. При свеобухватном праћењу, један UUID омогућава прикупљање комплетне слике корисничког пута.
На iOS структурирано логовање се може имплементирати кроз омотач око os_log који серијализује поља у формат logfmt. На Android — кроз Timber са прилагођеним Tree који претвара поруке у JSON или logfmt пре слања на сервер.
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)")
}
}
Избор између структурираних и текстованих дневника зависи од фазе пројекта. У раним фазама развоја, текстовани дневници су једноставнији и бржи — програмер пише поруку директно без додатних омотача. Али чим пројекат пређе границе једног тима или једног сервера, структурирани дневници постају обавезни.
| Критеријум | Текстовани дневници | Структурирани дневници |
|---|---|---|
| Читљивост | Висока у конзоли | Средња (захтева pretty-print) |
| Претрага | grep по поднисци | Упити по пољима и вредностима |
| Агрегација | Није подржана | Просек, медијана, перцентили |
| Интеграција | Захтева парсирање | Нативна у ELK/Loki/Datadog |
| Обим складиштења | Мањи (без метаподатака) | Већи (поља + вредности) |
Често постављана питања
За слање на сервер користите JSON — нативно је подржан од Firebase Crashlytics, Sentry и Datadog. За локални преглед у Xcode или Android Studio логовима користите logfmt — компактнији је и чита се без форматирања.
Да, структурирани дневници на клијенту омогућавају додавање контекста сваком извештају о паду: верзија ОС, стање мреже, последње радње корисника. Без структурираних breadcrumbs, извештај о паду садржи само стек позива без корисничког сценарија.
Logfmt је компактнији (обим 30–50% мањи) и лакше се чита у терминалу. JSON подржава угнежђене објекте и низове, али захтева екранизацију наводника. Избор зависи од инфраструктуре: за ELK — JSON, за конзолни преглед — logfmt.
Направите једну инстанцу UUID при покретању апликације, сачувајте је у синглтону или DI контејнеру и преносите свим логерима кроз конструктор. Алтернатива — коришћење threading-local или Continuation Local Storage у Kotlin корутинама.
Може, али се не препоручује — мешање губи могућност аутоматске индексације. Ако су неки дневници текстовани, морају се парсирати регуларним изразима, што смањује перформансе и поузданост претраге. Боље је мигрирати све дневнике на структурирани формат.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође