Logrus هو مسجل منظم لـ Go (golang)، متوافق تمامًا مع واجهة برمجة التطبيقات (API) مع مسجل المكتبة القياسي.
Logrus في وضع الصيانة. لن نقوم بتقديم ميزات جديدة. من الصعب جدًا القيام بذلك بطريقة لا تؤدي إلى تعطيل مشاريع العديد من الأشخاص، وهو آخر شيء تريده من مكتبة التسجيل الخاصة بك (مرة أخرى...).
هذا لا يعني أن Logrus قد مات. ستستمر صيانة Logrus للأمان وإصلاحات الأخطاء (المتوافقة مع الإصدارات السابقة) والأداء (حيث تكون الواجهة مقيدة).
أعتقد أن أكبر مساهمة لـ Logrus هي أنها لعبت دورًا في الاستخدام الواسع النطاق اليوم لقطع الأشجار المنظم في Golang. لا يبدو أن هناك سببًا للقيام بتكرار رئيسي في Logrus V2، نظرًا لأن مجتمع Go الرائع قام ببناء تلك البرامج بشكل مستقل. لقد ظهرت العديد من البدائل الرائعة. سيبدو Logrus مثل تلك، لو تم إعادة تصميمه بما نعرفه عن التسجيل المنظم في Go اليوم. تحقق، على سبيل المثال، من Zerolog وZap وApex.
هل ترى مشكلات غريبة حساسة لحالة الأحرف؟ كان من الممكن في الماضي استيراد Logrus بالأحرف الكبيرة والصغيرة. نظرًا لبيئة حزمة Go، تسبب هذا في حدوث مشكلات في المجتمع وكنا بحاجة إلى معيار. واجهت بعض البيئات مشاكل مع متغير الأحرف الكبيرة، لذلك تم تحديد الأحرف الصغيرة. كل شيء يستخدم logrus
سيحتاج إلى استخدام الأحرف الصغيرة: github.com/sirupsen/logrus
. يجب تغيير أي حزمة ليست كذلك.
لإصلاح Glide، راجع هذه التعليقات. للحصول على شرح متعمق لمشكلة الغلاف، راجع هذا التعليق.
مرمزة بالألوان بشكل جيد أثناء التطوير (عندما يتم إرفاق TTY، أو مجرد نص عادي):
باستخدام log.SetFormatter(&log.JSONFormatter{})
لتسهيل التحليل بواسطة logstash أو Splunk:
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
{"level":"warning","msg":"The group's number increased tremendously!",
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
باستخدام السجل الافتراضي log.SetFormatter(&log.TextFormatter{})
عندما لا يتم إرفاق TTY، يكون الإخراج متوافقًا مع تنسيق logfmt:
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
للتأكد من هذا السلوك حتى إذا تم إرفاق جهاز TTY، قم بتعيين المنسق الخاص بك على النحو التالي:
log . SetFormatter ( & log. TextFormatter {
DisableColors : true ,
FullTimestamp : true ,
})
إذا كنت ترغب في إضافة أسلوب الاستدعاء كحقل، قم بإرشاد المسجل عبر:
log . SetReportCaller ( true )
يؤدي هذا إلى إضافة المتصل كـ "طريقة" كما يلي:
{ "animal" : " penguin " , "level" : " fatal " , "method" : " github.com/sirupsen/arcticcreatures.migrate " , "msg" : " a penguin swims by " ,
"time" : " 2014-03-10 19:57:38.562543129 -0400 EDT " }
time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
لاحظ أن هذا يضيف نفقات عامة قابلة للقياس - ستعتمد التكلفة على إصدار Go، ولكنها تتراوح بين 20 و40% في الاختبارات الأخيرة مع 1.6 و1.7. يمكنك التحقق من صحة ذلك في بيئتك عبر المعايير:
go test -bench=.*CallerTracing
تم تغيير اسم المؤسسة إلى أحرف صغيرة، ولن يتم تغيير هذا مرة أخرى. إذا كنت تواجه تعارضات في الاستيراد بسبب حساسية حالة الأحرف، فيرجى استخدام الاستيراد بالأحرف الصغيرة: github.com/sirupsen/logrus
.
إن أبسط طريقة لاستخدام Logrus هي ببساطة المُسجل المُصدَّر على مستوى الحزمة:
package main
import (
log "github.com/sirupsen/logrus"
)
func main () {
log . WithFields (log. Fields {
"animal" : "walrus" ,
}). Info ( "A walrus appears" )
}
لاحظ أنه متوافق تمامًا مع واجهة برمجة التطبيقات (api) مع مسجل stdlib، لذا يمكنك استبدال عمليات استيراد log
في كل مكان log "github.com/sirupsen/logrus"
وستتمتع الآن بمرونة Logrus. يمكنك تخصيصه كما تريد:
package main
import (
"os"
log "github.com/sirupsen/logrus"
)
func init () {
// Log as JSON instead of the default ASCII formatter.
log . SetFormatter ( & log. JSONFormatter {})
// Output to stdout instead of the default stderr
// Can be any io.Writer, see below for File example
log . SetOutput ( os . Stdout )
// Only log the warning severity or above.
log . SetLevel ( log . WarnLevel )
}
func main () {
log . WithFields (log. Fields {
"animal" : "walrus" ,
"size" : 10 ,
}). Info ( "A group of walrus emerges from the ocean" )
log . WithFields (log. Fields {
"omg" : true ,
"number" : 122 ,
}). Warn ( "The group's number increased tremendously!" )
log . WithFields (log. Fields {
"omg" : true ,
"number" : 100 ,
}). Fatal ( "The ice breaks!" )
// A common pattern is to re-use fields between logging statements by re-using
// the logrus.Entry returned from WithFields()
contextLogger := log . WithFields (log. Fields {
"common" : "this is a common field" ,
"other" : "I also should be logged always" ,
})
contextLogger . Info ( "I'll be logged with common and other field" )
contextLogger . Info ( "Me too" )
}
لمزيد من الاستخدام المتقدم، مثل تسجيل الدخول إلى مواقع متعددة من نفس التطبيق، يمكنك أيضًا إنشاء مثيل لـ logrus
Logger:
package main
import (
"os"
"github.com/sirupsen/logrus"
)
// Create a new instance of the logger. You can have any number of instances.
var log = logrus . New ()
func main () {
// The API for setting attributes is a little different than the package level
// exported logger. See Godoc.
log . Out = os . Stdout
// You could set this to any `io.Writer` such as a file
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
// if err == nil {
// log.Out = file
// } else {
// log.Info("Failed to log to file, using default stderr")
// }
log . WithFields (logrus. Fields {
"animal" : "walrus" ,
"size" : 10 ,
}). Info ( "A group of walrus emerges from the ocean" )
}
يشجع Logrus التسجيل الدقيق والمنظم من خلال حقول التسجيل بدلاً من رسائل الخطأ الطويلة غير القابلة للتحليل. على سبيل المثال، بدلاً من: log.Fatalf("Failed to send event %s to topic %s with key %d")
، يجب عليك تسجيل ما هو أكثر قابلية للاكتشاف:
log . WithFields (log. Fields {
"event" : event ,
"topic" : topic ,
"key" : key ,
}). Fatal ( "Failed to send event" )
لقد وجدنا أن واجهة برمجة التطبيقات هذه تجبرك على التفكير في تسجيل الدخول بطريقة تنتج رسائل تسجيل أكثر فائدة. لقد مررنا بمواقف لا حصر لها حيث كان مجرد إضافة حقل واحد إلى بيان السجل الموجود بالفعل سيوفر لنا ساعات. استدعاء WithFields
اختياري.
بشكل عام، عند استخدام Logrus لأي من وظائف printf
-family، يجب أن يُنظر إليه على أنه تلميح يجب عليك إضافة حقل، ومع ذلك، لا يزال بإمكانك استخدام وظائف printf
-family مع Logrus.
غالبًا ما يكون من المفيد إرفاق الحقول دائمًا ببيانات السجل في تطبيق ما أو أجزاء منه. على سبيل المثال، قد ترغب دائمًا في تسجيل request_id
و user_ip
في سياق الطلب. بدلاً من كتابة log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
في كل سطر، يمكنك إنشاء logrus.Entry
للتمرير بدلاً من ذلك:
requestLogger := log . WithFields (log. Fields { "request_id" : request_id , "user_ip" : user_ip })
requestLogger. Info ( "something happened on that request" ) # will log request_id and user_ip
requestLogger . Warn ( "something not great happened" )
يمكنك إضافة خطافات لمستويات التسجيل. على سبيل المثال، لإرسال الأخطاء إلى خدمة تتبع الاستثناءات على Error
و Fatal
و Panic
أو المعلومات إلى StatsD أو تسجيل الدخول إلى أماكن متعددة في وقت واحد، على سبيل المثال، سجل النظام.
يأتي Logrus مع خطافات مدمجة. أضف تلك، أو خطافك المخصص، في init
:
import (
log "github.com/sirupsen/logrus"
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
"log/syslog"
)
func init () {
// Use the Airbrake hook to report errors that have Error severity or above to
// an exception tracker. You can create custom hooks, see the Hooks section.
log . AddHook ( airbrake . NewHook ( 123 , "xyz" , "production" ))
hook , err := logrus_syslog . NewSyslogHook ( "udp" , "localhost:514" , syslog . LOG_INFO , "" )
if err != nil {
log . Error ( "Unable to connect to local syslog daemon" )
} else {
log . AddHook ( hook )
}
}
ملاحظة: يدعم خطاف Syslog أيضًا الاتصال بسجل النظام المحلي (على سبيل المثال "/dev/log" أو "/var/run/syslog" أو "/var/run/log"). للحصول على التفاصيل، يرجى التحقق من رابط سجل النظام README.
يمكن العثور على قائمة بخطافات الخدمة المعروفة حاليًا في صفحة الويكي هذه
يحتوي Logrus على سبعة مستويات للتسجيل: التتبع والتصحيح والمعلومات والتحذير والخطأ والفادح والذعر.
log . Trace ( "Something very low level." )
log . Debug ( "Useful debugging information." )
log . Info ( "Something noteworthy happened!" )
log . Warn ( "You should probably take a look at this." )
log . Error ( "Something failed but I'm not quitting." )
// Calls os.Exit(1) after logging
log . Fatal ( "Bye." )
// Calls panic() after logging
log . Panic ( "I'm bailing." )
يمكنك تعيين مستوى التسجيل على Logger
، ثم سيقوم فقط بتسجيل الإدخالات بهذه الخطورة أو أي شيء أعلى منها:
// Will log anything that is info or above (warn, error, fatal, panic). Default.
log . SetLevel ( log . InfoLevel )
قد يكون من المفيد تعيين log.Level = logrus.DebugLevel
في بيئة تصحيح الأخطاء أو البيئة المطولة إذا كان تطبيقك يحتوي على ذلك.
ملاحظة: إذا كنت تريد مستويات سجل مختلفة للتسجيل العام ( log.SetLevel(...)
) وسجل النظام، فيرجى التحقق من ربط سجل النظام README.
إلى جانب الحقول المضافة باستخدام WithField
أو WithFields
تتم إضافة بعض الحقول تلقائيًا إلى جميع أحداث التسجيل:
time
. الطابع الزمني الذي تم فيه إنشاء الإدخال.msg
. تم تمرير رسالة التسجيل إلى {Info,Warn,Error,Fatal,Panic}
بعد استدعاء AddFields
. على سبيل المثال، Failed to send event.
level
. مستوى التسجيل. info
على سبيل المثال. Logrus ليس لديه فكرة عن البيئة.
إذا كنت ترغب في استخدام الخطافات والمنسقات في بيئات محددة فقط، فيجب عليك التعامل مع ذلك بنفسك. على سبيل المثال، إذا كان تطبيقك يحتوي على متغير عام Environment
، وهو عبارة عن سلسلة تمثل البيئة، فيمكنك القيام بما يلي:
import (
log "github.com/sirupsen/logrus"
)
func init () {
// do something here to set environment depending on an environment variable
// or command-line flag
if Environment == "production" {
log . SetFormatter ( & log. JSONFormatter {})
} else {
// The TextFormatter is default, you don't actually have to do this.
log . SetFormatter ( & log. TextFormatter {})
}
}
هذا التكوين هو الطريقة التي تم بها استخدام logrus
، ولكن JSON في الإنتاج يكون مفيدًا في الغالب فقط إذا كنت تقوم بتجميع السجلات باستخدام أدوات مثل Splunk أو Logstash.
تنسيقات التسجيل المضمنة هي:
logrus.TextFormatter
. يُسجل الحدث بالألوان إذا كان stdout عبارة عن tty، وإلا بدون ألوان.ForceColors
على true
. لفرض عدم وجود إخراج ملون حتى في حالة وجود TTY، قم بتعيين الحقل DisableColors
على true
. بالنسبة لنظام التشغيل Windows، راجع github.com/mattn/go-colorable.DisableLevelTruncation
إلى true
.PadLevelText
إلى true
إلى تمكين هذا السلوك، وذلك عن طريق إضافة مساحة حشو إلى نص المستوى.logrus.JSONFormatter
. يسجل الحقول كـ JSON.تنسيقات تسجيل الطرف الثالث:
FluentdFormatter
. إدخالات التنسيقات التي يمكن تحليلها بواسطة Kubernetes وGoogle Container Engine.GELF
. تنسيقات الإدخالات بحيث تتوافق مع مواصفات Graylog's GELF 1.1.logstash
. حقول السجلات كأحداث Logstash.prefixed
. يعرض مصدر إدخال السجل مع التخطيط البديل.zalgo
. استدعاء قوة Zalgo.nested-logrus-formatter
. تحويل حقول logrus إلى بنية متداخلة.powerful-logrus-formatter
. الحصول على اسم الملف ورقم سطر السجل واسم الوظيفة الأحدث عند طباعة السجل؛ حفظ السجل إلى الملفات.caption-json-formatter
. تمت إضافة تنسيق json لرسالة logrus مع تسمية توضيحية يمكن قراءتها بواسطة الإنسان. يمكنك تحديد المنسق الخاص بك عن طريق تنفيذ واجهة Formatter
، التي تتطلب طريقة Format
. يأخذ Format
*Entry
. entry.Data
هي نوع Fields
( map[string]interface{}
) مع جميع الحقول الخاصة بك بالإضافة إلى الحقول الافتراضية (راجع قسم الإدخالات أعلاه):
type MyJSONFormatter struct {
}
log . SetFormatter ( new ( MyJSONFormatter ))
func ( f * MyJSONFormatter ) Format ( entry * Entry ) ([] byte , error ) {
// Note this doesn't include Time, Level and Message which are available on
// the Entry. Consult `godoc` on information about those fields or read the
// source of the official loggers.
serialized , err := json . Marshal ( entry . Data )
if err != nil {
return nil , fmt . Errorf ( "Failed to marshal fields to JSON, %w" , err )
}
return append ( serialized , 'n' ), nil
}
io.Writer
يمكن تحويل Logrus إلى io.Writer
. هذا الكاتب هو نهاية io.Pipe
وتقع على عاتقك مسؤولية إغلاقه.
w := logger . Writer ()
defer w . Close ()
srv := http. Server {
// create a stdlib log.Logger that writes to
// logrus.Logger.
ErrorLog : log . New ( w , "" , 0 ),
}
ستتم طباعة كل سطر مكتوب لذلك الكاتب بالطريقة المعتادة، باستخدام المنسقات والخطافات. مستوى تلك الإدخالات هو info
.
هذا يعني أنه يمكننا تجاوز مسجل المكتبة القياسي بسهولة:
logger := logrus . New ()
logger . Formatter = & logrus. JSONFormatter {}
// Use logrus for standard log output
// Note that `log` here references stdlib's log
// Not logrus imported under the name `log`.
log . SetOutput ( logger . Writer ())
لا يتم توفير تدوير السجل مع Logrus. يجب أن يتم تدوير السجل بواسطة برنامج خارجي (مثل logrotate(8)
) يمكنه ضغط وحذف إدخالات السجل القديمة. لا ينبغي أن يكون سمة من سمات المسجل على مستوى التطبيق.
أداة | وصف |
---|---|
لوجروس ماتي | Logrus mate هي أداة لـ Logrus لإدارة أدوات قطع الأشجار، ويمكنك تهيئة مستوى المسجل والربط والمنسق من خلال ملف التكوين، وسيتم إنشاء المسجل بتكوينات مختلفة في بيئات مختلفة. |
مساعد لوجروس فايبر | مساعد حول Logrus للالتفاف باستخدام spf13/Viper لتحميل التكوين بالأنياب! ولتبسيط تكوين Logrus، استخدم بعض سلوكيات Logrus Mate. عينة |
يحتوي Logrus على وسيلة مدمجة لتأكيد وجود رسائل السجل. يتم تنفيذ ذلك من خلال خطاف test
ويوفر:
test.NewLocal
و test.NewGlobal
) والتي تضيف بشكل أساسي خطاف test
test.NewNullLogger
) يقوم فقط بتسجيل رسائل السجل (ولا يقوم بإخراج أي منها): import (
"github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"testing"
)
func TestSomething ( t * testing. T ){
logger , hook := test . NewNullLogger ()
logger . Error ( "Helloerror" )
assert . Equal ( t , 1 , len ( hook . Entries ))
assert . Equal ( t , logrus . ErrorLevel , hook . LastEntry (). Level )
assert . Equal ( t , "Helloerror" , hook . LastEntry (). Message )
hook . Reset ()
assert . Nil ( t , hook . LastEntry ())
}
يمكن لـ Logrus تسجيل وظيفة واحدة أو أكثر سيتم استدعاؤها عند تسجيل أي رسالة ذات مستوى fatal
. سيتم تنفيذ المعالجات المسجلة قبل أن يقوم logrus بتنفيذ os.Exit(1)
. قد يكون هذا السلوك مفيدًا إذا احتاج المتصلون إلى إيقاف التشغيل بأمان. على عكس مكالمة panic("Something went wrong...")
والتي يمكن اعتراضها من خلال استدعاء recover
مؤجل إلى os.Exit(1)
لا يمكن اعتراضها.
...
handler := func() {
// gracefully shutdown something...
}
logrus.RegisterExitHandler(handler)
...
بشكل افتراضي، يكون المسجل محميًا بواسطة كائن المزامنة (mutex) للكتابة المتزامنة. يتم الاحتفاظ بكائن المزامنة (mutex) عند استدعاء الخطافات وكتابة السجلات. إذا كنت متأكدًا من عدم الحاجة إلى هذا القفل، فيمكنك استدعاء logger.SetNoLock() لتعطيل القفل.
تتضمن الحالة التي لا تكون فيها الحاجة إلى القفل ما يلي:
ليس لديك أي خطافات مسجلة، أو أن استدعاء الخطافات آمن بالفعل.
تعتبر الكتابة إلى logger.Out آمنة بالفعل لمؤشر الترابط، على سبيل المثال:
logger.Out محمي بالأقفال.
logger.Out هو معالج os.File مفتوح بعلامة O_APPEND
، وكل كتابة أصغر من 4 كيلو بايت. (وهذا يسمح بالكتابة متعددة الخيوط/متعددة العمليات)
(ارجع إلى http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)