sqlslog

package module
v0.1.4 Latest Latest
Warning

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

Go to latest
Published: Jan 24, 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

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 at the start of the arguments.

Features

Additional Log Levels

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

In order to show the log levels correctly, the logger handler must be customized. You can create a handler by using sqlslog.NewJSONHandler and sqlslog.NewTextHandler.

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

db, err := sqlslog.Open(ctx, "sqlite3", dsn, sqlslog.Logger(yourLogger))
Configurable Log Message and Log Level 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 by 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 value if needed

REFERENCES

CONTRIBUTE

If you found a bug, typo, wrong test, idea, help with an existing issue, or anything constructive.

Don't hesitate to 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(16).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 the error of driver.Connector.Connect. The function returns completed and slice of slog.Attr.

# For Postgres: 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 DriverOpenErrorHandler added in v0.1.2

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

DriverOpenErrorHandler returns a function that handles the error of driver.Driver.Open. The function returns completed and slice of slog.Attr.

# For Postgres: 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 HandleRowsNextError added in v0.1.2

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

HandleRowsNextError returns completed and 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 by using slog.NewJSONHandler with custom options for sqlslog. See WrapHandlerOptions for the details of 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 by using slog.NewTextHandler with custom options for sqlslog. See WrapHandlerOptions for the details of 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 database driver name and a driver-specific data source name. And returns a new database handle with logger.

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

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

See the following example for the usage:

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

StepOptions: sets the options for the 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 the 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 WrapHandlerOptions added in v0.1.1

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

WrapHandlerOptions wraps the options with custom options for sqlslog. It merge 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.

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 // is the event when the step starts.
	EventError                     // is the event when the step ends with an error.
	EventComplete                  // is the 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 ID string.

type Level added in v0.1.1

type Level slog.Level

Level is 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) // the same as slog.LevelDebug.
	LevelInfo    Level = Level(slog.LevelInfo)  // the same as slog.LevelInfo.
	LevelWarn    Level = Level(slog.LevelWarn)  // the same as slog.LevelWarn.
	LevelError   Level = Level(slog.LevelError) // the 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 some 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. 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. 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. 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. 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 of slog.HandlerOptions

func MergeReplaceAttrs added in v0.1.1

func MergeReplaceAttrs(funcs ...ReplaceAttrFunc) ReplaceAttrFunc

ReplaceLevelAttr merges the ReplaceAttrFunc functions. If functions are nil or empty, it returns nil. If functions are only one, it returns the function. If functions are more than one, it returns the 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 an complete, it should return true.
	// And add the attributes to the log.
	ErrorHandler func(error) (bool, []slog.Attr)
}

StepOptions is the options for the step.

func (*StepOptions) SetLevel added in v0.1.1

func (o *StepOptions) SetLevel(lv Level)

Jump to

Keyboard shortcuts

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