slog في مثال واحد
تكتب log/slog (Go 1.21) سجلات مهيكلة: رسالة، ومستوى، وسمات مفتاح وقيمة.
يخرج كل سطر بالشكل time=... level=INFO msg="user logged in" user=ada attempts=1. ولأن كل قيمة حقل مستقل، يستطيع جامع السجلات (Loki أو Elasticsearch أو CloudWatch أو Datadog) التصفية على user=ada أو level=ERROR دون تعابير نمطية على نص حرّ.
تكتب أمثلة هذه الصفحة إلى os.Stdout لتظهر المخرجات بالترتيب. في خدمة حقيقية تذهب السجلات عادة إلى os.Stderr، وهو أيضًا حيث يكتب المسجِّل الافتراضي.
المستويات
| المستوى | القيمة | يُستخدم لـ |
|---|---|---|
slog.LevelDebug | -4 | تفاصيل للمطوّرين، معطّل في الإنتاج |
slog.LevelInfo | 0 | الأحداث العادية: البدء، خدمة طلب، انتهاء مهمة |
slog.LevelWarn | 4 | شيء غير متوقّع تعامل معه البرنامج |
slog.LevelError | 8 | فشل عملية |
يُسقط المعالج السجلات التي تحت حدّه الأدنى، والحد الأدنى الافتراضي هو Info. لهذا لا تطبع slog.Debug(...) شيئًا حتى تضبط معالجًا بـ Level: slog.LevelDebug. الفجوات بين القيم تترك مجالًا لمستويات مخصّصة مثل slog.Level(2).
لتغيير المستوى وقت التشغيل (من خيار، أو نقطة نهاية إدارية، أو إشارة)، ضع slog.LevelVar في الخيارات واستدعِ Set عليه لاحقًا:
var level slog.LevelVar // zero value: Info
logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: &level}))
level.Set(slog.LevelDebug) // from now on, debug records are written
نص أم JSON
يكتب slog.NewTextHandler أزواج key=value، سهلة القراءة في الطرفية. ويكتب slog.NewJSONHandler كائن JSON واحدًا في كل سطر، وهي الصيغة التي تتوقّعها معظم خطوط معالجة السجلات. تبقى استدعاءات التسجيل كما هي؛ المعالج وحده يتغيّر.
تحتفظ القيم بأنواعها: status رقم في مخرجات JSON وretry قيمة منطقية، وtime.Duration تُطبع 42ms في النص وبالنانوثانية في JSON، وerror يطبع رسالته. ReplaceAttr هو الخطّاف لإعادة كتابة السمات أو إزالتها، ويُستخدم هنا لإسقاط الطابع الزمني، وعمليًا لإعادة تسمية المفاتيح (msg إلى message) أو حجب القيم.
السمات
الشكل الأقل صرامة في الأنواع يناوب بين المفاتيح والقيم: "user", "ada", "attempts", 3. إنه قصير، وله نمط فشل واحد: عدد فردي من الوسائط. تُسجَّل القيمة المتبقّية تحت المفتاح !BADKEY. ويكتشف go vet ذلك:
./main.go:14:2: call to slog.Info missing a final value
لأمان الأنواع وحجز أقل قليلًا للذاكرة، استخدم دوال بناء السمات، وLogAttrs عند التسجيل في مسار ساخن:
logger.Info("order placed",
slog.Int("order_id", 1017),
slog.String("currency", "EUR"),
slog.Float64("total", 59.90),
slog.Duration("took", elapsed),
)
logger.LogAttrs(ctx, slog.LevelInfo, "order placed", slog.Int("order_id", 1017))
استخدم اصطلاح تسمية واحدًا للمفاتيح في كل الشيفرة (user_id في كل مكان، لا userID في حزمة وuid في أخرى). الاستعلامات في نظام سجلاتك تعتمد عليه.
With: مسجِّلات تحمل السياق
تعيد logger.With(attrs...) مسجِّلًا جديدًا يضيف تلك السمات إلى كل سجل. أنشئ واحدًا لكل طلب أو مهمة، فيمكن ربط كل الأسطر التي يكتبها معًا:
كل سطر يحمل service وversion وrequest_id وuser دون تكرارها في كل استدعاء. تُداخل slog.Group السمات، فيكتبها معالج JSON ككائن متداخل ("payment":{"amount":25,"currency":"USD"}) ومعالج النص كمفاتيح بنقاط (payment.amount=25). وتضع logger.WithGroup("db") كل السمات اللاحقة لذلك المسجِّل تحت مجموعة.
مرّر المسجِّل الخاص بالطلب إلى الأسفل كمعامل أو حقل في بنية. تخزينه في context.Context ممكن لكنه يخفي الاعتمادية؛ وتمرّر توابع InfoContext(ctx, ...) في slog السياق إلى المعالج، ويستطيع معالج مخصّص استخدامه لاستخراج معرّفات التتبّع.
إخفاء الأسرار بـ LogValuer
يستطيع النوع التحكّم في طريقة تسجيله بتطبيق slog.LogValuer. هذا يُبقي كلمات المرور والرموز خارج السجلات أيًا كان من يسجّل القيمة:
لا يسجّل User إلا معرّفه وبريده، وToken المسجّل وحده يطبع REDACTED. يستدعي المعالج LogValue فقط عند كتابة السجل فعلًا، فيعمل ذلك أيضًا مع القيم المكلفة الحساب.
الحزمة الكلاسيكية log
سبقت log الحزمة slog وما زالت مناسبة للبرامج الصغيرة والسكربتات. تكتب أسطرًا إلى الخطأ القياسي مع بادئة تاريخ ووقت:
| العلم | يضيف |
|---|---|
log.LstdFlags (الافتراضي) | التاريخ والوقت 2009/11/10 23:00:00 |
log.Lmicroseconds | الميكروثواني إلى الوقت |
log.LUTC | الوقت بتوقيت UTC |
log.Lshortfile / log.Llongfile | main.go:14 / المسار الكامل |
log.Lmsgprefix | يضع البادئة قبل الرسالة بدل بداية السطر |
ثلاث دوال تخرج أو تسبّب panic، والفرق مهم:
- تطبع
log.Fatalوlog.Fatalfوlog.Fatallnثم تستدعيos.Exit(1). لا تُنفَّذ الاستدعاءات المؤجّلة. استخدمها فيmainلإخفاقات بدء التشغيل، ولا تستخدمها أبدًا في شيفرة المكتبات أو معالجات الطلبات. - تطبع
log.Panicوأخواتها ثم تسبّب panic، فتُنفَّذ الاستدعاءات المؤجّلة ويمكن استعادة الـ panic. - كل دالة أخرى تكتب سطرًا فقط.
للتسجيل في ملف، افتحه ومرّره إلى log.New أو log.SetOutput؛ ويكتب io.MultiWriter(os.Stderr, f) إلى الاثنين.
log وslog معًا
تجعل slog.SetDefault(logger) المسجِّل logger افتراضيًا لدوال slog.Info العليا، وتوجّه أيضًا مخرجات الحزمة log عبره. فتخرج استدعاءات log.Printf الموجودة في شيفرتك أو في اعتمادياتك كسجلات مهيكلة بمستوى Info:
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr, nil)))
log.Printf("legacy message") // {"time":"...","level":"INFO","msg":"legacy message"}
قبل SetDefault يكتب مسجِّل slog الافتراضي عبر الحزمة log، ولهذا تطبع slog.Info("hi") وحدها 2026/09/23 14:30:00 INFO hi.
قواعد عملية
- سجّل الخطأ أو أعده، لا الاثنين. الدالة التي تسجّل خطأ وتعيده تجعل الفشل نفسه يُسجَّل في كل مستوى من مكدّس الاستدعاءات. أعد الأخطاء صعودًا مع سياق، وسجّلها مرة واحدة حيث تُعالج.
- ضع البيانات المتغيّرة في السمات، لا في الرسالة.
logger.Info("user created", "user_id", id)تتجمّع جيدًا في نظام السجلات؛ أماlogger.Info(fmt.Sprintf("user %d created", id))فتنشئ رسالة مختلفة لكل مستخدم. - لا تسجّل الأسرار أو أجسام الطلبات كاملة أبدًا. استخدم
LogValuerأوReplaceAttrللحجب. - استخدم JSON في الإنتاج، والنص في التطوير. اختر المعالج عند بدء التشغيل من خيار أو متغيّر بيئة.
- اختر المستويات عن قصد. إذا سُجّل كل شيء بمستوى Error، تصير التنبيهات على الأخطاء ضجيجًا.
الأسئلة الشائعة
ما هي slog في Go؟
log/slog حزمة التسجيل المهيكل التي أُضيفت إلى المكتبة القياسية في Go 1.21. بدل النصوص المنسّقة، لكل سجل رسالة ومستوى (Debug وInfo وWarn وError) وسمات مفتاح وقيمة، ويكتبه معالج كنص key=value أو كـ JSON: slog.Info("login", "user", "ada", "attempts", 3).
كيف أفعّل سجلات debug في slog؟
الحد الأدنى الافتراضي للمستوى هو Info، فلا تطبع slog.Debug شيئًا. أنشئ معالجًا بمستوى أدنى واجعله الافتراضي: slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))). استخدم slog.LevelVar بدل ثابت إذا أردت تغيير المستوى أثناء تشغيل البرنامج.
ما الفرق بين log وslog في Go؟
تكتب log أسطرًا حرّة مع بادئة وقت اختيارية ولا مستويات لها. أما slog فتكتب سجلات بمستويات وسمات مفتاح وقيمة ذات أنواع يستطيع جامعو السجلات تحليلها وتصفيتها. كلتاهما في المكتبة القياسية؛ وتعيد slog.SetDefault أيضًا توجيه مخرجات الحزمة log عبر معالج slog.
هل تنفّذ log.Fatal الدوال المؤجّلة؟
لا. تطبع log.Fatal وlog.Fatalf الرسالة وتستدعي os.Exit(1)، التي تتخطّى كل الاستدعاءات المؤجّلة. استخدمها فقط في main أو شيفرة الإعداد حيث لا شيء يحتاج تنظيفًا. أما log.Panic فتسبّب panic بدلًا من ذلك، فتُنفَّذ الاستدعاءات المؤجّلة.