Go开发学习 | 如何使用日志记录模块包针对日志按天数、按大小分隔文件示例...

news2024/11/19 10:31:05

欢迎关注「全栈工程师修炼指南」公众号

点击 👇 下方卡片 即可关注我哟!

设为星标⭐每天带你 基础入门 到 进阶实践 再到 放弃学习

  花开堪折直须折,莫待无花空折枝 


作者主页:[ https://www.weiyigeek.top ]  

博客:[ https://blog.weiyigeek.top ]

作者安全运维学习答疑交流群:请关注公众号回复【学习交流群


文章目录:

0x00 前言简述

    • sirupsen/logrus 模块 - 日志记录

    • lestrrat-go/file-rotatelogs 模块 - 日志分隔

    • rifflock/lfshook 模块 - 本地文件系统挂钩


描述: 日志是现代编程中必不可少的手段,除了处理基本的错误之外,通过记录日志,也可以帮助我们完成一些基本的功能,比如开发及测试期间的Debug,记录请求的上下文,排除故障原因,数据统计及分析等等。

所以本节将主要分享 Go 语言中常用的日志记录库(包)即相关依赖包的下载使用,当前Go语言常用的日志库模块有 logrus , Zerolog, Zap, and Apex 等。

sirupsen/logrus 模块 - 日志记录

描述: Logrus 是一个结构化、可插拔的Go日志库,并且完全兼容官方的log库,具有很强的灵活性,有 TEXT 和 JSON 两种可选的日志输出格式,同时还提供了自定义格式的插件功能,支持 Feild 机制和可扩展的 Hook 机制。

项目地址: https://github.com/sirupsen/logrus
项目文档: https://pkg.go.dev/github.com/sirupsen/logrus

logrus 不推荐使用冗长的消息来记录运行信息,它推荐使用Fields来进行精细化的结构化的信息记录,例如:

# 不建议此种方式
log.Fatalf("Failed to send event %s to topic %s with key %d", event, topic, key)

# 鼓励使用以下方式替代
log.WithFields(log.Fields{
  "event": event,
  "topic": topic,
  "key": key,
}).Fatal("Failed to send event")

日志等级: 其中 Trace 优先级最低,Panic 优先级最高。

  • Panic:恐慌级别,也是最高级别的日志,会打印出错误堆栈。

  • Fatal:致命错误,输出日志后,执行 exit(1) 退出

  • Error:错误日志,必须记录与跟踪的日志

  • Warn:警告日志,主要记录需要提醒开发者的日志

  • Info:主要是提供一些必要的日志信息,在业务出现问题时,可以结合error日志快速定位问题,一般会默认使用该级别的日志。

  • Debug:调试信息,方便开发测试阶段的问题定位

  • Trace:比 debug 级别还低,一般很少用。

log.Trace("Something very low level.")
log.Debug("Useful debugging information.")
log.Info("Something noteworthy happened!")
log.Warn("You should probably take a look at this.")
log.Error("Something failed but I'm not quitting.")
log.Fatal("Bye.") // Calls os.Exit(1) after logging
log.Panic("I'm bailing.") // Calls panic() after logging

默认字段:
除了使用WithField或WithFields添加的字段外,还会自动将一些字段添加到所有日志事件中:

  • time : The timestamp when the entry was created.

  • msg :The logging message passed to {Info,Warn,Error,Fatal,Panic} after the AddFields call. E.g. Failed to send event.

  • level :The logging level. E.g. info.

日志格式

  • TEXTFormatter 属性说明:https://pkg.go.dev/github.com/sirupsen/logrus#TEXTFormatter (支持tty终端颜色显示)

  • JSONFormatter 属性说明: https://pkg.go.dev/github.com/sirupsen/logrus#JSONFormatter

模块下载:

go get -v -u  github.com/sirupsen/logrus

示例演示:

  • 示例1.简单使用快速上手

package main
import (
	"os"
	log "github.com/sirupsen/logrus"
)

func main() {
	// logrus 标准库使用输出方法
	log.Println("标准输出!")

	// logrus 级别使用输出方法
	log.Traceln("Trace 级别")
	log.Infoln("Info 级别")
	log.Errorln("Error 级别")
	// 注意Fatal和Panic类型的日志会中断程序的运行。
	// log.Panicln("Panic 级别")

	// logrus 输出日志时可以附带参数
	log.WithFields(log.Fields{
		"flag": true,
		"name": "WeiyiGeek",
		"site": "https://blog.weiyigeek.top",
	}).Info("Info 级别信息")

	// logrus 日志输出的格式及颜色控制
	log.SetFormatter(&log.JSONFormatter{
    TimestampFormat: "2006-01-02 15:04:05",
  })
	log.SetFormatter(&log.TextFormatter{
    TimestampFormat: "2006-01-02 15:04:05",
		ForceColors:   true,
		FullTimestamp: true,
	})
	// 设置标准记录器输出。
	log.SetOutput(os.Stdout)
	// 设置最低的日志等级
	log.SetLevel(log.WarnLevel)
}

执行结果:

$ go run .\logging.go
time="2023-04-14T13:18:10+08:00" level=info msg="标准输出!"
time="2023-04-14T13:18:10+08:00" level=info msg="Info 级别"
time="2023-04-14T13:18:10+08:00" level=error msg="Error 级别"
time="2023-04-14T13:18:10+08:00" level=info msg="Info 级别信息" flag=true name=WeiyiGeek site="https://blog.weiyigeek.top"

示例2.同时将日志输出到终端和日志文件中。

package main
import (
	"io"
	"os"
	"path"
	"github.com/sirupsen/logrus"
)
// 实现输出日志文件到多个位置
func main() {
	// 实例化logrus
	var logger = logrus.New()

  // 格式设置
	logger.SetFormatter(&logrus.JSONFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})
  
  // 显示行号等信息
	logger.SetReportCaller(true) 
 
	// 日志目录与名称
	logPath := "D:/Study/Project/Go/hello-gin/logs"
	logName := "app"
	logFile := path.Join(logPath, logName+".log")

	// 创建并打开文件
	file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeAppend)
	fileAndStdout := io.MultiWriter(file, os.Stdout)
	if err == nil {
		logger.SetOutput(fileAndStdout)
	} else {
		logger.Errorf("Failed to log to file, using default stderr", err)
	}
	defer file.Close()

	// 设置最低显示日志
	logger.SetLevel(logrus.DebugLevel)

	// 各类别日志输出
	logger.Debugln("我是Debug信息!")
	logger.Info("我是普通信息!")
	logger.Errorln("我是Error信息!")

  // 生产环境中实践
  logger.WithFields(logrus.Fields{
		"request_id": "887B4F43-D330-5213-94DF-1B14FA3388C",
		"ip":         "110.110.110.110",
		"user_id":    1001,
	}).Info("Info Level")
  // 或者
  logger.WithField("request_id", "887B4F43-D330-5213-94DF-1B14FA3388C").WithField("ip", "110.110.110.110").WithField("user_id", 1001).Info("Info Level");
}

