A logger for Go SQL database driver without modify existing *sql.DB stdlib usage.
 Colored console writer output above only for sample/development
Colored console writer output above only for sample/development
- Leveled, detailed and configurable logging.
- Keep using (or re-use existing) *sql.DBas is.
- Bring your own logger backend via simple log interface.
- Trackable log output:
- Every call has its own unique ID.
- Prepared statement and execution will have same ID.
- On execution/result error, it will include the query, arguments, params, and related IDs.
 
go get -u -v github.com/simukti/sqldb-loggerVersion pinning using dependency manager such as Mod or Dep is highly recommended.
As a start, Logger is just a simple interface:
type Logger interface {
	Log(ctx context.Context, level Level, msg string, data map[string]interface{})
}There are 4 included basic implementation that uses well-known JSON structured logger for quickstart:
- Zerolog adapter: Using rs/zerolog as its logger.
- Onelog adapter: Using francoispqt/onelog as its logger.
- Zap adapter: Using uber-go/zap as its logger.
- Logrus adapter: Using sirupsen/logrus as its logger.
Note: those adapters does not use given context, you need to modify it and adjust with your needs.
(example: add http request id/whatever value from context to query log when you call QueryerContext andExecerContext methods)
Then for that logger to works, you need to integrate with a compatible driver which will be used by *sql.DB.
Re-use from existing *sql.DB driver, this is the simplest way:
For example, from:
dsn := "username:passwd@tcp(mysqlserver:3306)/dbname?parseTime=true"
db, err := sql.Open("mysql", dsn) // db is *sql.DB
db.Ping() // to check connectivity and DSN correctnessTo:
// import sqldblogger "github.com/simukti/sqldb-logger"
// import "github.com/simukti/sqldb-logger/logadapter/zerologadapter"
dsn := "username:passwd@tcp(mysqlserver:3306)/dbname?parseTime=true"
db, err := sql.Open("mysql", dsn) // db is *sql.DB
// handle err
loggerAdapter := zerologadapter.New(zerolog.New(os.Stdout))
db = sqldblogger.OpenDriver(dsn, db.Driver(), loggerAdapter/*, using_default_options*/) // db is STILL *sql.DB
db.Ping() // to check connectivity and DSN correctnessThat's it, all *sql.DB interaction now logged.
It is also possible to integrate with following public empty struct driver directly:
MySQL (go-sql-driver/mysql)
db := sqldblogger.OpenDriver(dsn, &mysql.MySQLDriver{}, loggerAdapter /*, ...options */)PostgreSQL (lib/pq)
db := sqldblogger.OpenDriver(dsn, &pq.Driver{}, loggerAdapter /*, ...options */) SQLite3 (mattn/go-sqlite3)
db := sqldblogger.OpenDriver(dsn, &sqlite3.SQLiteDriver{}, loggerAdapter /*, ...options */)Following struct drivers maybe compatible:
SQL Server (denisenkom/go-mssqldb)
db := sqldblogger.OpenDriver(dsn, &mssql.Driver{}, loggerAdapter /*, ...options */)Oracle (mattn/go-oci8)
db := sqldblogger.OpenDriver(dsn, oci8.OCI8Driver, loggerAdapter /*, ...options */)When using sqldblogger.OpenDriver(dsn, driver, logger, opt...) without 4th variadic argument, it will use default options.
Here is sample of OpenDriver() using all available options and use non-default value:
db = sqldblogger.OpenDriver(
    dsn, 
    db.Driver(), 
    loggerAdapter,
    // AVAILABLE OPTIONS
    sqldblogger.WithErrorFieldname("sql_error"),                    // default: error
    sqldblogger.WithDurationFieldname("query_duration"),            // default: duration
    sqldblogger.WithTimeFieldname("log_time"),                      // default: time
    sqldblogger.WithSQLQueryFieldname("sql_query"),                 // default: query
    sqldblogger.WithSQLArgsFieldname("sql_args"),                   // default: args
    sqldblogger.WithMinimumLevel(sqldblogger.LevelTrace),           // default: LevelDebug
    sqldblogger.WithLogArguments(false),                            // default: true
    sqldblogger.WithDurationUnit(sqldblogger.DurationNanosecond),   // default: DurationMillisecond
    sqldblogger.WithTimeFormat(sqldblogger.TimeFormatRFC3339),      // default: TimeFormatUnix
    sqldblogger.WithLogDriverErrorSkip(true),                       // default: false
    sqldblogger.WithSQLQueryAsMessage(true),                        // default: false
    sqldblogger.WithUIDGenerator(sqldblogger.UIDGenerator),         // default: *defaultUID
    sqldblogger.WithConnectionIDFieldname("con_id"),                // default: conn_id
    sqldblogger.WithStatementIDFieldname("stm_id"),                 // default: stmt_id
    sqldblogger.WithTransactionIDFieldname("trx_id"),               // default: tx_id
    sqldblogger.WithWrapResult(false),                              // default: true
    sqldblogger.WithIncludeStartTime(true),                         // default: false
    sqldblogger.WithStartTimeFieldname("start_time"),               // default: start
    sqldblogger.WithPreparerLevel(sqldblogger.LevelDebug),          // default: LevelInfo
    sqldblogger.WithQueryerLevel(sqldblogger.LevelDebug),           // default: LevelInfo
    sqldblogger.WithExecerLevel(sqldblogger.LevelDebug),            // default: LevelInfo
)Click here for options documentation.
I want to:
- Keep using *sql.DB.
- Have configurable output field.
- Leverage structured logging.
- Fetch and log context.Contextvalue if needed.
- Re-use pgx log interface.
I haven't found Go *sql.DB logger with that features, so why not created myself?
If you found a bug, typo, wrong test, idea, help with existing issue, or anything constructive.
Don't hesitate to create an issue or pull request.
- pgx for awesome PostgreSQL driver.