From 889e2e9c5977911b8ba6542b2b575c50d1067ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Maranh=C3=A3o?= <56287238+alexandremr01@users.noreply.github.com> Date: Mon, 4 Jul 2022 10:33:22 -0300 Subject: [PATCH] Feat/setup logger (#48) - Add zap logger - Add zapFactory to build the logger - Add LogService to manage logging - Add verbose and silent flags --- Makefile | 2 + README.md | 8 ++ .../{logs.go => migration_helpers.go} | 0 ...logs_test.go => migration_helpers_test.go} | 0 applications/migrationscripts.go | 8 +- applications/migrationscripts_test.go | 17 +++- cmd/apply.go | 2 +- cmd/command.go | 40 +++++++- cmd/diff.go | 11 ++- cmd/empty.go | 9 +- cmd/root.go | 18 +++- cmd/validate.go | 2 +- domain/schema.go | 7 +- drivers/postgres/parser.go | 20 +++- drivers/postgres/postgres_test.go | 9 +- go.mod | 5 +- go.sum | 11 ++- infrastructure/logger.go | 96 +++++++++++++++++++ mock/applications/log_mock.go | 82 ++++++++++++++++ 19 files changed, 306 insertions(+), 41 deletions(-) rename applications/{logs.go => migration_helpers.go} (100%) rename applications/{logs_test.go => migration_helpers_test.go} (100%) create mode 100644 infrastructure/logger.go create mode 100644 mock/applications/log_mock.go diff --git a/Makefile b/Makefile index cf6ad1e..d1bca14 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,8 @@ release: .PHONY: mock mock: @mockgen -source=domain/driver.go -destination=mock/domain/driver_mock.go -package=domain_mock + @mockgen -source=domain/diff_deque.go -destination=mock/domain/diff_deque_mock.go -package=domain_mock @mockgen -source=infrastructure/clock.go -destination=mock/infrastructure/clock_mock.go -package=infrastructure_mock @mockgen -source=infrastructure/file.go -destination=mock/infrastructure/file_mock.go -package=infrastructure_mock @mockgen -source=applications/migrationscripts.go -destination=mock/applications/migrationscripts_mock.go -package=applications_mock + @mockgen -source=applications/log.go -destination=mock/applications/log_mock.go -package=applications_mock diff --git a/README.md b/README.md index dd7d3aa..ee95403 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,14 @@ Set through command flag `--driver`. - **dir**: Migrations directory.
Set through command flag `--dir`. + +#### Optional flags + +- **verbose**: Prints info logs. Mutually exclusive with `silent`. + +- **silent**: Only prints critical logs. Mutually exclusive with `verbose`. + + ### Execute migrations Requires: **database**, **dir**, **driver**
diff --git a/applications/logs.go b/applications/migration_helpers.go similarity index 100% rename from applications/logs.go rename to applications/migration_helpers.go diff --git a/applications/logs_test.go b/applications/migration_helpers_test.go similarity index 100% rename from applications/logs_test.go rename to applications/migration_helpers_test.go diff --git a/applications/migrationscripts.go b/applications/migrationscripts.go index 24dcdd5..987e077 100644 --- a/applications/migrationscripts.go +++ b/applications/migrationscripts.go @@ -2,8 +2,6 @@ package applications import ( "fmt" - "log" - "github.com/migratemgr8/mgr8/domain" "github.com/migratemgr8/mgr8/infrastructure" ) @@ -18,13 +16,15 @@ type migrationFileService struct { fileService infrastructure.FileService driver domain.Driver fileNameFormatter FileNameFormatter + logService infrastructure.LogService } -func NewMigrationFileService(fService infrastructure.FileService, fileNameFormatter FileNameFormatter, driver domain.Driver) *migrationFileService { +func NewMigrationFileService(fService infrastructure.FileService, fileNameFormatter FileNameFormatter, driver domain.Driver, logService infrastructure.LogService) *migrationFileService { return &migrationFileService{ fileService: fService, driver: driver, fileNameFormatter: fileNameFormatter, + logService: logService, } } @@ -54,7 +54,7 @@ func (g *migrationFileService) GetSchemaFromFile(filename string) (*domain.Schem func (g *migrationFileService) WriteStatementsToFile(migrationDir string, statements []string, migrationNumber int, migrationType string) error { filename := g.fileNameFormatter.FormatFilename(migrationNumber, migrationType) - log.Printf("Generating file %s migration %s", migrationType, filename) + g.logService.Info("Generating file %s migration %s", migrationType, filename) content := g.driver.Deparser().WriteScript(statements) return g.fileService.Write(migrationDir, filename, content) } diff --git a/applications/migrationscripts_test.go b/applications/migrationscripts_test.go index 3ad92de..05ae2cc 100644 --- a/applications/migrationscripts_test.go +++ b/applications/migrationscripts_test.go @@ -67,7 +67,9 @@ var _ = Describe("Migration Scripts", func() { driver.EXPECT().Deparser().Return(mockDeparser) mockDeparser.EXPECT().WriteScript([]string{"statement"}).Return("sql") fileService.EXPECT().Write("directory", "formatted_filename", "sql") - subject = NewMigrationFileService(fileService, formatter, driver) + mockLogService := anyLog(ctrl) + + subject = NewMigrationFileService(fileService, formatter, driver, mockLogService) }) When(fmt.Sprintf("Asked to generated"), func() { It("Generates expected file", func() { @@ -83,7 +85,8 @@ var _ = Describe("Migration Scripts", func() { ctrl := gomock.NewController(_t) clock := infrastructure_mock.NewMockClock(ctrl) fileService = infrastructure_mock.NewMockFileService(ctrl) - subject = NewMigrationFileService(fileService, NewFileNameFormatter(clock), domain_mock.NewMockDriver(ctrl)) + mockLogService := anyLog(ctrl) + subject = NewMigrationFileService(fileService, NewFileNameFormatter(clock), domain_mock.NewMockDriver(ctrl), mockLogService) }) When("Has two migration files", func() { It("Next migration number is 3", func() { @@ -131,7 +134,7 @@ var _ = Describe("Migration Scripts", func() { clock := infrastructure_mock.NewMockClock(ctrl) fileService = infrastructure_mock.NewMockFileService(ctrl) driver = domain_mock.NewMockDriver(ctrl) - subject = NewMigrationFileService(fileService, NewFileNameFormatter(clock), driver) + subject = NewMigrationFileService(fileService, NewFileNameFormatter(clock), driver, applications_mock.NewMockLogService(ctrl)) }) When("Reads file successfully", func() { It("Generates expected filename", func() { @@ -154,3 +157,11 @@ var _ = Describe("Migration Scripts", func() { }) }) + +func anyLog(ctrl *gomock.Controller) *applications_mock.MockLogService{ + mockLogService := applications_mock.NewMockLogService(ctrl) + mockLogService.EXPECT().Info(gomock.Any()).AnyTimes() + mockLogService.EXPECT().Critical(gomock.Any()).AnyTimes() + mockLogService.EXPECT().Debug(gomock.Any()).AnyTimes() + return mockLogService +} diff --git a/cmd/apply.go b/cmd/apply.go index 6447ac9..4f3f0d7 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -36,7 +36,7 @@ type Migrations struct { isUpType bool } -func (a *apply) execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver) error { +func (a *apply) execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver, verbosity infrastructure.LogLevel) error { a.hashService = applications.NewHashService(infrastructure.NewFileService()) dir := migrationsDir migrationFiles, err := getMigrationsFiles(dir) diff --git a/cmd/command.go b/cmd/command.go index 319ad25..565d46d 100644 --- a/cmd/command.go +++ b/cmd/command.go @@ -1,34 +1,68 @@ package cmd import ( + "errors" "log" "github.com/migratemgr8/mgr8/domain" "github.com/migratemgr8/mgr8/drivers" + "github.com/migratemgr8/mgr8/infrastructure" + "github.com/spf13/cobra" ) var defaultDriverName = string(drivers.Postgres) type CommandExecutor interface { - execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver) error + execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver, verbosity infrastructure.LogLevel) error } type Command struct { driverName string databaseURL string migrationsDir string - cmd CommandExecutor + + cmd CommandExecutor } func (c *Command) Execute(cmd *cobra.Command, args []string) { + verbose, err := cmd.Flags().GetBool(verboseFlag) + if err != nil { + panic(err) + } + silent, err := cmd.Flags().GetBool(silentFlag) + if err != nil { + panic(err) + } + driver, err := drivers.GetDriver(c.driverName) if err != nil { log.Fatal(err) } - err = c.cmd.execute(args, c.databaseURL, c.migrationsDir, driver) + logLevel, err := c.getLogLevel(verbose, silent) if err != nil { log.Fatal(err) } + + err = c.cmd.execute(args, c.databaseURL, c.migrationsDir, driver, logLevel) + if err != nil { + log.Fatal(err) + } +} + +func (c *Command) getLogLevel(verbose, silent bool) (infrastructure.LogLevel, error) { + if silent && verbose { + return "", errors.New("flags silent and verbose are mutually exclusive") + } + + if silent { + return infrastructure.CriticalLogLevel, nil + } + + if verbose { + return infrastructure.DebugLogLevel, nil + } + + return infrastructure.InfoLogLevel, nil } diff --git a/cmd/diff.go b/cmd/diff.go index 91c6bbf..bdadf6c 100644 --- a/cmd/diff.go +++ b/cmd/diff.go @@ -1,6 +1,7 @@ package cmd import ( + "fmt" "log" "github.com/migratemgr8/mgr8/applications" @@ -10,19 +11,23 @@ import ( type diff struct{} -func (g *diff) execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver) error { +func (g *diff) execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver, verbosity infrastructure.LogLevel) error { newSchemaPath := args[0] fileService := infrastructure.NewFileService() clock := infrastructure.NewClock() + logService, err := infrastructure.NewLogService(verbosity) + if err != nil { + return fmt.Errorf("could not start logger, error: %w", err) + } generateCommand := applications.NewGenerateCommand( driver, - applications.NewMigrationFileService(fileService, applications.NewFileNameFormatter(clock), driver), + applications.NewMigrationFileService(fileService, applications.NewFileNameFormatter(clock), driver, logService), fileService, ) - err := generateCommand.Execute(&applications.GenerateParameters{ + err = generateCommand.Execute(&applications.GenerateParameters{ OldSchemaPath: ".mgr8/reference.sql", NewSchemaPath: newSchemaPath, MigrationDir: migrationsDir, diff --git a/cmd/empty.go b/cmd/empty.go index be57035..ca4973d 100644 --- a/cmd/empty.go +++ b/cmd/empty.go @@ -12,11 +12,16 @@ type empty struct { emptyCommand applications.EmptyCommand } -func (c *empty) execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver) error { +func (c *empty) execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver, verbosity infrastructure.LogLevel) error { if c.emptyCommand == nil { fileService := infrastructure.NewFileService() clock := infrastructure.NewClock() - migrationFileService := applications.NewMigrationFileService(fileService, applications.NewFileNameFormatter(clock), driver) + logService, err := infrastructure.NewLogService(verbosity) + if err != nil { + log.Fatal(err) + } + + migrationFileService := applications.NewMigrationFileService(fileService, applications.NewFileNameFormatter(clock), driver, logService) c.emptyCommand = applications.NewEmptyCommand(migrationFileService) } diff --git a/cmd/root.go b/cmd/root.go index 9fbd8e2..6f18480 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -7,7 +7,11 @@ import ( "github.com/spf13/cobra" ) -const defaultMigrationDir = "migrations" +const ( + defaultMigrationDir = "migrations" + verboseFlag = "verbose" + silentFlag = "silent" +) func Execute() { rootCmd := &cobra.Command{ @@ -16,6 +20,9 @@ func Execute() { Long: `Long version: mgr8 is an agnostic tool that abstracts database migration operations`, } + rootCmd.PersistentFlags().Bool(verboseFlag, false, "Verbose") + rootCmd.PersistentFlags().Bool(silentFlag, false, "Silent") + generateCmd := &cobra.Command{ Use: "generate", Short: "generate creates migration scripts", @@ -37,13 +44,14 @@ func Execute() { Use: "empty", Short: "empty creates empty migration", Run: emptyCommand.Execute, + Args: cobra.NoArgs, } emptyCmd.Flags().StringVar(&emptyCommand.migrationsDir, "dir", defaultMigrationDir, "Migrations Directory") emptyCmd.Flags().StringVar(&emptyCommand.driverName, "driver", defaultDriverName, "Driver Name") initCommand := &InitCommand{} initCmd := &cobra.Command{ - Use: "init", + Use: "init file", Short: "init sets the schema as reference", Run: initCommand.Execute, Args: cobra.MinimumNArgs(1), @@ -51,7 +59,7 @@ func Execute() { checkCommand := &CheckCommand{} checkCmd := &cobra.Command{ - Use: "check", + Use: "check file", Short: "check returns 0 if files match", Run: checkCommand.Execute, Args: cobra.MinimumNArgs(1), @@ -61,7 +69,7 @@ func Execute() { applyCommand := Command{cmd: &apply{}} applyCmd := &cobra.Command{ - Use: "apply", + Use: "apply n", Short: "apply runs migrations in the selected database", Run: applyCommand.Execute, Args: cobra.MinimumNArgs(1), @@ -72,7 +80,7 @@ func Execute() { validateCommand := Command{cmd: &validate{}} validateCmd := &cobra.Command{ - Use: "validate", + Use: "validate file", Short: "validate compares migrations sql scripts against hashing ", Run: validateCommand.Execute, Args: cobra.MinimumNArgs(1), diff --git a/cmd/validate.go b/cmd/validate.go index 4aec190..5f3b1a9 100644 --- a/cmd/validate.go +++ b/cmd/validate.go @@ -12,7 +12,7 @@ import ( type validate struct{} -func (v *validate) execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver) error { +func (v *validate) execute(args []string, databaseURL string, migrationsDir string, driver domain.Driver, verbosity infrastructure.LogLevel) error { dir := args[0] return driver.ExecuteTransaction(databaseURL, func() error { err := applications.CheckAndInstallTool(driver) diff --git a/domain/schema.go b/domain/schema.go index a318993..8b3efbe 100644 --- a/domain/schema.go +++ b/domain/schema.go @@ -15,9 +15,10 @@ func NewTable(name string, columns map[string]*Column) *Table { } type Column struct { - Datatype string - Parameters map[string]interface{} - IsNotNull bool + Datatype string + Parameters map[string]interface{} + IsNotNull bool + DefaultValue interface{} } type View struct { diff --git a/drivers/postgres/parser.go b/drivers/postgres/parser.go index 1cad026..b6fd941 100644 --- a/drivers/postgres/parser.go +++ b/drivers/postgres/parser.go @@ -275,15 +275,27 @@ func (d *postgresDriver) parseColumn(columnDefinition *pg_query.ColumnDef) *doma } isNotNull := false + defaultValue := interface{}(nil) + for _, constraint := range columnDefinition.Constraints { - if constraint.GetConstraint().GetContype().String() == "CONSTR_NOTNULL" { + if constraint.GetConstraint().GetContype() == pg_query.ConstrType_CONSTR_NOTNULL { isNotNull = true } + if constraint.GetConstraint().GetContype() == pg_query.ConstrType_CONSTR_DEFAULT { + constant := constraint.GetConstraint().RawExpr.GetAConst().Val + switch constant.Node.(type) { + case *pg_query.Node_String_: + defaultValue = constant.GetString_().Str + case *pg_query.Node_Integer: + defaultValue = constant.GetInteger().Ival + } + } } return &domain.Column{ - Datatype: datatype, - Parameters: parameters, - IsNotNull: isNotNull, + Datatype: datatype, + Parameters: parameters, + IsNotNull: isNotNull, + DefaultValue: defaultValue, } } diff --git a/drivers/postgres/postgres_test.go b/drivers/postgres/postgres_test.go index 27efb96..a358dcb 100644 --- a/drivers/postgres/postgres_test.go +++ b/drivers/postgres/postgres_test.go @@ -19,16 +19,15 @@ var _ = Describe("Postgres Driver", func() { BeforeEach(func() { subject = NewPostgresDriver() }) - When("Table has all data types", func() { It("Parses each of them", func() { migration := ` CREATE TABLE users ( social_number VARCHAR(9) PRIMARY KEY, phone VARCHAR(11), - name VARCHAR(15) NOT NULL, + name VARCHAR(15) NOT NULL DEFAULT 'oi', age INTEGER, - size INT, + size INT DEFAULT 5, height DECIMAL(2, 3), ddi VARCHAR(3) ); @@ -45,10 +44,10 @@ var _ = Describe("Postgres Driver", func() { Columns: map[string]*domain.Column{ "social_number": {Datatype: "varchar", IsNotNull: false, Parameters: map[string]interface{}{"size": int32(9)}}, "phone": {Datatype: "varchar", IsNotNull: false, Parameters: map[string]interface{}{"size": int32(11)}}, - "name": {Datatype: "varchar", IsNotNull: true, Parameters: map[string]interface{}{"size": int32(15)}}, + "name": {Datatype: "varchar", IsNotNull: true, Parameters: map[string]interface{}{"size": int32(15)}, DefaultValue: "oi"}, "ddi": {Datatype: "varchar", IsNotNull: false, Parameters: map[string]interface{}{"size": int32(3)}}, "age": {Datatype: "int4", IsNotNull: false, Parameters: map[string]interface{}{}}, - "size": {Datatype: "int4", IsNotNull: false, Parameters: map[string]interface{}{}}, + "size": {Datatype: "int4", IsNotNull: false, Parameters: map[string]interface{}{}, DefaultValue: int32(5)}, "height": {Datatype: "numeric", IsNotNull: false, Parameters: map[string]interface{}{"precision": int32(2), "scale": int32(3)}}, }, }, diff --git a/go.mod b/go.mod index 9885a5a..0970471 100644 --- a/go.mod +++ b/go.mod @@ -20,11 +20,13 @@ require ( github.com/golang/mock v1.6.0 github.com/pingcap/parser v0.0.0-20210831085004-b5390aa83f65 github.com/pingcap/tidb v1.1.0-beta.0.20220418060002-fe5aa9760b58 + go.uber.org/zap v1.21.0 ) require ( github.com/BurntSushi/toml v0.3.1 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect + github.com/benbjohnson/clock v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect @@ -59,8 +61,7 @@ require ( github.com/uber/jaeger-lib v2.4.1+incompatible // indirect go.etcd.io/etcd v0.5.0-alpha.5.0.20200824191128-ae9734ed278b // indirect go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.7.0 // indirect - go.uber.org/zap v1.19.1 // indirect + go.uber.org/multierr v1.8.0 // indirect golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 // indirect golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect diff --git a/go.sum b/go.sum index 43cbf89..cbc1c17 100644 --- a/go.sum +++ b/go.sum @@ -775,15 +775,16 @@ go.uber.org/dig v1.8.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/fx v1.10.0/go.mod h1:vLRicqpG/qQEzno4SYU86iCwfT95EZza+Eba0ItuxqY= go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -793,8 +794,8 @@ go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/infrastructure/logger.go b/infrastructure/logger.go new file mode 100644 index 0000000..7d0f914 --- /dev/null +++ b/infrastructure/logger.go @@ -0,0 +1,96 @@ +package infrastructure + +import ( + "log" + "time" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type LogService interface { + Info(args ...interface{}) + Debug(args ...interface{}) + Critical(args ...interface{}) +} + +type LogLevel string + +const ( + InfoLogLevel = "info" + DebugLogLevel = "debug" + CriticalLogLevel = "critical" +) + +type zapFactory struct { } + +func newZapFactory() *zapFactory { + return &zapFactory{} +} + +func (*zapFactory) getConfig(level zapcore.Level) zap.Config { + encoderConfig := zap.NewProductionEncoderConfig() + encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + encoderConfig.EncodeTime = func(t time.Time, e zapcore.PrimitiveArrayEncoder) { + e.AppendString(t.Format("15:04:05.000")) + } + + config := zap.Config{ + Level: zap.NewAtomicLevelAt(level), + Development: true, + Encoding: "console", + EncoderConfig: encoderConfig, + OutputPaths: []string{"stdout"}, + ErrorOutputPaths: []string{"stdout"}, + DisableCaller: true, + } + + return config +} + +func (*zapFactory) getLevel(level LogLevel) zapcore.Level { + if level == InfoLogLevel { + return zap.InfoLevel + } else if level == CriticalLogLevel { + return zap.ErrorLevel + } else if level == DebugLogLevel { + return zap.DebugLevel + } + return zap.InfoLevel +} + +func (z *zapFactory) Build(level LogLevel) (*zap.SugaredLogger, error) { + zapLevel := z.getLevel(level) + conf := z.getConfig(zapLevel) + logger, err := conf.Build() + if err != nil { + log.Fatalf("could not build logger %s", err) + } + return logger.Sugar(), nil +} + +type logService struct { + logger *zap.SugaredLogger +} + +func NewLogService(logLevel LogLevel) (*logService, error){ + zapFact := newZapFactory() + logger, err := zapFact.Build(logLevel) + if err != nil { + return nil, err + } + return &logService{logger: logger}, nil +} + +func (l *logService) Info(args ...interface{}) { + l.logger.Info(args) +} + +func (l *logService) Debug(args ...interface{}) { + l.logger.Debug(args) +} + +func (l *logService) Critical(args ...interface{}) { + l.logger.Error(args) +} + diff --git a/mock/applications/log_mock.go b/mock/applications/log_mock.go new file mode 100644 index 0000000..d19ba1a --- /dev/null +++ b/mock/applications/log_mock.go @@ -0,0 +1,82 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: applications/log.go + +// Package applications_mock is a generated GoMock package. +package applications_mock + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockLogService is a mock of LogService interface. +type MockLogService struct { + ctrl *gomock.Controller + recorder *MockLogServiceMockRecorder +} + +// MockLogServiceMockRecorder is the mock recorder for MockLogService. +type MockLogServiceMockRecorder struct { + mock *MockLogService +} + +// NewMockLogService creates a new mock instance. +func NewMockLogService(ctrl *gomock.Controller) *MockLogService { + mock := &MockLogService{ctrl: ctrl} + mock.recorder = &MockLogServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLogService) EXPECT() *MockLogServiceMockRecorder { + return m.recorder +} + +// Critical mocks base method. +func (m *MockLogService) Critical(args ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Critical", varargs...) +} + +// Critical indicates an expected call of Critical. +func (mr *MockLogServiceMockRecorder) Critical(args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Critical", reflect.TypeOf((*MockLogService)(nil).Critical), args...) +} + +// Debug mocks base method. +func (m *MockLogService) Debug(args ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Debug", varargs...) +} + +// Debug indicates an expected call of Debug. +func (mr *MockLogServiceMockRecorder) Debug(args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogService)(nil).Debug), args...) +} + +// Info mocks base method. +func (m *MockLogService) Info(args ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Info", varargs...) +} + +// Info indicates an expected call of Info. +func (mr *MockLogServiceMockRecorder) Info(args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogService)(nil).Info), args...) +}