执行结果:

$ go run .\logging.go
{"level":"debug","msg":"我是Debug信息!","time":"2023-04-14 14:39:44"}
{"level":"info","msg":"我是普通信息!","time":"2023-04-14 14:39:44"}
{"level":"error","msg":"我是Error信息!","time":"2023-04-14 14:39:44"}

温馨提示:我们还可以创建一个logrus.Entry实例,为这个实例设置默认Fields,把logrus.Entry实例设置到记录器Logger,再记录日志时每次都会附带上这些默认的字段。

logger := log.WithFields(log.Fields{"request_id": "887B4F43-D330-5213-94DF-1B14FA3388C"})
logger.Info("Info Level") // 后续输出也会记录 request_id
logger.Warn("Warn Levle")

温馨提示:默认情况下,logrus的api都是线程安全的,其内部通过互斥锁来保护并发写。互斥锁工作于调用hooks或者写日志的时候。如果不需要锁,可以调用logger.SetNoLock()来关闭之,通常在没有设置hook,或者所有的hook都是线程安全的实现,再或者写日志到logger.Out已经是线程安全的了

扩展学习:

  • 自定义 HOOK :
    logrus最令人心动的功能就是其可扩展的HOOK机制了,通过在初始化时为logrus添加hook,便可以实现各种扩展功能.
    logrus的hook接口定义如下,其原理是每此写入日志时拦截修改logrus.Entry.

// logrus在记录Levels()返回的日志级别的消息时会触发HOOK,
// 按照Fire方法定义的内容修改logrus.Entry.
type Hook interface {
    Levels() []Level
    Fire(*Entry) error
}

例如,自定义一个DefaultFieldHook,它在所有级别的日志消息中加入默认字段appName="weiyigeek".

type DefaultFieldHook struct {
}

func (hook *DefaultFieldHook) Fire(entry *log.Entry) error {
    entry.Data["appName"] = "weiyigeek"
    return nil
}

func (hook *DefaultFieldHook) Levels() []log.Level {
    return log.AllLevels
}

偷偷的告诉你哟?极客全栈修炼】微信小程序已经上线了,

可直接在微信里面直接浏览博主博客了哟,后续将上线更多有趣的小工具。


lestrrat-go/file-rotatelogs 模块 - 日志分隔

描述: 由于logrus并不自带日志本地文件分割功能,所以我们使用file-rotatelogs模块进行分隔,它是提供一个 io.Writer 那定期转录文件在应用程序中, 注意 file-rotatelogs 项目已于 Jul 19, 2021 停止更新维护。

官网地址: https://github.com/lestrrat-go/file-rotatelogs

模块属性

  • Pattern : 日志文件名称模式。

  • WithLinkName("/path/to/log"):实际日志文件的符号链接所在的路径。

Main log file name   ->	Link name 	   -> Linked path
/path/to/log.%Y%m%d  ->	/path/to/log   ->	log.YYYYMMDD
  • WithRotationTime(24*time.Hour):默认24小时, 文件旋转之间的间隔。

  • WithMaxAge(-1):默认每7天清除之前旧日志。

  • WithRotationCount(7): 设置应保留文件的数量。

  • ForceNewFile() :确保每次调用new()时都会创建一个新文件,如果基本文件名已经存在,则执行隐式旋转。

温馨提示: WithMaxAge 和 WithRotationCount 二者只能设置一个。

示例演示:

// 使用rotatelogs完成日志分割、日志定期清理、生成软链文件指向最新日志
InfologWriter, err := rotatelogs.New(
  // 分割后的文件名称
  infologFile+".%Y%m%d",
  // 生成软链,指向最新日志文件
  rotatelogs.WithLinkName(infologFile),
  // 设置最大保存时间(7天)
  rotatelogs.WithMaxAge(7*24*time.Hour),
  // 设置日志切割时间间隔(1天)
  rotatelogs.WithRotationTime(24*time.Hour),
)
if err != nil {
  fmt.Println("Failed to create logfile" + errorlogFile)
  panic(err)
}

rifflock/lfshook 模块 - 本地文件系统挂钩

描述: 有时开发人员喜欢直接写入文件系统上的文件, 它是logrus的一个钩子,旨在允许用户这样做, 日志级别在钩子的实例化时是动态的,因此它能够在某些或所有级别进行日志记录。

项目地址: https://github.com/rifflock/lfshook

亲,文章就要看完了,不关注一下作者吗?

fdc33ed9ccb285637dc86dab2ed72ba0.jpeg

综合演示:

package main

import (
	"io"
	"os"
	"path"
	"time"

	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
)

