sqlslog

package module
v0.1.7 Latest Latest
Warning

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

Go to latest
Published: Feb 3, 2025 License: MIT Imports: 12 Imported by: 0

README

sql-slog

CI codecov Go Report Card Documentation license

A logger for Go SQL database drivers without modifying existing *sql.DB stdlib usage.

LOG EXAMPLES

FEATURES

  • Keep using (or re-use existing) *sql.DB as is.
  • No logger adapters. Just use log/slog
  • No dependencies except stdlib.
  • Leveled, detailed and configurable logging.
  • Duration
  • Trackable log output
    • conn_id
    • tx_id
    • stmt_id

INSTALL

go get -u github.com/akm/sql-slog

USAGE

This is a simple example of how to use sql.Open:

db, err := sql.Open("mysql", dsn)

When using sqlslog, you can use it like this:

ctx := context.TODO() // or a context.Context
db, err := sqlslog.Open(ctx, "mysql", dsn)
  1. Replace sql.Open with sqlslog.Open.
  2. Insert a context.Context as the first argument.

Features

Additional Log Levels

sqlslog provides additional log levels LevelTrace and LevelVerbose as sqlslog.Level.

To display the log levels correctly, the logger handler must be customized. You can create a handler using sqlslog.NewJSONHandler and sqlslog.NewTextHandler.

Pass an sqlslog.Option created by sqlslog.Logger to sqlslog.Open to use them.

db, err := sqlslog.Open(ctx, "sqlite3", dsn, sqlslog.Logger(yourLogger))
Configurable Log Messages and Log Levels for Each Step

In sqlslog terms, a step is a logical operation in the database driver, such as a query, a ping, a prepare, etc.

A step has three events: start, error, and complete.

sqlslog provides a way to customize the log message and log level for each step event.

You can customize them using functions that take StepOptions and return Option, like ConnPrepareContext or StmtQueryContext.

Tests

MOTIVATION

I want to:

  • Keep using *sql.DB.
    • To work with thin ORM
  • Use log/slog
    • Leverage structured logging
    • Fetch and log context.Context values if needed

REFERENCES

CONTRIBUTING

If you find a bug, typo, incorrect test, have an idea, or want to help with an existing issue, please create an issue or pull request.

INSPIRED BY

LICENSE

MIT

Documentation

Overview

sqlslog is a logger for Go SQL database drivers without modifying existing *sql.DB stdlib usage. sqlslog uses *slog.Logger to log SQL database driver operations.

How to use

ctx := context.TODO() // or a context.Context
db, err := sqlslog.Open(ctx, "mysql", dsn)

You can also use options to customize the logger's behavior.

Open takes Option s to customize the logging behavior. Option is created by using functions like Logger, ConnPrepareContext, StmtQueryContext, etc.

Logger

Logger sets the slog.Logger to be used. If not set, the default is slog.Default().

The logger from slog.Default() does not have options for the log levels LevelTrace and LevelVerbose.

You can create a slog.Handler by using sqlslog.NewTextHandler or sqlslog.NewJSONHandler customized for sqlslog Level. See examples for NewTextHandler and NewJSONHandler for more details.

Step

In sqlslog terms, a step is a logical operation in the database driver, such as a query, a ping, a prepare, etc.

A step has three events: start, error, and complete.

sqlslog provides a way to customize the log message and log level for each step event.

You can customize them by using functions that take StepOptions and return Option, like ConnPrepareContext or StmtQueryContext.

Default Step Log Message Formatter

The default step log message formatter is StepLogMsgWithEventName.

You can change the default step log message formatter by calling SetStepLogMsgFormatter.

Index

Examples

Constants

View Source
const (
	ConnIDKeyDefault = "conn_id"
	TxIDKeyDefault   = "tx_id"
	StmtIDKeyDefault = "stmt_id"
)
View Source
const DurationKeyDefault = "duration"

DurationKeyDefault is the default key for duration value in log.

Variables

View Source
var IDGeneratorDefault = NewChaCha8IDGenerator(defaultIDLength).Generate

