Log Level — طبقهبندی پیامهای لاگ بر اساس درجه بحرانی بودن که به توسعهدهندگان اجازه میدهد حجم اطلاعات خروجی را در مراحل مختلف کار برنامه کنترل کنند. طبق دادههای Google Android Developers, 2024، انتخاب صحیح سطح لاگگیری حجم لاگها در محیط تولید را 85–95% کاهش میدهد و تشخیص خطاها را سرعت میبخشد. هر سطح وظیفه خود را حل میکند — از اشکالزدایی در مرحله توسعه تا نظارت بر خرابیهای بحرانی در محیط تولید.
مهمترین نکات
Log Level — ویژگی هر پیام لاگ است که اهمیت و فوریت پردازش آن را تعیین میکند. پلتفرمهای مدرن iOS و اندروید از یک مقیاس واحد ۶–۷ سطحی پشتیبانی میکنند: از حداکثر جزئیات (Verbose/Trace) تا بحرانی (Error/Assert). انتخاب سطح تعیین میکند که آیا پیام در پیکربندی فعلی برنامه در لاگ ثبت میشود یا خیر.
مفهوم Log Level بر اساس اصل هرم بحرانی بودن استوار است: هرچه سطح بالاتر باشد، پیامهای کمتری در آن سطح نمایش داده میشود. طبق دادههای Semaphore CI, 2024، در یک برنامه تولیدی توزیع به این شکل است: Info — ۶۰٪ پیامها، Warn — ۲۵٪، Error — ۱۰٪، Debug — ۵٪. پیامهای Verbose باید در محیط تولید کاملاً غیرفعال شوند.
هر پلتفرم Log Level را از طریق API خود پیادهسازی میکند. اندروید از android.util.Log با متدهای v()، d()، i()، w()، e() استفاده میکند. اپل — OSLog با سطوح default، info، debug، error، fault. کتابخانههایی مانند Timber و CocoaLumberjack قابلیتهای اضافی را روی این APIهای استاندارد میسازند.
طبق دادههای Google I/O 2023، انتخاب نادرست Log Level علت ۴۰٪ مشکلات عملکرد در محیط تولید است. توسعهدهندگان لاگهای Debug را در بیلد release باقی میگذارند که منجر به نوشتن بیش از حد روی دیسک و تخلیه سریعتر باتری میشود.
Verbose (TRACE) — دقیقترین سطح که منحصراً برای توسعه در نظر گرفته شده است. در این سطح تمام محاسبات میانی، تکرارهای حلقه، نتایج هر مرحله از الگوریتم نمایش داده میشود. در اندروید این سطح معادل Log.v() و در iOS — OSLog با نوع debug است (قبل از iOS 14 از os_trace استفاده میشد).
Debug — پیامهای اشکالزدایی مفید در حین توسعه و تست. شامل اطلاعاتی درباره وضعیت اشیاء کلیدی، نتایج کوئریهای SQL، پارامترهای فراخوانی API است. برخلاف Verbose، پیامهای Debug ساختاریافته و از نظر معنایی معنادار هستند. در iOS این سطح معادل OSLogType.debug است.
Info — پیامهای اطلاعاتی درباره رویدادهای عادی برنامه: راهاندازی SDK، احراز هویت موفق، باز شدن صفحه، دریافت داده از سرور. پیامهای Info نباید حاوی اطلاعات شخصی کاربران باشند و باید برای تحلیل در محیط تولید ایمن باشند. در iOS از OSLogType.info و در اندروید از Log.i() استفاده میشود.
Warn — هشدار درباره مشکلات بالقوه. برنامه به کار خود ادامه میدهد اما وضعیت نیاز به توجه دارد: اندازه کش نزدیک به حد مجاز، نسخه قدیمی API، پاسخ آهسته شبکه، تلاش مجدد برای اتصال. در اندروید — Log.w()، در iOS — OSLogType.default (برای هشدارها).
Error — خطاهای بحرانی که در آنها برنامه نمیتواند عملیات درخواستی را انجام دهد اما به کار خود ادامه میدهد: درخواست ناموفق به API، قطع اتصال، خطای نوشتن در پایگاه داده، عدم وجود مجوزها. در iOS برای خطاها از OSLogType.error و در اندروید از Log.e() استفاده میشود.
Assert (WTF) — بالاترین سطح که وضعیت ‛این نمیتواند اتفاق بیفتد“ را نشان میدهد. برای ثبت باگهایی استفاده میشود که اصول ثابت بنیادی سیستم را نقض میکنند. در اندروید پیامهای Assert به طور پیشفرض در بیلدهای release نمایش داده نمیشوند. در iOS WTF (What a Terrible Failure) از طریق OSLogType.fault پردازش میشود.
Android Log API — مکانیزم داخلی لاگگیری از بسته android.util.Log. شش متد ایستا ارائه میدهد: Log.v()، Log.d()، Log.i()، Log.w()، Log.e() و Log.wtf(). هر متد یک tag (رشته شناسایی منبع) و msg (متن پیام) دریافت میکند.
class UserRepository {
companion object {
private val TAG = "UserRepo"
}
suspend fun loadUser(id: String): User {
Log.d(TAG, "در حال بارگذاری کاربر با شناسه: $id")
return try {
val response = api.fetchUser(id)
Log.i(TAG, "کاربر با موفقیت بارگذاری شد")
response.toUser()
} catch (e: Exception) {
Log.e(TAG, "بارگذاری کاربر ناموفق بود: ${e.message}")
throw e
}
}
}
فیلتر کردن بر اساس سطح در Android Logcat از طریق ADB انجام میشود: adb logcat *:E فقط پیامهای Error را نشان میدهد. در بیلدهای تولیدی تمام فراخوانیهای Log.v() و Log.d() توسط ProGuard/R8 در صورت فعال بودن کوچکسازی حذف میشوند. Log.i()، Log.w() و Log.e() باقی میمانند، بنابراین مهم است که دادههای حساس را از طریق این متدها خروجی ندهید.
برای فیلتر کردن سفارشی در زمان اجرا، اندروید Log.isLoggable(tag, level) را ارائه میدهد — متدی که بررسی میکند آیا سطح مشخص شده برای یک tag فعال است یا خیر. این امکان را فراهم میکند که بدون بازسازی برنامه، لاگگیری دقیق برای یک ماژول خاص را به صورت پویا فعال کنید.
OSLog — سیستم یکپارچه لاگگیری اپل که جایگزین NSLog منسوخ شده است. OSLog ۵ سطح ارائه میدهد: debug، info، default (notice)، error و fault. مزیت اصلی — لاگگیری ساختاریافته با پشتیبانی از رشتههای فرمت شده و فیلتر کردن پویا از طریق کنسول.
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
func fetchData(from url: URL) {
logger.debug("Starting request to \(url.absoluteString)")
do {
let data = try Data(contentsOf: url)
logger.info("Received \(data.count) bytes")
} catch {
logger.error("Request failed: \(error.localizedDescription)")
}
}
سیستم فیلتر کردن OSLog در سطح سیستم عامل کار میکند. پیامهای Debug فقط هنگام اتصال دیباگر یا فعال کردن آرگومان -com.apple.CoreData.Logging.debug 1 ثبت میشوند. پیامهای Info در حافظه دستگاه جمعآوری میشوند (تا ۵۱۲ کیلوبایت) و از طریق Console.app قابل دسترسی هستند. Error و fault به طور مداوم ثبت میشوند و برای جمعآوری از طریق سیستمهای گزارش خرابی در دسترس هستند.
ویژگی مهم OSLog: رشتههای فرمت شده با جایگذاری. به جای درونیابی رشتههای Swift (که همیشه بدون توجه به سطح محاسبه میشود)، OSLog از فرمت os_log با %{public}@ و %{private}@ برای تفکیک دادههای حساس استفاده میکند. پارامترهای خصوصی در لاگهای تولیدی پوشانده میشوند.
قاعده اصلی — حداقل مجموعه سطوح در محیط تولید: Info، Warn، Error، Assert. Debug و Verbose باید غیرفعال شوند. دلیل آن نه چندان در امنیت بلکه در عملکرد است: هر فراخوانی لاگ زمان پردازنده را برای قالببندی رشته صرف میکند، حتی اگر پیام نمایش داده نشود.
بهینهسازی بحرانی — هرگز از درونیابی رشتهها در فراخوانیهای لاگ استفاده نکنید. اگر رشته قبل از فراخوانی log() تشکیل شود، زمان پردازنده حتی با سطح غیرفعال تلف میشود. از قالببندی تنبل از طریق لامبداها یا شرایط محافظ استفاده کنید.
در اندروید متد Log.isLoggable() این هدف را برآورده میکند، در OSLog — پشتیبانی بومی از رشتههای فرمت شده با جایگذاری. Timber برای اندروید مشکل را از طریق timber.log.Tree با بررسی سطح در داخل درخت حل میکند.
Remote Log Level — روشی که در آن سطح لاگگیری از طریق Firebase Remote Config یا سرویس مشابه از سرور کنترل میشود. اگر در محیط تولید خطای پیچیدهای رخ دهد، توسعهدهنده میتواند از راه دور لاگگیری Debug را برای یک ماژول خاص روی دستگاههای گروه انتخابی کاربران فعال کند.
طبق دادههای Firebase, 2024، این روش زمان تشخیص باگهای نادر را ۶۰٪ کاهش میدهد و امکان دریافت تصویر کامل از مشکل را بدون نصب بیلد debug فراهم میکند. محدودیت اصلی این است که لاگها فقط در راهاندازی بعدی برنامه پس از دریافت پیکربندی فعال میشوند.
BuildConfig.DEBUG در اندروید و #if DEBUG در Swift — مکانیزمهای استاندارد کامپایل شرطی که سطوح اشکالزدایی را در بیلدهای release غیرفعال میکنند. برای معماری تمیز توصیه میشود انتخاب Log Level را به کانتینر DI یا کارخانه لاگر منتقل کنید تا منطق تجاری با دستورات شرطی شلوغ نشود.
قاعده اول — هر فراخوانی لاگ باید به سوال ‛چه کسی، چه چیزی، چه زمانی“ پاسخ دهد. چه کسی — کامپوننت یا ماژول (tag در اندروید، category در iOS). چه چیزی — رویداد خاص یا تغییر وضعیت. چه زمانی — برچسب زمانی که به طور خودکار توسط سیستم لاگگیری ثبت میشود.
قاعده دوم — دادههای حساس را از طریق Info و بالاتر لاگ نکنید. رمزهای عبور، توکنها، ایمیلها، شماره تلفنها، مختصات جغرافیایی دقیق — در هر لاگی که به محیط تولید وارد میشود به شدت ممنوع هستند. در صورت نیاز از پوشاندن استفاده کنید: ‛ایمیل: us***@example.com“.
قاعده سوم — سطح Warn منطقه مسئولیت توسعهدهنده است، Error — تیم. Warn به معنای ‛اینجا مشکل بالقوه وجود دارد، نظارت کن“ است. Error — ‛اینجا مشکل وجود دارد، رفع کن“. از Error برای موقعیتهایی که مورد انتظار و مدیریت شده هستند (مثلاً خطای 404 API) استفاده نکنید.
قاعده چهارم — سازگاری. کل پروژه باید از قراردادهای یکسان برای نامگذاری tag و دستهبندی استفاده کند. برای tag اندروید ClassName.methodName و برای category iOS module.subsystem توصیه میشود. این امکان فیلتر کردن سریع لاگها بر اساس کامپوننت را فراهم میکند.
قاعده پنجم — لاگها را تست کنید. در تستهای واحد بررسی کنید که در سناریوهای مشخصی Log Level صحیح فراخوانی میشود. برای این منظور کتابخانههای mock لاگگیری وجود دارند: Mockito برای اندروید، Cuckoo برای iOS. بررسی سطوح در تستها از نشت پیامهای اشکالزدایی به محیط تولید جلوگیری میکند.
سوالات متداول
تخلیه سریع باتری و نوشتن بیش از حد روی دیسک. هر لاگ Debug رشته را قالببندی کرده و داده را در بافر مینویسد. در دستگاههای با حافظه Flash این کار فرسودگی حافظه را تسریع میکند. علاوه بر این، لاگهای Debug ممکن است حاوی دادههای حساسی باشند که برای مشاهده در محیط تولید در دسترس نیستند.
Debug — برای بدنه درخواست و پاسخ، هدرها و کد وضعیت. Info — برای واقعیت اجرای درخواست (URL، روش، مدت زمان). Error — برای درخواستهای ناموفق با کد 4xx/5xx. هرگز از Verbose برای لاگهای شبکه در محیط تولید استفاده نکنید.
OSLogType.default (سطح notice) — پیامهای با اهمیت متوسط، در لاگ سیستم ذخیره میشوند و در Console.app قابل مشاهده هستند. OSLogType.info — پیامهای فنی، به طور دائم ذخیره نمیشوند، فقط هنگام پروفایلگیری فعال از طریق Instruments در دسترس هستند.
R8/ProGuard Log.v() و Log.d() را در صورت فعال بودن کوچکسازی در بیلد release حذف میکند. Log.i()، Log.w()، Log.e() حفظ میشوند. برای حذف کامل تمام لاگها یک قانون سفارشی -assumenosideeffects class android.util.Log با مشخص کردن تمام سطوح مورد نیاز است.
خیر — لاگگیری بیش از حد خوانایی و عملکرد را کاهش میدهد. ورود را فقط در متدهای پیچیده یا ناهمزمان لاگ کنید. برای متدهای همزمان یک لاگ در نقطه بازگشت یا خطا کافی است. از سطح Debug برای ردیابی فراخوانیها استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید