تولید لاگ تمیز و قابل جستجو در سوئیفت با OSLog — راهنمای کاربردی
زمانی که از تولید لاگ در سوئیفت و به طور خاص اپلیکیشنهای iOS صحبت میکنیم، نخستین چیزی که به ذهن متبادر میشود، استفاده از print و NSLog است. با این حال اپل اخیراً استاندارد جدیدی برای لاگ کردن به شکل unified logging (+) معرفی کرده است که از طریق OSLog قابل دسترسی است. روش توصیهشده فعلی برای تولید لاگ همین OSLog است که روشی بهینه برای به دست آوردن اطلاعات در میان اپلیکیشنهای مختلف محسوب میشود.
Unified logging چندین بهبود نسبت به تکنیکهای قبلی و همچنین برخی تفاوتها در مورد چیزهایی که به آنها عادت کردهایم به همراه دارد.
- هر پیام میتواند در سطح مناسبی لاگ شود که شامل سطوح default ،error ،debug و info میشود.
- پیامها درون سیستمهای فرعی و دستهبندیها گروهبندی میشوند تا امکان جستجو و فیلتر کردن به روش بهینهای پدید آید.
- نیازی به قرار دادن گزارههای log درون گزارههای شرطی وجود ندارد، چون سیستم برای ارتقای عملکرد طراحی شده است و لاگها تنها زمانی که خوانده شوند، رندر خواهند شد.
- حریم خصوصی کاربر به دقت محافظت میشود. محتوای رشته دینامیک باید صراحتاً به صورت public علامتگذاری شود و در غیر این صورت در هر نوع لاگ سانسور میشود.
آغاز لاگ کردن
استفاده از تکنیک unified logging ارائه شده از سوی اپل به سادگی استفاده از تابع os_log است، اما به جای یک string معمولی، یک StaticString به عنوان آرگومان میگیرد.
سادهترین روش برای لاگ کردن پیامها، قرار دادن آنها به صورت مستقیم در ثابت String در فراخوانی تابع است. استخراج پیام از یک مشخصه امکانپذیر است، اما لازم است که نوع آن به صورت StaticString تعریف شود.
1os_log("User signed in")
2
3let errorMessage: StaticString = "404 - NOT FOUND"
4os_log(errorMessage)
به دلیل الزام StaticString به جای استفاده از میانیابی رشته، باید از آرگومانهای قالب استفاده کنیم. این وضعیت ممکن است در ابتدا آزاردهنده باشد، اما انطباق با آن دشوار نیست و مزیتهایی برای حریم خصوصی کاربر ارائه میکند که در ادامه بیشتر توضیح میدهیم. ما به همه آرگومانهای قالب استاندارد و همچنین تعدادی از دیکودرهای نوع مقدار دیگر دسترسی داریم. ایده کلی این است که سیستم لاگینگ تا حد امکان همه وظایف قالببندی را انجام دهد تا سیستم کارایی بیشتری داشته باشد.
1let responseCode = "404 - NOT FOUND"
2os_log("HTTP response: %@", responseCode)
3
4let logMessage = "HTTP response: \(responseCode)"
5os_log("%@", logMessage)
6
7// error: cannot convert value of type 'String'
8// to expected argument type 'StaticString'
9os_log("HTTP response: \(responseCode)")
مزایای سازمان یافته بودن
فراخوانیها به os_log میتوانند از یک OSLog استفاده کنند که شامل سیستم فرعی و دستهبندی خاصی است. این اطلاعات در زمان فیلتر کردن، جستجو کردن و تلاش برای درک لاگ در زمانهای آتی بسیار ارزشمند هستند. هنگامی که آرگومانی برای لاگ ارائه نشود، از آرگومان پیشفرض استفاده میشود که هیچ سیستم فرعی یا دستهبندی برای آن پیکربندی نشده است.
1let uiLog = OSLog(subsystem: "com.lordcodes.chat.ChatApp", category: "UI")
2os_log("Contact selected", log: uiLog)
3
4let networkLog = OSLog(subsystem: "com.lordcodes.chat.ChatKit", category: "Network")
5os_log("HTTP response: %@", log: networkLog, responseCode)
سیستم فرعی همه لاگها را برای یک اپلیکیشن یا ماژول خاص گروهبندی میکند و امکان فیلتر کردن همه لاگها را به دست میدهد. بر اساس ارزیابی لاگهای اپل، روش معمول «سیستم فرعی» یک استایل دامنه معکوس مانند Bundle Identifier خود اپلیکیشن یا فریمورک است. اگر اپلیکیشن درون فریمورک ماژولهسازی شده باشد، بهتر است که از Bundle Identifier فریمورک برای افراز لاگها درون کامپوننتهای متناظر استفاده کنیم.
دستهبندیها برای گروهبندی لاگها در زمینههای مرتبط استفاده میشوند تا به کاهش دامنه پیامهای لاگ کمک کند. رسم استفاده از دستهبندی به منظور استفاده از نامهای قابل خواندن از سوی انسان مانند UI یا User ارائه شده است. ما میتوانستیم لاگها را درون لایههایی روی چندین سیستم فرعی یا قابلیت مانند Network یا Contacts گروهبندی کنیم. به طور جایگزین میتوانستیم همه لاگها را برای یک کلاس خاص مانند Contacts Repository گروهبندی کنیم. این لاگها ترجیحاً برای ترکیب رویکردها در یک پروژه واحد پذیرفتنی هستند و میتوانیم از دستهبندیهای مناسبتر استفاده کنیم تا چارچوب پیامهای لاگ پروژه را درک کنیم.
ما میتوانیم دستهبندیها و سیستمهای فرعی مختلف را به صورت یک اکستنشن از OSLog اضافه کنیم و در بخشهای مختلف اپلیکیشن به آنها دسترسی داشته باشیم. ذخیرهسازی آنها در یک مکان، از ایجاد وهلههای OSLog در همه جای کدبیس جلوگیری کرده و به سازمانیافته ماندن دستهبندیهای مختلف کمک میکند.
1extension OSLog {
2 private static var subsystem = Bundle.main.bundleIdentifier!
3
4 static let ui = OSLog(subsystem: subsystem, category: "UI")
5 static let network = OSLog(subsystem: subsystem, category: "Network")
6}
7
8os_log("Contact selected", log: .ui)
9os_log("HTTP response: %@", log: .network, responseCode)
سطوح لاگ کردن
سیستم unified logging از یک مجموعه سطوح متفاوت لاگ کردن استفاده میکند که با استفاده از آن میتوانیم انواع متفاوتی از پیامها را هدفگیری کنیم. این سطوح چگونگی نمایش پیامها را کنترل میکنند و همچنین شیوه و زمان حضور و مواقع ذخیره آنها در محیطهای متفاوت را تعیین مینمایند. شیوه مدیریت هر سطح از سوی سیستم را میتوان از طریق خط فرمان (+) کنترل کرد. به این منظور بهتر است از مناسبترین سطح لاگ برای هر پیام استفاده کنیم تا بیشترین بهره را از سیستم لاگ داشته باشیم. سطوح مختلف ارائه شده به شرح زیر هستند:
- Default – همه چیزهایی را که موجب شکست و اساساً یک fall-back میشوند، در صورت نامناسب بودن سطوح دیگر لاگ میکند. در صورت عدم تغییر، پیامها در بافرهای حافظه ذخیره میشوند و تا زمانی که بافر پر نشده در آنجا حضور دارند.
- Info – هر چیزی که ممکن است مفید باشد، اما شاید مستقیماً برای عیبیابی یا خطایابی خطاها مفید نباشد، ذخیره میشود. در صورت عدم تغییر، پیامها به صوت دائمی ذخیره نمیشوند و صرفاً در بافرهای حافظه ذخیرهشده و به محض پر شدن بافر جایگزین میشوند.
- Debug – برای به دست آوردن اطلاعاتی در طی فاز توسعه و جهت عیبیابی یک مشکل خاص استفاده میشود. در این حالت پیامها دریافت نمیشوند مگر این که بر اثر یک تغییر پیکربندی فعال شده باشند.
- Error – برای به دست آوردن خطاها و شکستهای اپلیکیشن که به طور خاص حیاتی هستند. پیامها به صورت دائمی ذخیره میشوند تا مطمئن شویم که گم نمیشوند.
- Fault – برای به دست آوردن خطاهای صرفاً در سطح سیستم یا چند پردازشی که ممکن است در کد اپلیکیشن ما حضور ندارند، استفاده میشود. همانند سطح Error پیامها به صورت دائمی ذخیره میشوند.
لاگ کردن در هر سطح به سادگی تعیین OSLogType به عنوان یک آرگومان فراخوانی os_log است.
1os_log("Contact selected", log: .ui, type: .info)
2os_log("Saving contact failed", log: .database, type: .error)
حریم خصوصی کاربر
برای تضمین این که دادههای خصوصی کاربر به طور تصادفی در لاگهای اپلیکیشن حضور نمییابند و مورد دسترسی افراد دیگر قرار نمیگیرند، سیستم unified logging یک فرایند آرگومان عمومی و خصوصی دارد. به صورت پیشفرض تنها مقادیر اسکالر (بولی و عدد صحیح) گردآوری میشوند و رشتههای دینامیک یا اشیای پیچیده دینامیک سانسور میشوند. در صورتی که نیاز باشد، میتوان آرگومانهای رشتهای دینامیک را به صورت عمومی اعلان کرد و همچنین آرگومانهای اسکالر را نیز میتوان به صورت خصوصی اعلان نمود.
1os_log("Contact %ld selected", 2)
2os_log("Contact %{private}ld selected", 2)
3
4os_log("HTTP response: %@", responseCode)
5os_log("HTTP response: %{public}@", responseCode)
توجه کنید که همواره بهتر است که از عمومی ساختن همه آرگومانها اجتناب کنیم، چون در نتیجه ممکن است به آسانی دادههای خصوصی کاربر یا شرکتها از طریق لاگهای دستگاه افشا شوند.
خواندن لاگها
در زمانی که دیباگر الصاق یافته است، پیامهای لاگ در کنسول Xcode نمایش مییابند. با این حال، بهترین روش برای خواندن لاگها، استفاده از اپلیکیشن Console MacOS است. در این اپلیکیشن میتوان لاگها را مرتبسازی، فیلتر و جستجو کرد و همچنین آنها را به آسانی خواند.
- لاگها در یک جدول نمایش مییابند و هر بخش از دادهها به آسانی خوانده میشوند.
- پیامها بر اساس سیستم فرعی و دستهبندی، جستجو و فیلتر میشوند.
- فیلدها برای هر پیام لاگ نمایش یافته و یا پنهان میشوند.
- پیامهای سطح دیباگ و خطا فعال یا غیرفعال میشوند.
- الگوهای جستجو برای دسترسی آسان در مراجعههای بعدی ذخیره میشوند.
سخن پایانی
سیستم Unified logging یک راهحل نویدبخش و قدرتمند است که به طور خاص در مواردی که عملکرد و فیلترینگ پیامهای لاگ مهم است به کار میآید. این سیستم به طور داخلی شاید تفاوتهایی با NSLog و print داشته باشد. پس از کمی مطالعه بیشتر و ارائه os_log به همراه سیستمهای فرعی و دستهبندیها میتوانیم از سطوح لاگ مختلف به خوبی استفاده کنیم و به این ترتیب متوجه میشویم که کار با لاگها بسیار آسانتر شده است. اگر نیازمند پوشش بیشتر لاگ و عملکرد بهتر هستید، میتوانید گزارههای NSLog و print را کنار گذاشته و شروع به استفاده از os_log در اپلیکیشنهای خود بکنید.
OSLog رویکرد لاگ توصیهشده فعلی از سوی اپل است. در مورد OSLog نکات بسیار بیشتری نسبت به آن چه در این مقاله مطرح شد، میتوان گفت. مثلاً میتوان در مورد استفاده از signposts (+) برای رصد عملکرد اپلیکیشن صحبت کرد که خود نیازمند مقاله مستقل و مفصل دیگری است.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- مجموعه آموزشهای دروس علوم و مهندسی کامپیوتر
- آموزش برنامه نویسی Swift (سوئیفت) برای برنامه نویسی iOS
- آموزش سوئیفت (Swift) — مجموعه مقالات مجله فرادرس
- عملگرهای سفارشی در سوئیفت — از صفر تا صد
==