当前位置: 首页 > news >正文

Zap熟练使用一篇学会

Zap介绍

ZAP(Zed Attack Proxy)是一种开源的网络应用程序安全扫描工具,旨在帮助发现和解决Web应用程序中的安全漏洞。以下是关于ZAP的介绍:

一个好的项目日志系统是必不可少的。

Zap的日志功能

  1. 记录活动:ZAP日志记录了ZAP工具的各种活动,包括扫描过程中发现的漏洞、HTTP请求和响应、插件运行情况等。
  2. 调试信息:日志还可以用于记录调试信息,有助于了解ZAP的内部运行情况,识别问题和优化性能。
  3. 级别设置:ZAP日志通常支持多个日志级别,如调试、信息、警告和错误。用户可以根据需要设置不同级别的日志详细程度。
  4. 可配置性:用户通常可以配置日志的输出位置(如控制台、文件)、日志格式、日志滚动策略等,以满足不同的需求和偏好。
  5. 安全性:由于日志可能包含敏感信息,如用户凭证、敏感数据等,ZAP通常会提供安全措施,如日志加密、访问控制等来保护日志的安全性。

使用日志功能的好处:

  1. 故障排查:日志记录可帮助用户追踪和诊断问题,找出导致错误或异常行为的原因。
  2. 性能优化:通过分析日志,可以发现性能瓶颈,优化ZAP的配置和运行方式。
  3. 合规性:在一些情况下,需要记录ZAP的活动以符合合规性要求,如在安全审计中需要提供详细的操作日志。
  4. 学习和培训:ZAP的日志可以用于培训新用户,了解ZAP的功能和操作流程。
  5. 报告生成:日志记录的信息可以用于生成详细的报告,包括漏洞报告、扫描结果等。

Zap的基本使用

在使用Zap之前首先拉取一下Zap,同时配合gin和lumberjack使用,gin是go语言中 Web 框架。lumberjack 是一个 Go 语言的库,它提供了简单的轮转日志文件的功能,配合Zap以实现日志的轮转和压缩。

go get -u github.com/gin-gonic/gin
go get -u go.uber.org/zap
go get -u gopkg.in/natefinch/lumberjack.v2 

没有报错,然后go.mod文件里面有依赖说明拉取成功。

首先一个包,创建log.go把这个日志实现成一个接口,之后让我们的web程序都能调用它

zap分多个日志级别

Error级别的日志,说明日志是错误类型,在排障时,会首先查看错误级别的日志。Warn级别日志说明出现异常,但还不至于影响程序运行,如果程序执行的结果不符合预期,则可以参考Warn级别的日志,定位出异常所在。Info级别的日志,可以协助我们Debug,并记录一些有用的信息,供后期进行分析。

Fatal > Panic > Error > Warn > Info > Debug > Trace

启动应用程序时,期望哪些输出级别的日志被打印。例如,使用日志级别是info ,说明了只有日志级别高于info的日志才会被打印。

zap多个日志输出位置

通过zap的zapcore.NewMultiWriteSynce()还可以设定将日志输入到多个位置,比如zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), 将日志同时输出到控制台和文件(hook结构体有配置日志文件的存储)

zap多个日志输出格式

通过zap的zapcore.NewConsoleEncoder和zapcore.NewJSONEncoder可以实现让日志是生成TEXT格式格式还是JSON格式。

  • TEXT格式:TEXT格式的日志具有良好的可读性,可以方便我们在开发联调阶段查看日志,例如:

