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

admin 2025年3月17日17:58:26评论7 views字数 10611阅读35分22秒阅读模式

免责声明

📌 本公众号专注于网络安全知识的分享,旨在提升安全意识,绝不涉及任何非法用途。

  1. 1. 合法合规:本公众号所有内容仅供学习与研究,严禁用于非法活动,否则后果自负。
  2. 2. 信息来源:文章涉及的技术、工具、方法均来自公开资料,不保证其准确性、完整性。
  3. 3. 风险提示:网络安全涉及敏感内容,请在法律允许范围内使用,任何不当操作可能导致法律责任。
  4. 4. 免责申明:因使用本公众号内容导致的任何后果,均与本公众号无关。

💡 请合理、合法地使用网络安全技术,共同维护一个健康、安全的网络环境!

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

日志

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

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

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")
  • • 日志输出目的

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

    代码中log.Println("123")是输出到标准输出,让你go run main.go > xx.txt的时候那个123才会输出到你的xx.txt文件中

//输出到文件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红队开发—日志打印优化
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")

测试源码

// log包测试funclog_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格式输出
//日志打印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格式输出
fmt.Println("slog日志:text格式输出")logger = slog.New(slog.NewTextHandler(os.Stdout, nil))logger.Info("json logger info")

修改默认等级

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

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的时候是打印不出来的)
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格式

//自定义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 get github.com/fatih/color

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

go mod tidy

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

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
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就行了。(不知道讲明白没有)
// 修改打印颜色其实随时可以修改的// 这里只是实现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.Valuereturntrue    })//map类型的logContent拿到数据后,格式化为json    dataJson, 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))returnnil}

源码

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

Go红队开发—日志打印优化
// 实现日志颜色修改type ColorHander struct {//这个是 结构体内嵌(也叫匿名字段)这样 ColorHander 继承了 slog.Handler 的行为,但这里 slog.Handler 只是一个接口,它不会自动实现 Handler 方法,你仍然需要手动实现 Handle()    slog.Handler//用来实现slog.handler的接口,因为接口只有一个,实现了就能作为*log.handler传参了//目的是拿*log.Logger的打印方式来打印,也可以用*slog.Logger但是这种的话只能是分级打印,handle方法打印的时候可能需要多写代码了,方便点使用*log.Logger    colorlogger *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.Valuereturntrue    })//map类型的logContent拿到数据后,格式化为json    dataJson, 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))returnnil}funcslog_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))}funcmain() {    slog_colorTest()}

原文始发于微信公众号(竹等寒):Go红队开发—日志打印优化

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月17日17:58:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Go红队开发—日志打印优化http://cn-sec.com/archives/3850939.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息