Go红队开发—日志打印优化

devtools/2025/3/20 2:35:29/

文章目录

  • 日志
    • log
      • 输出打印
      • 日志控制
      • 测试源码
    • slog
      • 输出打印
      • 修改默认等级
      • 修改输出目的
      • 自定义logger
      • 添加日志细节
      • 子logger
    • 日志颜色修改
      • 源码

各位师傅exp与poc编写暂时鸽了,感觉web编程那章节学完自己就能编写,想不出有什么能够学习的地方,因为poc已知基本都是发包收包判断仅此而已,目前是日志章节过后直接开启工具编写,后面exp与poc可能会以工具功能的形式穿插进去。

日志

最终实现的效果:
(这里是json格式化了,你到时候可以不格式化看起来更装b点)
在这里插入图片描述

log

输出打印

log日志最容易上手,默认自带时间戳打印日志内容

  • 打印
go">//打印,默认带时间戳log.Print("log Print")log.Println("log Println")log.Printf("%s", "log Printf")
go">//日志前缀(可以用来区别日志级别)log.SetPrefix("[info] ")log.Println("info log")log.SetPrefix("[warn] ")log.Println("warn log")
  • 终止程序
    使用日志的时候可能会希望在某些严重错误日志的时候退出程序
  • fatal终止
go">//终止程序
//fatal 终止
log.Fatal("触发日志,终止程序!")
fmt.Println("test log") //终止后并不会执行后面代码
  • panic终止
go">//panic 终止
log.Panic("触发恐慌日志,终止程序!")
fmt.Println("test log") //panic程序崩溃的同时,并不会执行后面代码

日志控制

  • 格式控制
go">log.SetFlags(log.Ldate) //日期(YYYY/MM/DD)log.Println("test log")log.SetFlags(log.Ltime) //时间(HH:MM:SS)log.Println("test log")log.SetFlags(log.Lmicroseconds) //微秒log.Println("test log")log.SetFlags(log.Llongfile) //完整文件路径log.Println("test log")log.SetFlags(log.Lshortfile) //文件名+行号log.Println("test log")
  • 日志输出目的

    代码中log.Println("123")是输出到标准输出,让你go run main.go > xx.txt的时候那个123才会输出到你的xx.txt文件中
go">//输出到文件file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_RDWR, 0666)if err != nil {log.Println("打开文件失败:", err)}defer file.Close()log.SetOutput(file)log.Println("输出到文件中的日志:xxx")//设置标准输出log.SetOutput(os.Stdout) //标准输出,这里就是当你 go run main.go > log.txt的时候,log输出的都回到log.txt中//设置输出标准错误//log.SetOutput(os.Stderr) //输出标准错误,这个是一直在终端显示的,在终端使用 > 的时候,打印的日志是无法重定向到文件中去log.Println("123")

go">file2, err := os.OpenFile("info.log", os.O_CREATE|os.O_RDWR, 0666)if err != nil {log.Println("打开文件失败:", err)}defer file2.Close()infoLogger := log.New(file2, "[info] ", log.Ldate)infoLogger.Println("i am info log")file3, err := os.OpenFile("warn.log", os.O_CREATE|os.O_RDWR, 0666)if err != nil {log.Println("打开文件失败:", err)}defer file3.Close()warnlogger := log.New(file3, "[warn] ", log.Ltime)warnlogger.Println("i am warn log")

测试源码