func NewLogger() *logrus.Logger {
	var logger = logrus.New()

	// 日志目录与名称
	logPath := "D:/Study/Project/Go/hello-gin/logs"
	logName := "app"
	logFile := path.Join(logPath, logName)

	// 创建并打开文件
	file, err := os.OpenFile(logFile+".log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModeAppend)
	fileAndStdout := io.MultiWriter(file, os.Stdout)
	if err == nil {
		logger.SetOutput(fileAndStdout)
	} else {
		logger.Errorf("Failed to log to file, using default stderr", err)
	}
	// defer file.Close()

	// 分别针对INFO/DEBUG/WARN/ERROR等日志等级,进行日志分割、日志定期清理、生成软链文件指向最新日志
	InfologWriter, _ := rotatelogs.New(
		logFile+"-info.%Y%m%d",
		rotatelogs.WithLinkName(logFile+".log"),
		rotatelogs.WithMaxAge(7*time.Duration(86400)*time.Second),
		rotatelogs.WithRotationTime(time.Duration(86400)*time.Second),
	)

	DebuglogWriter, _ := rotatelogs.New(
		logFile+"-debug.%Y%m%d",
		rotatelogs.WithMaxAge(7*time.Duration(86400)*time.Second),
		rotatelogs.WithRotationTime(time.Duration(86400)*time.Second),
	)

	WarnlogWriter, _ := rotatelogs.New(
		logFile+"-warn.%Y%m%d",
		rotatelogs.WithMaxAge(7*time.Duration(86400)*time.Second),
		rotatelogs.WithRotationTime(time.Duration(86400)*time.Second),
	)

	ErrorlogWriter, _ := rotatelogs.New(
		logFile+"-error.%Y%m%d",
		rotatelogs.WithMaxAge(7*time.Duration(86400)*time.Second),
		rotatelogs.WithRotationTime(time.Duration(86400)*time.Second),
	)

	// 使用lfshook决定哪些级别的日志可以使用rotatelogs的切割设置,并决定输出格式(TEXT / JSON)。
	lfHook := lfshook.NewHook(lfshook.WriterMap{
		logrus.DebugLevel: DebuglogWriter,
		logrus.InfoLevel:  InfologWriter,
		logrus.WarnLevel:  WarnlogWriter,
		logrus.ErrorLevel: ErrorlogWriter,
	}, &logrus.JSONFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})

	// 为 logurs 新增 Hook
	logger.AddHook(lfHook)

	// 日志输出格式设置
	logger.SetFormatter(&logrus.JSONFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})

	// 设置最低显示日志
	logger.SetLevel(logrus.DebugLevel)

	return logger
}

func main() {
	log := NewLogger()
	log.Debug("Debug - Useful debugging information.")
	log.Info("Info - Something noteworthy happened!")
	log.Warn("Warn - You should probably take a look at this.")
	log.Error("Error - Something failed but I'm not quitting.")
}

执行结果:

$ go run .\logrotate.go
{"level":"debug","msg":"Debug - Useful debugging information.","time":"2023-04-14 16:04:25"}
failed to rotate: failed to rename new symlink: rename D:/Study/Project/Go/hello-gin/logs/app-info.20230414_symlink D:/Study/Project/Go/hello-gin/logs/app.log: Access is denied.
{"level":"info","msg":"Info - Something noteworthy happened!","time":"2023-04-14 16:04:25"}
{"level":"warning","msg":"Warn - You should probably take a look at this.","time":"2023-04-14 16:04:25"}
{"level":"error","msg":"Error - Something failed but I'm not quitting.","time":"2023-04-14 16:04:25"}

温馨提示: 在执行时如果出现 failed to rotate: failed to create new symlink: symlink m A required privilege is not held by the client. 表示执行的终端没有管理员权限,如果你是WINDOWS此处你需要在开始菜单中右键以管理员运行Shell终端或者在Powershell中执行Start-Process powershell -Verb runAs命令。

本文至此完毕,更多技术文章,尽情等待下篇好文!

原文地址: https://blog.weiyigeek.top/2023/4-15-729.html

如果此篇文章对你有帮助,请你将它分享给更多的人! 

c93ceb6f099e14ed0f650cbb51ff1713.gif

f06d19d3b623ea0cb8882f95e9c63259.png 学习书籍推荐 往期发布文章 676c1fe8038772cfab88d77a73d2acb7.png

公众号回复【0008】获取【Ubuntu22.04安装与加固建脚本】

公众号回复【10001】获取【WinServer安全加固脚本】

公众号回复【1000】获取【PowerShell操作FTP脚本】

公众号回复【0015】获取【Jenkins学习之路汇总】

 热文推荐  

  • 容灾恢复 | 记一次K8S集群中etcd数据快照的备份恢复实践

  • 企业实践 | 如何从VMWare ESXi Shell中挂载以及拷贝NTFS或者FAT32分区格式的USB闪存驱动器

  • 奇技淫巧 | 快速提升网站权重,巧用GPT4自动化大数据生成关键字指数进行SEO排名优化

  • 网安等保-国产Linux操作系统银河麒麟KylinOS-V10SP3常规配置、系统优化与安全加固基线实践文档

