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

golang:微服务架构下的日志追踪系统(二)

背景

在使用Gin框架进行服务开发时,我们遇到了一个日志记录的问题。由于Gin的上下文(*gin.Context)实现了context.Context接口,在调用日志记录器的InfoWarnError等方法时,直接传递Gin的上下文通常不会导致编译错误。会导致我们在《golang:微服务架构下的日志追踪系统》一文中定义的日志统计维度信息无法正确串联。

为了解决这一问题,我们之前的解决方案是让业务方在使用日志记录器时,将Gin的上下文手动转换为context.Context

但这种方案带来了一个明显的弊端:它依赖于开发人员的主动转换,而开发人员往往会忘记进行这一步操作,直接传递Gin的上下文,从而导致日志并未按照预期格式打印。这不仅增加了开发人员的负担,也降低了日志系统的可靠性和准确性。

解决方案

针对上述问题,我们提出了一种更为优雅的解决方案:在日志记录器的方法内部封装Gin上下文到context.Context的转换逻辑。这样一来,开发人员在使用日志记录器时无需再关心上下文的转换问题,只需直接传递Gin的上下文即可。

接下来,我们将这一转换逻辑集成到日志记录器的各个方法中,确保无论传入的是Gin的上下文还是context.Context,都能正确记录日志信息。

在封装的方法中实现gin的context的转换即可。

// gin context to context
func GinContextToContext(ctx context.Context) context.Context {if v, ok := ctx.(*gin.Context); ok {if v == nil || v.Request == nil {return context.Background()}return v.Request.Context()}return ctx
}

完整的逻辑。

