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...)
+}