欢迎长按(扫描)二维码 391c5c924703c237c83cde82064fc4b8.gif取更多渠道哟!

0673de6c6d23de740188a43a87972bd2.jpeg1b2aa7b8c3b2637999b29dcf68874253.jpeg7daabe93967871d861225be955fe7328.jpeg

a4bcf553f74e07a16eb5e89f4cd42e3b.gif

欢迎关注 【全栈工程师修炼指南】(^U^)ノ~YO

== 全栈工程师修炼指南 ==

微信沟通交流: weiyigeeker 

关注回复【学习交流群】即可加入【安全运维沟通交流小群

温馨提示: 由于作者水平有限,本章错漏缺点在所难免,希望读者批评指正,若有问题或建议请在文章末尾留下您宝贵的经验知识,或联系邮箱地址

master@weiyigeek.top 或 关注公众号 [全栈工程师修炼指南] 留言。

[全栈工程师修炼指南]  关注 企业运维实践、网络安全、系统运维、应用开发、物联网实战、全栈文章,尽在博客站点,谢谢支持!

点个【 赞 + 在 】看吧!

4f982c87e05baabd61a88cbb0c2e0083.gif 点击【"阅读原文"】获取更多有趣的知识!   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/591775.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

比较专业的成体系OJ题库和近期整理的比赛题目

比较专业的成体系OJ题库和近期整理的比赛题目,一个优秀的OJ系统是不断剔除和补充题目的完善过程 ,不是越多越好,而是符合孩子们学习阶段的需求, 类似的题目有2-3题就好,方便学生举一反三,另外同一题 要求回…

两个offer:一个996,月薪3万;一个885,月薪2万,怎么选?

找工作时,钱和闲,你选哪个? 一位网友拿到了两个offer,一个996,月薪3万,一个885,月薪2万,怎么选? 一部分网友选择885,因为自己是打工人,不是打工奴…

为什么职场中35岁之后很难找到合适的工作?

(点击即可收听) 为什么职场中35岁之后很难找到合适的工作 无论是初入职场还是,职场多年的老司机,都听过一个35岁危机的一个话题 无论是企业还是一些招聘者,针对35,甚至就是30的人,充满了不是这样,就是那样的偏见的理由 每个公司都喜欢有激情,有想法,有干劲的年轻人,无论哪个公司…

大学四年,因为这8个网站,我成为同学眼中的学霸

「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 大学期间,几乎每一个教过我的老师都反应,我的学习态度不好&#x…

设计模式之~观察者模式

观察者模式又叫做发布-订阅(Publish/Subscribe)模式。 观察者模式observer:定义了一种一对多的依赖关系,让多个观察者对象同时监听某个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们…

《计算机组成原理》唐朔飞 第8章 CPU的结构和功能 - 学习笔记

写在前面的话:此系列文章为笔者学习计算机组成原理时的个人笔记,分享出来与大家学习交流。使用教材为唐朔飞第3版,笔记目录大体与教材相同。 网课 计算机组成原理(哈工大刘宏伟)135讲(全)高清_…

实现分布式实体追踪:提升系统可见性和故障排查能力

引言: 在当今复杂的分布式系统中,追踪用户请求的执行过程变得越来越重要。为了获得全面的系统可见性和更高效的故障排查能力,我们在Flipkart采用了分布式实体追踪的解决方案。本文将介绍我们的实施策略,以及如何使用结构化日志和集…

IMG CXM GPU:面向复杂消费级设备的无缝视觉体验

上周我们推出了一款新的GPU,即IMG CXM。它的三种配置可扩展,为可穿戴设备和高级数字电视等多种消费设备提供无缝用户界面。 消费级设备需要GPU提供什么? 涵盖智能手表和智能眼镜的可穿戴市场为移动中的消费者提供了易于访问的信息。鉴于屏幕尺…

一文解读 AIGC 驱动高绩效商业的落地与思考

本文根据神策数据智能业务负责人郭荣锋《AIGC 驱动高绩效商业的实践》的主题演讲整理所得,主要围绕神策对 AIGC (即 AI-Generated Content,人工智能生成内容)业务应用的理解、AIGC 的落地实践及心得体会等方面展开。 以下为本文的…