go">// log包测试func log_test() {//打印,默认带时间戳log.Print("log Print")log.Println("log Println")log.Printf("%s", "log Printf")//日志前缀(可以用来区别日志级别)log.SetPrefix("[info] ")log.Println("info log")log.SetPrefix("[warn] ")log.Println("warn log")//终止程序//fatal 终止// log.Fatal("触发日志,终止程序!")// fmt.Println("test log") //终止后并不会执行后面代码//panic 终止// log.Panic("触发恐慌日志,终止程序!")// fmt.Println("test log") //panic程序崩溃的同时,并不会执行后面代码//日志控制//格式控制log.SetFlags(log.Ldate) //日期(YYYY/MM/DD)log.Println("test log")log.SetFlags(log.Ltime) //时间(HH:MM:SS)log.Println("test log")log.SetFlags(log.Lmicroseconds) //微秒log.Println("test log")log.SetFlags(log.Llongfile) //完整文件路径log.Println("test log")log.SetFlags(log.Lshortfile) //文件名+行号log.Println("test log")//日志输出目的//输出到文件file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_RDWR, 0666)if err != nil {log.Println("打开文件失败:", err)}defer file.Close()log.SetOutput(file)log.Println("输出到文件中的日志:xxx")//设置标准输出log.SetOutput(os.Stdout) //标准输出,这里就是当你 go run main.go > log.txt的时候,log输出的都回到log.txt中//设置输出标准错误//log.SetOutput(os.Stderr) //输出标准错误,这个是一直在终端显示的,在终端使用 > 的时候,打印的日志是无法重定向到文件中去log.Println("123")//创建自定义日志file2, err := os.OpenFile("info.log", os.O_CREATE|os.O_RDWR, 0666)if err != nil {log.Println("打开文件失败:", err)}defer file2.Close()infoLogger := log.New(file2, "[info] ", log.Ldate)infoLogger.Println("i am info log")file3, err := os.OpenFile("warn.log", os.O_CREATE|os.O_RDWR, 0666)if err != nil {log.Println("打开文件失败:", err)}defer file3.Close()warnlogger := log.New(file3, "[warn] ", log.Ltime)warnlogger.Println("i am warn log")}

slog

输出打印

  • json格式输出
go">//日志打印fmt.Println("slog日志:json格式输出")logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))fmt.Println("注意:debug不会输出,没有改默认等级")logger.Debug("json logger Debug") //没有设置默认等级,现在处于info,所以这里debug不会打印出来logger.Info("json logger Info")logger.Warn("json logger Warning")logger.Error("json logger Error")
  • text格式输出
go">fmt.Println("slog日志:text格式输出")logger = slog.New(slog.NewTextHandler(os.Stdout, nil))logger.Info("json logger info")

修改默认等级

等级排序为:debug < info < warn < error,在log包中一般默认等级都是info,所以不修改打印debug的日志类型是打印不出来的,只能修改。

go">fmt.Println("更改默认日志等级:")logger2 := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug,
}))logger2.Debug("logger2 Debug")

修改输出目的

  • slog.NewJSONHandler(os.Stdout, nil):这里的os.Stdout表示标准输出,可以通过>输出到文件中,其他stderr等等其他自己按照情况切换即可,在log包中详细讲了这几个有什么区别了。

  • slog.NewJSONHandler(os.Stdout, nil): 这里第二个参数nil是给自定义log的时候用的,常用的就是可以控制默认等级操作。
    (不做修改的默认等级就是info,你打印debug的时候是打印不出来的)

go">logger3 := slog.New(slog.NewJSONHandler(os.Stdout, nil)) //标准输出
//logger3 := slog.New(slog.NewJSONHandler(os.Stderr, nil)) //标准错误输出
logger3.Debug("logger3 Debug")

自定义logger

意思是说当你通过slog.New控制好一切参数后,将一个实例给到slog默认的logger,那么之后使用slog打印的时候都是使用你那个控制好的参数来打印。

比如这里默认是slog.info是单纯打印一个info日志而已,我们通过修改后以后得slog.info打印出来的是json格式

go">//自定义sloglogger4 := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug, //设置默认等级为debug}))slog.SetDefault(logger4) //slog默认设置为我们配置好的loggerslog.Debug("debug test") //现在debug默认的时候就能够打印出来了同时是以json格式打印的

添加日志细节

当我们想要打印日志的时候肯定是希望在发生一些错误的时候我们能够第一时间定位到错误代码以及错误原因,但是普通的打印错误给到的细节可能会比较少,一股脑堆在一起又难看,slog就可以通过添加细节来给到你的log日志
slog.Group:意思是分组,很简单看截图就知道,json最明显,就是属性中开一个{}继续存信息。
看截图:

go">//为slog日志添加更详细的说明logger5 := slog.New(slog.NewJSONHandler(os.Stdout, nil))logger5.Info("错误代码",slog.String("函数名", "xxx"),slog.String("参数值", "xxx xxx"),slog.Int("返回值", 666),slog.Group("分组",slog.String("code信息", "xxxx"),slog.Int("随便整点", 123),),)

子logger

这里我认为非常重要,没看到解释比较好的文章,有看到大佬务必告诉我,我这里就尽量以实用以及意义来讲

  • 子logger是继承父logger的,父logger可以理解为我们最初定义的那个logger,子logger创建的意义就是系统在父logger的日志解释上,加上自己独有的日志解释。
    以下代码就是实现了database_logger继承了logger6的一些参数设置,然后自己扩展属于自己的一些日志字段解释。

go">fmt.Println("子logger")logger6 := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug, //这里多弄点基础性的东西,后面子logger继承的时候就不用设置这么多}))database_logger := logger6.With(//这里就是添加你的log其他解释就行了slog.String("我是数据库的日志分支", "sql语句报错"),slog.Int("报错代码code", 111),)database_logger.Debug("依旧可以debug日志出来", slog.Int("当然,当你log的时候依旧可以添加更多信息解释你这段log", 123123))

日志颜色修改

结合slog日志,颜色修改需要借助其他包
下载fatih/color包,不要下载错了,这里使用的是fatih/color,我记得有另外一个包也可以修改日志颜色,后续可自行学习,目前用一个就够了

go">go get github.com/fatih/color

下载完了记得go mod tidy找到这个包才能用,之前忘记说了

go">go mod tidy

有了/fatih/color,其实不管是不是日志其实都可以修改颜色,这里最简单一个例子:

go">fmt.Println(color.CyanString("日志打印完毕"))

结合slog只是为了可控性以及方便性更强,而不是说每次打印都要修改一下颜色这样子,明确了需求下面就开始对slog的改造。


  • 实现slog.Handler的接口Handle(context.Context, Record) error
  • 实现上面接口就要自定义一个结构体,里面slog.Handler的接口其实都有默认实现,所以我们很多接口都不用实现,只需要实现一个Handle就能代表你是slog.Handler类型了
  • slog.Handler这个是 结构体内嵌(也叫匿名字段)这样 ColorHander 继承了 slog.Handler 的行为,但这里 slog.Handler,它不会自动实现 Handler 方法,你仍然需要手动实现 Handle()
  • colorlogger *log.Logger目的是拿*log.Logger的打印方式来打印,也可以用*slog.Logger但是这种的话只能是分级打印,handle方法打印的时候可能需要多写代码了,方便点使用*log.Logger
go">type ColorHander struct {slog.Handlercolorlogger *log.Logger}
  • Handle接口实现
  • 为什么只实现Handle?
    因为Handle在slog.Handler中我没看到默认实现,所以想要继承slog.Handler作为他的类型的的话一定要实现它,他是一个接口,同时也时日志打印的核心调用函数,我们实现了这个,之后的打印过程中都会调用Handle的参数设置
  • ColorHander在函数代码最后进行调用了*log.logger的Println函数进行打印,如果说你当初在结构体给的是*slog.Logger类型的话可能你又要进行分级的一次打印了,同时也时没有必要,其实这里已经将日志完全拆分开给了颜色,然后最后打印即可,甚至你可以使用fmt.Println()进行打印,给一个*log.Logger的话就是为了可控性更加强,我们有一个自定义的外部结构体的logger在handle核心函数里面,控制的时候尽可能少的去动handle核心函数,只需要修改每一个实例化出来的logger就行了。
    (不知道讲明白没有)
go">// 修改打印颜色其实随时可以修改的// 这里只是实现slog的自定义日志接口,在里面操作修改日志颜色func (h *ColorHander) Handle(ctx context.Context, r slog.Record) error {//这里就是实现了slog.handler的接口level := r.Level.String()switch r.Level {//将level更改颜色case slog.LevelDebug:level = color.MagentaString(level)case slog.LevelInfo:level = color.GreenString(level)case slog.LevelWarn:level = color.YellowString(level)case slog.LevelError:level = color.RedString(level)}//获取日志字段//开辟空间后续要用,r.NumAttrs()是获取日志字段的个数//开辟空间后续要用,r.NumAttrs()是获取日志字段的个数logContent := make(map[string]interface{}, r.NumAttrs())//Attrs是获取日志字段的方法并且返回一个迭代器遍历//参数给一个匿名函数,这个匿名函数的参数类型和返回值对应上即可自定义遍历日志字段r.Attrs(func(i slog.Attr) bool { //目的就是获取日志字段,放到logContent里后续使用logContent[i.Key] = i.Valuereturn true})//map类型的logContent拿到数据后,格式化为jsondataJson, err := json.MarshalIndent(logContent, "", "\t")if err != nil {return err}//打印日志,因为我们拿到handle函数,就是要修改为自己的日志形式,所以需要进行打印日志//1.日志中有时间,那么自定义的话自然要将日志的时间格式化timeStr := r.Time.Format("[15:05:05.000]")//2.日志的内容也可以在这里修改颜色,上面只是对level进行了颜色修改message := color.CyanString(r.Message)//3.设置完成后就可以打印了属于你自己的日志//这里的h就派上用场了,其实不给那个colorlogger也行,只是你在使用的时候无法支持log.Logger的用法//没有colorlogger 的话,我们外部创建logger的单个示例的时候就无法应用到全部身上了h.colorlogger.Println(timeStr, level, message, string(dataJson))return nil}

源码

想必看到日志打印出来的那一刻,是十分的优雅

go">  // 实现日志颜色修改type ColorHander struct {//这个是 结构体内嵌(也叫匿名字段)这样 ColorHander 继承了 slog.Handler 的行为,但这里 slog.Handler 只是一个接口,它不会自动实现 Handler 方法,你仍然需要手动实现 Handle()slog.Handler//用来实现slog.handler的接口,因为接口只有一个,实现了就能作为*log.handler传参了//目的是拿*log.Logger的打印方式来打印,也可以用*slog.Logger但是这种的话只能是分级打印,handle方法打印的时候可能需要多写代码了,方便点使用*log.Loggercolorlogger *log.Logger}// 修改打印颜色其实随时可以修改的// 这里只是实现slog的自定义日志接口,在里面操作修改日志颜色func (h *ColorHander) Handle(ctx context.Context, r slog.Record) error {//这里就是实现了slog.handler的接口level := r.Level.String()switch r.Level {//将level更改颜色case slog.LevelDebug:level = color.MagentaString(level)case slog.LevelInfo:level = color.GreenString(level)case slog.LevelWarn:level = color.YellowString(level)case slog.LevelError:level = color.RedString(level)}//获取日志字段//开辟空间后续要用,r.NumAttrs()是获取日志字段的个数//开辟空间后续要用,r.NumAttrs()是获取日志字段的个数logContent := make(map[string]interface{}, r.NumAttrs())//Attrs是获取日志字段的方法并且返回一个迭代器遍历//参数给一个匿名函数,这个匿名函数的参数类型和返回值对应上即可自定义遍历日志字段r.Attrs(func(i slog.Attr) bool { //目的就是获取日志字段,放到logContent里后续使用logContent[i.Key] = i.Valuereturn true})//map类型的logContent拿到数据后,格式化为jsondataJson, err := json.MarshalIndent(logContent, "", "\t")if err != nil {return err}//打印日志,因为我们拿到handle函数,就是要修改为自己的日志形式,所以需要进行打印日志//1.日志中有时间,那么自定义的话自然要将日志的时间格式化timeStr := r.Time.Format("[15:05:05.000]")//2.日志的内容也可以在这里修改颜色,上面只是对level进行了颜色修改message := color.CyanString(r.Message)//3.设置完成后就可以打印了属于你自己的日志//这里的h就派上用场了,其实不给那个colorlogger也行,只是你在使用的时候无法支持log.Logger的用法//没有colorlogger 的话,我们外部创建logger的单个示例的时候就无法应用到全部身上了h.colorlogger.Println(timeStr, level, message, string(dataJson))return nil}func slog_colorTest() {fmt.Println("-----------------------slog_colorTest------------------------")myColorLogger := slog.New(&ColorHander{Handler: slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug,}),colorlogger: log.New(os.Stdout, "", 0),})myColorLogger.Debug("debug日志", slog.String("code信息", "xxxx"), slog.Int("随便整点", 123))myColorLogger.Info("info日志", slog.String("code信息", "xxxx"), slog.Int("随便整点", 123))myColorLogger.Warn("warn日志", slog.String("code信息", "xxxx"), slog.Int("随便整点", 123))myColorLogger.Error("error日志", slog.String("code信息", "xxxx"), slog.Int("随便整点", 123))}func main() {slog_colorTest()
}

http://www.ppmy.cn/devtools/168507.html

相关文章

[网络][tcp协议]:tcp报头

tcp(传输控制协议)是一种面向字节流的传输层协议,相较于udp协议,tcp能保证传输数据的可靠性与准确性,tcp也是目前最常见的传输层协议 本文主要介绍tcp报头各个字段的含义与用途 注:保留6位和6位标记位是目前最普遍的写法,在我查资料时,发现有一些拓展情况,会在后文细说 最简单的…

算法刷题记录——LeetCode篇(6) [第501~600题](持续更新)

(优先整理热门100及面试150&#xff0c;不定期持续更新&#xff0c;欢迎关注) 543. 二叉树的直径 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路…

爬虫逆向:详细讲述iOS底层原理及机制

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 1. iOS 系统架构1.1 Core OS 层1.2 Core Services 层1.3 Media 层1.4 Cocoa Touch 层2. iOS 的核心机制2.1 应用生命周期2.2 内存管理2.3 多线程2.4 文件系统2.5 网络通信3. iOS 的启动流程4. iOS 的安全机制4.1 代码签…

LabVIEW烟气速度场实时监测

本项目针对燃煤电站烟气流速实时监测需求&#xff0c;探讨了静电传感器结构与速度场超分辨率重建方法&#xff0c;结合LabVIEW多板卡同步采集与实时处理技术&#xff0c;开发出一个高效的烟气速度场实时监测系统。该系统能够在高温、高尘的复杂工况下稳定运行&#xff0c;提供高…

算法沉淀五:位运算

位运算内容总结 1.基础位运算 << >> ~ &:有0就是0 | :有1就是1 ^:相同为0&#xff0c;相异为1 / 无进位相加 2.给一个数n&#xff0c;确定它的二进制表示中的第x位是0还是1 &上一个1&#xff0c;要么让第x位右移&#xff0c;要么让1左移&#xff0c;一般…

「清华大学、北京大学」DeepSeek 课件PPT专栏

你要的 这里都打包好啦&#xff0c;快快收藏起来&#xff01; 名称 链接 团队简介 类型 DeepSeek——从入门到精通 1️⃣ DeepSeek从入门到精通「清华团队」 清华大学新闻与传播学院 新媒体研究中心 元宇宙文化实验室 PPT课件 DeepSeek如何赋能职场应用? ——从提示语…

2025国际数字能源展全球招商开启,助力数字能源产业新发展

为深入贯彻“四个革命、一个合作”能源安全新战略&#xff0c;服务碳达峰碳中和国家战略&#xff0c;2025国际数字能源展将于9月18 - 21日在深圳会展中心举办&#xff0c;目前全球招商工作已全面启动。 此次展会由深圳市人民政府、中国电力企业联合会等指导&#xff0c;深圳市…

docker-compose install nginx(解决fastgpt跨区域)

CORS前言 CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种安全措施,它允许或拒绝来自不同源(协议、域名、端口任一不同即为不同源)的网页访问另一源中的资源。它的主要作用如下: 同源策略限制:Web 浏览器的同源策略限制了从一个源加载的文档或脚本如何与另一…