driver

package
v0.0.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 28, 2026 License: MIT Imports: 9 Imported by: 0

README

Database SQL Driver Hook

这个包提供了一个 Go 语言数据库驱动的扩展,允许在数据库操作的前后添加钩子(Hook)函数。通过这个扩展,你可以轻松地实现数据库操作的监控、日志记录、性能分析等功能。

设计思路

核心组件
  1. 操作类型(OpType)

    • 定义了所有可能的数据库操作类型
    • 包括:连接、事务、预处理语句、执行、查询等
    • 每个操作类型都有对应的字符串表示
  2. 钩子上下文(HookContext)

    • 包含操作的完整上下文信息
    • 原始 Context
    • 操作类型
    • SQL 语句
    • 参数列表
    • 开始时间
    • 结束时间
    • 操作结果
    • 错误信息
    • 自定义数据存储(hookMap)
  3. 钩子接口(Hook)

    • Before:操作执行前调用
    • After:操作执行后调用
    • 可以访问完整的操作上下文
  4. 钩子管理器(HookManager)

    • 管理多个钩子的执行
    • 支持添加多个钩子
    • Before 按正序执行
    • After 按倒序执行
  5. 驱动包装器(KitDriver)

    • 包装原始数据库驱动
    • 在所有操作前后调用钩子
    • 保持与原始驱动完全兼容
工作流程
  1. 创建钩子实例
  2. 创建钩子管理器
  3. 将钩子添加到管理器
  4. 使用管理器包装原始驱动
  5. 注册包装后的驱动
  6. 正常使用数据库,钩子会自动执行

使用方法

基本用法
import (
    "database/sql"
    "github.com/fsyyft-go/kit/database/sql/driver"
)

// 1. 实现自定义钩子
type MyHook struct{}

func (h *MyHook) Before(ctx *driver.HookContext) error {
    // 操作执行前的逻辑
    return nil
}

func (h *MyHook) After(ctx *driver.HookContext) error {
    // 操作执行后的逻辑
    return nil
}

// 2. 创建并注册包装的驱动
func main() {
    // 创建原始驱动
    originalDriver := &mysql.MySQLDriver{}

    // 创建钩子
    hook := &MyHook{}

    // 创建钩子管理器
    hookManager := driver.NewHookManager()
    hookManager.AddHook(hook)

    // 创建包装的驱动
    wrappedDriver := driver.NewKitDriver(originalDriver, hookManager)

    // 注册驱动
    sql.Register("mysql-with-hook", wrappedDriver)

    // 使用包装的驱动
    db, err := sql.Open("mysql-with-hook", "user:password@/dbname")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // 正常使用数据库
    // 所有操作都会触发钩子
}
示例:性能监控钩子
type TimingHook struct{}

func (h *TimingHook) Before(ctx *driver.HookContext) error {
    log.Printf("[%s] 开始执行,SQL: %s,参数: %v", 
        ctx.OpType(), ctx.Query(), ctx.Args())
    return nil
}

func (h *TimingHook) After(ctx *driver.HookContext) error {
    duration := ctx.EndTime().Sub(ctx.StartTime())
    log.Printf("[%s] 执行完成,耗时: %v,错误: %v", 
        ctx.OpType(), duration, ctx.OriginError())
    return nil
}

支持的操作类型

  • OpConnect: 连接数据库
  • OpBegin: 开始事务
  • OpCommit: 提交事务
  • OpRollback: 回滚事务
  • OpPrepare: 预处理语句
  • OpStmtExec: 执行预处理语句
  • OpStmtQuery: 查询预处理语句
  • OpStmtClose: 关闭预处理语句
  • OpExec: 执行 SQL
  • OpQuery: 查询 SQL
  • OpPing: Ping 操作