运行 100 万个并发任务究竟需要多少内存?

Laf 公众号已接入了 AI 绘画工具 Midjourney&#xff0c;可以让你轻松画出很多“大师”级的作品。同时还接入了 AI 聊天机器人&#xff0c;支持 GPT、Claude 以及 Laf 专有模型&#xff0c;可通过指令来随意切换模型。欢迎前来调戏&#x1f447; <<< 左右滑动见更多 &…

Tomcat文件夹属性

Tomcat安装完成后&#xff0c;其安装目录下包含bin、conf、lib、logs、temp、webapps、work等子目录&#xff0c;各个子目录简介如下&#xff1a; &#xff08;1&#xff09;bin目录。主要存放Tomcat的命令文件。&#xff08;解压缩版点击bin下的startup.bat&#xff0c;即可运…

4.2 字节流与字符流

在Java中&#xff0c;有两种基本的数据流类型&#xff1a;字节流和字符流。字节流处理原始二进制数据&#xff0c;而字符流处理Unicode字符。本章节我们将学习字节流与字符流的基本概念以及如何使用它们进行文件的输入输出操作。 4.2.1 字节流 字节流处理原始二进制数据&…

打造音视频极致消费体验

在观看视频时&#xff0c;用户最看重的是什么呢&#xff1f;清晰度&#xff1f;流畅度&#xff1f;还是播放时的稳定性&#xff1f;作为视频厂商&#xff0c;不仅要考虑到常见的指标&#xff0c;一些关乎用户体验的隐藏性指标也需要重点关注。如何持续升级优化代码并在成本和用…

JointJS+ v3.7 Crack

JointJS v3.7 改进了对 SVG 上下文中的外部对象的支持。 2023 年 5 月 30 日 - 16:00 新版本 特征 改进了对外部对象 (HTML) 的支持- 外部对象已成为 Web 开发的标准&#xff0c;JointJS 现在已经在 SVG 上下文中引入了对外部对象的全面且功能齐全的支持。这意味着您现在可以在…

工作积极主动分享,善于业务沟通

工作积极主动分享&#xff0c;善于业务沟通 目录概述需求&#xff1a; 设计思路实现思路分析1.工作积极主动承担责任2.善于沟通3.一起常常lauch 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;…

【JavaSE】Java基础语法(三十九):网络编程入门

文章目录 1. 网络编程概述2. 网络编程三要素3. IP地址4. InetAddress5. 端口和协议 1. 网络编程概述 计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络 操作系统&#xff0c;网络管理软件及网络通信协…

MyBatis 环境搭建+基本使用

目录 MyBatis创建MyBatis环境搭建MyBatis模式开发MyBatis 获取动态参数&#xff08;查询操作&#xff09;${} 直接替换#{} 占位符模式替换like查询&#xff08;模糊查询&#xff09;多表查询一对一的表映射一对多的表映射 增、删、改操作改操作删除操作增加操作添加用户添加用户…

chatgpt赋能python:Python中的英文单词

Python中的英文单词 Python是一种流行的编程语言&#xff0c;它具有人类易读性、功能强大、支持多种编程范例等特点。Python中包含着大量的英文单词&#xff0c;这些单词在Python编程中极为重要&#xff0c;因为它们直接影响代码的可读性和理解难度。本文将介绍一些最常用的Py…

Go开发学习 | 如何使用Gomail.v2模块包发送邮箱验证码消息及附件学习记录

欢迎关注「全栈工程师修炼指南」公众号 点击 &#x1f447; 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习&#xff01; “ 花开堪折直须折&#xff0c;莫待无花空折枝。 ” 作者主页&#xff1a;[ https://www.weiyigeek.top ] 博客&…

vulhub-Jarbas(易)

打靶练习Jarbas 0x00 部署0x01 信息收集&#xff1a;端口扫描、服务发现0x02 路径爬取0x03 反弹shell0x04 内网信息收集0x05 crontab定时任务提权0x06 总结 0x00 部署 靶机&#xff1a;下载地址 宿主机&#xff1a;kali2021版本 0x01 信息收集&#xff1a;端口扫描、服务发现…