Golang Zap日志

Zap日志解析

Config.yaml

zap:
  level: 'info' #日志级别
  format: 'console' #输出的级别,有console和json
  prefix: '[catering]' #输出的前缀,[catering]xxxxxxxxxxx
  director: 'log' #存放的文件夹
  show-line: true #是否显示行号
  encode-level: 'LowercaseColorLevelEncoder' #编码级别,目前支持四种LowercaseLevelEncoder,LowercaseColorLevelEncoder,CapitalLevelEncoder,CapitalColorLevelEncoder
  stacktrace-key: 'stacktrace' #栈名
  log-in-console: true #输出控制台

配置文件

package config

type Zap struct {
    Level         string mapstructure:"level" json:"level" yaml:"level"                           // 级别
    Format        string mapstructure:"format" json:"format" yaml:"format"                        // 输出
    Prefix        string mapstructure:"prefix" json:"prefix" yaml:"prefix"                        // 日志前缀
    Director      string mapstructure:"director" json:"director"  yaml:"director"                 // 日志文件夹
    ShowLine      bool   mapstructure:"show-line" json:"showLine" yaml:"showLine"                 // 显示行
    EncodeLevel   string mapstructure:"encode-level" json:"encodeLevel" yaml:"encode-level"       // 编码级
    StacktraceKey string mapstructure:"stacktrace-key" json:"stacktraceKey" yaml:"stacktrace-key" // 栈名
    LogInConsole  bool   mapstructure:"log-in-console" json:"logInConsole" yaml:"log-in-console"  // 输出控制台
}

初始化

func InitZap() {
    cfg := global.Config.Zap //获取上述的配置文件

    // 判断是否有Director文件夹
    if ok, _ := pkg.PathExists(cfg.Director); !ok {
        fmt.Printf("create %v directory\n", cfg.Director)
        _ = os.Mkdir(cfg.Director, os.ModePerm)
    }

    // zap.LevelEnablerFunc(func(lev zapcore.Level) bool 用来划分不同级别的输出
    // 根据不同的级别输出到不同的日志文件

    // 调试级别
    debugPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
        return lev == zap.DebugLevel
    })
    // 日志级别
    infoPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
        return lev == zap.InfoLevel
    })
    // 警告级别
    warnPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
        return lev == zap.WarnLevel
    })
    // 错误级别
    errorPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
        return lev >= zap.ErrorLevel
    })

    cores := [...]zapcore.Core{
        getEncoderCore(fmt.Sprintf("./%s/server_debug.log", cfg.Director), debugPriority),
        getEncoderCore(fmt.Sprintf("./%s/server_info.log", cfg.Director), infoPriority),
        getEncoderCore(fmt.Sprintf("./%s/server_warn.log", cfg.Director), warnPriority),
        getEncoderCore(fmt.Sprintf("./%s/server_error.log", cfg.Director), errorPriority),
    }

    //zapcore.NewTee(cores ...zapcore.Core) zapcore.Core
    //NewTee创建一个Core,将日志条目复制到两个或更多的底层Core中

    logger := zap.New(zapcore.NewTee(cores[:]...))

    //用文件名、行号和zap调用者的函数名注释每条消息
    if cfg.ShowLine {
        logger = logger.WithOptions(zap.AddCaller())
    }

    //把初始化好的logger赋值到全局日志变量中
    global.Log = logger
}

getEncoderCode函数

// getEncoderCore 获取Encoder的zapcore.Core
func getEncoderCore(fileName string, level zapcore.LevelEnabler) (core zapcore.Core) {
    writer := pkg.GetWriteSyncer(fileName) // 使用file-rotatelogs进行日志分割
    return zapcore.NewCore(getEncoder(), writer, level)
}

getEncoder函数

// getEncoder 获取zapcore.Encoder
func getEncoder() zapcore.Encoder {
    //获取配置文件的输出格式,json or console
    cfg := global.Config.Zap
    if cfg.Format == "json" {
        return zapcore.NewJSONEncoder(getEncoderConfig())
    }
    return zapcore.NewConsoleEncoder(getEncoderConfig())
}

getEncoderConfig函数

获取自定义的编码器的配置