2020-12-02T01:16:18+08:00 INFO example.go:11 std log

  • JSON格式:JSON格式的日志可以记录更详细的信息,日志中包含一些通用的或自定义的字段,可供日后的查询、分析使用,而且可以很方便地供filebeat、logstash这类日志采集工具采集并上报。下面是JSON格式的日志:
{"level":"DEBUG","time":"2020-12-02T01:16:18+08:00","file":"example.go:15","func":"main.main","message":"log in json format"}
{"level":"INFO","time":"2020-12-02T01:16:18+08:00","file":"example.go:16","func":"main.main","message":"an
zap在一次请求中支持RequestID

通过配合gin的中间件使用,可以让zap支持RequestID:使用RequestID串联一次请求的所有日志,这些日志可能分布在不同的组件,不同的机器上。支持RequestID可以大大提高排障的效率,降低排障难度。在一些大型分布式系统中,没有RequestID排障简直就是灾难。意思就是让一次请求中产生的日志都有同样的RequestID字段,便于搜索

{"level":"info","ts":1724831082.265542,"caller":"useZap/zapMain.go:57","msg":"Incoming request","RequestID":1828700161077940224,"RequestID":1828700218124668928,"Path":"/"}
{"level":"info","ts":1724831082.2659457,"caller":"useZap/zapMain.go:33","msg":"test request1","RequestID":1828700161077940224,"RequestID":1828700218124668928,"Path":"/"}
{"level":"info","ts":1724831082.2659457,"caller":"useZap/zapMain.go:35","msg":"test request2","RequestID":1828700161077940224,"RequestID":1828700218124668928,"Path":"/"}
{"level":"info","ts":1724831082.2659457,"caller":"useZap/zapMain.go:74","msg":"HTTP Request","RequestID":1828700161077940224,"RequestID":1828700218124668928,"method":"GET","path":"/","status":"200","duration":"0s"}
{"level":"info","ts":1724831082.2659457,"caller":"useZap/zapMain.go:61","msg":"Request completed","RequestID":1828700161077940224,"RequestID":1828700218124668928,"Path":"/"}

代码

在项目下创建pkg/log.go文件,相关代码的解释,注释已经放上了,即粘即用。

package logimport ("context""github.com/gin-gonic/gin""go.uber.org/zap""go.uber.org/zap/zapcore""gopkg.in/natefinch/lumberjack.v2""time"
)const ctxLoggerKey = "zapLogger"type Logger struct {*zap.Logger
}func NewLog() *Logger {// log address "out.log" User-definedlp := "./storage/logs/server.log"lv := "debug"var level zapcore.Level//debug<info<warn<error<fatal<panicswitch lv {case "debug":level = zap.DebugLevelcase "info":level = zap.InfoLevelcase "warn":level = zap.WarnLevelcase "error":level = zap.ErrorLeveldefault:level = zap.InfoLevel}//创建了一个 lumberjack.Logger 实例,用于配置日志文件的存储。hook := lumberjack.Logger{Filename:   lp,   // Log file path 日志文件的路径MaxSize:    1024, // Maximum size unit for each log file: M 每个日志最大大小MaxBackups: 30,   // The maximum number of backups that can be saved for log files 可以保存的日志文件数量MaxAge:     7,    // Maximum number of days the file can be saved 日志文件保存的最大天数Compress:   true, // Compression or not 是否对日志文件进行压缩}//encoder:这是一个 zapcore.Encoder 实例,用于定义日志的格式。//定义一个打印格式 json还是consoleconsole := "json"//console := "console"var encoder zapcore.Encoderif console == "console" {encoder = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{TimeKey:        "ts",LevelKey:       "level",NameKey:        "Logger",CallerKey:      "caller",MessageKey:     "msg",StacktraceKey:  "stacktrace",LineEnding:     zapcore.DefaultLineEnding,EncodeLevel:    zapcore.LowercaseColorLevelEncoder,EncodeTime:     timeEncoder,EncodeDuration: zapcore.SecondsDurationEncoder,EncodeCaller:   zapcore.FullCallerEncoder,})} else {encoder = zapcore.NewJSONEncoder(zapcore.EncoderConfig{TimeKey:        "ts",LevelKey:       "level",NameKey:        "logger",CallerKey:      "caller",FunctionKey:    zapcore.OmitKey,MessageKey:     "msg",StacktraceKey:  "stacktrace",LineEnding:     zapcore.DefaultLineEnding,EncodeLevel:    zapcore.LowercaseLevelEncoder,EncodeTime:     zapcore.EpochTimeEncoder,EncodeDuration: zapcore.SecondsDurationEncoder,EncodeCaller:   zapcore.ShortCallerEncoder,})}core := zapcore.NewCore(encoder,//通过(NewMultiWriteSyncer)它将日志输出到多个目标。//zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // 同时讲日志输出到控制台和文件zapcore.NewMultiWriteSyncer(zapcore.AddSync(&hook)), // Print to filelevel,)//定义一个环境生产环境还是开发环境env := "prod"//env := "local"if env != "prod" {// 开发模式下 Zap 会记录更详细的日志信息,包括调用者信息和错误级别的堆栈跟踪return &Logger{zap.New(core, zap.Development(), zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}}// 如果是生产环境,创建一个 Logger 实例,仅记录调用者信息和错误级别的堆栈跟踪return &Logger{zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}
}// timeEncoder 格式化一下当前实际
func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {//enc.AppendString(t.Format("2006-01-02 15:04:05"))enc.AppendString(t.Format("2006-01-02 15:04:05.000000000"))
}// WithValue 将 Zap 日志字段和当前的 context.Context 绑定,以便在 HTTP 请求处理过程中可以方便地记录日志。
func (l *Logger) WithValue(ctx context.Context, fields ...zapcore.Field) context.Context {// 检查传入的 context 是否为 *gin.Context 类型if c, ok := ctx.(*gin.Context); ok {// 将 gin.Context 转换为标准的 context.Contextctx = c.Request.Context()// 使用 context.WithValue 将 Zap 日志字段绑定到 context// 这样在请求处理过程中就可以通过 context 获取日志记录器实例c.Request = c.Request.WithContext(context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...)))return c}// 如果 context 不是 *gin.Context 类型,直接绑定 Zap 日志字段return context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...))
}// WithContext Returns a zap instance from the specified context
func (l *Logger) WithContext(ctx context.Context) *Logger {//检查传入的 context 是否为 *gin.Context 类型//将 gin.Context 转换为标准的 context.Contextif c, ok := ctx.(*gin.Context); ok {ctx = c.Request.Context()}//从上下文中提取键为ctxLoggerKey的值,并尝试将其转换为*zap.Logger。zl := ctx.Value(ctxLoggerKey)ctxLogger, ok := zl.(*zap.Logger)if ok {return &Logger{ctxLogger}}return l
}

创建storage/logs/server.log 用来存储日志

在main里面创建一个路由,同时把zap的功能通过中间件放进去

package mainimport ("bytes""fmt""github.com/bwmarrin/snowflake""github.com/duke-git/lancet/v2/cryptor""github.com/duke-git/lancet/v2/random""github.com/gin-gonic/gin""go.uber.org/zap""io""net/http""os""strconv""time"log "useZap/pkg"
)func main() {// 设置 Gin 模式为 Debug 模式//Gin 框架有三种运行模式:DebugMode、ReleaseMode 和 TestMode。在 DebugMode 下,Gin 会提供更多的调试信息,gin.SetMode(gin.DebugMode)// 初始化 Gin 引擎r := gin.Default()//r := gin.New()f, _ := os.Create("gin.log")gin.DefaultWriter = f // 将日志输出到文件logger := log.NewLog()// 添加 Zap 中间件r.Use(generateRequestID(logger),zapMiddleware(logger),RequestLogMiddleware(logger))// 设置路由r.GET("/", func(c *gin.Context) {logger.Info("test request1", zap.String("Path", c.Request.URL.Path))logger.WithValue(c, zap.String("request_url", c.Request.URL.String()))logger.Info("test request2", zap.String("Path", c.Request.URL.Path))c.JSON(http.StatusOK, gin.H{"message": "Hello, World!"})})// 启动服务r.Run(":8080")}var node *snowflake.Nodefunc init() {var err errornode, err = snowflake.NewNode(1) // 1 是机器 IDif err != nil {fmt.Printf("Error creating snowflake node: %v", err)}
}func generateRequestID(logger *log.Logger) gin.HandlerFunc {return func(c *gin.Context) {requestID := node.Generate().Int64()logger.Info("Incoming request", zap.Any("RequestID", requestID), zap.String("Path", c.Request.URL.Path))log := logger.With(zap.Any("RequestID", requestID))logger.Logger = logc.Next()logger.Info("Request completed", zap.String("Path", c.Request.URL.Path))}
}// zapMiddleware 记录每个请求的基本信息
func zapMiddleware(zapLogger *log.Logger) gin.HandlerFunc {return func(c *gin.Context) {// 请求开始时间start := time.Now()// 处理请求c.Next()// 记录请求信息zapLogger.Info("HTTP Request",zap.String("method", c.Request.Method),zap.String("path", c.Request.URL.Path),zap.String("status", strconv.Itoa(c.Writer.Status())),zap.String("duration", time.Since(start).String()),)}
}// RequestLogMiddleware 使用WithValuef方法,将每个请求的请求ID、请求方法、请求路径、请求参数等信息记录到日志中
// 也是将请求的ctx给绑定到日志的ctx中 这样子,在日志中就可以直接看到请求的请求ID等信息了
// 之后在多个层中的日志对象用到的ctx就会是同一个了
func RequestLogMiddleware(logger *log.Logger) gin.HandlerFunc {return func(ctx *gin.Context) {// The configuration is initialized once per requestuuid, err := random.UUIdV4()if err != nil {return}trace := cryptor.Md5String(uuid)logger.WithValue(ctx, zap.String("trace", trace))logger.WithValue(ctx, zap.String("request_method", ctx.Request.Method))logger.WithValue(ctx, zap.Any("request_headers", ctx.Request.Header))logger.WithValue(ctx, zap.String("request_url", ctx.Request.URL.String()))if ctx.Request.Body != nil {bodyBytes, _ := ctx.GetRawData()ctx.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 关键点logger.WithValue(ctx, zap.String("request_params", string(bodyBytes)))}logger.WithContext(ctx).Info("Request")ctx.Next()}
}

http://www.mrgr.cn/news/20966.html

相关文章:

  • 千万补贴计划,“健康中国行,陪诊惠民工程——陪诊志愿服务”全面开展
  • 诚邀参与:CloudWeGo 技术沙龙 · 暨三周年庆典9月北京开幕
  • 目标检测-YOLOv10
  • 【苍穹外卖】Day 7 地址簿、下单、定时处理
  • 1.1分布式集群介绍
  • 启动spring boot项目时,第三方jar包扫描不到的问题
  • 三星的新款笔记本电脑AI性能提升一倍
  • TRIZ在充电桩安全中的应用探究
  • 2024 年 10 款最佳免费数据库软件(功能)
  • 【深度学习】多层感知机的从零开始实现与简洁实现
  • AI项目的商业模式与回报支持者【AI创业、AI项目】
  • 2、#include和#define
  • 如何为 DigitalOcean 静态路由操作员设置故障转移
  • ubuntu 安装 jdk
  • php实用命令
  • 一文搞懂性能测试
  • 独立站卖家必看!一文讲清楚独立站收单及收款结汇流程
  • 企业微信群管理:构建高效沟通与协作的社群生态
  • 【深度学习】卷积神经网络与 LeNet
  • 美联储降息在即:加密市场风云再起,机遇与挑战并存