عندما يكون لديك ملف Python صغير، قد يبدو استخدام print() كافيًا لمعرفة ما يحدث داخل البرنامج. لكن بعد أن يبدأ مشروعك بالنمو، ستحتاج إلى معرفة أشياء أكثر: متى بدأ البرنامج؟ هل تمت قراءة ملف بنجاح؟ لماذا فشلت عملية معينة؟ ما الخطأ الذي ظهر؟ وهل تكرر الخطأ لاحقًا؟
هنا تأتي مكتبة logging المدمجة مع Python. بدل أن تضع عشرات أوامر الطباعة داخل المشروع، يمكنك تسجيل الأحداث المهمة بمستويات واضحة، ثم عرضها في الطرفية أو حفظها داخل ملف للرجوع إليها لاحقًا.
{alertInfo} الفكرة ببساطة: استخدمprint()عندما تريد عرض نتيجة عادية للمستخدم، واستخدمloggingعندما تريد تسجيل ما يحدث داخل البرنامج لمتابعة الحالة أو تشخيص مشكلة أو حفظ سجل للأخطاء.
{getToc} $title={محتوى المقال}
ما هي مكتبة logging في Python؟
logging هي مكتبة قياسية تأتي مع Python، لذلك لا تحتاج إلى تثبيتها عبر pip. مهمتها تسجيل رسائل منظمة تصف أحداث البرنامج أثناء تشغيله، مثل بدء مهمة، نجاح قراءة ملف، ظهور إدخال غير مناسب، أو حدوث خطأ.
الميزة المهمة ليست مجرد كتابة رسالة، بل أنك تستطيع تحديد مستوى الرسالة، وإظهارها في المكان المناسب، وإضافة وقت التسجيل واسم الملف أو أي معلومات أخرى تحتاجها.
هذا مهم خصوصًا بعد أن بدأت في بناء مشاريع منظمة، مثل مشروع CSV أو أداة سطر أوامر CLI. في هذه المرحلة لم يعد السؤال فقط: “هل يعمل البرنامج؟” بل: “ماذا حدث داخله عندما لم يعمل كما توقعت؟”
الفرق بين print وlogging
| الحالة | استخدم print | استخدم logging |
|---|---|---|
| عرض نتيجة للمستخدم | نعم، مثل عرض اسم ملف تم إنشاؤه أو نتيجة عملية حسابية. | ليس الخيار الأساسي عادةً. |
| تتبع خطوات البرنامج | مفيد مؤقتًا أثناء التعلم فقط. | نعم، لأنه منظم ويمكن التحكم في مستواه ومكان حفظه. |
| حفظ سجل يمكن مراجعته لاحقًا | لا، إلا إذا نسخت مخرجات الطرفية يدويًا. | نعم، يمكن الكتابة تلقائيًا داخل ملف .log. |
| تسجيل خطأ داخل برنامج طويل | قد يوضح رسالة بسيطة، لكنه لا ينظم السياق جيدًا. | نعم، ويمكن تسجيل تفاصيل الاستثناء ووقت حدوثه. |
لا يعني هذا أنك يجب أن تحذف print() من كل مشروع. البرامج التي تتعامل مع المستخدم في الطرفية تحتاج رسائل واضحة مثل “تم الحفظ بنجاح”. أما logging فيعمل كدفتر ملاحظات تقني للمشروع والمطور.
مستويات الرسائل في logging
تستخدم logging مستويات مختلفة لتوضح مدى أهمية الحدث. الترتيب التالي يساعدك في اختيار المستوى المناسب:
| المستوى | متى تستخدمه؟ | مثال |
|---|---|---|
DEBUG | تفاصيل دقيقة تساعدك أثناء التطوير والتشخيص. | عدد الصفوف التي قرأها البرنامج من ملف CSV. |
INFO | حدث طبيعي مهم أثناء سير البرنامج. | تم حفظ التقرير بنجاح. |
WARNING | حدث غير مثالي، لكن البرنامج ما زال قادرًا على الاستمرار. | الملف غير موجود، لذلك سيتم إنشاء ملف جديد. |
ERROR | فشل حدث محدد ويحتاج إلى مراجعة. | تعذر قراءة البيانات بسبب تنسيق غير صالح. |
CRITICAL | مشكلة كبيرة قد تمنع البرنامج من العمل بشكل أساسي. | تعذر بدء التطبيق لأن ملف الإعدادات الرئيسي تالف. |
أول مثال: تسجيل رسالة بسيطة
ابدأ باستيراد المكتبة، ثم استخدم basicConfig() لتحديد أقل مستوى تريد إظهاره. في المثال التالي سنعرض رسائل INFO وما فوقها:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(levelname)s - %(message)s"
)
logging.debug("هذه رسالة تفصيلية للتشخيص")
logging.info("تم تشغيل البرنامج بنجاح")
logging.warning("لم يتم العثور على صورة المستخدم")
لن تظهر رسالة DEBUG هنا، لأننا حددنا المستوى INFO. لكن رسالتي INFO وWARNING ستظهران.
INFO - تم تشغيل البرنامج بنجاح
WARNING - لم يتم العثور على صورة المستخدم
جرّب تغيير level=logging.INFO إلى level=logging.DEBUG ثم شغّل الكود مرة أخرى. ستظهر الرسالة التفصيلية أيضًا.
إضافة الوقت إلى السجل
عندما تتكرر عمليات البرنامج، تصبح معرفة وقت الحدث مهمة. يمكنك إضافة التاريخ والوقت إلى شكل السجل باستخدام %(asctime)s:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logging.info("بدأت عملية إنشاء التقرير")
logging.info("انتهت عملية إنشاء التقرير")
قد يكون الناتج بهذا الشكل:
2026-07-02 20:15:03 | INFO | بدأت عملية إنشاء التقرير
2026-07-02 20:15:04 | INFO | انتهت عملية إنشاء التقرير
الوقت الظاهر يعتمد على وقت جهازك أو بيئة التشغيل التي يعمل عليها البرنامج.
كيف تحفظ السجلات داخل ملف؟
أحيانًا تحتاج أن تعرف ما حدث بعد أن يغلق المستخدم البرنامج أو بعد أن تتوقف الطرفية عن العرض. لذلك يمكنك توجيه الرسائل إلى ملف مثل app.log.
import logging
logging.basicConfig(
filename="app.log",
encoding="utf-8",
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s"
)
logging.info("بدأ البرنامج")
logging.warning("تم استخدام قيمة افتراضية")
logging.error("تعذر حفظ التقرير")
بعد تشغيل الملف، سيظهر بجانبه ملف جديد باسم app.log. بشكل افتراضي، يضيف Python السجلات الجديدة إلى نهاية الملف بدل حذف السجل السابق.
{alertWarning}
ملاحظة مهمة: لا تضع basicConfig() في ملفات كثيرة داخل المشروع. الأفضل أن تضبط إعدادات logging مرة واحدة عند بداية تشغيل التطبيق، ثم تستخدم التسجيل في بقية الملفات.
استخدام logger باسم الملف
في المشروع الصغير يمكن استعمال logging.info() مباشرة. لكن عندما يصبح المشروع من عدة ملفات، من الأفضل إنشاء logger باسم كل ملف. الطريقة الشائعة هي استخدام __name__:
import logging
logger = logging.getLogger(__name__)
logger.info("تمت قراءة ملف البيانات")
logger.warning("بعض الصفوف تحتوي على قيم ناقصة")
وعند ضبط التنسيق، يمكنك إظهار اسم logger أيضًا:
format="%(asctime)s | %(name)s | %(levelname)s | %(message)s"
هذا يصبح مفيدًا عندما تريد معرفة أي جزء من المشروع سجل الرسالة: ملف قراءة البيانات، أو ملف الحسابات، أو ملف واجهة سطر الأوامر.
تسجيل الأخطاء مع try و except
تعلمت سابقًا استخدام try وexcept حتى لا يتوقف البرنامج فجأة. الآن يمكنك جعل التعامل مع الخطأ أكثر فائدة عبر تسجيله أيضًا.
في المثال التالي نحاول قراءة ملف JSON. إذا كان الملف غير موجود أو كان محتواه غير صالح، نعرض رسالة مناسبة للمستخدم ونسجل الخطأ للتشخيص:
import json
import logging
from pathlib import Path
logging.basicConfig(
filename="app.log",
encoding="utf-8",
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s"
)
logger = logging.getLogger(__name__)
file_path = Path("data.json")
try:
content = file_path.read_text(encoding="utf-8")
data = json.loads(content)
logger.info("تمت قراءة البيانات بنجاح")
print(data)
except FileNotFoundError:
logger.warning("ملف data.json غير موجود")
print("لم يتم العثور على ملف البيانات.")
except json.JSONDecodeError:
logger.exception("محتوى ملف JSON غير صالح")
print("محتوى ملف البيانات غير صحيح.")
الدالة logger.exception() تستخدم داخل except عندما تريد تسجيل رسالة الخطأ مع تفاصيل الاستثناء. هذا مفيد للتشخيص، لكنه لا يعني أن تعرض تلك التفاصيل التقنية كاملة للمستخدم النهائي.
مشروع صغير: سجل عمليات برنامج مهام
لنطبق الفكرة في مثال قريب من المشاريع الواقعية. البرنامج التالي يضيف مهمة إلى ملف نصي، ثم يسجل نجاح العملية أو فشلها في ملف logs/app.log.
هيكل المشروع
task_manager/
│
├── main.py
├── tasks.txt
└── logs/
└── app.log
ملف main.py
import logging
from pathlib import Path
BASE_DIR = Path(__file__).parent
LOGS_DIR = BASE_DIR / "logs"
TASKS_FILE = BASE_DIR / "tasks.txt"
LOGS_DIR.mkdir(exist_ok=True)
logging.basicConfig(
filename=LOGS_DIR / "app.log",
encoding="utf-8",
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s"
)
logger = logging.getLogger(__name__)
task = input("اكتب المهمة الجديدة: ").strip()
if not task:
logger.warning("حاول المستخدم إضافة مهمة فارغة")
print("لا يمكن إضافة مهمة فارغة.")
else:
try:
with TASKS_FILE.open("a", encoding="utf-8") as file:
file.write(task + "\n")
logger.info("تمت إضافة مهمة جديدة")
print("تمت إضافة المهمة بنجاح.")
except OSError:
logger.exception("فشلت عملية حفظ المهمة")
print("تعذر حفظ المهمة. حاول مرة أخرى.")
لاحظ أن البرنامج لا يسجل نص المهمة نفسها. هذه عادة جيدة، لأن بعض البيانات التي يدخلها المستخدم قد تكون خاصة أو حساسة. سجّل ما تحتاجه للتشخيص فقط، ولا تجعل السجل مكانًا لحفظ كلمات المرور أو الرموز السرية أو البيانات الشخصية غير الضرورية.
أخطاء شائعة عند استخدام logging
1. توقع ظهور رسائل DEBUG دون تغيير المستوى
المستوى الافتراضي لا يعرض رسائل DEBUG. إذا كنت تحتاجها أثناء التشخيص، اضبط level=logging.DEBUG.
2. استدعاء basicConfig أكثر من مرة دون تنظيم
في أغلب المشاريع، اضبط الإعدادات عند بداية البرنامج فقط. ثم أنشئ loggers في الملفات الأخرى باستخدام getLogger(__name__).
3. استخدام logging بدل الاستثناءات
تسجيل الخطأ لا يحل المشكلة وحده. عندما تكون العملية لا تستطيع الاستمرار بشكل صحيح، قد تحتاج إلى رفع استثناء أو التعامل معه بوضوح. التسجيل يوثق ما حدث، لكنه لا يغني عن منطق معالجة الأخطاء.
4. تسجيل بيانات حساسة
تجنب تسجيل كلمات المرور، رموز API، ملفات تعريف الارتباط، الأرقام البنكية، أو نصوص المستخدمين كاملة دون حاجة. افترض أن ملف السجل قد يراه شخص آخر داخل بيئة العمل أو أثناء الدعم الفني.
5. جعل السجل مليئًا برسائل غير مفيدة
لا تسجل كل سطر من البرنامج. سجل القرارات والعمليات المهمة: بداية مهمة، نجاح عملية، تحذير يستحق المراجعة، أو خطأ يحتاج تشخيصًا.
متى تستخدم logging في مشاريعك؟
- عند بناء أداة سطر أوامر CLI تحتاج إلى تتبع الأوامر أو الأخطاء.
- عند قراءة ملفات CSV أو JSON وتريد معرفة سبب فشل عملية القراءة أو الحفظ.
- عند إنشاء تقارير أو ملفات تلقائيًا وتريد حفظ تاريخ العمليات.
- عند وجود أكثر من ملف داخل المشروع وتحتاج معرفة مصدر كل رسالة.
- عند تشغيل برنامج متكرر أو طويل لا تكون موجودًا أمامه طوال الوقت.
روابط داخلية مفيدة من بايثون العرب
- بايثون بعد الأساسيات 4: بناء برنامج سطر أوامر CLI بسيط
- بايثون بعد الأساسيات 2: تنظيم ملفات مشروع Python بطريقة صحيحة
- بايثون بعد الأساسيات 3: قراءة وكتابة ملفات CSV داخل مشروع منظم
- أساسيات بايثون 31: شرح try و except في Python
- أساسيات بايثون 33: شرح assert واختبار صحة الشروط
الخلاصة
print()مناسب لعرض نتائج عادية للمستخدم، بينماloggingمناسب لتسجيل أحداث البرنامج وتشخيص المشكلات.- استخدم مستويات الرسائل لتوضيح أهمية الحدث:
DEBUGوINFOوWARNINGوERRORوCRITICAL. - يمكنك حفظ السجلات داخل ملف
.logومراجعته عند حدوث مشكلة لاحقًا. - استخدم
logger.exception()داخلexceptعندما تريد تسجيل تفاصيل الاستثناء. - اضبط إعدادات التسجيل مرة واحدة عند بداية المشروع، وتجنب كتابة بيانات حساسة في ملفات السجل.
{alertSuccess}
خطوتك التالية: افتح مشروعًا صغيرًا كتبته سابقًا، وأضف سجلًا واحدًا لبدء البرنامج، وسجلًا لنجاح عملية أساسية، وسجلًا للأخطاء داخل except. بهذه الخطوة يتحول مشروعك من مثال تعليمي إلى برنامج أسهل في المتابعة والصيانة.
مصادر خارجية رسمية للتوسع
- Logging HOWTO — Python Documentation
- logging — Python Standard Library
- Logging handlers — Python Documentation
أسئلة شائعة
هل أحتاج إلى تثبيت logging باستخدام pip؟
لا. مكتبة logging جزء من مكتبة Python القياسية، لذلك يكفي أن تكتب import logging.
هل أستبدل print بالكامل بـ logging؟
لا. استخدم print() للرسائل والنتائج التي يجب أن يراها مستخدم البرنامج مباشرة. واستخدم logging لتسجيل أحداث التشغيل والتفاصيل التقنية والأخطاء.
أين أضع basicConfig في المشروع؟
ضعه عادةً في الملف الذي يبدأ تشغيل التطبيق، مثل main.py. لا تكرر الإعدادات في كل ملف، ثم استخدم getLogger(__name__) داخل الملفات الأخرى.
هل يمكن أن أحفظ السجلات في ملف وعرضها في الطرفية معًا؟
نعم، لكن ذلك يحتاج استخدام handlers متعددة. ابدأ أولًا بالكتابة إلى ملف أو العرض في الطرفية، ثم انتقل إلى handlers عندما تحتاج إعدادًا أكثر تنظيمًا.
هل logger.exception مختلف عن logger.error؟
نعم. داخل كتلة except، تستخدم logger.exception() عندما تريد تسجيل الرسالة مع تفاصيل الاستثناء. أما logger.error() فيسجل رسالة خطأ فقط ما لم تمرر له معلومات إضافية.