// getEncoderConfig 获取zapcore.EncoderConfig
func getEncoderConfig() (config zapcore.EncoderConfig) {
    cfg := global.Config.Zap
    config = zapcore.EncoderConfig{
        MessageKey:     "message",
        LevelKey:       "level",
        TimeKey:        "time",
        NameKey:        "logger",
        CallerKey:      "caller",
        StacktraceKey:  cfg.StacktraceKey, //栈名
        LineEnding:     zapcore.DefaultLineEnding, //默认的结尾\n
        EncodeLevel:    zapcore.LowercaseLevelEncoder, //小写字母输出
        EncodeTime:     CustomTimeEncoder, //自定义时间格式
        EncodeDuration: zapcore.SecondsDurationEncoder, //编码间隔 s
        EncodeCaller:   zapcore.FullCallerEncoder, //控制打印的文件位置是绝对路径,ShortCallerEncoder 是相对路径
    }
    //根据配置文件重新配置编码颜色和字体
    switch {
    case cfg.EncodeLevel == "LowercaseLevelEncoder": // 小写编码器(默认)
        config.EncodeLevel = zapcore.LowercaseLevelEncoder
    case cfg.EncodeLevel == "LowercaseColorLevelEncoder": // 小写编码器带颜色
        config.EncodeLevel = zapcore.LowercaseColorLevelEncoder
    case cfg.EncodeLevel == "CapitalLevelEncoder": // 大写编码器
        config.EncodeLevel = zapcore.CapitalLevelEncoder
    case cfg.EncodeLevel == "CapitalColorLevelEncoder": // 大写编码器带颜色
        config.EncodeLevel = zapcore.CapitalColorLevelEncoder
    default:
        config.EncodeLevel = zapcore.LowercaseLevelEncoder
    }
    return config
}

CustomTimeEncoder函数

用于自定义日志前缀的输出格式

// 自定义日志输出时间格式
// 输出格式为 prfix + 具体的时间日期
func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
    cfg := global.Config.Zap
    enc.AppendString(t.Format(cfg.Prefix + "2006-01-02 15:04:05.000"))
}

pkg.GetWriteSyncer函数

用于切割文件,防止日志文件过大

func GetWriteSyncer(file string) zapcore.WriteSyncer {
    lumberJackLogger := &lumberjack.Logger{
        Filename:   file, // 日志文件的位置
        MaxSize:    10,   // 在进行切割之前,日志文件的最大大小(以MB为单位)
        MaxBackups: 200,  // 保留旧文件的最大个数
        MaxAge:     30,   // 保留旧文件的最大天数
        Compress:   true, // 是否压缩/归档旧文件
    }

    if global.Config.Zap.LogInConsole {
        return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger))
    }
    return zapcore.AddSync(lumberJackLogger)
}

用法

伪代码,切勿直接粘贴复制

package global

var log *zap.Logger
package main

import (
    "catering/initialize"
    "catering/global"
)

func main() {
    initialize.InitZap()
    global.log.Info("test")
}

打印日志示例

log
├── server_error.log
├── server_info.log

server_info.log

