您现在的位置是:网站首页> 编程资料编程资料
logrus hook输出日志到本地磁盘的操作_Golang_
2023-05-26
522人已围观
简介 logrus hook输出日志到本地磁盘的操作_Golang_
logrus是go的一个日志框架,它最让人激动的应该是hook机制,可以在初始化时为logrus添加hook,logrus可以实现各种扩展功能,可以将日志输出到elasticsearch和activemq等中间件去,甚至可以输出到你的email和叮叮中去,不要问为为什么可以发现可以输入到叮叮中去,都是泪,手动笑哭!
言归正传,这里就简单的通过hook机制将文件输出到本地磁盘。
首先
go get github.com/sirupsen/logrus
然后
logrus和go lib里面一样有6个等级,可以直接调用
logrus.Debug("Useful debugging information.") logrus.Info("Something noteworthy happened!") logrus.Warn("You should probably take a look at this.") logrus.Error("Something failed but I'm not quitting.") logrus.Fatal("Bye.") //log之后会调用os.Exit(1) logrus.Panic("I'm bailing.") //log之后会panic()项目例子结构

main.go
package main import ( "fmt" "github.com/sirupsen/logrus" "logT/logS" ) func main() { //创建一个hook,将日志存储路径输入进去 hook := logS.NewHook("d:/log/golog.log") //加载hook之前打印日志 logrus.WithField("file", "d:/log/golog.log").Info("New logrus hook err.") logrus.AddHook(hook) //加载hook之后打印日志 logrus.WithFields(logrus.Fields{ "animal": "walrus", }).Info("A walrus appears") } hook.go
不要看下面三个go文件代码很长,其实大多数都是固定代码,也就NewHook函数自己扩展定义就好
package logS
import ( "fmt" "github.com/sirupsen/logrus" "os" "strings" ) // Hook 写文件的Logrus Hook type Hook struct { W LoggerInterface } func NewHook(file string) (f *Hook) { w := NewFileWriter() config := fmt.Sprintf(`{"filename":"%s","maxdays":7}`, file) err := w.Init(config) if err != nil { return nil } return &Hook{w} } // Fire 实现Hook的Fire接口 func (hook *Hook) Fire(entry *logrus.Entry) (err error) { message, err := getMessage(entry) if err != nil { fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) return err } switch entry.Level { case logrus.PanicLevel: fallthrough case logrus.FatalLevel: fallthrough case logrus.ErrorLevel: return hook.W.WriteMsg(fmt.Sprintf("[ERROR] %s", message), LevelError) case logrus.WarnLevel: return hook.W.WriteMsg(fmt.Sprintf("[WARN] %s", message), LevelWarn) case logrus.InfoLevel: return hook.W.WriteMsg(fmt.Sprintf("[INFO] %s", message), LevelInfo) case logrus.DebugLevel: return hook.W.WriteMsg(fmt.Sprintf("[DEBUG] %s", message), LevelDebug) default: return nil } } // Levels 实现Hook的Levels接口 func (hook *Hook) Levels() []logrus.Level { return []logrus.Level{ logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel, logrus.WarnLevel, logrus.InfoLevel, logrus.DebugLevel, } } func getMessage(entry *logrus.Entry) (message string, err error) { message = message + fmt.Sprintf("%s ", entry.Message) file, lineNumber := GetCallerIgnoringLogMulti(2) if file != "" { sep := fmt.Sprintf("%s/src/", os.Getenv("GOPATH")) fileName := strings.Split(file, sep) if len(fileName) >= 2 { file = fileName[1] } } message = fmt.Sprintf("%s:%d ", file, lineNumber) + message for k, v := range entry.Data { message = message + fmt.Sprintf("%v:%v ", k, v) } return } caller.go
package logS import ( "runtime" "strings" ) func GetCaller(callDepth int, suffixesToIgnore ...string) (file string, line int) { // bump by 1 to ignore the getCaller (this) stackframe callDepth++ outer: for { var ok bool _, file, line, ok = runtime.Caller(callDepth) if !ok { file = "???" line = 0 break } for _, s := range suffixesToIgnore { if strings.HasSuffix(file, s) { callDepth++ continue outer } } break } return } // GetCallerIgnoringLogMulti TODO func GetCallerIgnoringLogMulti(callDepth int) (string, int) { // the +1 is to ignore this (getCallerIgnoringLogMulti) frame return GetCaller(callDepth+1, "logrus/hooks.go", "logrus/entry.go", "logrus/logger.go", "logrus/exported.go", "asm_amd64.s") } file.go
package logS import ( "encoding/json" "errors" "fmt" "io/ioutil" "log" "os" "path/filepath" "strings" "sync" "time" ) // RFC5424 log message levels. const ( LevelError = iota LevelWarn LevelInfo LevelDebug ) // LoggerInterface Logger接口 type LoggerInterface interface { Init(config string) error WriteMsg(msg string, level int) error Destroy() Flush() } // LogWriter implements LoggerInterface. // It writes messages by lines limit, file size limit, or time frequency. type LogWriter struct { *log.Logger mw *MuxWriter // The opened file Filename string `json:"filename"` Maxlines int `json:"maxlines"` maxlinesCurlines int // Rotate at size Maxsize int `json:"maxsize"` maxsizeCursize int // Rotate daily Daily bool `json:"daily"` Maxdays int64 `json:"maxdays"` dailyOpendate int Rotate bool `json:"rotate"` startLock sync.Mutex // Only one log can write to the file Level int `json:"level"` } // MuxWriter an *os.File writer with locker. type MuxWriter struct { sync.Mutex fd *os.File } // write to os.File. func (l *MuxWriter) Write(b []byte) (int, error) { l.Lock() defer l.Unlock() return l.fd.Write(b) } // SetFd set os.File in writer. func (l *MuxWriter) SetFd(fd *os.File) { if l.fd != nil { _ = l.fd.Close() } l.fd = fd } // NewFileWriter create a FileLogWriter returning as LoggerInterface. func NewFileWriter() LoggerInterface { w := &LogWriter{ Filename: "", Maxlines: 1000000, Maxsize: 1 << 28, //256 MB Daily: true, Maxdays: 7, Rotate: true, Level: LevelDebug, } // use MuxWriter instead direct use os.File for lock write when rotate w.mw = new(MuxWriter) // set MuxWriter as Logger's io.Writer w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) return w } // Init file logger with json config. // jsonconfig like: // { // "filename":"logs/sample.log", // "maxlines":10000, // "maxsize":1<<30, // "daily":true, // "maxdays":15, // "rotate":true // } func (w *LogWriter) Init(jsonconfig string) error { err := json.Unmarshal([]byte(jsonconfig), w) if err != nil { return err } if len(w.Filename) == 0 { return errors.New("jsonconfig must have filename") } err = w.startLogger() return err } // start file logger. create log file and set to locker-inside file writer. func (w *LogWriter) startLogger() error { fd, err := w.createLogFile() if err != nil { return err } w.mw.SetFd(fd) err = w.initFd() if err != nil { return err } return nil } func (w *LogWriter) docheck(size int) { w.startLock.Lock() defer w.startLock.Unlock() if w.Rotate && ((w.Maxlines > 0 && w.maxlinesCurlines >= w.Maxlines) || (w.Maxsize > 0 && w.maxsizeCursize >= w.Maxsize) || (w.Daily && time.Now().Day() != w.dailyOpendate)) { if err := w.DoRotate(); err != nil { fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) return } } w.maxlinesCurlines++ w.maxsizeCursize += size } // WriteMsg write logger message into file. func (w *LogWriter) WriteMsg(msg string, level int) error { if level > w.Level { return nil } n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] " w.docheck(n) w.Logger.Print(msg) return nil } func (w *LogWriter) createLogFile() (*os.File, error) { // Open the log file fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) return fd, err } func (w *LogWriter) initFd() error { fd := w.mw.fd finfo, err := fd.Stat() if err != nil { return fmt.Errorf("get stat err: %s", err) } w.maxsizeCursize = int(finfo.Size()) w.dailyOpendate = time.Now().Day() if finfo.Size() > 0 { content, err := ioutil.ReadFile(w.Filename) if err != nil { return err } w.maxlinesCurlines = len(strings.Split(string(content), "\n")) } else { w.maxlinesCurlines = 0 } return nil } // DoRotate means it need to write file in new file. // new file name like xx.log.2013-01-01.2 func (w *LogWriter) DoRotate() error { _, err := os.Lstat(w.Filename) if err == nil { // file exists // Find the next available number num := 1 fname := "" for ; err == nil && num <= 999; num++ { fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num) _, err = os.Lstat(fname) } // return error if the last file checked still existed if err == nil { return fmt.Errorf("Rotate: Cannot find free log number to rename %s", w.Filename) } // block Logger's io.Writer w.mw.Lock() defer w.mw.Unlock() fd := w.mw.fd _ = fd.Close() // close fd before rename // Rename the file to its newfound home err = os.Rename(w.Filename, fname) if err != nil { return fmt.Errorf("Rotate: %s", err) } // re-start logger err = w.startLogger() if err != nil { return fmt.Errorf("Rotate StartLogger: %s", err) } go w.deleteOldLog() } return nil } func (w *LogWriter) deleteOldLog() { dir := filepath.Dir(w.Filename) _ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) { defer func() { if r := recover(); r != nil { returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r) fmt.Println(returnErr) } }() if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) { if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) { _ = os.Remove(path) } } return }) } // Destroy destroy file logger, close file writer. func (w *LogWriter) Destroy() { _ = w.mw.fd.Close() } // Flush file logger. // there are no buffering messages in file logger in memory. // flush file means sync file from disk. func (w *LogWriter) Flush() { _ = w.mw.fd.Sync() } 补充知识:golang logrus自定义hook:日志切片hook、邮件警报hook、kafkahook
logrus Hook 分析
logrus hook 接口定义很简单。如下
package logrus // A hook to be fired when logging on the logging levels returned from // `Levels()` on your implementation of the interface. Note that this is not // fired in a goroutine or a channel with workers, you should handle such // functionality yourself if your call is non-blocking and you don't wish for // the logging calls for levels returned from `Levels()` to block. type Hook interface { Levels() []Level Fire(*Entry) error } // Internal type for storing the hooks on
相关内容
- go日志系统logrus显示文件和行号的操作_Golang_
- logrus日志自定义格式操作_Golang_
- 详解Golang中Channel的用法_Golang_
- golang中的三个点 '...'的用法示例详解_Golang_
- 破解IDEA(Goland)注册码设置 license server一直有效不过期的过程详解_Golang_
- Go+Vue开发一个线上外卖应用的流程(用户名密码和图形验证码)_Golang_
- 部署Go语言项目的 N 种方法(小结)_Golang_
- goland2020.2.x永久激活码破解详细教程亲测可用(Windows Linux Mac)_Golang_
- golang使用 gomodule 在公共测试环境管理go的依赖的实例详解_Golang_
- golang一些常用的静态检查工具详解_Golang_
点击排行
本栏推荐