package loggerimport ("context""errors""fmt""time""{{your module}}}/go-core/utils/constant""github.com/gin-gonic/gin""go.uber.org/zap""go.uber.org/zap/zapcore""gorm.io/gorm""gorm.io/gorm/logger""gorm.io/gorm/utils"
)// Logger logger for gorm2
type Logger struct {log *zap.Loggerlogger.ConfigcustomFields []func(ctx context.Context) zap.Field
}// Option logger/recover option
type Option func(l *Logger)// WithCustomFields optional custom field
func WithCustomFields(fields ...func(ctx context.Context) zap.Field) Option {return func(l *Logger) {l.customFields = fields}
}// WithConfig optional custom logger.Config
func WithConfig(cfg logger.Config) Option {return func(l *Logger) {l.Config = cfg}
}// SetGormDBLogger set db logger
func SetGormDBLogger(db *gorm.DB, l logger.Interface) {db.Logger = l
}// New logger form gorm2
func New(zapLogger *zap.Logger, opts ...Option) logger.Interface {l := &Logger{log: zapLogger,Config: logger.Config{SlowThreshold:             200 * time.Millisecond,Colorful:                  false,IgnoreRecordNotFoundError: false,LogLevel:                  logger.Warn,},}for _, opt := range opts {opt(l)}return l
}// NewDefault new default logger
// 初始化一个默认的 logger
func NewDefault(zapLogger *zap.Logger) logger.Interface {return New(zapLogger, WithCustomFields(func(ctx context.Context) zap.Field {v := ctx.Value("Request-Id")if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String("trace", vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {v := ctx.Value("method")if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String("method", vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {v := ctx.Value("path")if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String("path", vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {v := ctx.Value("version")if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String("version", vv)}return zap.Skip()}),WithConfig(logger.Config{SlowThreshold:             200 * time.Millisecond,Colorful:                  false,IgnoreRecordNotFoundError: false,LogLevel:                  logger.Info,}))
}// 用于支持微服务架构下的链路追踪
func NewTracingLogger(zapLogger *zap.Logger) logger.Interface {return New(zapLogger, WithCustomFields(// trace是链路追踪的唯一标识// span是当前请求的唯一标识// parent_span是父请求的唯一标识func(ctx context.Context) zap.Field {v := ctx.Value(constant.CONTEXT_KEY_TRACE)if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String(constant.CONTEXT_KEY_TRACE, vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {v := ctx.Value(constant.CONTEXT_KEY_SPAN)if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String(constant.CONTEXT_KEY_SPAN, vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {v := ctx.Value(constant.CONTEXT_KEY_PARENT_SPAN)if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String(constant.CONTEXT_KEY_PARENT_SPAN, vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {v := ctx.Value(constant.CONTEXT_KEY_METHOD)if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String(constant.CONTEXT_KEY_METHOD, vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {v := ctx.Value(constant.CONTEXT_KEY_PATH)if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String(constant.CONTEXT_KEY_PATH, vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {v := ctx.Value(constant.CONTEXT_KEY_VERSION)if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String(constant.CONTEXT_KEY_VERSION, vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {// 用于标识调用方服务名v := ctx.Value(constant.CONTEXT_KEY_CALLER_SERVICE_NAME)if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String(constant.CONTEXT_KEY_CALLER_SERVICE_NAME, vv)}return zap.Skip()}, func(ctx context.Context) zap.Field {// 用于标识调用方ipv := ctx.Value(constant.CONTEXT_KEY_CALLER_IP)if v == nil {return zap.Skip()}if vv, ok := v.(string); ok {return zap.String(constant.CONTEXT_KEY_CALLER_IP, vv)}return zap.Skip()}),WithConfig(logger.Config{SlowThreshold:             200 * time.Millisecond,Colorful:                  false,IgnoreRecordNotFoundError: false,LogLevel:                  logger.Info,}))
}// gin context to context
func GinContextToContext(ctx context.Context) context.Context {if v, ok := ctx.(*gin.Context); ok {if v == nil || v.Request == nil {return context.Background()}return v.Request.Context()}return ctx
}// LogMode log mode
func (l *Logger) LogMode(level logger.LogLevel) logger.Interface {newLogger := *lnewLogger.LogLevel = levelreturn &newLogger
}// Info print info
func (l Logger) Info(ctx context.Context, msg string, args ...interface{}) {if l.LogLevel >= logger.Info {ctx = GinContextToContext(ctx)//预留10个字段位置fields := make([]zap.Field, 0, 10+len(l.customFields))fields = append(fields, zap.String("file", utils.FileWithLineNum()))for _, customField := range l.customFields {fields = append(fields, customField(ctx))}now := time.Now().UnixMilli()// 从ctx中获取操作的开始时间if v := ctx.Value(constant.CONTEXT_KEY_EXECUTE_START_TIME); v != nil {if vv, ok := v.(int64); ok {// 计算操作的执行时间,以毫秒为单位duration := now - vv// 将操作的执行时间放入ctxfields = append(fields, zap.Int64(constant.CONTEXT_KEY_EXECUTE_DURATION, duration))}}for _, arg := range args {if vv, ok := arg.(zapcore.Field); ok {if len(vv.String) > 0 {fields = append(fields, zap.String(vv.Key, vv.String))} else if vv.Integer > 0 {fields = append(fields, zap.Int64(vv.Key, vv.Integer))} else {fields = append(fields, zap.Any(vv.Key, vv.Interface))}}}l.log.Info(msg, fields...)}
}// Warn print warn messages
func (l Logger) Warn(ctx context.Context, msg string, args ...interface{}) {if l.LogLevel >= logger.Warn {ctx = GinContextToContext(ctx)//预留10个字段位置fields := make([]zap.Field, 0, 10+len(l.customFields))fields = append(fields, zap.String("file", utils.FileWithLineNum()))for _, customField := range l.customFields {fields = append(fields, customField(ctx))}for _, arg := range args {if vv, ok := arg.(zapcore.Field); ok {if len(vv.String) > 0 {fields = append(fields, zap.String(vv.Key, vv.String))} else if vv.Integer > 0 {fields = append(fields, zap.Int64(vv.Key, vv.Integer))} else {fields = append(fields, zap.Any(vv.Key, vv.Interface))}}}l.log.Warn(msg, fields...)}
}// Error print error messages
func (l Logger) Error(ctx context.Context, msg string, args ...interface{}) {if l.LogLevel >= logger.Error {ctx = GinContextToContext(ctx)//预留10个字段位置fields := make([]zap.Field, 0, 10+len(l.customFields))fields = append(fields, zap.String("file", utils.FileWithLineNum()))for _, customField := range l.customFields {fields = append(fields, customField(ctx))}for _, arg := range args {if vv, ok := arg.(zapcore.Field); ok {if len(vv.String) > 0 {fields = append(fields, zap.String(vv.Key, vv.String))} else if vv.Integer > 0 {fields = append(fields, zap.Int64(vv.Key, vv.Integer))} else {fields = append(fields, zap.Any(vv.Key, vv.Interface))}}}l.log.Error(msg, fields...)}
}// Trace print sql message
func (l Logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {if l.LogLevel <= logger.Silent {return}ctx = GinContextToContext(ctx)fields := make([]zap.Field, 0, 6+len(l.customFields))elapsed := time.Since(begin)switch {case err != nil && l.LogLevel >= logger.Error && (!l.IgnoreRecordNotFoundError || !errors.Is(err, gorm.ErrRecordNotFound)):for _, customField := range l.customFields {fields = append(fields, customField(ctx))}fields = append(fields,zap.Error(err),zap.String("file", utils.FileWithLineNum()),zap.Duration("latency", elapsed),)sql, rows := fc()if rows == -1 {fields = append(fields, zap.String("rows", "-"))} else {fields = append(fields, zap.Int64("rows", rows))}fields = append(fields, zap.String("sql", sql))l.log.Error("", fields...)case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= logger.Warn:for _, customField := range l.customFields {fields = append(fields, customField(ctx))}fields = append(fields,zap.Error(err),zap.String("file", utils.FileWithLineNum()),zap.String("slow!!!", fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold)),zap.Duration("latency", elapsed),)sql, rows := fc()if rows == -1 {fields = append(fields, zap.String("rows", "-"))} else {fields = append(fields, zap.Int64("rows", rows))}fields = append(fields, zap.String("sql", sql))l.log.Warn("", fields...)case l.LogLevel == logger.Info:for _, customField := range l.customFields {fields = append(fields, customField(ctx))}fields = append(fields,zap.Error(err),zap.String("file", utils.FileWithLineNum()),zap.Duration("latency", elapsed),)sql, rows := fc()if rows == -1 {fields = append(fields, zap.String("rows", "-"))} else {fields = append(fields, zap.Int64("rows", rows))}fields = append(fields, zap.String("sql", sql))l.log.Info("", fields...)}
}// Immutable custom immutable field
// Deprecated: use Any instead
func Immutable(key string, value interface{}) func(ctx context.Context) zap.Field {return Any(key, value)
}// Any custom immutable any field
func Any(key string, value interface{}) func(ctx context.Context) zap.Field {field := zap.Any(key, value)return func(ctx context.Context) zap.Field { return field }
}// String custom immutable string field
func String(key string, value string) func(ctx context.Context) zap.Field {field := zap.String(key, value)return func(ctx context.Context) zap.Field { return field }
}// Int64 custom immutable int64 field
func Int64(key string, value int64) func(ctx context.Context) zap.Field {field := zap.Int64(key, value)return func(ctx context.Context) zap.Field { return field }
}// Uint64 custom immutable uint64 field
func Uint64(key string, value uint64) func(ctx context.Context) zap.Field {field := zap.Uint64(key, value)return func(ctx context.Context) zap.Field { return field }
}// Float64 custom immutable float32 field
func Float64(key string, value float64) func(ctx context.Context) zap.Field {field := zap.Float64(key, value)return func(ctx context.Context) zap.Field { return field }
}

测试用例

package loggerimport ("context""fmt""net/http""net/http/httptest""testing""time""{{your module}}/go-core/utils/constant""github.com/gin-gonic/gin""go.uber.org/zap"
)// Mock function to create a gin context
func createGinContext() *gin.Context {w := httptest.NewRecorder()// Create a mock request with an attached contextreq, _ := http.NewRequest(http.MethodPost, "/api/v1/users", nil)ctx := context.Background()// Here you can set values in the contextctx = context.WithValue(ctx, constant.CONTEXT_KEY_SPAN, "123456")ctx = context.WithValue(ctx, constant.CONTEXT_KEY_PARENT_SPAN, "parent_span_123456")ctx = context.WithValue(ctx, constant.CONTEXT_KEY_TRACE, "trace_id_123456")ctx = context.WithValue(ctx, constant.CONTEXT_KEY_METHOD, "POST")ctx = context.WithValue(ctx, constant.CONTEXT_KEY_PATH, "/api/v1/users")ctx = context.WithValue(ctx, constant.CONTEXT_KEY_VERSION, "v1.0.0")ctx = context.WithValue(ctx, constant.CONTEXT_KEY_CALLER_SERVICE_NAME, "user-service")ctx = context.WithValue(ctx, constant.CONTEXT_KEY_CALLER_IP, "172.0.0.3")req = req.WithContext(ctx)// Create the Gin contextc, _ := gin.CreateTestContext(w)c.Request = reqreturn c
}// 测试ginContextWithLogger
func TestGinContextWithLogger(t *testing.T) {// 创建一个 zap logger 实例zapLogger, _ := zap.NewProduction()defer zapLogger.Sync() // 确保日志被刷新// 创建一个带有自定义字段和配置的 Logger 实例customLogger := NewTracingLogger(zapLogger)// 创建一个 Gin contextc := createGinContext()// 测试 Info 方法customLogger.Info(c, "This is an info message")// 测试 Warn 方法customLogger.Warn(c, "This is a warning message")// 测试 Error 方法customLogger.Error(c, "This is an error message")// 测试 Trace 方法,模拟一个慢查询slowQueryBegin := time.Now()slowQueryFunc := func() (string, int64) {return "SELECT * FROM users", 100}time.Sleep(2 * time.Second) // 模拟一个慢查询customLogger.Trace(c, slowQueryBegin, slowQueryFunc, nil)// 测试 Trace 方法,模拟一个错误查询errorQueryBegin := time.Now()errorQueryFunc := func() (string, int64) {return "SELECT * FROM non_existent_table", 0}customLogger.Trace(c, errorQueryBegin, errorQueryFunc, fmt.Errorf("table not found"))// 由于日志是异步的,我们需要在测试结束时等待一段时间以确保所有日志都被输出time.Sleep(500 * time.Millisecond)
}

 

相关文章:

golang:微服务架构下的日志追踪系统(二)

背景 在使用Gin框架进行服务开发时&#xff0c;我们遇到了一个日志记录的问题。由于Gin的上下文&#xff08;*gin.Context&#xff09;实现了context.Context接口&#xff0c;在调用日志记录器的Info、Warn、Error等方法时&#xff0c;直接传递Gin的上下文通常不会导致编译错误…...

单片机的存储器类型

单片机&#xff08;Microcontroller Unit, MCU&#xff09;是一种将计算机的主要部分集成在一个芯片上的微型计算机。它集成了处理器(CPU)、存储器、输入输出接口等必要的功能模块&#xff0c;广泛应用于各种嵌入式系统中。单片机的存储器结构对于理解和使用单片机至关重要&…...

深入剖析MySQL数据库架构:核心组件、存储引擎与优化策略(一)

sql语句分为两大类&#xff1a;查询&#xff08;select&#xff09;、增删改----修改&#xff08;update&#xff09; select语句的执行流程 执行sql语句的流程&#xff1a;连接数据库、缓存查询、解析器、优化器、执行器、存储引擎操作数据 客户端&#xff1a;图形界面工具…...

java常见的面试题

目录 一、 spring的配置文件的哪四处? 1.数据源&#xff08;Data Source&#xff09;配置 2.事务管理器&#xff08;Transaction Manager&#xff09;配置 3. 扫描组件&#xff08;Component Scanning&#xff09;配置 4. AOP&#xff08;面向切面编程&#xff09;配置&…...

2025吉林大学软件学院研究生人工智能原理真题

选择题 5题共10分 5.以下模型不需要训练过程的是&#xff1f; A.支持向量机 B.决策树 C.KNN近邻算法 D.卷积神经网络 简答题 4题35分 1.大模型是否已经通过图灵测试 2.三个近二十年来的机器学习模型和应用案例 3.LDA原理和算法流程 4.CNN原理和算法流程 证明题 谓词逻辑15…...

Qt判别不同平台操作系统调用相应动态库读取RFID

本示例使用的读卡器&#xff1a;https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.52de2c1b8jdyXi&ftt&id562957272162 #include <QDebug> #include "mainwindow.h" #include "./ui_mainwindow.h" #include "QLibrary"…...

智慧工地系统:建筑施工智能化管理的全新模式

智慧工地概述 智慧工地是将互联网的理念和和物联网的技术引入建筑工地&#xff0c;依托物联网、互联网、大数据、5G技术&#xff0c;建立云端数据平台&#xff0c;形成大数据的业务体系&#xff0c;打通一线操作与远程监管的链条&#xff0c;实现劳务、安全、环境、材料等各个…...

js将object整个实体对象作为参数传递

①将object实体转化成json字符串传递&#xff1a; JSON.stringify(obj) ②将json字符串转化成JSON对象值&#xff1a;JSON.parse(json) 实际应用&#xff1a; <div id"div_notice" stylefont-size:14px; width:100%; height:200px; overflow-y:auto;></di…...

shell的循环结构

1、思维导图 2、定义一个find函数&#xff0c;查找ubuntu和root的gid并使用变量接收结果 #!/bin/bash #第二题查找gid并返回结果 find() {u_gidid -g ubuntur_gidid -g rootecho "root-gid$r_gid"echo "ubuntu-gid$u_gid" } ret$(find) echo $ret3、定义一…...

STM32-笔记29-蓝牙遥控插座项目

一、实验前期准备 手机通过蓝牙模块远程遥控风扇。&#xff08;插座的原理就是继电器&#xff0c;所以控制继电器就是控制插座电源&#xff09; 二、项目实现 复制项目文件夹32-蓝牙模块实现&#xff0c;重命名33-蓝牙遥控插座项目 找到一个继电器项目&#xff0c;把继电器复…...

计算机网络复习(习题)

术语辨析 数据链路层 该层在两个通信实体之间传送以帧为单位的数据&#xff0c;通过差错控制方法,使有差错的物理线路变成无差错数据链路。 网络层 负责使分组以适当的路径通过通信子网的层次。 运输层 负责向两台主机中进程之间的通信提供通用的数据传输服务的层次。 应用层…...

webserver的http实现

1、用了状态机&#xff0c;为什么要用状态机&#xff1f; 在逻辑处理模块中&#xff0c;响应的http请求采用主从状态机完成&#xff0c; 传统的控制流程都是按照顺序执行的&#xff0c;状态机能够处理任意顺序的事件&#xff0c;并能提供有意义的响应--即使这些事件发生的顺序和…...

C语言----指针

目录 1.概念 2.格式 3.指针操作符 4.初始化 1. 将普通变量的地址赋值给指针变量 a. 将数组的首地址赋值给指针变量 b. 将指针变量里面保存的地址赋值给另一个指针变量 5.指针运算 5.1算术运算 5.2 关系运算 指针的大小 总结&#xff1a; 段错误 指针修饰 1. con…...

Elasticsearch与数据库数据一致性:最佳实践与解决方案

在现代应用程序中&#xff0c;Elasticsearch&#xff08;ES&#xff09;作为一个高效的分布式搜索引擎&#xff0c;常常与数据库一同使用&#xff0c;以提供强大的搜索、分析和数据可视化功能。然而&#xff0c;数据库和Elasticsearch之间的同步与一致性常常成为一个挑战。如何…...

C# 基本语法

C# 基本语法 介绍 C#&#xff08;读作 "C sharp"&#xff09;是一种现代的、面向对象的编程语言&#xff0c;由微软开发&#xff0c;并在2000年首次发布。它是.NET框架的一部分&#xff0c;被广泛用于开发各种类型的应用程序&#xff0c;包括桌面应用、Web应用、移…...

CDGA|浅析自动化对数据治理的深远影响

在数字化时代&#xff0c;数据治理已成为企业管理的核心议题之一。随着数据量的快速增长和复杂性的不断提升&#xff0c;传统的手工数据管理方式已难以满足企业的需求。而自动化技术的引入&#xff0c;为数据治理带来了新的动力&#xff0c;正在逐步改变数据治理的面貌。 自动化…...

【机器学习】【朴素贝叶斯分类器】从理论到实践:朴素贝叶斯分类器在垃圾短信过滤中的应用

&#x1f31f; 关于我 &#x1f31f; 大家好呀&#xff01;&#x1f44b; 我是一名大三在读学生&#xff0c;目前对人工智能领域充满了浓厚的兴趣&#xff0c;尤其是机器学习、深度学习和自然语言处理这些酷炫的技术&#xff01;&#x1f916;&#x1f4bb; 平时我喜欢动手做实…...

小程序租赁系统的优势与应用探索

内容概要 小程序租赁系统&#xff0c;听起来很高大上&#xff0c;但实际上它比你想象的要实用得多&#xff01;设想一下&#xff0c;几乎所有的租赁需求都能通过手机轻松解决。这种系统的便捷性体现在让用户随时随地都能发起租赁请求&#xff0c;而不再受制于传统繁琐的手续。…...

汇编环境搭建

学习视频 将MASM所在目录 指定为C盘...

ubuntu24.04使用open-vm-tools无法在主机和虚拟机之间拖拽文件夹

最近安装了vmware用ubuntu24.04作为虚拟机 然后发现无法在主机和虚拟机之间复制粘贴 然后安装了 sudo apt-get install open-vm-tools-desktop重启虚拟机&#xff0c;发现可以复制粘贴文字&#xff0c;但是文件和文件夹仍然不行 搜索发现是Ubuntu&#xff08;22.04&#xff0…...

C++软件设计模式之模板方法模式

模板方法模式是面向对象软件设计模式之一&#xff0c;其主要意图是在一个方法中定义一个算法的骨架&#xff0c;而将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下重新定义算法的某些特定步骤。 动机 在软件开发中&#xff0c;常常会遇到这样的情…...

20241231 机器学习ML -(2)KNN(scikitlearn)

1. build DKTree 递推创建Tree&#xff1b;当前维度找中位数分割 数据集 left set&#xff0c;Node(mid), right set. * 循环维度&#xff08;当log(Nsample)>featureSize) 2. DKTree KNN search * 理论部分向量几何有介绍。 每个维度列中&#xff0c;中位数对应的数据点…...

Prometheus之终极指南(The Ultimate Guide to Prometheus)

Prometheus之终极指南 Prometheus 彻底改变了我们在现代 DevOps 生态系统中监控基础设施、应用程序和服务的方式。它不仅仅是一个工具&#xff1b;它是一个由指标收集、告警和实时监控组成的生态系统&#xff0c;受到 Uber、Google 和 SoundCloud 等组织的信任。在本文中&…...

如何使用Python调用淘宝api接口获取商品详情信息?

使用 Python 调用淘宝 API 接口获取商品详情信息&#xff0c;可按照以下步骤进行&#xff1a; 注册并获取 API 密钥 访问淘宝api文档&#xff0c;点击 “立即测试” 按钮&#xff0c;按照提示完成注册流程。注册成功后&#xff0c;登录测试平台&#xff0c;进入 “控制台” 页面…...

ubuntu 22下解决Unment dependencies问题

问题现象 在使用apt安装包的时候&#xff0c;出现如下错误&#xff1a; 解决方案 第一步 sudo apt-get -f install sudo apt-get update sudo apt-get upgrade第二步 sudo apt-get update sudo apt-get clean sudo apt-get autoremove第三步 sudo apt --fix-broken inst…...

leetcode 热题100(155. 最小栈)multiset c++

链接&#xff1a;155. 最小栈 - 力扣&#xff08;LeetCode&#xff09; 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。…...

某小程序sign签名参数逆向分析

文章目录 1. 写在前面2. 接口分析3. 分析还原 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…...

阿里云redis内存优化——PCP数据清理

在阿里云安装了一个redis节点&#xff0c;今天使用时忽然想着点击了一下分析内存。好家伙&#xff0c;居然崩出了一个30多M的块出来。问题是我本地安装的redis没有这个啊&#xff0c;怎么奇怪冒出这个来了。 本着把系统用干榨尽的态度&#xff0c;研究了下这个问题的来源。网上…...

YOLOv10-1.1部分代码阅读笔记-head.py

head.py ultralytics\nn\modules\head.py 目录 head.py 1.所需的库和模块 2.class Detect(nn.Module): 3.class Segment(Detect): 4.class OBB(Detect): 5.class Pose(Detect): 6.class Classify(nn.Module): 7.class WorldDetect(Detect): 8.class RTDETRDec…...

java开发中注解汇总​​

注解作用位置注意mybatis Data Getter Setter ToString EqualsAndHashCode AllArgsConstructor NoArgsConstructor Data 代替&#xff1a;无参构造&#xff0c;get&#xff0c;set&#xff0c;toString&#xff0c;hashCode&#xff0c;equals Getter Setter 可放在类和方法上&…...

Java开发 PDF文件生成方案

业务需求背景 业务端需要能够将考试答卷内容按指定格式呈现并导出为pdf格式进行存档&#xff0c;作为紧急需求插入。导出内容存在样式复杂性&#xff0c;包括特定的字体&#xff08;中文&#xff09;、字号、颜色&#xff0c;页面得有页眉、页码&#xff0c;数据需要进行表格聚…...

Python机器学习笔记(十七、分箱、离散化、线性模型与树)

数据表示的最佳方法&#xff1a;取决于数据的语义&#xff0c;所使用的模型种类。 线性模型与基于树的模型&#xff08;决策树、梯度提升树和随机森林&#xff09;是两种成员很多同时又非常常用的模 型&#xff0c;它们在处理不同的特征表示时就具有非常不同的性质。我们使用w…...

[极客大挑战 2019]Http 1

进入环境&#xff1a; 检查源码发现有一个链接&#xff0c;但是这里没有绑定&#xff0c;需要手动跳转&#xff0c;打开后&#xff0c;发现提示&#xff1a; 这里就是需要我们从https://Sycsecret.buuoj.cn来访问它 因此我们抓包&#xff0c;使用referer&#xff1a;服务器伪造…...

最近学习shader的一些总结

旨在总结最近学习shader过程中一些关键要素&#xff0c;强化下记忆&#xff0c;如果有错误也烦请指出。 1.Properties 可调节变量,用于定义从外部传入到内部的变量, 以及外部通过访问这些变量名, 可以获取这些变量的值 其中定义时指定的类型, 在后文中类型不一定相同(基本不…...

庐山派K230学习日记1 从点灯到吃灰

1 简介​ 庐山派以K230为主控芯片&#xff0c;支持三路摄像头同时输入&#xff0c;典型网络下的推理能力可达K210的13.7倍&#xff08;算力约为6TOPS&#xff09;。支持CanMV&#xff0c;可作为AI与边缘计算平台 K230简介 K230芯片集成了两颗RISC-V处理器核心&#xff0c;双核…...

在Swagger(现称为OpenAPI)中各类@api之间的区别

在Swagger&#xff08;现称为OpenAPI&#xff09;中&#xff0c;ApiOperation 是用来描述单个API操作的注解。除此之外&#xff0c;Swagger还提供了其他一些类似的注解&#xff0c;它们用于不同层次或目的来增强API文档的详细程度和可读性。以下是这些注解及其之间的区别&#…...

【网络协议】开放式最短路径优先协议OSPF详解(二)

前言 第一部分&#xff1a;【网络协议】开放式最短路径优先协议OSPF详解&#xff08;一&#xff09; 在第一部分中&#xff0c;我们了解了链路状态路由协议并讨论了 OSPF 的工作原理&#xff0c;同时学习了如何配置 OSPF。在本章的第二部分中&#xff0c;我们将进一步探讨 OS…...

windows文件夹自定义右键调用powershell完成7zip加密打包

准备powershell脚本 2. regedit的路径是&#xff1a;计算机\HKEY_CLASSES_ROOT\Directory\shell\&#xff0c;在此项目下新增子项目diy_command\command&#xff0c;command的数据值为powershell D:\windowsProjects\directory_diy.ps1 %1 效果&#xff0c;点击后进入和power…...

MySQL 入门教程

MySQL是最流行的关系型数据库管理系统&#xff0c;在WEB应用方面MySQL是最好的RDBMS(Relational Database Management System&#xff1a;关系数据库管理系统)应用软件之一。 在本教程中&#xff0c;会让大家快速掌握MySQL的基本知识&#xff0c;并轻松使用MySQL数据库。 什么…...

BOOST 库在机器视觉中的应用及示例代码分析

一、引言 机器视觉是一门让计算机模拟人类视觉功能&#xff0c;对图像或视频数据进行理解、分析和决策的学科领域。在机器视觉的开发过程中&#xff0c;常常需要高效处理各种数据结构、进行数值计算、实现多线程并行处理以及运用优秀的算法框架等。BOOST 库作为一个功能强大、…...

第二十六天 自然语言处理(NLP)词嵌入(Word2Vec、GloVe)

自然语言处理&#xff08;NLP&#xff09;中的词嵌入&#xff08;Word2Vec、GloVe&#xff09;技术&#xff0c;是NLP领域的重要组成部分&#xff0c;它们为词汇提供了高维空间到低维向量的映射&#xff0c;使得语义相似的词汇在向量空间中的距离更近。以下是对这些技术的详细解…...

Log4j2 详解(异步日志打印及CSV格式日志输出)

Log4j2 详解 Apache Log4j2 是一个功能强大的 Java 日志记录框架&#xff0c;提供高性能和灵活的配置。本文档涵盖了 Log4j2 的核心功能及其详细使用方式&#xff0c;包括基础配置、异步日志、CSV 格式日志的输出以及使用注意事项。 一 Log4j2 基础概念与配置 1.1 Log4j2 介绍…...

[网络安全] DVWA之Content Security Policy (CSP) Bypass 攻击姿势及解题详析合集

CSP概念 CSP 是 Content Security Policy&#xff08;内容安全策略&#xff09;的缩写&#xff0c;是一种用于增强 Web 应用程序安全性的安全机制。它通过允许网站管理员控制页面中加载内容的来源来减少跨站脚本攻击&#xff08;XSS&#xff09;等常见的安全风险。 CSP 的工作…...

linux shell脚本 【分支结构case...in 、循环结构、函数】内附练习

1.思维导图 2.练习 1.定义一个find函数&#xff0c;查找ubuntu和root的gid 2.定义一个数组&#xff0c;写一个函数完成对数组的冒泡排序 bubble() {n${#arr[*]}for((i0;i<n-1;i));dofor((j0;j<n-1-i;j));doif ((arr[j]>arr[j1]));thentemp${arr[j]}arr[j]${arr[j1]}a…...

C# 设计模式(结构型模式):桥接模式

C# 设计模式&#xff08;结构型模式&#xff09;&#xff1a;桥接模式 在软件设计中&#xff0c;我们经常会遇到系统的变化频繁&#xff0c;或者需要灵活扩展功能的场景。这时&#xff0c;桥接模式&#xff08;Bridge Pattern&#xff09;便显得尤为重要。桥接模式是一个结构型…...

RC充电电路仿真与分析

RC充电原理 下图是一个常见的RC充电电路&#xff1a;&#xff08;假设R10K&#xff0c;C100nF&#xff09; SW断开时&#xff0c;这个电路处于断路状态&#xff0c;C既没有充电也没有放电&#xff1b;SW闭合时&#xff0c;直流电源5V为电容C充电&#xff1b; 充电时电容两端…...

在 SQL 中获取第m个开始的n条记录方法汇总

在 SQL 中&#xff0c;要获取第m个开始的n条记录&#xff0c;主要取决于你使用的数据库系统和支持的功能。以要获取第10个开始的20条记录为例说明几种常见的方法&#xff1a; 1. 使用 LIMIT 和 OFFSET 适用于 MySQL、PostgreSQL 等支持 LIMIT 的数据库。 SELECT * FROM table…...

Linux 35.6 + JetPack v5.1.4之编译 pytorch

Linux 35.6 JetPack v5.1.4之编译 pytorch 1. 源由2. 折腾3. 构建步骤3.1 下载代码3.2 编译选项3.3 CUDA选项3.4 CUDA路径3.5 版本控制3.6 编译whl 4. 总结5. 参考资料 1. 源由 目前&#xff0c;有很多科研性质的自动导航的开源代码&#xff0c;例如&#xff1a; Linux 35.5…...

docker 部署nginx

1、拉取阿里的nginx镜像&#xff1a; docker pull crpi-k5k93ldwfc7o75ip.cn-hangzhou.personal.cr.aliyuncs.com/list_su/nginx:stable-perl 2、官方nginx镜像&#xff1a; docker pull nginx:stable-perl 3、创建挂载文件目录 mkdir nginx && cd nginx mkdir c…...

深入刨析数据结构之排序(上)

目录 1.内部排序 1.1概述 1.2插入排序 1.2.1其他插入排序 1.2.1.1 折半插入排序 1.2.1.2 2-路插入排序 1.3希尔排序 1.4快速排序 1.4.1起泡排序 1.4.2快速排序 1.4.2.1hoare版本 1.4.2.2挖坑版本 1.4.2.3前后指针版本 1.4.2.4优化版本 1.4.2.4.1小区间插入排序优…...