flash网站动画,郑州seo公司排名,电商平台业务流程图,给wordpress博客加上一个娃娃关于日志的一些问题: 单个文件过大会影响写入效率#xff0c;所以会做拆分#xff0c;但是到多大拆分? 最多保留几个日志文件#xff1f;最多保留多少天#xff0c;要不要做压缩处理#xff1f; 一般都使用 lumberjack[1]这个库完成上述这些操作 lumberjack //info文件wr… 关于日志的一些问题: 单个文件过大会影响写入效率所以会做拆分但是到多大拆分? 最多保留几个日志文件最多保留多少天要不要做压缩处理 一般都使用 lumberjack[1]这个库完成上述这些操作 lumberjack //info文件writeSyncer infoFileWriteSyncer : zapcore.AddSync(lumberjack.Logger{ Filename: ./log/info.log, //日志文件存放目录如果文件夹不存在会自动创建 MaxSize: 2, //文件大小限制,单位MB MaxBackups: 100, //最大保留日志文件数量 MaxAge: 30, //日志文件保留天数 Compress: false, //是否压缩处理 }) infoFileCore : zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer, zapcore.AddSync(os.Stdout)), lowPriority) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志 //error文件writeSyncer errorFileWriteSyncer : zapcore.AddSync(lumberjack.Logger{ Filename: ./log/error.log, //日志文件存放目录 MaxSize: 1, //文件大小限制,单位MB MaxBackups: 5, //最大保留日志文件数量 MaxAge: 30, //日志文件保留天数 Compress: false, //是否压缩处理 }) 测试日志到达指定大小后自动会切分 例如当info级别的日志文件到达2M时会根据当时的时间戳切分出一个info-2023-04-13T05-27-18.296.log。 后续新写入的info级别的日志将写入到info.log直到又到达2M继续会切分。 测试日志到达指定最大保留日志文件数量后将作何操作 清掉log文件夹修改error日志配置: errorFileWriteSyncer : zapcore.AddSync(lumberjack.Logger{ Filename: ./log/error.log, //日志文件存放目录 MaxSize: 1, //文件大小限制,单位MB MaxBackups: 3, //最大保留日志文件数量 MaxAge: 30, //日志文件保留天数 Compress: false, //是否压缩处理 }) 代码中只打印error日志执行代码 进行观察 继续执行 继续执行 可见最早拆分出的那个error-2023-04-13T05-40-48.715.log文件不见了~ 继续执行切分出来的文件数量也会始终保持3个 完整变化图: 测试压缩处理的效果 清掉log文件夹修改error日志配置: errorFileWriteSyncer : zapcore.AddSync(lumberjack.Logger{ Filename: ./log/error.log, //日志文件存放目录 MaxSize: 5, //文件大小限制,单位MB MaxBackups: 10, //最大保留日志文件数量 MaxAge: 30, //日志文件保留天数 Compress: false, //是否压缩处理 }) 代码中只打印error日志执行代码循环10000000次 进行观察 不压缩共占用814M存储空间 清掉log文件夹修改Compress字段为true执行代码 启用压缩后仅占用了30M磁盘空间 不太好的地方就是不方便直接查看了需要解压后查看。但大大省了所占用的空间 golang zap日志库使用[2] lumberjack这个库目前只支持按文件大小切割(按时间切割效率低且不能保证日志数据不被破坏,详情见https://github.com/natefinch/lumberjack/issues/54 想按日期切割可以使用github.com/lestrrat-go/file-rotatelogs[3]这个库(目前不维护了) file-rotatelogs实现按时间的切割 注意: github.com/lestrrat-go/file-rotatelogs[4](2021年后不更新了) 和 github.com/lestrrat/go-file-rotatelogs[5](2018年以后就不更新了) 两个不一样。。前面那个是更新的作者是一个人... (有一个linux系统上的日志工具也叫logrotate) logrotate 是一个用于日志文件轮换的 Go 语言库支持按时间轮换、按文件大小轮换和按行数轮换。还支持在轮换时压缩文件、删除旧文件、给文件添加时间戳等功能 用zap和go-file-rotatelogs实现日志的记录和日志按时间分割[6] WithRotationCount和WithMaxAge两个选项不能共存只能设置一个(都设置编译时不会出错但运行时会报错。也是为了防止影响切分的处理逻辑): panic: options MaxAge and RotationCount cannot be both set package mainimport ( fmt io net/http time rotatelogs github.com/lestrrat-go/file-rotatelogs go.uber.org/zap go.uber.org/zap/zapcore)// 使用file-rotatelogs做切分var sugarLogger *zap.SugaredLoggerfunc main() { fmt.Println(shuang提示:begin main) InitLogger() defer sugarLogger.Sync() for i : 0; i 100000; i { simpleHttpGet(www.cnblogs.com) simpleHttpGet(https://www.baidu.com) }}// 例子http访问url,返回状态func simpleHttpGet(url string) { fmt.Println(begin simpleHttpGet: url) sugarLogger.Debugf(Trying to hit GET request for %s, url) resp, err : http.Get(url) if err ! nil { sugarLogger.Errorf(Error fetching URL %s : Error %s, url, err) } else { sugarLogger.Infof(Success! statusCode %s for URL %s, resp.Status, url) resp.Body.Close() }}func InitLogger() { encoder : getEncoder() //两个interface,判断日志等级 //warnlevel以下归到info日志 infoLevel : zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl zapcore.WarnLevel }) //warnlevel及以上归到warn日志 warnLevel : zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl zapcore.WarnLevel }) infoWriter : getLogWriter(/Users/fliter/zap-demo/demo2-log/info) warnWriter : getLogWriter(/Users/fliter/zap-demo/demo2-log/warn) //创建zap.Core,for logger core : zapcore.NewTee( zapcore.NewCore(encoder, infoWriter, infoLevel), zapcore.NewCore(encoder, warnWriter, warnLevel), ) //生成Logger logger : zap.New(core, zap.AddCaller()) sugarLogger logger.Sugar()}// getEncoderfunc getEncoder() zapcore.Encoder { encoderConfig : zap.NewProductionEncoderConfig() encoderConfig.EncodeTime zapcore.ISO8601TimeEncoder encoderConfig.EncodeLevel zapcore.CapitalLevelEncoder return zapcore.NewConsoleEncoder(encoderConfig)}// 得到LogWriterfunc getLogWriter(filePath string) zapcore.WriteSyncer { warnIoWriter : getWriter(filePath) return zapcore.AddSync(warnIoWriter)}// 日志文件切割func getWriter(filename string) io.Writer { //保存日志30天每1分钟分割一次日志 hook, err : rotatelogs.New( filename_%Y-%m-%d %H:%M:%S.log, // 为最新的日志建立软连接指向最新日志文件 rotatelogs.WithLinkName(filename), // 清理条件 将已切割的日志文件按条件(数量or时间)直接删除 //--- MaxAge and RotationCount cannot be both set 两者不能同时设置 //--- RotationCount用来设置最多切割的文件数(超过的会被 从旧到新 清理) //--- MaxAge 是设置文件清理前的最长保存时间 最小分钟为单位 //--- if both are 0, give maxAge a default 7 * 24 * time.Hour // WithRotationCount和WithMaxAge两个选项不能共存只能设置一个(都设置编译时不会出错但运行时会报错。也是为了防止影响切分的处理逻辑) //rotatelogs.WithRotationCount(10), // 超过这个数的文件会被清掉 rotatelogs.WithMaxAge(time.Hour*24*30), // 保存多久(设置文件清理前的最长保存时间 最小分钟为单位) // 切分条件(将日志文件做切割WithRotationTime and WithRotationSize ~~两者任意一个条件达到都会切割~~) // 经过亲测后发现如果日志没有持续增加WithRotationTime设置较小(如10s)并不会按WithRotationTime频次切分文件。当日志不停增加时会按照WithRotationTime设置来切分(即便WithRotationTime设置的很小) rotatelogs.WithRotationTime(time.Second*10), // 10秒分割一次(设置日志切割时间间隔,默认 24 * time.Hour) rotatelogs.WithRotationSize(int64(1*1024*1024*1024)), // 文件达到多大则进行切割单位为 bytes ) if err ! nil { panic(err) } return hook} 验证其切分功能: 将触发切分的文件大小设置得很大(110241024*1024 Byte即1 GB)切分时间设置得较小(10秒分割一次),执行代码可以观察到日志文件的变化: 再将触发切分的文件大小设置得很小(1102450 Byte即50 KB)切分时间设置得较大(24h分割一次),执行代码清掉之前的日志再观察到日志文件的变化: 将触发切分的文件大小设置得很小(1102435 Byte即35 KB)同时切分时间也设置得很小(10s分割一次),执行代码清掉之前的日志再观察到日志文件的变化: 当前日志容量大于配置的容量时会生成新的日志文件如果时间一样在时间后缀后面会自动加上一个数字后缀以此区分同一时间的不同日志文件如果时间不一样则生成新的时间后缀文件 (golang实现分割日志[7]) 日志文件中是会出现有的命中时间规则有的命中文件大小规则的情况两者命名格式不同参考上图 切分之后执行压缩命令 默认是没有的不像lumberjack那样提供Compress选项 前面所提的还支持在轮换时压缩文件、删除旧文件、给文件添加时间戳等功能需要自己实现。 提供了一个WithHandler回调函数发生切分后会触发该函数可以在其中进项压缩等操作 改一下代码(不再请求网站因为速度太慢直接在for里面写日志) 不启用压缩 启用压缩效果显著: 相关代码: package mainimport ( archive/zip fmt io net/http os path/filepath reflect time github.com/davecgh/go-spew/spew rotatelogs github.com/lestrrat-go/file-rotatelogs go.uber.org/zap go.uber.org/zap/zapcore)// 使用file-rotatelogs做切分var sugarLogger *zap.SugaredLoggerfunc main() { fmt.Println(shuang提示:begin main) InitLogger() defer sugarLogger.Sync() for i : 0; i 10000000; i { sugarLogger.Infof(测试压缩后少占用的空间这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本这是填充文本,i is %d, i) //simpleHttpGet(www.cnblogs.com, i) //simpleHttpGet(https://www.baidu.com, i) } time.Sleep(10000e9)}// 例子http访问url,返回状态func simpleHttpGet(url string, i int) { //fmt.Println(begin simpleHttpGet: url) sugarLogger.Debugf(Trying to hit GET request for %s, i is %d, url, i) resp, err : http.Get(url) if err ! nil { sugarLogger.Errorf(Error fetching URL %s : Error %s, i is %d, url, err, i) } else { sugarLogger.Infof(Success! statusCode %s for URL %s,i is %d, resp.Status, url, i) resp.Body.Close() }}func InitLogger() { encoder : getEncoder() //两个interface,判断日志等级 //warnlevel以下归到info日志 infoLevel : zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl zapcore.WarnLevel }) //warnlevel及以上归到warn日志 warnLevel : zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl zapcore.WarnLevel }) infoWriter : getLogWriter(/Users/fliter/zap-demo/demo2-log/info) warnWriter : getLogWriter(/Users/fliter/zap-demo/demo2-log/warn) //创建zap.Core,for logger core : zapcore.NewTee( zapcore.NewCore(encoder, infoWriter, infoLevel), zapcore.NewCore(encoder, warnWriter, warnLevel), ) //生成Logger logger : zap.New(core, zap.AddCaller()) sugarLogger logger.Sugar()}// getEncoderfunc getEncoder() zapcore.Encoder { encoderConfig : zap.NewProductionEncoderConfig() encoderConfig.EncodeTime zapcore.ISO8601TimeEncoder encoderConfig.EncodeLevel zapcore.CapitalLevelEncoder return zapcore.NewConsoleEncoder(encoderConfig)}// 得到LogWriterfunc getLogWriter(filePath string) zapcore.WriteSyncer { warnIoWriter : getWriter(filePath) return zapcore.AddSync(warnIoWriter)}// 日志文件切割func getWriter(filename string) io.Writer { //保存日志30天每1分钟分割一次日志 hook, err : rotatelogs.New( filename_%Y-%m-%d %H:%M:%S.log, // 为最新的日志建立软连接指向最新日志文件 rotatelogs.WithLinkName(filename), // 清理条件 将已切割的日志文件按条件(数量or时间)直接删除 //--- MaxAge and RotationCount cannot be both set 两者不能同时设置 //--- RotationCount用来设置最多切割的文件数(超过的会被 从旧到新 清理) //--- MaxAge 是设置文件清理前的最长保存时间 最小分钟为单位 //--- if both are 0, give maxAge a default 7 * 24 * time.Hour // WithRotationCount和WithMaxAge两个选项不能共存只能设置一个(都设置编译时不会出错但运行时会报错。也是为了防止影响切分的处理逻辑) //rotatelogs.WithRotationCount(10), // 超过这个数的文件会被清掉 rotatelogs.WithMaxAge(time.Hour*24*30), // 保存多久(设置文件清理前的最长保存时间 最小分钟为单位) // 切分条件(将日志文件做切割WithRotationTime and WithRotationSize ~~两者任意一个条件达到都会切割~~) // 经过亲测后发现如果日志没有持续增加WithRotationTime设置较小(如10s)并不会按WithRotationTime频次切分文件。当日志不停增加时会按照WithRotationTime设置来切分(即便WithRotationTime设置的很小) rotatelogs.WithRotationTime(time.Second*10), // 10秒分割一次(设置日志切割时间间隔,默认 24 * time.Hour) rotatelogs.WithRotationSize(int64(1*1024*35000*1024)), // 文件达到多大则进行切割单位为 bytes // 其他可选配置 //default: rotatelogs.Local ,you can set rotatelogs.UTC //rotatelogs.WithClock(rotatelogs.UTC), //rotatelogs.WithLocation(time.Local), //--- 当rotatelogs.New()创建的文件存在时强制创建新的文件 命名为原文件的名称序号如a.log存在则创建创建 a.log.1 //rotatelogs.ForceNewFile(), rotatelogs.WithHandler(rotatelogs.Handler(rotatelogs.HandlerFunc(func(e rotatelogs.Event) { if e.Type() ! rotatelogs.FileRotatedEventType { return } fmt.Println(切割完成进行打包压缩操作) spew.Dump(e is:, e) prevFile : e.(*rotatelogs.FileRotatedEvent).PreviousFile() if prevFile ! { // 进行压缩 paths, fileName : filepath.Split(prevFile) //_ paths //err : Zip(archive.zip, paths, prevFile) err : ZipFiles(pathsfileName.zip, []string{prevFile}) fmt.Println(err is, err) if err nil { os.RemoveAll(prevFile) } } fmt.Println(e的类型为:, reflect.TypeOf(e)) fmt.Println(------------------) fmt.Println() fmt.Println() fmt.Println() //ctx : CleanContext{ // Dir: LogsConfig.LogOutputDir, // DirMaxSizeG: LogsConfig.LogDirMaxSizeG, // DirMaxCount: LogsConfig.LogDirMaxFileCount, //} //strategyOne : CleanStrategyOne{} //result, err : NewCleanStrategy(ctx, strategyOne). // Clean(). // Result() //Warn(文件切割清理文件策略one已经执行完毕; 结果:%v; 错误:%v, result, err) }))), ) if err ! nil { panic(err) } return hook}// ZipFiles compresses one or many files into a single zip archive file.// Param 1: filename is the output zip files name.// Param 2: files is a list of files to add to the zip.func ZipFiles(filename string, files []string) error { newZipFile, err : os.Create(filename) if err ! nil { return err } defer newZipFile.Close() zipWriter : zip.NewWriter(newZipFile) defer zipWriter.Close() // Add files to zip for _, file : range files { if err AddFileToZip(zipWriter, file); err ! nil { return err } } return nil}func AddFileToZip(zipWriter *zip.Writer, filename string) error { fileToZip, err : os.Open(filename) if err ! nil { return err } defer fileToZip.Close() // Get the file information info, err : fileToZip.Stat() if err ! nil { return err } header, err : zip.FileInfoHeader(info) if err ! nil { return err } // Using FileInfoHeader() above only uses the basename of the file. If we want // to preserve the folder structure we can overwrite this with the full path. header.Name filename // Change to deflate to gain better compression // see http://golang.org/pkg/archive/zip/#pkg-constants header.Method zip.Deflate writer, err : zipWriter.CreateHeader(header) if err ! nil { return err } _, err io.Copy(writer, fileToZip) return err}// Zip compresses the specified files or dirs to zip archive. If a path is a dir dont need to specify the trailing path separator. For example calling Zip(archive.zip, dir, csv/baz.csv) will get archive.zip and the content of which is baz.csv dir ├── bar.txt └── foo.txt Note that if a file is a symbolic link it will be skipped.// https://blog.csdn.net/K346K346/article/details/122441250//func Zip(zipPath string, paths ...string) error {// // Create zip file and its parent dir.// if err : os.MkdirAll(filepath.Dir(zipPath), os.ModePerm); err ! nil {// return err// }// archive, err : os.Create(zipPath)// if err ! nil {// return err// }// defer archive.Close()//// // New zip writer.// zipWriter : zip.NewWriter(archive)// defer zipWriter.Close()//// // Traverse the file or directory.// for _, rootPath : range paths {// // Remove the trailing path separator if path is a directory.// rootPath strings.TrimSuffix(rootPath, string(os.PathSeparator))//// // Visit all the files or directories in the tree.// err filepath.Walk(rootPath, walkFunc(rootPath, zipWriter))// if err ! nil {// return err// }// }// return nil//}////func walkFunc(rootPath string, zipWriter *zip.Writer) filepath.WalkFunc {// return func(path string, info fs.FileInfo, err error) error {// if err ! nil {// return err// }//// // If a file is a symbolic link it will be skipped.// if info.Mode()os.ModeSymlink ! 0 {// return nil// }//// // Create a local file header.// header, err : zip.FileInfoHeader(info)// if err ! nil {// return err// }//// // Set compression method.// header.Method zip.Deflate//// // Set relative path of a file as the header name.// header.Name, err filepath.Rel(filepath.Dir(rootPath), path)// if err ! nil {// return err// }// if info.IsDir() {// header.Name string(os.PathSeparator)// }//// // Create writer for the file header and save content of the file.// headerWriter, err : zipWriter.CreateHeader(header)// if err ! nil {// return err// }// if info.IsDir() {// return nil// }// f, err : os.Open(path)// if err ! nil {// return err// }// defer f.Close()// _, err io.Copy(headerWriter, f)// return err// }//} 完整demo项目代码 以zap为例展示如何切割日志文件。 使用Go生态两个使用最高的切分库[8] 关于压缩: 压缩解压文件[9] Golang 学习笔记五- archive/zip 实现压缩及解压[10] Golang zip 压缩与解压[11] 更多参考: zap日志切割同时支持按日期拆分也支持按日志固定大小拆分支持定时清理[12] go-logrus 日志框架封装使用[13] Go zap日志[14] 设计自用的golang日志模块[15] golang log rotate file[16] golang高性能日志库zap的使用[17] 参考资料 [1] lumberjack: https://github.com/natefinch/lumberjack [2] golang zap日志库使用: https://segmentfault.com/a/1190000040443996 [3] github.com/lestrrat-go/file-rotatelogs: https://github.com/lestrrat/go-file-rotatelogs [4] github.com/lestrrat-go/file-rotatelogs: https://github.com/lestrrat-go/file-rotatelogs [5] github.com/lestrrat/go-file-rotatelogs: https://github.com/lestrrat/go-file-rotatelogs [6] 用zap和go-file-rotatelogs实现日志的记录和日志按时间分割: https://blog.csdn.net/weixin_43881017/article/details/110200176 [7] golang实现分割日志: https://blog.csdn.net/qq_42119514/article/details/121372416 [8] 以zap为例展示如何切割日志文件。 使用Go生态两个使用最高的切分库: https://github.com/cuishuang/zap-demo/tree/main [9] 压缩解压文件: https://www.topgoer.com/%E5%85%B6%E4%BB%96/%E5%8E%8B%E7%BC%A9%E8%A7%A3%E5%8E%8B%E6%96%87%E4%BB%B6.html [10] Golang 学习笔记五- archive/zip 实现压缩及解压: https://learnku.com/articles/23434/golang-learning-notes-five-archivezip-to-achieve-compression-and-decompression [11] Golang zip 压缩与解压: https://blog.csdn.net/K346K346/article/details/122441250 [12] zap日志切割同时支持按日期拆分也支持按日志固定大小拆分支持定时清理: https://blog.csdn.net/qq_22186119/article/details/122003691 [13] go-logrus 日志框架封装使用: https://www.jianshu.com/p/722250f0b609 [14] Go zap日志: https://blog.csdn.net/qq_41004932/article/details/119760061 [15] 设计自用的golang日志模块: https://studygolang.com/articles/12537 [16] golang log rotate file: https://juejin.cn/s/golang%20log%20rotate%20file [17] golang高性能日志库zap的使用: https://www.jianshu.com/p/910b626f67d9 本文由 mdnice 多平台发布