IDGeneratorDefault is the default ID generator.

Functions

func ConnExecContextErrorHandler added in v0.1.2

func ConnExecContextErrorHandler(driverName string) func(err error) (bool, []slog.Attr)

func ConnQueryContextErrorHandler added in v0.1.2

func ConnQueryContextErrorHandler(driverName string) func(err error) (bool, []slog.Attr)

func ConnectorConnectErrorHandler added in v0.1.2

func ConnectorConnectErrorHandler(driverName string) func(err error) (bool, []slog.Attr)

ConnectorConnectErrorHandler returns a function that handles errors from driver.Connector.Connect. The function returns a boolean indicating completion and a slice of slog.Attr.

# For Postgres: If err is nil, it returns true and a slice of slog.Attr{slog.Bool("success", true)}. If err is io.EOF, it returns true and a slice of slog.Attr{slog.Bool("success", false)}. Otherwise, it returns false and nil.

func DriverOpenErrorHandler added in v0.1.2

func DriverOpenErrorHandler(driverName string) func(err error) (bool, []slog.Attr)

DriverOpenErrorHandler returns a function that handles errors from driver.Driver.Open. The function returns a boolean indicating completion and a slice of slog.Attr.

# For Postgres: If err is nil, it returns true and a slice of slog.Attr{slog.Bool("success", true)}. If err is io.EOF, it returns true and a slice of slog.Attr{slog.Bool("success", false)}. Otherwise, it returns false and nil.

func HandleRowsNextError added in v0.1.2

func HandleRowsNextError(err error) (bool, []slog.Attr)

HandleRowsNextError returns a boolean indicating completion and a slice of slog.Attr. If err is nil, it returns true and a slice of slog.Attr{slog.Bool("eof", false)}. If err is io.EOF, it returns true and a slice of slog.Attr{slog.Bool("eof", true)}. Otherwise, it returns false and nil.

func NewJSONHandler added in v0.1.1

func NewJSONHandler(w io.Writer, opts *slog.HandlerOptions) *slog.JSONHandler

NewJSONHandler returns a new JSON handler using slog.NewJSONHandler with custom options for sqlslog. See WrapHandlerOptions for details on the options.

Example
package main

import (
	"context"
	"log/slog"
	"os"

	sqlslog "github.com/akm/sql-slog"
)

func main() {
	dsn := "file::memory:?cache=shared"
	ctx := context.TODO()
	logger := slog.New(sqlslog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		Level: slog.LevelDebug,
	}))
	db, _ := sqlslog.Open(ctx, "sqlite3", dsn, sqlslog.Logger(logger))
	defer db.Close()
}

func NewTextHandler added in v0.1.1

func NewTextHandler(w io.Writer, opts *slog.HandlerOptions) *slog.TextHandler

NewTextHandler returns a new Text handler using slog.NewTextHandler with custom options for sqlslog. See WrapHandlerOptions for details on the options.

Example
package main

import (
	"context"
	"log/slog"
	"os"

	sqlslog "github.com/akm/sql-slog"
)

func main() {
	dsn := "file::memory:?cache=shared"
	ctx := context.TODO()
	logger := slog.New(sqlslog.NewTextHandler(os.Stdout, nil))
	db, _ := sqlslog.Open(ctx, "sqlite3", dsn, sqlslog.Logger(logger))
	defer db.Close()
}

func Open

func Open(ctx context.Context, driverName, dsn string, opts ...Option) (*sql.DB, error)

Open opens a database specified by its driver name and a driver-specific data source name, and returns a new database handle with logging capabilities.

ctx is the context for the open operation. driverName is the name of the database driver, same as the driverName in sql.Open. dsn is the data source name, same as the dataSourceName in sql.Open. opts are the options for logging behavior. See Option for details.

The returned DB can be used the same way as *sql.DB from sql.Open.

See the following example for usage:

Logger: sets the slog.Logger to be used. If not set, the default is slog.Default().

StepOptions: sets the options for logging behavior.

SetStepLogMsgFormatter: sets the function to format the step name.

Example
package main

import (
	"context"

	sqlslog "github.com/akm/sql-slog"
)

func main() {
	dsn := "file::memory:?cache=shared"
	ctx := context.TODO()
	db, err := sqlslog.Open(ctx, "sqlite3", dsn)
	if err != nil {
		// Handle error
	}
	defer db.Close()
	// Use db as a regular *sql.DB
}
Example (WithLevel)
package main

import (
	"context"
	"log/slog"
	"os"

	sqlslog "github.com/akm/sql-slog"
)

func main() {
	dsn := "file::memory:?cache=shared"
	ctx := context.TODO()
	logger := slog.New(sqlslog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		Level: sqlslog.LevelTrace,
	}))
	db, _ := sqlslog.Open(ctx, "sqlite3", dsn, sqlslog.Logger(logger))
	defer db.Close()
}
Example (WithStmtQueryContext)
package main

import (
	"context"
	"log/slog"
	"os"

	sqlslog "github.com/akm/sql-slog"
)

func main() {
	dsn := "file::memory:?cache=shared"
	ctx := context.TODO()
	logger := slog.New(sqlslog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		Level: sqlslog.LevelTrace,
	}))
	db, _ := sqlslog.Open(ctx, "sqlite3", dsn,
		sqlslog.Logger(logger),
		sqlslog.StmtQueryContext(func(o *sqlslog.StepOptions) {
			o.SetLevel(sqlslog.LevelDebug)
		}),
	)
	defer db.Close()
}

func ReplaceLevelAttr added in v0.1.1

func ReplaceLevelAttr(_ []string, a slog.Attr) slog.Attr

ReplaceLevelAttr replaces the log level as sqlslog.Level with its string representation.

func SetStepLogMsgFormatter added in v0.1.1

func SetStepLogMsgFormatter(f StepLogMsgFormatter)

SetStepLogMsgFormatter sets the formatter for the step name used in logs. If not set, the default is StepLogMsgWithEventName.

Example
package main

import (
	"context"
	"log/slog"
	"os"

	sqlslog "github.com/akm/sql-slog"
)

func main() {
	sqlslog.SetStepLogMsgFormatter(func(name string, event sqlslog.Event) string {
		return name + "/" + event.String()
	})
	dsn := "file::memory:?cache=shared"
	ctx := context.TODO()
	logger := slog.New(sqlslog.NewJSONHandler(os.Stdout, nil))
	db, _ := sqlslog.Open(ctx, "sqlite3", dsn, sqlslog.Logger(logger))
	defer db.Close()
}

func StepLogMsgWithEventName added in v0.1.1

func StepLogMsgWithEventName(name string, event Event) string

StepLogMsgWithEventName returns the formatted step log message with the event name.

func StepLogMsgWithoutEventName added in v0.1.5

func StepLogMsgWithoutEventName(name string, _ Event) string

StepLogMsgWithoutEventName returns the formatted step log message without the event name.

func WrapHandlerOptions added in v0.1.1

func WrapHandlerOptions(opts *slog.HandlerOptions) *slog.HandlerOptions

WrapHandlerOptions wraps the options with custom options for sqlslog. It merges ReplaceAttr functions with ReplaceLevelAttr.

Types

type ChaCha8IDGenerator added in v0.1.4

type ChaCha8IDGenerator struct {
	*rand.Rand
	// contains filtered or unexported fields
}

ChaCha8IDGenerator generates random IDs using ChaCha8 in math/rand/v2. The seed is generated using crypto/rand.

func NewChaCha8IDGenerator added in v0.1.4

func NewChaCha8IDGenerator(length int) *ChaCha8IDGenerator

NewChaCha8IDGenerator creates a new ChaCha8IDGenerator with a random seed.

func (*ChaCha8IDGenerator) Generate added in v0.1.4

func (g *ChaCha8IDGenerator) Generate() string

Generate generates a random ID.

type DurationType added in v0.1.3

type DurationType int
const (
	DurationNanoSeconds  DurationType = iota // Duration in nanoseconds. Durations in log are expressed by slog.Int64
	DurationMicroSeconds                     // Duration in microseconds. Durations in log are expressed by slog.Int64
	DurationMilliSeconds                     // Duration in milliseconds. Durations in log are expressed by slog.Int64
	DurationGoDuration                       // Values in log are expressed with slog.Duration
	DurationString                           // Values in log are expressed with slog.String and time.Duration.String
)

type Event added in v0.1.1

type Event int

Event is the event type of the step.

const (
	EventStart    Event = iota + 1 // Event when the step starts.
	EventError                     // Event when the step ends with an error.
	EventComplete                  // Event when the step completes successfully.
)

func (*Event) String added in v0.1.1

func (pe *Event) String() string

String returns the string representation of the event.

type EventOptions added in v0.1.1

type EventOptions struct {
	Msg   string
	Level Level
}

type IDGen added in v0.1.4

type IDGen = func() string

IDGen is a function that generates an ID string.

type Level added in v0.1.1

type Level slog.Level

Level is the log level for sqlslog.

const (
	LevelVerbose Level = Level(-12)             // Lower than slog.LevelTrace.
	LevelTrace   Level = Level(-8)              // Lower than slog.LevelDebug.
	LevelDebug   Level = Level(slog.LevelDebug) // Same as slog.LevelDebug.
	LevelInfo    Level = Level(slog.LevelInfo)  // Same as slog.LevelInfo.
	LevelWarn    Level = Level(slog.LevelWarn)  // Same as slog.LevelWarn.
	LevelError   Level = Level(slog.LevelError) // Same as slog.LevelError.
)

func (Level) Level added in v0.1.1

func (l Level) Level() slog.Level

Level returns the slog.Level.

func (Level) String added in v0.1.1

func (l Level) String() string

String returns the string representation of the log level.

type Option added in v0.1.1

type Option func(*options)

Option is a function that sets an option on the options struct.

func ConnBegin added in v0.1.1

func ConnBegin(f func(*StepOptions)) Option

Set the options for Conn.Begin.

func ConnBeginTx added in v0.1.1

func ConnBeginTx(f func(*StepOptions)) Option

Set the options for Conn.BeginTx.

func ConnClose added in v0.1.1

func ConnClose(f func(*StepOptions)) Option

Set the options for Conn.Close.

func ConnExecContext added in v0.1.1

func ConnExecContext(f func(*StepOptions)) Option

Set the options for Conn.ExecContext.

func ConnIDKey added in v0.1.4

func ConnIDKey(key string) Option

ConnIDKey sets the key for the connection ID. The default is ConnIDKeyDefault.

func ConnPing added in v0.1.1

func ConnPing(f func(*StepOptions)) Option

Set the options for Conn.Ping.

func ConnPrepare added in v0.1.1

func ConnPrepare(f func(*StepOptions)) Option

Set the options for Conn.Prepare.

func ConnPrepareContext added in v0.1.1

func ConnPrepareContext(f func(*StepOptions)) Option

Set the options for Conn.PrepareContext.

func ConnQueryContext added in v0.1.1

func ConnQueryContext(f func(*StepOptions)) Option

Set the options for Conn.QueryContext.

func ConnResetSession added in v0.1.1

func ConnResetSession(f func(*StepOptions)) Option

Set the options for Conn.ResetSession.

func ConnectorConnect added in v0.1.1

func ConnectorConnect(f func(*StepOptions)) Option

Set the options for Connector.Connect.

func DriverOpen added in v0.1.1

func DriverOpen(f func(*StepOptions)) Option

Set the options for Driver.Open.

func DriverOpenConnector added in v0.1.1

func DriverOpenConnector(f func(*StepOptions)) Option

Set the options for Driver.OpenConnector.

func Duration added in v0.1.3

func Duration(v DurationType) Option

Duration is an option to specify duration value in log. The default is DurationNanoSeconds.

func DurationKey added in v0.1.3

func DurationKey(key string) Option

DurationKey is an option to specify the key for duration value in log. The default is specified by DurationKeyDefault.

func IDGenerator added in v0.1.4

func IDGenerator(idGen IDGen) Option

IDGenerator returns an Option that sets the ID generator. The default is IDGeneratorDefault.

func Logger added in v0.1.1

func Logger(logger *slog.Logger) Option

Logger sets the slog.Logger to be used. If not set, the default is slog.Default().

Example
package main

import (
	"context"
	"log/slog"
	"os"

	sqlslog "github.com/akm/sql-slog"
)

func main() {
	dsn := "file::memory:?cache=shared"
	ctx := context.TODO()
	logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
	db, _ := sqlslog.Open(ctx, "sqlite3", dsn, sqlslog.Logger(logger))
	defer db.Close()
}

func RowsClose added in v0.1.1

func RowsClose(f func(*StepOptions)) Option

Set the options for Rows.Close.

func RowsNext added in v0.1.1

func RowsNext(f func(*StepOptions)) Option

Set the options for Rows.Next.

func RowsNextResultSet added in v0.1.1

func RowsNextResultSet(f func(*StepOptions)) Option

Set the options for Rows.NextResultSet.

func SqlslogOpen added in v0.1.1

func SqlslogOpen(f func(*StepOptions)) Option

Set the options for sqlslog.Open.

func StmtClose added in v0.1.1

func StmtClose(f func(*StepOptions)) Option

Set the options for Stmt.Close.

func StmtExec added in v0.1.1

func StmtExec(f func(*StepOptions)) Option

Set the options for Stmt.Exec.

func StmtExecContext added in v0.1.1

func StmtExecContext(f func(*StepOptions)) Option

Set the options for Stmt.ExecContext.

func StmtIDKey added in v0.1.4

func StmtIDKey(key string) Option

StmtIDKey sets the key for the statement ID. The default is StmtIDKeyDefault.

func StmtQuery added in v0.1.1

func StmtQuery(f func(*StepOptions)) Option

Set the options for Stmt.Query.

func StmtQueryContext added in v0.1.1

func StmtQueryContext(f func(*StepOptions)) Option

Set the options for Stmt.QueryContext.

func TxCommit added in v0.1.1

func TxCommit(f func(*StepOptions)) Option

Set the options for Tx.Commit.

func TxIDKey added in v0.1.4

func TxIDKey(key string) Option

TxIDKey sets the key for the transaction ID. The default is TxIDKeyDefault.

func TxRollback added in v0.1.1

func TxRollback(f func(*StepOptions)) Option

Set the options for Tx.Rollback.

type ReplaceAttrFunc added in v0.1.1

type ReplaceAttrFunc = func([]string, slog.Attr) slog.Attr

ReplaceLevelAttr is a type of ReplaceAttr for slog.HandlerOptions.

func MergeReplaceAttrs added in v0.1.1

func MergeReplaceAttrs(funcs ...ReplaceAttrFunc) ReplaceAttrFunc

MergeReplaceAttrs merges multiple ReplaceAttrFunc functions. If functions are nil or empty, it returns nil. If there is only one function, it returns that function. If there are multiple functions, it returns a merged function.

type StepLogMsgFormatter added in v0.1.1

type StepLogMsgFormatter func(name string, event Event) string

StepLogMsgFormatter is the function type to format the step log message.

type StepOptions added in v0.1.1

type StepOptions struct {
	Start    EventOptions
	Error    EventOptions
	Complete EventOptions

	// ErrorHandler is the function to handle the error.
	// When the error should not be logged as an error but as complete, it should return true.
	// It can also add attributes to the log.
	ErrorHandler func(error) (bool, []slog.Attr)
}

StepOptions is an struct that expresses the options for the step.

func (*StepOptions) SetLevel added in v0.1.1

func (o *StepOptions) SetLevel(lv Level)

Directories

Path Synopsis
tests
testhelper module

Jump to

Keyboard shortcuts

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