GO语言框架快速集成日志模块的操作方法
IT行业相对于一般传统行业,发展更新速度更快,一旦停止了学习,很快就会被行业所淘汰。所以我们需要踏踏实实的不断学习,精进自己的技术,尤其是初学者。今天golang学习网给大家整理了《GO语言框架快速集成日志模块的操作方法》,聊聊模块、go语言日志,我们一起来看看吧!
zap包的集成
简介
zap是一个可以在go项目中进行快速, 结构化且分级的日志记录包, git star数高达16.3k, Git 项目地址, 在各大公司项目中被广泛使用;
最基础的使用
package main
import (
"go.uber.org/zap"
"time"
)
func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info(" log info msg",
zap.String("name", "掘金"),
zap.Int("num", 3),
zap.Duration("timer", time.Minute),
)
}
{"level":"info","ts":1657600159.826612,"caller":"log/main.go:11","msg":" log info msg","name":"脚本","num":3,"timer":60}
{"level":"warn","ts":1657600159.8266969,"caller":"log/main.go:16","msg":" this is err msg","msg":"code err"}
可以看到上面就是打印出来的log, 当然这是用的默认配置, 所以格式和输出可能不太符合我们的要求, 我们可以自己修改配置来完成定制化log;
定制化
// NewProduction builds a sensible production Logger that writes InfoLevel and
// above logs to standard error as JSON.
//
// It's a shortcut for NewProductionConfig().Build(...Option).
func NewProduction(options ...Option) (*Logger, error) {
return NewProductionConfig().Build(options...)
}
可以看到生成log的方法其实就是用 config 和 build 来构造一个记录器, 我们试试自定义一下;
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"time"
)
func main() {
conf := zap.NewProductionConfig()
// 可以把输出方式改为控制台编码, 更容易阅读
conf.Encoding = "console"
// 时间格式自定义
conf.EncoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString("[" + t.Format("2006-01-02 15:04:05") + "]")
}
// 打印路径自定义
conf.EncoderConfig.EncodeCaller = func(caller zapcore.EntryCaller, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString("[" + caller.TrimmedPath() + "]")
}
// 级别显示自定义
conf.EncoderConfig.EncodeLevel = func(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString("[" + level.String() + "]")
}
logger, _ := conf.Build()
logger.Info("service start")
logger.Info("info msg",
zap.String("name", "掘金"),
zap.Int("num", 3),
zap.Duration("timer", time.Minute),
)
}
[2022-07-12 14:57:18] [info] [log/main.go:28] service start
[2022-07-12 14:57:18] [info] [log/main.go:30] info msg {"name": "掘金", "num": 3, "timer": 60}
这种日志一般大家看起来就比较舒服了, 特别是打印json结构的话可以直接复制解析器里面去看, 没有json编码的各种转义;
zap包还是很灵活的, 基本上配置参数都支持自定义(输出键名, 格式等等), 大家可以按需配置使用;
// An EncoderConfig allows users to configure the concrete encoders supplied by
// zapcore.
type EncoderConfig struct {
// Set the keys used for each log entry. If any key is empty, that portion
// of the entry is omitted.
MessageKey string `json:"messageKey" yaml:"messageKey"`
LevelKey string `json:"levelKey" yaml:"levelKey"`
TimeKey string `json:"timeKey" yaml:"timeKey"`
NameKey string `json:"nameKey" yaml:"nameKey"`
CallerKey string `json:"callerKey" yaml:"callerKey"`
FunctionKey string `json:"functionKey" yaml:"functionKey"`
StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
LineEnding string `json:"lineEnding" yaml:"lineEnding"`
// Configure the primitive representations of common complex types. For
// example, some users may want all time.Times serialized as floating-point
// seconds since epoch, while others may prefer ISO8601 strings.
EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"`
EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"`
EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"`
// Unlike the other primitive type encoders, EncodeName is optional. The
// zero value falls back to FullNameEncoder.
EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
// Configures the field separator used by the console encoder. Defaults
// to tab.
ConsoleSeparator string `json:"consoleSeparator" yaml:"consoleSeparator"`
}
进阶封装
项目里面生产环境为了方便收集日志可能都比较喜欢用json, 开发环境中大家又都比较喜欢console方便调试, 还有比如希望接口/服务日志带上属于自己的唯一请求标识这种, 以及对日志进行分类保存等等, 就需要对zap包进行再一次的封装;
下面是我自己封装的log包代码, 可以很方便的解决上面的问题, 大家可以当做参考;
index.go记录器初始化, 根据配置选择编码输出方式及日志文件保存逻辑, 以及一些自己自定义的输出标准;
package logging
import (
"go.uber.org/zap"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
"os"
"path"
"strings"
)
type (
Conf struct {
Path string // 日志路径
Encoder string // 编码器选择
}
logItem struct {
FileName string
Level zap.LevelEnablerFunc
}
Encoder interface {
Config() zapcore.Encoder
WithKey(key string) Encoder
WithField(key, val string) Encoder
Debug(msg string)
Debugf(format string, v ...interface{})
Info(msg string)
Infof(format string, v ...interface{})
Warn(msg string)
Warnf(format string, v ...interface{})
Error(msg string)
Errorf(format string, v ...interface{})
Fatal(msg string)
Fatalf(format string, v ...interface{})
}
)
var (
maxSize = 200 // 每个日志文件最大尺寸200M
maxBackups = 20 // 日志文件最多保存20个备份
maxAge = 30 // 保留最大天数
_logger *zap.Logger
_pool = buffer.NewPool()
c Conf
ConsoleEncoder = "console" // 控制台输出
JsonEncoder = "json" // json输出
)
// Init 初始化日志.
func Init(conf Conf) {
c = conf
prefix, suffix := getFileSuffixPrefix(c.Path)
infoPath := path.Join(prefix + ".info" + suffix)
errPath := path.Join(prefix + ".err" + suffix)
items := []logItem{
{
FileName: infoPath,
Level: func(level zapcore.Level) bool {
return level zap.InfoLevel
},
},
}
NewLogger(items)
}
// NewLogger 日志.
func NewLogger(items []logItem) {
var (
cfg zapcore.Encoder
cores []zapcore.Core
)
switch c.Encoder {
case JsonEncoder:
cfg = NewJsonLog().Config()
case ConsoleEncoder:
cfg = NewConsoleLog().Config()
default:
cfg = NewConsoleLog().Config()
}
for _, v := range items {
hook := lumberjack.Logger{
Filename: v.FileName,
MaxSize: maxSize, // 每个日志文件保存的最大尺寸 单位:M
MaxBackups: maxBackups, // 日志文件最多保存多少个备份
MaxAge: maxAge, // 文件最多保存多少天
Compress: true, // 是否压缩
LocalTime: true, // 备份文件名本地/UTC时间
}
core := zapcore.NewCore(
cfg, // 编码器配置;
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // 打印到控制台和文件
v.Level, // 日志级别
)
cores = append(cores, core)
}
// 开启开发模式,堆栈跟踪
caller := zap.AddCaller()
// 开发模式
development := zap.Development()
// 二次封装
skip := zap.AddCallerSkip(1)
// 构造日志
_logger = zap.New(zapcore.NewTee(cores...), caller, development, skip)
return
}
// GetEncoder 获取自定义编码器.
func GetEncoder() Encoder {
switch c.Encoder {
case JsonEncoder:
return NewJsonLog()
case ConsoleEncoder:
return NewConsoleLog()
default:
return NewConsoleLog()
}
}
// GetLogger 获取日志记录器.
func GetLogger() *zap.Logger {
return _logger
}
// getFileSuffixPrefix 文件路径切割
func getFileSuffixPrefix(fileName string) (prefix, suffix string) {
paths, _ := path.Split(fileName)
base := path.Base(fileName)
suffix = path.Ext(fileName)
prefix = strings.TrimSuffix(base, suffix)
prefix = path.Join(paths, prefix)
return
}
// getFilePath 自定义获取文件路径.
func getFilePath(ec zapcore.EntryCaller) string {
if !ec.Defined {
return "undefined"
}
buf := _pool.Get()
buf.AppendString(ec.Function)
buf.AppendByte(':')
buf.AppendInt(int64(ec.Line))
caller := buf.String()
buf.Free()
return caller
}
console.go控制台编码输出, 支持自定义;
package logging
import (
"fmt"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"time"
)
type ConsoleLog struct {
val string
}
func NewConsoleLog() Encoder {
return new(ConsoleLog)
}
// Config 自定义配置.
func (slf *ConsoleLog) Config() zapcore.Encoder {
var (
cfg = zap.NewProductionEncoderConfig()
)
// 时间格式自定义
cfg.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString("[" + t.Format("2006-01-02 15:04:05") + "]")
}
// 打印路径自定义
cfg.EncodeCaller = func(caller zapcore.EntryCaller, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString("[" + getFilePath(caller) + "]")
}
// 级别显示自定义
cfg.EncodeLevel = func(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString("[" + level.String() + "]")
}
return zapcore.NewConsoleEncoder(cfg)
}
// WithKey 添加单个键.
func (slf *ConsoleLog) WithKey(key string) Encoder {
slf.val = slf.val + "[" + key + "] "
return slf
}
// WithField 添加字段.
func (slf *ConsoleLog) WithField(key, val string) Encoder {
slf.val = slf.val + fmt.Sprintf("[%s:%s] ", key, val)
return slf
}
func (slf *ConsoleLog) Debug(msg string) {
_logger.Debug(slf.val + msg)
}
func (slf *ConsoleLog) Debugf(format string, v ...interface{}) {
_logger.Debug(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Info(msg string) {
_logger.Info(slf.val + msg)
}
func (slf *ConsoleLog) Infof(format string, v ...interface{}) {
_logger.Info(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Warn(msg string) {
_logger.Warn(slf.val + msg)
}
func (slf *ConsoleLog) Warnf(format string, v ...interface{}) {
_logger.Warn(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Error(msg string) {
_logger.Error(slf.val + msg)
}
func (slf *ConsoleLog) Errorf(format string, v ...interface{}) {
_logger.Error(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Fatal(msg string) {
_logger.Fatal(slf.val + msg)
}
func (slf *ConsoleLog) Fatalf(format string, v ...interface{}) {
_logger.Fatal(fmt.Sprintf(slf.val+format, v...))
}
json.gojson编码输出, 支持自定义;
package logging
import (
"fmt"
"go.uber.org/zap/zapcore"
"time"
"go.uber.org/zap"
)
type JsonLog struct {
fields []zap.Field
val string
}
// NewJsonLog 自定义添加log field.
func NewJsonLog() Encoder {
return &JsonLog{fields: make([]zap.Field, 0)}
}
// Config 自定义配置.
func (slf *JsonLog) Config() zapcore.Encoder {
var (
cfg = zap.NewProductionEncoderConfig()
)
// 时间格式自定义
cfg.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05"))
}
// 打印路径自定义
cfg.EncodeCaller = func(caller zapcore.EntryCaller, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString(getFilePath(caller))
}
// 级别显示自定义
cfg.EncodeLevel = func(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString(level.String())
}
return zapcore.NewJSONEncoder(cfg)
}
// WithKey 添加单个键.
func (slf *JsonLog) WithKey(key string) Encoder {
slf.val = slf.val + key + " "
return slf
}
// WithField 添加字段.
func (slf *JsonLog) WithField(key, val string) Encoder {
slf.fields = append(slf.fields, zap.String(key, val))
return slf
}
func (slf *JsonLog) Debug(msg string) {
_logger.Debug(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Debugf(format string, v ...interface{}) {
_logger.Debug(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Info(msg string) {
_logger.Info(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Infof(format string, v ...interface{}) {
_logger.Info(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Warn(msg string) {
_logger.Warn(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Warnf(format string, v ...interface{}) {
_logger.Warn(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Error(msg string) {
_logger.Error(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Errorf(format string, v ...interface{}) {
_logger.Error(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Fatal(msg string) {
_logger.Fatal(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Fatalf(format string, v ...interface{}) {
_logger.Fatal(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
service.go标准输出方法, 方便直接调用;
package logging
import (
"fmt"
)
func Sync() {
_ = _logger.Sync()
}
func Debug(msg string) {
_logger.Debug(msg)
}
func Debugf(format string, v ...interface{}) {
_logger.Debug(fmt.Sprintf(format, v...))
}
func Info(msg string) {
_logger.Info(msg)
}
func Infof(format string, v ...interface{}) {
_logger.Info(fmt.Sprintf(format, v...))
}
func Warn(msg string) {
_logger.Warn(msg)
}
func Warnf(format string, v ...interface{}) {
_logger.Warn(fmt.Sprintf(format, v...))
}
func Error(msg string) {
_logger.Error(msg)
}
func Errorf(format string, v ...interface{}) {
_logger.Error(fmt.Sprintf(format, v...))
}
func Fatal(msg string) {
_logger.Fatal(msg)
}
func Fatalf(format string, v ...interface{}) {
_logger.Fatal(fmt.Sprintf(format, v...))
}
上面的进阶代码摘自我开发的一个git项目中, 主要是一个Go的标准项目布局, 封装了一些常用的组件, 有兴趣的朋友可以了解一下, 对新手还是很友好的;
今天带大家了解了模块、go语言日志的相关知识,希望对你有所帮助;关于Golang的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~
Go语言异常处理(Panic和recovering)用法详解
- 上一篇
- Go语言异常处理(Panic和recovering)用法详解
- 下一篇
- Go语言简介和环境配置
-
- Golang · Go教程 | 1小时前 | 格式化输出 printf fmt库 格式化动词 Stringer接口
- Golangfmt库用法与格式化技巧解析
- 140浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang配置Protobuf安装教程
- 147浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang中介者模式实现与通信解耦技巧
- 378浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang多协程通信技巧分享
- 255浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang如何判断变量类型?
- 393浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang云原生微服务实战教程
- 310浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Golang迭代器与懒加载结合应用
- 110浏览 收藏
-
- Golang · Go教程 | 2小时前 | 性能优化 并发安全 Golangslicemap 预设容量 指针拷贝
- Golangslicemap优化技巧分享
- 412浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Golang代理模式与访问控制实现解析
- 423浏览 收藏
-
- Golang · Go教程 | 3小时前 |
- Golang事件管理模块实现教程
- 274浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3163次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3375次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3403次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4506次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3784次使用
-
- go module化 import 调用本地模块 tidy的方法
- 2023-01-07 471浏览
-
- 模块一 GO语言基础知识-库源码文件
- 2022-12-27 312浏览
-
- go语言日志记录库简单使用方法实例分析
- 2023-01-23 120浏览
-
- muduo源码分析之TcpServer模块详细介绍
- 2022-12-31 344浏览

