Structured Logging — ماهیت، فرمت‌های داده و اصل کار در برنامه‌ها

نویسنده: IT Sectr منتشر شده: 2026-05-28 زمان مطالعه: 8 دقیقه

Structured Logging رویکردی برای ثبت لاگ‌ها است که در آن هر پیام در قالبی ماشین‌خوان با جفت‌های کلید-مقدار ارائه می‌شود، نه به صورت متن غیرساختاریافته. برخلاف رشته‌های مسطح، لاگ‌های ساختاریافته شامل فراداده هستند: timestamp، سطح، ماژول، شناسه درخواست — و می‌توانند توسط سیستم‌های تحلیلی ایندکس شوند. بر اساس داده‌های O'Reilly Effective Logging، انتقال به فرمت‌های ساختاریافته زمان جستجوی حادثه را از ساعت‌ها به دقیقه کاهش می‌دهد به لطف امکان فیلتر کردن بر اساس فیلدها. این استاندارد دوفاکتو در توسعه مدرن موبایل و سرور است: JSON و logfmt امکان پردازش لاگ‌ها توسط برنامه‌ها را فراهم می‌کنند، نه با چشم.

نکات اصلی

  • Structured Logging — نمایش لاگ‌ها در قالب کلید-مقدار به جای متن مسطح، مناسب برای پردازش خودکار
  • JSON — رایج‌ترین فرمت لاگ‌های ساختاریافته، پشتیبانی شده توسط تمام سیستم‌های مدرن جمع‌آوری و تحلیل
  • Logfmt — فرمت فشرده از Heroku، مناسب برای خواندن توسط انسان و تجزیه با grep
  • ELK Stack — Elasticsearch، Logstash، Kibana — زیرساخت استاندارد برای ذخیره و بصری‌سازی لاگ‌های ساختاریافته
  • زمینه — شناسه درخواست، نشست کاربر، نسخه برنامه — فیلدهای اجباری هر پیام ساختاریافته

Structured Logging چیست

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 در تولید استفاده می‌کنند، حوادث را به طور متوسط ۴ برابر سریع‌تر از تیم‌هایی که به لاگ‌های متنی و grep تکیه می‌کنند، تشخیص می‌دهند.

فرمت‌های لاگ‌های ساختاریافته

Structured Logging از چندین فرمت سریال‌سازی پشتیبانی می‌کند. انتخاب فرمت به زیرساخت بستگی دارد: JSON برای یکپارچه‌سازی با Elasticsearch و سیستم‌های ابری مناسب است، logfmt — برای مشاهده کنسولی از طریق tail و grep، Protocol Buffers — برای سیستم‌های با کارایی بالا با محدودیت پهنای باند.

فرمتمثالچه زمانی استفاده شود
JSON{"event":"login","user_id":42}ELK Stack، جمع‌آوری‌کننده‌های ابری، میکروسرویس‌ها
Logfmtevent=login user_id=42 duration_ms=150کنسول، tail، heroku logs
MessagePackمعادل باینری JSONسیستم‌های با بار بالا، IoT

JSON — فرمت جهانی

JSON رایج‌ترین فرمت برای لاگ‌های ساختاریافته است. توسط تمام سیستم‌های جمع‌آوری به صورت بومی پشتیبانی می‌شود: Logstash، Fluentd، Amazon CloudWatch، Google Cloud Logging. لاگ‌های JSON به راحتی توسط انسان خوانده می‌شوند و توسط هر زبان برنامه‌نویسی بدون کتابخانه‌های اضافی تجزیه می‌شوند. عیب اصلی — افزونگی: هر جفت کلید-مقدار نیاز به گیومه و دو نقطه دارد که حجم داده‌های ذخیره‌شده را ۳۰–۵۰٪ نسبت به logfmt افزایش می‌دهد.

Logfmt — فرمت فشرده

Logfmt در Heroku برای دید کنسولی توسعه یافته است. از JSON فشرده‌تر است، خوانایی برای انسان را حفظ می‌کند و به راحتی با cut و awk بریده می‌شود. مثال: ts=2026-07-04T10:30:00Z level=error module=api status=500. Logfmt نیاز به escape کردن بیشتر کاراکترها ندارد و برای لاگ‌نویسی stdout در کانتینرها مناسب است.

چرا Structured Logging در توسعه موبایل نیاز است

در برنامه‌های موبایل Structured Logging سه وظیفه کلیدی را حل می‌کند: یافتن دلایل crashes بدون بازتولید روی دستگاه، ردیابی نشست‌های کاربر و تحلیل عملکرد بر اساس نسخه‌های برنامه.

