Write log lines to DB.Log output (#195)

This makes it possible to redirect the logs somewhere else, useful if you embed dbmate into your application.
This commit is contained in:
Bouke van der Bijl 2021-02-18 23:10:57 +01:00 committed by GitHub
parent 454f93a000
commit 2bac2c7590
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 44 additions and 26 deletions

View file

@ -4,6 +4,7 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"os" "os"
@ -41,6 +42,7 @@ type DB struct {
WaitBefore bool WaitBefore bool
WaitInterval time.Duration WaitInterval time.Duration
WaitTimeout time.Duration WaitTimeout time.Duration
Log io.Writer
} }
// migrationFileRegexp pattern for valid migration files // migrationFileRegexp pattern for valid migration files
@ -63,6 +65,7 @@ func New(databaseURL *url.URL) *DB {
WaitBefore: false, WaitBefore: false,
WaitInterval: DefaultWaitInterval, WaitInterval: DefaultWaitInterval,
WaitTimeout: DefaultWaitTimeout, WaitTimeout: DefaultWaitTimeout,
Log: os.Stdout,
} }
} }
@ -80,6 +83,7 @@ func (db *DB) GetDriver() (Driver, error) {
config := DriverConfig{ config := DriverConfig{
DatabaseURL: db.DatabaseURL, DatabaseURL: db.DatabaseURL,
MigrationsTableName: db.MigrationsTableName, MigrationsTableName: db.MigrationsTableName,
Log: db.Log,
} }
return driverFunc(config), nil return driverFunc(config), nil
@ -104,22 +108,22 @@ func (db *DB) wait(drv Driver) error {
return nil return nil
} }
fmt.Print("Waiting for database") fmt.Fprint(db.Log, "Waiting for database")
for i := 0 * time.Second; i < db.WaitTimeout; i += db.WaitInterval { for i := 0 * time.Second; i < db.WaitTimeout; i += db.WaitInterval {
fmt.Print(".") fmt.Fprint(db.Log, ".")
time.Sleep(db.WaitInterval) time.Sleep(db.WaitInterval)
// attempt connection to database server // attempt connection to database server
err = drv.Ping() err = drv.Ping()
if err == nil { if err == nil {
// connection successful // connection successful
fmt.Print("\n") fmt.Fprint(db.Log, "\n")
return nil return nil
} }
} }
// if we find outselves here, we could not connect within the timeout // if we find outselves here, we could not connect within the timeout
fmt.Print("\n") fmt.Fprint(db.Log, "\n")
return fmt.Errorf("unable to connect to database: %s", err) return fmt.Errorf("unable to connect to database: %s", err)
} }
@ -214,7 +218,7 @@ func (db *DB) dumpSchema(drv Driver) error {
return err return err
} }
fmt.Printf("Writing: %s\n", db.SchemaFile) fmt.Fprintf(db.Log, "Writing: %s\n", db.SchemaFile)
// ensure schema directory exists // ensure schema directory exists
if err = ensureDir(filepath.Dir(db.SchemaFile)); err != nil { if err = ensureDir(filepath.Dir(db.SchemaFile)); err != nil {
@ -252,7 +256,7 @@ func (db *DB) NewMigration(name string) error {
// check file does not already exist // check file does not already exist
path := filepath.Join(db.MigrationsDir, name) path := filepath.Join(db.MigrationsDir, name)
fmt.Printf("Creating migration: %s\n", path) fmt.Fprintf(db.Log, "Creating migration: %s\n", path)
if _, err := os.Stat(path); !os.IsNotExist(err) { if _, err := os.Stat(path); !os.IsNotExist(err) {
return fmt.Errorf("file already exists") return fmt.Errorf("file already exists")
@ -345,7 +349,7 @@ func (db *DB) migrate(drv Driver) error {
continue continue
} }
fmt.Printf("Applying: %s\n", filename) fmt.Fprintf(db.Log, "Applying: %s\n", filename)
up, _, err := parseMigration(filepath.Join(db.MigrationsDir, filename)) up, _, err := parseMigration(filepath.Join(db.MigrationsDir, filename))
if err != nil { if err != nil {
@ -358,7 +362,7 @@ func (db *DB) migrate(drv Driver) error {
if err != nil { if err != nil {
return err return err
} else if db.Verbose { } else if db.Verbose {
printVerbose(result) db.printVerbose(result)
} }
// record migration // record migration
@ -386,14 +390,14 @@ func (db *DB) migrate(drv Driver) error {
return nil return nil
} }
func printVerbose(result sql.Result) { func (db *DB) printVerbose(result sql.Result) {
lastInsertID, err := result.LastInsertId() lastInsertID, err := result.LastInsertId()
if err == nil { if err == nil {
fmt.Printf("Last insert ID: %d\n", lastInsertID) fmt.Fprintf(db.Log, "Last insert ID: %d\n", lastInsertID)
} }
rowsAffected, err := result.RowsAffected() rowsAffected, err := result.RowsAffected()
if err == nil { if err == nil {
fmt.Printf("Rows affected: %d\n", rowsAffected) fmt.Fprintf(db.Log, "Rows affected: %d\n", rowsAffected)
} }
} }
@ -485,7 +489,7 @@ func (db *DB) Rollback() error {
return err return err
} }
fmt.Printf("Rolling back: %s\n", filename) fmt.Fprintf(db.Log, "Rolling back: %s\n", filename)
_, down, err := parseMigration(filepath.Join(db.MigrationsDir, filename)) _, down, err := parseMigration(filepath.Join(db.MigrationsDir, filename))
if err != nil { if err != nil {
@ -498,7 +502,7 @@ func (db *DB) Rollback() error {
if err != nil { if err != nil {
return err return err
} else if db.Verbose { } else if db.Verbose {
printVerbose(result) db.printVerbose(result)
} }
// remove migration record // remove migration record
@ -548,15 +552,15 @@ func (db *DB) Status(quiet bool) (int, error) {
line = fmt.Sprintf("[ ] %s", res.Filename) line = fmt.Sprintf("[ ] %s", res.Filename)
} }
if !quiet { if !quiet {
fmt.Println(line) fmt.Fprintln(db.Log, line)
} }
} }
totalPending := len(results) - totalApplied totalPending := len(results) - totalApplied
if !quiet { if !quiet {
fmt.Println() fmt.Fprintln(db.Log)
fmt.Printf("Applied: %d\n", totalApplied) fmt.Fprintf(db.Log, "Applied: %d\n", totalApplied)
fmt.Printf("Pending: %d\n", totalPending) fmt.Fprintf(db.Log, "Pending: %d\n", totalPending)
} }
return totalPending, nil return totalPending, nil

View file

@ -2,6 +2,7 @@ package dbmate
import ( import (
"database/sql" "database/sql"
"io"
"net/url" "net/url"
"github.com/amacneil/dbmate/pkg/dbutil" "github.com/amacneil/dbmate/pkg/dbutil"
@ -25,6 +26,7 @@ type Driver interface {
type DriverConfig struct { type DriverConfig struct {
DatabaseURL *url.URL DatabaseURL *url.URL
MigrationsTableName string MigrationsTableName string
Log io.Writer
} }
// DriverFunc represents a driver constructor // DriverFunc represents a driver constructor

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"database/sql" "database/sql"
"fmt" "fmt"
"io"
"net/url" "net/url"
"regexp" "regexp"
"sort" "sort"
@ -23,6 +24,7 @@ func init() {
type Driver struct { type Driver struct {
migrationsTableName string migrationsTableName string
databaseURL *url.URL databaseURL *url.URL
log io.Writer
} }
// NewDriver initializes the driver // NewDriver initializes the driver
@ -30,6 +32,7 @@ func NewDriver(config dbmate.DriverConfig) dbmate.Driver {
return &Driver{ return &Driver{
migrationsTableName: config.MigrationsTableName, migrationsTableName: config.MigrationsTableName,
databaseURL: config.DatabaseURL, databaseURL: config.DatabaseURL,
log: config.Log,
} }
} }
@ -108,7 +111,7 @@ func (drv *Driver) quoteIdentifier(str string) string {
// CreateDatabase creates the specified database // CreateDatabase creates the specified database
func (drv *Driver) CreateDatabase() error { func (drv *Driver) CreateDatabase() error {
name := drv.databaseName() name := drv.databaseName()
fmt.Printf("Creating: %s\n", name) fmt.Fprintf(drv.log, "Creating: %s\n", name)
db, err := drv.openClickHouseDB() db, err := drv.openClickHouseDB()
if err != nil { if err != nil {
@ -124,7 +127,7 @@ func (drv *Driver) CreateDatabase() error {
// DropDatabase drops the specified database (if it exists) // DropDatabase drops the specified database (if it exists)
func (drv *Driver) DropDatabase() error { func (drv *Driver) DropDatabase() error {
name := drv.databaseName() name := drv.databaseName()
fmt.Printf("Dropping: %s\n", name) fmt.Fprintf(drv.log, "Dropping: %s\n", name)
db, err := drv.openClickHouseDB() db, err := drv.openClickHouseDB()
if err != nil { if err != nil {

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"database/sql" "database/sql"
"fmt" "fmt"
"io"
"net/url" "net/url"
"strings" "strings"
@ -21,6 +22,7 @@ func init() {
type Driver struct { type Driver struct {
migrationsTableName string migrationsTableName string
databaseURL *url.URL databaseURL *url.URL
log io.Writer
} }
// NewDriver initializes the driver // NewDriver initializes the driver
@ -28,6 +30,7 @@ func NewDriver(config dbmate.DriverConfig) dbmate.Driver {
return &Driver{ return &Driver{
migrationsTableName: config.MigrationsTableName, migrationsTableName: config.MigrationsTableName,
databaseURL: config.DatabaseURL, databaseURL: config.DatabaseURL,
log: config.Log,
} }
} }
@ -92,7 +95,7 @@ func (drv *Driver) quoteIdentifier(str string) string {
// CreateDatabase creates the specified database // CreateDatabase creates the specified database
func (drv *Driver) CreateDatabase() error { func (drv *Driver) CreateDatabase() error {
name := dbutil.DatabaseName(drv.databaseURL) name := dbutil.DatabaseName(drv.databaseURL)
fmt.Printf("Creating: %s\n", name) fmt.Fprintf(drv.log, "Creating: %s\n", name)
db, err := drv.openRootDB() db, err := drv.openRootDB()
if err != nil { if err != nil {
@ -109,7 +112,7 @@ func (drv *Driver) CreateDatabase() error {
// DropDatabase drops the specified database (if it exists) // DropDatabase drops the specified database (if it exists)
func (drv *Driver) DropDatabase() error { func (drv *Driver) DropDatabase() error {
name := dbutil.DatabaseName(drv.databaseURL) name := dbutil.DatabaseName(drv.databaseURL)
fmt.Printf("Dropping: %s\n", name) fmt.Fprintf(drv.log, "Dropping: %s\n", name)
db, err := drv.openRootDB() db, err := drv.openRootDB()
if err != nil { if err != nil {

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"database/sql" "database/sql"
"fmt" "fmt"
"io"
"net/url" "net/url"
"strings" "strings"
@ -22,6 +23,7 @@ func init() {
type Driver struct { type Driver struct {
migrationsTableName string migrationsTableName string
databaseURL *url.URL databaseURL *url.URL
log io.Writer
} }
// NewDriver initializes the driver // NewDriver initializes the driver
@ -29,6 +31,7 @@ func NewDriver(config dbmate.DriverConfig) dbmate.Driver {
return &Driver{ return &Driver{
migrationsTableName: config.MigrationsTableName, migrationsTableName: config.MigrationsTableName,
databaseURL: config.DatabaseURL, databaseURL: config.DatabaseURL,
log: config.Log,
} }
} }
@ -112,7 +115,7 @@ func (drv *Driver) openPostgresDB() (*sql.DB, error) {
// CreateDatabase creates the specified database // CreateDatabase creates the specified database
func (drv *Driver) CreateDatabase() error { func (drv *Driver) CreateDatabase() error {
name := dbutil.DatabaseName(drv.databaseURL) name := dbutil.DatabaseName(drv.databaseURL)
fmt.Printf("Creating: %s\n", name) fmt.Fprintf(drv.log, "Creating: %s\n", name)
db, err := drv.openPostgresDB() db, err := drv.openPostgresDB()
if err != nil { if err != nil {
@ -129,7 +132,7 @@ func (drv *Driver) CreateDatabase() error {
// DropDatabase drops the specified database (if it exists) // DropDatabase drops the specified database (if it exists)
func (drv *Driver) DropDatabase() error { func (drv *Driver) DropDatabase() error {
name := dbutil.DatabaseName(drv.databaseURL) name := dbutil.DatabaseName(drv.databaseURL)
fmt.Printf("Dropping: %s\n", name) fmt.Fprintf(drv.log, "Dropping: %s\n", name)
db, err := drv.openPostgresDB() db, err := drv.openPostgresDB()
if err != nil { if err != nil {
@ -233,7 +236,7 @@ func (drv *Driver) CreateMigrationsTable(db *sql.DB) error {
// in theory we could attempt to create the schema every time, but we avoid that // in theory we could attempt to create the schema every time, but we avoid that
// in case the user doesn't have permissions to create schemas // in case the user doesn't have permissions to create schemas
fmt.Printf("Creating schema: %s\n", schema) fmt.Fprintf(drv.log, "Creating schema: %s\n", schema)
_, err = db.Exec(fmt.Sprintf("create schema if not exists %s", schema)) _, err = db.Exec(fmt.Sprintf("create schema if not exists %s", schema))
if err != nil { if err != nil {
return err return err

View file

@ -6,6 +6,7 @@ import (
"bytes" "bytes"
"database/sql" "database/sql"
"fmt" "fmt"
"io"
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
@ -27,6 +28,7 @@ func init() {
type Driver struct { type Driver struct {
migrationsTableName string migrationsTableName string
databaseURL *url.URL databaseURL *url.URL
log io.Writer
} }
// NewDriver initializes the driver // NewDriver initializes the driver
@ -34,6 +36,7 @@ func NewDriver(config dbmate.DriverConfig) dbmate.Driver {
return &Driver{ return &Driver{
migrationsTableName: config.MigrationsTableName, migrationsTableName: config.MigrationsTableName,
databaseURL: config.DatabaseURL, databaseURL: config.DatabaseURL,
log: config.Log,
} }
} }
@ -56,7 +59,7 @@ func (drv *Driver) Open() (*sql.DB, error) {
// CreateDatabase creates the specified database // CreateDatabase creates the specified database
func (drv *Driver) CreateDatabase() error { func (drv *Driver) CreateDatabase() error {
fmt.Printf("Creating: %s\n", ConnectionString(drv.databaseURL)) fmt.Fprintf(drv.log, "Creating: %s\n", ConnectionString(drv.databaseURL))
db, err := drv.Open() db, err := drv.Open()
if err != nil { if err != nil {
@ -70,7 +73,7 @@ func (drv *Driver) CreateDatabase() error {
// DropDatabase drops the specified database (if it exists) // DropDatabase drops the specified database (if it exists)
func (drv *Driver) DropDatabase() error { func (drv *Driver) DropDatabase() error {
path := ConnectionString(drv.databaseURL) path := ConnectionString(drv.databaseURL)
fmt.Printf("Dropping: %s\n", path) fmt.Fprintf(drv.log, "Dropping: %s\n", path)
exists, err := drv.DatabaseExists() exists, err := drv.DatabaseExists()
if err != nil { if err != nil {