sqlslog

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Jan 19, 2025 License: MIT Imports: 8 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

See logs with sqlite3 in tests/sqlite3/demo-results.

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

This section is empty.

Variables

This section is empty.

Functions

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: slog.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: slog.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 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 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 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 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 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 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
}

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