لاگ‌های متنی روی دستگاه‌های موبایل تقریباً بی‌فایده هستند — توسعه‌دهنده نمی‌تواند لاگ‌ها را روی دستگاه کاربر grep کند. لاگ‌های ساختاریافته به سیستم‌های ابری (Firebase، Sentry، Datadog) ارسال می‌شوند و در آنجا ایندکس می‌شوند. می‌توان پرس‌وجویی ساخت: نمایش همه crashهای iOS 17.4، نسخه برنامه 3.2، در ماژول checkouts — و در چند ثانیه انتخاب دقیق دریافت کرد.

بر اساس داده‌های Sentry (2024)، برنامه‌هایی که از structured breadcrumbs استفاده می‌کنند، ۶۰٪ زمینه بیشتر در هر گزارش crash نسبت به برنامه‌هایی دارند که فقط متن خطا را لاگ می‌کنند. این مستقیماً بر سرعت رفع باگ تأثیر می‌گذارد.

ابزارهای جمع‌آوری و تحلیل

ELK Stack — Elasticsearch، Logstash، Kibana — زیرساخت استاندارد برای کار با لاگ‌های ساختاریافته باقی می‌ماند. Logstash لاگ‌ها را در JSON دریافت می‌کند، تبدیل می‌کند و برای ایندکس به Elasticsearch می‌فرستد، Kibana رابط بصری برای پرس‌وجوها و داشبوردها فراهم می‌کند.

برای برنامه‌های موبایل راه‌حل‌های ابری محبوب هستند: Firebase Crashlytics با لاگ‌های سفارشی، Sentry با breadcrumbs، Datadog با ردیابی APM. آن‌ها لاگ‌های ساختاریافته را مستقیماً از SDK موبایل دریافت می‌کنند و نیاز به استقرار بک‌اند اختصاصی ندارند. Firebase بسته رایگان برای گزارش crash ارائه می‌دهد، Sentry ردیابی توزیع‌شده اضافه می‌کند و Datadog با APM برای ردیابی همزمان عملکرد درخواست‌ها روی کلاینت و سرور یکپارچه می‌شود.

Grafana Loki — جایگزین Elasticsearch، بهینه‌شده برای لاگ‌ها. Loki به طور پیش‌فرض محتوای پیام‌ها را ایندکس نمی‌کند، بلکه از برچسب‌ها (labels) برای فیلتر کردن استفاده می‌کند. این کار ذخیره‌سازی را بسیار ارزان‌تر و پرس‌وجوها را بر اساس مجموعه ثابت فیلدها سریع‌تر می‌کند.

swift
// 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
    }
}

بهترین روش‌های Structured Logging

اولین قانون لاگ‌نویسی ساختاریافته: هر پیام باید حاوی شناسه درخواست یا نشست باشد. بدون زمینه، یک لاگ تکی بی‌فایده است — نمی‌توان فهمید به کدام کاربر یا درخواست مربوط است. correlation ID را در شروع نشست اضافه کنید و آن را از تمام لایه‌های برنامه عبور دهید.

دومین قانون: تایپ‌بندی فیلدها. فیلدهای عددی (duration_ms، status_code، retry_count) باید به صورت عدد ارسال شوند نه به صورت رشته. Elasticsearch و سیستم‌های مشابه اعداد و رشته‌ها را متفاوت ایندکس می‌کنند: به اعداد می‌توان تجمیع (میانگین، میانه، درصدک) اعمال کرد، به رشته‌ها — جستجوی متن کامل. تایپ‌بندی نادرست امکان ساخت داشبوردهای تحلیلی را از بین می‌برد.

سومین قانون: از اشیاء تو در تو خودداری کنید. لاگ‌های JSON با عمق تو در توی بیش از ۲ سطح فیلتر و بصری‌سازی دشوار هستند. به جای {"user": {"name": "Alice", "role": "admin"}} از کلیدهای مسطح استفاده کنید: user_name=Alice user_role=admin.

kotlin
// 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 امکان جمع‌آوری تصویر کامل مسیر کاربر را فراهم می‌کند.

نمونه‌های لاگ‌نویسی ساختاریافته در Swift و Kotlin

در iOS لاگ‌نویسی ساختاریافته را می‌توان از طریق یک پوشش بر روی os_log پیاده‌سازی کرد که فیلدها را به فرمت logfmt سریال‌سازی می‌کند. در Android — از طریق Timber با Tree سفارشی که پیام‌ها را قبل از ارسال به سرور به JSON یا logfmt تبدیل می‌کند.

swift
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)")
    }
}

Structured vs Unstructured: مقایسه رویکردها

انتخاب بین لاگ‌های ساختاریافته و متنی به مرحله پروژه بستگی دارد. در مراحل اولیه توسعه، لاگ‌های متنی ساده‌تر و سریع‌تر هستند — توسعه‌دهنده پیام را مستقیماً بدون پوشش‌های اضافی می‌نویسد. اما به محض اینکه پروژه از مرز یک تیم یا یک سرور فراتر می‌رود، لاگ‌های ساختاریافته اجباری می‌شوند.

معیارلاگ‌های متنیلاگ‌های ساختاریافته
خواناییبالا در کنسولمتوسط (نیاز به pretty-print)
جستجوgrep بر زیررشتهپرس‌وجو بر اساس فیلدها و مقادیر
تجمیعپشتیبانی نمی‌شودمیانگین، میانه، درصدک‌ها
یکپارچه‌سازینیاز به تجزیه داردبومی در ELK/Loki/Datadog
حجم ذخیره‌سازیکمتر (بدون فراداده)بیشتر (فیلدها + مقادیر)

سوالات متداول

کدام فرمت برای لاگ‌نویسی موبایل بهتر است؟

برای ارسال به سرور از JSON استفاده کنید — به صورت بومی توسط Firebase Crashlytics، Sentry و Datadog پشتیبانی می‌شود. برای مشاهده محلی در لاگ‌های Xcode یا Android Studio از logfmt استفاده کنید — فشرده‌تر است و بدون فرمت‌بندی خوانده می‌شود.

آیا باید در سمت کلاینت به صورت ساختاریافته لاگ کرد؟

بله، لاگ‌های ساختاریافته در سمت کلاینت امکان اضافه کردن زمینه به هر گزارش crash را فراهم می‌کنند: نسخه سیستم‌عامل، وضعیت شبکه، آخرین اقدامات کاربر. بدون breadcrumbs ساختاریافته، گزارش crash فقط شامل پشته فراخوانی بدون سناریوی کاربر است.

تفاوت logfmt با JSON چیست؟

Logfmt فشرده‌تر است (حجم ۳۰–۵۰٪ کمتر) و در ترمینال راحت‌تر خوانده می‌شود. JSON از اشیاء تو در تو و آرایه‌ها پشتیبانی می‌کند، اما نیاز به escape کردن گیومه دارد. انتخاب بستگی به زیرساخت دارد: برای ELK — JSON، برای مشاهده کنسولی — logfmt.

چگونه correlation ID را به همه لاگ‌ها اضافه کنیم؟

یک نمونه UUID در راه‌اندازی برنامه ایجاد کنید، آن را در singleton یا کانتینر DI ذخیره کنید و از طریق سازنده به تمام loggerها ارسال کنید. جایگزین — استفاده از threading-local یا Continuation Local Storage در کوروتین‌های Kotlin.

آیا می‌توان لاگ‌های ساختاریافته و متنی را مخلوط کرد؟

می‌توان، اما توصیه نمی‌شود — مخلوط کردن امکان ایندکس خودکار را از بین می‌برد. اگر بخشی از لاگ‌ها متنی هستند، باید با عبارات منظم تجزیه شوند که عملکرد و قابلیت اطمینان جستجو را کاهش می‌دهد. بهتر است همه لاگ‌ها را به فرمت ساختاریافته مهاجرت دهید.

خلاصه

  • Structured Logging — فرمت لاگ با جفت‌های کلید-مقدار، مناسب برای ایندکس خودکار و پرس‌وجو، برخلاف رشته‌های متنی
  • JSON و logfmt — فرمت‌های اصلی: JSON برای سیستم‌های جمع‌آوری جهانی است، logfmt برای مشاهده کنسولی و لاگ‌های docker فشرده است
  • Correlation ID — فیلد اجباری هر پیام ساختاریافته، بدون آن لاگ‌ها را نمی‌توان در نشست کاربر متصل کرد
  • ELK Stack و Grafana Loki — راه‌حل‌های زیرساختی استاندارد برای ذخیره، ایندکس و بصری‌سازی لاگ‌های ساختاریافته
  • عملکرد — تیم‌های دارای structured logging حوادث را ۴ برابر سریع‌تر به لطف پرس‌وجو بر اساس فیلدها به جای grep بر متن تشخیص می‌دهند
  • تایپ‌بندی — اعداد باید به صورت عدد ارسال شوند نه رشته، برای امکان تجمیع (میانگین، میانه، درصدک‌ها) در سیستم‌های تحلیلی

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید