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 и не изискват развъртване на собствен backend. 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 — той е по-компактен и се чете без форматиране.
Да, структурираните логове на клиента позволяват добавяне на контекст към всяко репортиране за срив: версия на OS, състояние на мрежата, последните действия на потребителя. Без структурирани breadcrumbs репортирането за срив съдържа само стека на повиканията без потребителски сценарий.
Logfmt е по-компактен (с 30–50% по-малък обем) и се чете по-лесно в терминала. JSON поддържа вложени обекти и масиви, но изисква екраниране на кавичките. Изборът зависи от инфраструктурата: за ELK — JSON, за конзолен преглед — logfmt.
Създайте един екземпляр UUID при стартиране на приложението, запазете го в singleton или DI контейнер и го пренасяйте към всички logger-и чрез конструктора. Алтернатива — използване на threading-local или Continuation Local Storage в Kotlin корутините.
Може, но не се препоръчва — смесването губи възможността за автоматична индексация. Ако част от логовете са текстови, те трябва да се парсират с регулярни изрази, което намалява производителността и надеждостта на търсенето. По-добре е да мигрирате всички логове към структуриран формат.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също