注意事项

  1. 钩子执行顺序

    • Before 钩子按添加顺序执行
    • After 钩子按添加顺序的反序执行
    • 任何钩子返回错误都会中断执行
  2. 上下文数据

    • HookContext 实现了 context.Context 接口
    • 可以通过 GetHookValue/SetHookValue 在钩子间共享数据
    • 所有时间相关的字段都是只读的
  3. 性能考虑

    • 钩子的执行会增加一定的开销
    • 建议在钩子中避免耗时操作
    • 可以使用 goroutine 处理异步任务
  4. 错误处理

    • Before 钩子的错误会阻止操作执行
    • After 钩子的错误会覆盖操作的错误
    • 建议在 After 钩子中避免返回错误

贡献

欢迎提交 Issue 和 Pull Request!

许可证

使用 BSD 许可证 - 查看 LICENSE 文件了解更多信息。

Documentation

Overview

Package driver 提供 database/sql/driver 的包装驱动和 Hook 基础设施。

本包通过 NewKitDriver 将底层 driver.Driver 包装为可插入 Hook 的实现, 使连接、预处理、执行、查询、Ping、事务开始、提交和回滚等阶段都可以 在调用前后同步执行自定义逻辑。

HookContext 记录操作类型、SQL、参数、耗时、原始结果和原始错误,并实现 context.Context 以便 Hook 共享取消信号和上下文值。HookManager 按注册顺序 执行 Before、按逆序执行 After;NewHookLogError 和 NewHookLogSlow 则提供 错误日志与慢查询日志的现成 Hook。

本包只负责驱动包装与 Hook 编排,不负责注册具体数据库驱动或创建 *sql.DB。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Hook

type Hook interface {
	// Before 在底层数据库操作开始前执行。
	//
	// 参数:
	//   - ctx: 当前操作的 HookContext;Before 阶段尚未写入 OriginResult、OriginError 和 EndTime。
	//
	// 返回:
	//   - error: 返回非 nil 错误会中止底层操作,并将该错误直接返回给调用方。
	Before(ctx *HookContext) error

	// After 在底层数据库操作完成后执行。
	//
	// 参数:
	//   - ctx: 当前操作的 HookContext;After 阶段可以读取底层操作写入的结果、错误和耗时。
	//
	// 返回:
	//   - error: 返回非 nil 错误会覆盖底层操作原本要返回给调用方的错误。
	After(ctx *HookContext) error
}

Hook 定义数据库驱动操作前后的扩展点。

Before 在底层操作执行前调用;返回错误会阻止底层操作继续执行。 After 在底层操作返回并写入 HookContext 后调用;返回错误会覆盖底层操作 原本准备返回给调用方的错误。

type HookContext

type HookContext struct {
	// contains filtered or unexported fields
}

HookContext 描述一次数据库驱动操作在 Hook 链中的共享上下文。

HookContext 记录操作类型、SQL、参数、开始与结束时间、底层操作的原始结果 和原始错误,并实现 context.Context 以透传取消信号、截止时间和上下文值。 NewHookContext 创建后会立即记录开始时间;调用 SetResult 后,Duration 才表示 本次操作的实际耗时。Hook 之间还可以通过 SetHookValue 和 GetHookValue 在当前 操作内共享数据。

func NewHookContext

func NewHookContext(ctx context.Context, opType OpType, query string, args []driver.NamedValue) *HookContext

NewHookContext 创建一次数据库操作对应的 HookContext。

参数:

  • ctx: 底层操作使用的原始上下文;调用方应传入非 nil 上下文。
  • opType: 当前数据库操作的类型。
  • query: 当前操作关联的 SQL;对连接、事务或 Ping 等无 SQL 的操作通常为空字符串。
  • args: 当前操作的命名参数列表;没有参数时可为 nil。

返回:

  • *HookContext: 已记录开始时间的 HookContext。

func (*HookContext) Args

func (h *HookContext) Args() []driver.NamedValue

Args 返回当前操作携带的命名参数列表。

返回的切片应视为只读,供 Hook 观察参数内容使用。

参数:无。

返回:

  • []driver.NamedValue: 当前操作的参数列表;没有参数时可能为 nil。

func (*HookContext) Deadline

func (h *HookContext) Deadline() (deadline time.Time, ok bool)

Deadline 返回原始上下文的截止时间。

参数:无。

返回:

  • deadline: 原始上下文的截止时间。
  • ok: 原始上下文设置了截止时间时返回 true,否则返回 false。

func (*HookContext) Done

func (h *HookContext) Done() <-chan struct{}

Done 返回原始上下文的 Done channel。

参数:无。

返回:

  • <-chan struct{}: 当原始上下文被取消时关闭的 channel。

func (*HookContext) Duration

func (h *HookContext) Duration() time.Duration

Duration 返回当前操作从开始到记录结果之间的耗时。

在 SetResult 调用前,Duration 的返回值不表示有效耗时。

参数:无。

返回:

  • time.Duration: 当前操作的记录耗时。

func (*HookContext) EndTime

func (h *HookContext) EndTime() time.Time

EndTime 返回当前操作记录结束结果的时间戳。

在 SetResult 调用前,EndTime 返回零值。

参数:无。

返回:

  • time.Time: 记录底层操作结果写入 HookContext 的时间。

func (*HookContext) Err

func (h *HookContext) Err() error

Err 返回原始上下文的取消错误。

参数:无。

返回:

  • error: 原始上下文被取消或超时时返回对应错误,否则返回 nil。

func (*HookContext) GetHookValue

func (h *HookContext) GetHookValue(key string) (interface{}, bool)

GetHookValue 读取当前操作中由 Hook 保存的共享数据。

参数:

  • key: 要读取的共享数据键名。

返回:

  • interface{}: 与 key 关联的值。
  • bool: key 存在时返回 true,否则返回 false。

func (*HookContext) OpType

func (h *HookContext) OpType() OpType

OpType 返回当前操作的类型。

参数:无。

返回:

  • OpType: 当前 HookContext 记录的数据库操作类型。

func (*HookContext) OriginError

func (h *HookContext) OriginError() error

OriginError 返回底层操作产生的原始错误。

如果底层操作成功,OriginError 返回 nil。

参数:无。

返回:

  • error: 底层操作返回的原始错误。

func (*HookContext) OriginResult

func (h *HookContext) OriginResult() interface{}

OriginResult 返回底层操作产生的原始结果。

结果类型取决于当前操作,例如 driver.Conn、driver.Stmt、driver.Result、 driver.Rows、driver.Tx 或 nil。

参数:无。

返回:

  • interface{}: 底层操作返回的原始结果。

func (*HookContext) Query

func (h *HookContext) Query() string

Query 返回当前操作关联的 SQL 语句。

对连接、Ping、事务提交和回滚等无 SQL 的操作,Query 可能为空字符串。

参数:无。

返回:

  • string: 当前操作关联的 SQL 文本。

func (*HookContext) SetHookValue

func (h *HookContext) SetHookValue(key string, value interface{})

SetHookValue 在当前操作的 Hook 链中保存共享数据。

参数:

  • key: 要写入的共享数据键名。
  • value: 与 key 关联的值。

func (*HookContext) SetResult

func (h *HookContext) SetResult(result interface{}, err error)

SetResult 记录底层操作的原始结果、原始错误和结束时间。

SetResult 通常在底层 driver 调用返回后由包装器调用一次;调用后 Duration 才表示本次操作的实际耗时。

参数:

  • result: 底层操作返回的原始结果;类型随操作而变化。
  • err: 底层操作返回的原始错误;成功时为 nil。

func (*HookContext) StartTime

func (h *HookContext) StartTime() time.Time

StartTime 返回当前操作开始执行的时间戳。

参数:无。

返回:

  • time.Time: 记录 HookContext 创建时的开始时间。

func (*HookContext) Value

func (h *HookContext) Value(key interface{}) interface{}

Value 返回原始上下文中与 key 关联的值。

参数:

  • key: 要读取的上下文键。

返回:

  • interface{}: 原始上下文中与 key 关联的值。

type HookLogError

type HookLogError struct {
	// contains filtered or unexported fields
}

HookLogError 是一个只记录失败操作的 Hook。

HookLogError 会在 After 阶段检查 HookContext.OriginError;当底层操作返回 错误时,它会异步提交一条包含操作类型、耗时以及可选 namespace、SQL 和 参数摘要的错误日志。调用方应提供可用的 logger。

func NewHookLogError

func NewHookLogError(namespace string, logger kitlog.Logger) *HookLogError

NewHookLogError 创建一个错误日志 Hook。

参数:

  • namespace: 写入日志字段的命名空间;为空时省略该字段。
  • logger: 用于输出错误日志的记录器;调用方应传入非 nil 实例。

返回:

  • *HookLogError: 在数据库操作失败时异步写日志的 Hook。

func (*HookLogError) After

func (h *HookLogError) After(ctx *HookContext) error

After 在底层操作返回错误时异步记录错误日志。

After 仅在 HookContext.OriginError 非 nil 时写日志。日志字段包含 operation、 duration,以及存在时的 namespace、query 和 args。

参数:

  • ctx: 当前操作的 HookContext。

返回:

  • error: 始终返回 nil,不会覆盖原始操作结果。

func (*HookLogError) Before

func (h *HookLogError) Before(ctx *HookContext) error

Before 在执行数据库操作前不做任何处理。

参数:

  • ctx: 当前操作的 HookContext。

返回:

  • error: 始终返回 nil,不会阻止底层操作执行。

type HookLogSlow

type HookLogSlow struct {
	// contains filtered or unexported fields
}

HookLogSlow 是一个记录慢操作的 Hook。

HookLogSlow 会在 After 阶段比较 HookContext.Duration 与 threshold;当耗时 大于等于阈值时,它会异步提交一条包含操作类型、耗时以及可选 namespace、 SQL 和参数摘要的警告日志。调用方应提供可用的 logger。

func NewHookLogSlow

func NewHookLogSlow(namespace string, logger kitlog.Logger, threshold time.Duration) *HookLogSlow

NewHookLogSlow 创建一个慢操作日志 Hook。

参数:

  • namespace: 写入日志字段的命名空间;为空时省略该字段。
  • logger: 用于输出慢操作日志的记录器;调用方应传入非 nil 实例。
  • threshold: 慢操作阈值;当 Duration 大于等于该值时记录日志。

返回:

  • *HookLogSlow: 在数据库操作达到慢阈值时异步写日志的 Hook。

func (*HookLogSlow) After

func (h *HookLogSlow) After(ctx *HookContext) error

After 在操作耗时达到阈值时异步记录慢操作日志。

After 仅在 HookContext.Duration 大于等于 threshold 时写日志。日志字段包含 operation、duration,以及存在时的 namespace、query 和 args。

参数:

  • ctx: 当前操作的 HookContext。

返回:

  • error: 始终返回 nil,不会覆盖原始操作结果。

func (*HookLogSlow) Before

func (h *HookLogSlow) Before(ctx *HookContext) error

Before 在执行数据库操作前不做任何处理。

参数:

  • ctx: 当前操作的 HookContext。

返回:

  • error: 始终返回 nil,不会阻止底层操作执行。

type HookManager

type HookManager struct {
	// contains filtered or unexported fields
}

HookManager 按顺序编排多个 Hook。

HookManager 会按 AddHook 的注册顺序调用 Before,并按相反顺序调用 After, 以便成对组织前置和后置逻辑。HookManager 不做并发保护,通常应在初始化阶段 完成 AddHook,再作为只读 Hook 链共享使用。

func NewHookManager

func NewHookManager() *HookManager

NewHookManager 创建一个空的 HookManager。

参数:无。

返回:

  • *HookManager: 可继续通过 AddHook 注册 Hook 的管理器。

func (*HookManager) AddHook

func (m *HookManager) AddHook(hook Hook)

AddHook 按注册顺序向 HookManager 追加一个 Hook。

后续 Before 会按照追加顺序执行该 Hook,After 会按逆序执行。

参数:

  • hook: 要注册到管理器末尾的 Hook。

func (*HookManager) After

func (m *HookManager) After(ctx *HookContext) error

After 按 Hook 的注册逆序执行所有后置逻辑。

参数:

  • ctx: 当前操作的 HookContext。

返回:

  • error: 逆序执行过程中遇到的第一个 Hook 错误;发生错误后不会继续执行剩余 Hook。

func (*HookManager) Before

func (m *HookManager) Before(ctx *HookContext) error

Before 按 Hook 的注册顺序执行所有前置逻辑。

参数:

  • ctx: 当前操作的 HookContext。

返回:

  • error: 第一个返回的 Hook 错误;发生错误后不会继续执行后续 Hook。

type KitDriver

type KitDriver struct {
	// contains filtered or unexported fields
}

KitDriver 包装底层 driver.Driver,并在连接及其派生对象的操作前后执行 Hook。

KitDriver 只负责把 Open 创建出的连接,以及后续 PrepareContext 和 BeginTx 创建出的语句、事务再包装为带 Hook 的实现, 不改变底层驱动对 DSN、结果类型或错误值的基础语义。调用方应为 d 和 h 提供 可用的非 nil 实现。

func NewKitDriver

func NewKitDriver(d driver.Driver, h Hook) *KitDriver

NewKitDriver 创建一个带 Hook 的 driver.Driver 包装器。

参数:

  • d: 实际执行数据库协议的底层 driver。
  • h: 在 Open 以及后续连接、语句和事务操作前后执行的 Hook;调用方应传入非 nil 实现。

返回:

  • *KitDriver: 对 d 的包装实例。

func (*KitDriver) Open

func (d *KitDriver) Open(name string) (driver.Conn, error)

Open 打开一个新的底层数据库连接,并为该连接安装 Hook 包装。

Open 会先以 OpConnect 调用 Hook.Before,再调用底层 driver 的 Open,随后 将连接或错误写入 HookContext 并调用 Hook.After。由于 driver.Driver.Open 不接收上下文,HookContext 使用 context.Background 作为原始上下文。

参数:

  • name: 原样传递给底层 driver 的 DSN。

返回:

  • driver.Conn: 成功时返回带 Hook 包装的连接。
  • error: Hook.Before 返回错误、底层 driver.Open 失败或 Hook.After 返回错误时返回错误。

type OpType

type OpType int

OpType 表示 database/sql/driver 包装层观察到的数据库操作类型。

可选值包括:

  • OpConnect: 打开底层连接。
  • OpBegin: 开始事务。
  • OpCommit: 提交事务。
  • OpRollback: 回滚事务。
  • OpPrepare: 创建预处理语句。
  • OpStmtExec: 执行预处理语句。
  • OpStmtQuery: 查询预处理语句。
  • OpStmtClose: 关闭预处理语句。
  • OpExec: 执行普通 SQL。
  • OpQuery: 查询普通 SQL。
  • OpPing: 检测连接可用性。
const (
	// OpConnect 表示打开底层连接。
	OpConnect OpType = iota
	// OpBegin 表示开始事务。
	OpBegin
	// OpCommit 表示提交事务。
	OpCommit
	// OpRollback 表示回滚事务。
	OpRollback
	// OpPrepare 表示创建预处理语句。
	OpPrepare
	// OpStmtExec 表示执行预处理语句。
	OpStmtExec
	// OpStmtQuery 表示查询预处理语句。
	OpStmtQuery
	// OpStmtClose 表示关闭预处理语句。
	OpStmtClose
	// OpExec 表示执行普通 SQL。
	OpExec
	// OpQuery 表示查询普通 SQL。
	OpQuery
	// OpPing 表示检测连接可用性。
	OpPing
)

func (OpType) String

func (o OpType) String() string

String 返回操作类型的字符串表示。

参数:无。

返回:

  • string: 已知操作类型返回固定英文名称;未知值返回 "Unknown"。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL