mirror of https://github.com/pressly/goose.git
feat: add optional logging in Provider with verbose option (#668)
parent
274b8fb5cb
commit
b114337ef6
28
provider.go
28
provider.go
|
@ -62,6 +62,7 @@ func NewProvider(dialect Dialect, db *sql.DB, fsys fs.FS, opts ...ProviderOption
|
||||||
registered: make(map[int64]*Migration),
|
registered: make(map[int64]*Migration),
|
||||||
excludePaths: make(map[string]bool),
|
excludePaths: make(map[string]bool),
|
||||||
excludeVersions: make(map[int64]bool),
|
excludeVersions: make(map[int64]bool),
|
||||||
|
logger: &stdLogger{},
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
if err := opt.apply(&cfg); err != nil {
|
if err := opt.apply(&cfg); err != nil {
|
||||||
|
@ -152,7 +153,7 @@ func (p *Provider) Status(ctx context.Context) ([]*MigrationStatus, error) {
|
||||||
// which migrations were applied. For example, if migrations were applied out of order (1,4,2,3),
|
// which migrations were applied. For example, if migrations were applied out of order (1,4,2,3),
|
||||||
// this method returns 4. If no migrations have been applied, it returns 0.
|
// this method returns 4. If no migrations have been applied, it returns 0.
|
||||||
func (p *Provider) GetDBVersion(ctx context.Context) (int64, error) {
|
func (p *Provider) GetDBVersion(ctx context.Context) (int64, error) {
|
||||||
return p.getDBMaxVersion(ctx)
|
return p.getDBMaxVersion(ctx, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListSources returns a list of all migration sources known to the provider, sorted in ascending
|
// ListSources returns a list of all migration sources known to the provider, sorted in ascending
|
||||||
|
@ -369,6 +370,7 @@ func (p *Provider) down(
|
||||||
}
|
}
|
||||||
// We never migrate the zero version down.
|
// We never migrate the zero version down.
|
||||||
if dbMigrations[0].Version == 0 {
|
if dbMigrations[0].Version == 0 {
|
||||||
|
p.printf("no migrations to run, current version: 0")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
var apply []*Migration
|
var apply []*Migration
|
||||||
|
@ -413,15 +415,15 @@ func (p *Provider) apply(
|
||||||
// 1. direction is up
|
// 1. direction is up
|
||||||
// a. migration is applied, this is an error (ErrAlreadyApplied)
|
// a. migration is applied, this is an error (ErrAlreadyApplied)
|
||||||
// b. migration is not applied, apply it
|
// b. migration is not applied, apply it
|
||||||
|
if direction && result != nil {
|
||||||
|
return nil, fmt.Errorf("version %d: %w", version, ErrAlreadyApplied)
|
||||||
|
}
|
||||||
// 2. direction is down
|
// 2. direction is down
|
||||||
// a. migration is applied, rollback
|
// a. migration is applied, rollback
|
||||||
// b. migration is not applied, this is an error (ErrNotApplied)
|
// b. migration is not applied, this is an error (ErrNotApplied)
|
||||||
if result == nil && !direction {
|
if !direction && result == nil {
|
||||||
return nil, fmt.Errorf("version %d: %w", version, ErrNotApplied)
|
return nil, fmt.Errorf("version %d: %w", version, ErrNotApplied)
|
||||||
}
|
}
|
||||||
if result != nil && direction {
|
|
||||||
return nil, fmt.Errorf("version %d: %w", version, ErrAlreadyApplied)
|
|
||||||
}
|
|
||||||
d := sqlparser.DirectionDown
|
d := sqlparser.DirectionDown
|
||||||
if direction {
|
if direction {
|
||||||
d = sqlparser.DirectionUp
|
d = sqlparser.DirectionUp
|
||||||
|
@ -462,15 +464,23 @@ func (p *Provider) status(ctx context.Context) (_ []*MigrationStatus, retErr err
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) getDBMaxVersion(ctx context.Context) (_ int64, retErr error) {
|
// getDBMaxVersion returns the highest version recorded in the database, regardless of the order in
|
||||||
conn, cleanup, err := p.initialize(ctx)
|
// which migrations were applied. conn may be nil, in which case a connection is initialized.
|
||||||
|
//
|
||||||
|
// optimize(mf): we should only fetch the max version from the database, no need to fetch all
|
||||||
|
// migrations only to get the max version. This means expanding the Store interface.
|
||||||
|
func (p *Provider) getDBMaxVersion(ctx context.Context, conn *sql.Conn) (_ int64, retErr error) {
|
||||||
|
if conn == nil {
|
||||||
|
var cleanup func() error
|
||||||
|
var err error
|
||||||
|
conn, cleanup, err = p.initialize(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to initialize: %w", err)
|
return 0, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
retErr = multierr.Append(retErr, cleanup())
|
retErr = multierr.Append(retErr, cleanup())
|
||||||
}()
|
}()
|
||||||
|
}
|
||||||
res, err := p.store.ListMigrations(ctx, conn)
|
res, err := p.store.ListMigrations(ctx, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
@ -184,6 +184,11 @@ type config struct {
|
||||||
disableVersioning bool
|
disableVersioning bool
|
||||||
allowMissing bool
|
allowMissing bool
|
||||||
disableGlobalRegistry bool
|
disableGlobalRegistry bool
|
||||||
|
|
||||||
|
// Let's not expose the Logger just yet. Ideally we consolidate on the std lib slog package
|
||||||
|
// added in go1.21 and then expose that (if that's even necessary). For now, just use the std
|
||||||
|
// lib log package.
|
||||||
|
logger Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type configFunc func(*config) error
|
type configFunc func(*config) error
|
||||||
|
|
|
@ -121,6 +121,17 @@ func (p *Provider) prepareMigration(fsys fs.FS, m *Migration, direction bool) er
|
||||||
return fmt.Errorf("invalid migration type: %+v", m)
|
return fmt.Errorf("invalid migration type: %+v", m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printf is a helper function that prints the given message if verbose is enabled. It also prepends
|
||||||
|
// the "goose: " prefix to the message.
|
||||||
|
func (p *Provider) printf(msg string, args ...interface{}) {
|
||||||
|
if p.cfg.verbose {
|
||||||
|
if !strings.HasPrefix(msg, "goose:") {
|
||||||
|
msg = "goose: " + msg
|
||||||
|
}
|
||||||
|
p.cfg.logger.Printf(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// runMigrations runs migrations sequentially in the given direction. If the migrations list is
|
// runMigrations runs migrations sequentially in the given direction. If the migrations list is
|
||||||
// empty, return nil without error.
|
// empty, return nil without error.
|
||||||
func (p *Provider) runMigrations(
|
func (p *Provider) runMigrations(
|
||||||
|
@ -131,6 +142,15 @@ func (p *Provider) runMigrations(
|
||||||
byOne bool,
|
byOne bool,
|
||||||
) ([]*MigrationResult, error) {
|
) ([]*MigrationResult, error) {
|
||||||
if len(migrations) == 0 {
|
if len(migrations) == 0 {
|
||||||
|
if !p.cfg.disableVersioning {
|
||||||
|
// No need to print this message if versioning is disabled because there are no
|
||||||
|
// migrations being tracked in the goose version table.
|
||||||
|
maxVersion, err := p.getDBMaxVersion(ctx, conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.printf("no migrations to run, current version: %d", maxVersion)
|
||||||
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
apply := migrations
|
apply := migrations
|
||||||
|
@ -162,7 +182,7 @@ func (p *Provider) runMigrations(
|
||||||
|
|
||||||
var results []*MigrationResult
|
var results []*MigrationResult
|
||||||
for _, m := range apply {
|
for _, m := range apply {
|
||||||
current := &MigrationResult{
|
result := &MigrationResult{
|
||||||
Source: &Source{
|
Source: &Source{
|
||||||
Type: m.Type,
|
Type: m.Type,
|
||||||
Path: m.Source,
|
Path: m.Source,
|
||||||
|
@ -175,18 +195,25 @@ func (p *Provider) runMigrations(
|
||||||
if err := p.runIndividually(ctx, conn, m, direction.ToBool()); err != nil {
|
if err := p.runIndividually(ctx, conn, m, direction.ToBool()); err != nil {
|
||||||
// TODO(mf): we should also return the pending migrations here, the remaining items in
|
// TODO(mf): we should also return the pending migrations here, the remaining items in
|
||||||
// the apply slice.
|
// the apply slice.
|
||||||
current.Error = err
|
result.Error = err
|
||||||
current.Duration = time.Since(start)
|
result.Duration = time.Since(start)
|
||||||
return nil, &PartialError{
|
return nil, &PartialError{
|
||||||
Applied: results,
|
Applied: results,
|
||||||
Failed: current,
|
Failed: result,
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
current.Duration = time.Since(start)
|
result.Duration = time.Since(start)
|
||||||
results = append(results, current)
|
results = append(results, result)
|
||||||
|
p.printf("%s", result)
|
||||||
|
}
|
||||||
|
if !p.cfg.disableVersioning && !byOne {
|
||||||
|
maxVersion, err := p.getDBMaxVersion(ctx, conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.printf("successfully migrated database, current version: %d", maxVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,12 +36,31 @@ type MigrationResult struct {
|
||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the migration result.
|
||||||
|
//
|
||||||
|
// Example down:
|
||||||
|
//
|
||||||
|
// EMPTY down 00006_posts_view-copy.sql (607.83µs)
|
||||||
|
// OK down 00005_posts_view.sql (646.25µs)
|
||||||
|
//
|
||||||
|
// Example up:
|
||||||
|
//
|
||||||
|
// OK up 00005_posts_view.sql (727.5µs)
|
||||||
|
// EMPTY up 00006_posts_view-copy.sql (378.33µs)
|
||||||
func (m *MigrationResult) String() string {
|
func (m *MigrationResult) String() string {
|
||||||
state := "OK"
|
var format string
|
||||||
|
if m.Direction == "up" {
|
||||||
|
format = "%-5s %-2s %s (%s)"
|
||||||
|
} else {
|
||||||
|
format = "%-5s %-4s %s (%s)"
|
||||||
|
}
|
||||||
|
var state string
|
||||||
if m.Empty {
|
if m.Empty {
|
||||||
state = "EMPTY"
|
state = "EMPTY"
|
||||||
|
} else {
|
||||||
|
state = "OK"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%-6s %-4s %s (%s)",
|
return fmt.Sprintf(format,
|
||||||
state,
|
state,
|
||||||
m.Direction,
|
m.Direction,
|
||||||
filepath.Base(m.Source.Path),
|
filepath.Base(m.Source.Path),
|
||||||
|
|
Loading…
Reference in New Issue