از متن رخداد تا جدول شمارش

در یک برنامه، رخدادها ممکن است سطح‌هایی مانند INFO و ERROR داشته باشند. شمارش سطح‌ها اولین گام برای دیدن ترکیب گزارش است، اما هنوز علت خطا یا سلامت سرویس را ثابت نمی‌کند. در این تمرین متن کامل رخداد، شناسهٔ کاربر و اطلاعات حساس را نمی‌خوانیم؛ فقط فهرستی از نام سطح‌ها ورودی است. همین جداسازی، دامنهٔ مسئله و معیار خروجی را روشن نگه می‌دارد.

نرمال‌سازی کوچک و قابل‌توضیح

پیش از شمارش، فاصله‌های ابتدا و انتهای نام را حذف و حروف را بزرگ کنید. در نتیجه info و INFO به یک کلید تبدیل می‌شوند. رشتهٔ خالی پس از strip باید کنار گذاشته شود. سطح ناشناخته را حذف نکنید، چون ممکن است یک نام سفارشی معتبر باشد؛ قرارداد فعلی آن را نیز شمارش می‌کند. تغییر نام WARN به WARNING در این تمرین تعریف نشده و نباید خودسرانه انجام شود.

ساخت شمارنده بدون از دست‌دادن داده

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

عدد بزرگ‌تر همیشه وضعیت بدتر نیست

اگر امروز تعداد ERROR دو برابر شد، ابتدا حجم کل درخواست‌ها و بازهٔ زمانی را مقایسه کنید. ده خطا در هزار درخواست با ده خطا در ده درخواست یکسان نیست. همچنین یک رخداد می‌تواند چند بار ثبت شود؛ تکرار log لزوماً چند خطای مستقل نیست. برای گزارش واقعی بهتر است نرخ خطا، شدت اثر و شناسهٔ رخداد را با تعریف روشن کنار شمارش خام قرار دهید.

ترتیب نمایش باید صریح باشد

دیکشنری ترتیب درج را نگه می‌دارد، اما این ترتیب رتبه‌بندی شدت نیست. most_common در Counter بر اساس تعداد مرتب می‌کند و در تساوی ترتیب نخستین مشاهده را حفظ می‌کند. اگر رابط باید همیشه ERROR را پیش از WARNING نشان دهد، نگاشت اولویت جداگانه لازم است. ترتیب یک خروجی نمایشی را با معنای سطح یا ترتیب زمانی ثبت رخداد اشتباه نگیرید.

آزمون‌هایی که خطا را آشکار می‌کنند

فهرست خالی باید counts خالی بدهد؛ رشتهٔ تمام‌فاصله نباید به کلید خالی تبدیل شود. ترکیب info با فاصله و INFO بدون فاصله باید یک دسته باشد. همچنین یک سطح سفارشی مانند TRACE را اضافه کنید تا مطمئن شوید کد فقط چند نام از پیش شناخته‌شده را قبول نمی‌کند. مجموع تعدادها باید برابر تعداد سطح‌های غیرخالی پس از پاک‌سازی باشد؛ این یک ناوردای مفید برای بررسی گزارش است.

گام بعدی برای گزارش عملیاتی

برای فایل بزرگ، خواندن تدریجی خطوط از بارگذاری همهٔ فایل در حافظه بهتر است. بااین‌حال ابتدا قالب log و روش استخراج سطح را مشخص کنید؛ جست‌وجوی کلمهٔ ERROR در متن آزاد ممکن است پیام عادی را هم خطا بشمارد. در توسعهٔ پروژه، ورودی را به رکوردهای دارای زمان و سطح گسترش دهید و گزارش روزانه بسازید. قبل از ذخیره، اطلاعات شخصی یا محرمانه را از متن رخداد کنار بگذارید.

نمونه‌کد و قرارداد پروژه

levels: فهرست رشته‌های سطح رخداد؛ هر رشته با strip و upper نرمال شود. رشتهٔ خالی کنار گذاشته شود؛ سطح ناشناخته هم شمارش می‌شود.

counts: نگاشت سطح نرمال‌شده به تعداد؛ فهرست خالی نگاشت خالی برمی‌گرداند.

def solve(data):
    counts = {}
    for raw in data['levels']:
        level = raw.strip().upper()
        if level:
            counts[level] = counts.get(level, 0) + 1
    return {'counts': counts}

print(solve({'levels': ['INFO', 'error', 'INFO', 'warning', 'ERROR']}))

خروجی مورد انتظار

{
  "counts": {
    "INFO": 2,
    "ERROR": 2,
    "WARNING": 1
  }
}

پرسش‌های متداول

آیا شمارش ERROR همان تعداد شکست درخواست است؟

نه. یک درخواست ممکن است چند رخداد خطا ثبت کند یا یک شکست بدون log بماند؛ نسبت این دو باید در طراحی گزارش مشخص شود.

آیا Counter نام سطح را اصلاح می‌کند؟

خیر. Counter فقط مقادیر ورودی را می‌شمارد؛ strip و upper یا هر نگاشت دیگر باید پیش از شمارش انجام شوند.

تمرین عملی و قدم بعدی

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

منابع و مطالعهٔ بیشتر

برای بررسی جزئیات و ادامهٔ مطالعه؛ متن این راهنما مستقلاً برای نوونتیکس نوشته شده است.