[catering]2022-03-17 17:36:42.200   [34minfo[0m   E:/GoPath/project/go-catering/admin/initialize/router.go:32 use middleware logger
[catering]2022-03-17 17:36:42.201   [34minfo[0m   E:/GoPath/project/go-catering/admin/initialize/router.go:36 use middleware cors
[catering]2022-03-17 17:36:42.201   [34minfo[0m   E:/GoPath/project/go-catering/admin/initialize/router.go:38 register swagger handler
[catering]2022-03-17 17:36:42.225   [34minfo[0m   E:/GoPath/project/go-catering/admin/initialize/router.go:104    router register success
[catering]2022-03-17 17:36:42.228   [34minfo[0m   E:/GoPath/project/go-catering/admin/core/server.go:27   server run success on   {"address": ":8888"}

server_error.log

[catering]2022-03-09 21:35:33.812   [31merror[0m  D:/goProject/src/catering/admin/pkg/app/request.go:13   Id.Required.    {"message": "Id Can not be empty"}
[catering]2022-03-09 21:35:33.813   [31merror[0m  D:/goProject/src/catering/admin/pkg/app/request.go:13   LevelName.Required. {"message": "LevelName Can not be empty"}
[catering]2022-03-09 21:35:33.813   [31merror[0m  D:/goProject/src/catering/admin/pkg/app/request.go:13   UpNeedIntegration.Required. {"message": "UpNeedIntegration Can not be empty"}

Original: https://www.cnblogs.com/xiaofua/p/16173894.html
Author: 小傅啊
Title: Golang Zap日志

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516344/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Go编译过程

    一、 Go编译流程 二、过程说明 词法解析 读取Go源文件,将字符序列转换为符号(token)序列,比如将”:=”转换为_Define 代码中的标识符、关键…

    Go语言 2023年5月25日
    066
  • 开始读 Go 源码了

    学完 Go 的基础知识已经有一段时间了,那么接下来应该学什么呢?有几个方向可以考虑,比如说 Web 开发,网络编程等。 在写项目的过程中,发现一个问题。实现功能是没问题的,但不知道…

    Go语言 2023年5月25日
    048
  • golang低级编程:一.unsafe包

    go语言在设计上确保了一些安全的属性,限制了程序可能出错的途径。例如严格的类型转换规则。但也使得很多实现的细节无法通过go程序来访问,例如对于聚合类型(如结构体)的内存布局,或者一…

    Go语言 2023年5月25日
    051
  • Go 学习路线(2022)

    原文链接: Go 学习路线(2022) Go 语言的发展越来越好了,很多大厂使用 Go 作为主要开发语言,也有很多人开始学习 Go,准备转 Go 开发。 那么,怎么学呢? 我发现,…

    Go语言 2023年5月25日
    058
  • golang 中 sync.Mutex 的实现

    mutex 主要有两个 method: Lock() 和 Unlock() Lock() 可以通过一个 CAS 操作来实现 func (m *Mutex) Lock() { for…

    Go语言 2023年5月25日
    076
  • 系统调用跟踪——分析(一)

    通过strace工具可跟踪用户进程与Linux内核的调用交互,可看到其中的System Call(系统调用)情况; 安装strace&a…

    Go语言 2023年5月25日
    096
  • Go语言之结构体与方法

    结构体是一系列属性的集合(类似于 Python 中的类) 1、结构体的定义与使用 // 定义 type Person struct { Name string Age int Se…

    Go语言 2023年5月25日
    072
  • Go语言之函数

    函数就是一块执行特定任务的代码,在输入源的基础上通过一些算法生成预期的输出。 Go 语言中的函数声明语法如下: func 函数名(参数名 类型,参数名 类型)(返回值1类型,返回值…

    Go语言 2023年5月25日
    042
  • Golang接口型函数使用技巧

    什么是接口型函数?顾名思义接口函数指的是用函数实现接口,这样在调用的时候就会非常简便,这种方式适用于只有一个函数的接口。 这里以迭代一个map为例,演示这一实现的技巧。 常规接口实…

    Go语言 2023年5月25日
    059
  • croncli 定时器命令(golang)

    定时器是执行任务时的常用功能,配置系统的定时任务太麻烦,所以就想用golang简单实现一个定时器命令。 定时器的参数包括: $ croncli -h 定时器命令 Usage: cr…

    Go语言 2023年5月25日
    059
  • golang常用库包:Go依赖注入(DI)工具-wire使用

    google 出品的依赖注入库 wire:https://github.com/google/wire 什么是依赖注入 依赖注入 ,英文全名是 dependency injecti…

    Go语言 2023年5月25日
    069
  • [grpc快速入门] 一 grpc生成与调用

    下载通用编译器 地址:https://github.com/protocolbuffers/protobuf/releases选择对应的版本,解压后将文件夹下bin目录配置到环境变…

    Go语言 2023年5月25日
    087
  • 《Go语言圣经》 读书笔记与个人思考 ① 第一章、包括源码分析

    《The Go Programming Language》 知识点记载,学习笔记、章节练习与个人思考。前言 · Go语言圣经 (itsfun.top) 标题后标记了小丑符号的表示还…

    Go语言 2023年5月25日
    085
  • 使用Go http重试请求

    原文连接:https://www.zhoubotong.site/post/78.html开发中对于http请求是经常遇到,一般可能网络延迟或接口返回超时,对于发起客户端的请求, …

    Go语言 2023年5月25日
    062
  • 基于LSM的Key-Value数据库实现WAL篇

    上篇文章简单的实现了基于LSM数据库的初步版本,在该版本中如数据写入到内存表后但还未持久化到SSTable排序字符串表,此时正好程序崩溃,内存表中暂未持久化的数据将会丢失。 引入W…

    Go语言 2023年5月25日
    064
  • Go 语言快速开发入门

    需求 开发的步骤 linux下如何开发Go程序 MAC下如何开发Go程序 Golang执行流程分析 编译和运行说明 Go程序开发的注意事项 Go语言的转义字符(escapechar…

    Go语言 2023年5月25日
    082
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球