diff --git a/Makefile b/Makefile index 8a432a676c..48ec022c94 100644 --- a/Makefile +++ b/Makefile @@ -453,8 +453,7 @@ run-server: prepackaged-binaries validate-go-version start-docker ## Starts the @echo Running mattermost for development mkdir -p $(BUILD_WEBAPP_DIR)/dist/files - $(GO) run $(GOFLAGS) -ldflags '$(LDFLAGS)' $(PLATFORM_FILES) 2>&1 | \ - $(GO) run $(GOFLAGS) -ldflags '$(LDFLAGS)' $(PLATFORM_FILES) logs --logrus $(RUN_IN_BACKGROUND) + $(GO) run $(GOFLAGS) -ldflags '$(LDFLAGS)' $(PLATFORM_FILES) $(RUN_IN_BACKGROUND) debug-server: start-docker ## Compile and start server using delve. mkdir -p $(BUILD_WEBAPP_DIR)/dist/files diff --git a/api4/apitestlib.go b/api4/apitestlib.go index 9d259b7365..ce765a4ed3 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -33,7 +33,6 @@ import ( "github.com/mattermost/mattermost-server/v6/store/localcachelayer" "github.com/mattermost/mattermost-server/v6/store/storetest/mocks" "github.com/mattermost/mattermost-server/v6/testlib" - "github.com/mattermost/mattermost-server/v6/utils" "github.com/mattermost/mattermost-server/v6/web" "github.com/mattermost/mattermost-server/v6/wsapi" ) @@ -67,6 +66,8 @@ type TestHelper struct { LocalClient *model.Client4 IncludeCacheLayer bool + + TestLogger *mlog.Logger } var mainHelper *testlib.MainHelper @@ -120,6 +121,15 @@ func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, ent options = append(options, app.StoreOverride(dbStore)) } + testLogger, _ := mlog.NewLogger() + logCfg, _ := config.MloggerConfigFromLoggerConfig(&memoryConfig.LogSettings, nil, config.GetLogFileLocation) + if errCfg := testLogger.ConfigureTargets(logCfg); errCfg != nil { + panic("failed to configure test logger: " + errCfg.Error()) + } + // lock logger config so server init cannot override it during testing. + testLogger.LockConfiguration() + options = append(options, app.SetLogger(testLogger)) + s, err := app.NewServer(options...) if err != nil { panic(err) @@ -131,6 +141,7 @@ func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, ent ConfigStore: configStore, IncludeCacheLayer: includeCache, Context: &request.Context{}, + TestLogger: testLogger, } if s.SearchEngine != nil && s.SearchEngine.BleveEngine != nil && searchEngine != nil { @@ -336,15 +347,11 @@ func (th *TestHelper) ShutdownApp() { } func (th *TestHelper) TearDown() { - utils.DisableDebugLogForTest() if th.IncludeCacheLayer { // Clean all the caches th.App.Srv().InvalidateAllCaches() } - th.ShutdownApp() - - utils.EnableDebugLogForTest() } var initBasicOnce sync.Once @@ -501,12 +508,10 @@ func (th *TestHelper) CreateBotWithClient(client *model.Client4) *model.Bot { Description: "bot", } - utils.DisableDebugLogForTest() rbot, _, err := client.CreateBot(bot) if err != nil { panic(err) } - utils.EnableDebugLogForTest() return rbot } @@ -527,12 +532,10 @@ func (th *TestHelper) CreateTeamWithClient(client *model.Client4) *model.Team { Type: model.TeamOpen, } - utils.DisableDebugLogForTest() rteam, _, err := client.CreateTeam(team) if err != nil { panic(err) } - utils.EnableDebugLogForTest() return rteam } @@ -548,7 +551,6 @@ func (th *TestHelper) CreateUserWithClient(client *model.Client4) *model.User { Password: "Pa$$word11", } - utils.DisableDebugLogForTest() ruser, _, err := client.CreateUser(user) if err != nil { panic(err) @@ -559,7 +561,6 @@ func (th *TestHelper) CreateUserWithClient(client *model.Client4) *model.User { if err != nil { return nil } - utils.EnableDebugLogForTest() return ruser } @@ -651,12 +652,10 @@ func (th *TestHelper) CreateChannelWithClientAndTeam(client *model.Client4, chan TeamId: teamId, } - utils.DisableDebugLogForTest() rchannel, _, err := client.CreateChannel(channel) if err != nil { panic(err) } - utils.EnableDebugLogForTest() return rchannel } @@ -680,12 +679,10 @@ func (th *TestHelper) CreatePostWithClient(client *model.Client4, channel *model Message: "message_" + id, } - utils.DisableDebugLogForTest() rpost, _, err := client.CreatePost(post) if err != nil { panic(err) } - utils.EnableDebugLogForTest() return rpost } @@ -698,12 +695,10 @@ func (th *TestHelper) CreatePinnedPostWithClient(client *model.Client4, channel IsPinned: true, } - utils.DisableDebugLogForTest() rpost, _, err := client.CreatePost(post) if err != nil { panic(err) } - utils.EnableDebugLogForTest() return rpost } @@ -713,12 +708,10 @@ func (th *TestHelper) CreateMessagePostWithClient(client *model.Client4, channel Message: message, } - utils.DisableDebugLogForTest() rpost, _, err := client.CreatePost(post) if err != nil { panic(err) } - utils.EnableDebugLogForTest() return rpost } @@ -738,13 +731,11 @@ func (th *TestHelper) CreateMessagePostNoClient(channel *model.Channel, message } func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel { - utils.DisableDebugLogForTest() var err *model.AppError var channel *model.Channel if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil { panic(err) } - utils.EnableDebugLogForTest() return channel } @@ -769,82 +760,59 @@ func (th *TestHelper) LoginSystemManager() { } func (th *TestHelper) LoginBasicWithClient(client *model.Client4) { - utils.DisableDebugLogForTest() _, _, err := client.Login(th.BasicUser.Email, th.BasicUser.Password) if err != nil { panic(err) } - utils.EnableDebugLogForTest() } func (th *TestHelper) LoginBasic2WithClient(client *model.Client4) { - utils.DisableDebugLogForTest() _, _, err := client.Login(th.BasicUser2.Email, th.BasicUser2.Password) if err != nil { panic(err) } - utils.EnableDebugLogForTest() } func (th *TestHelper) LoginTeamAdminWithClient(client *model.Client4) { - utils.DisableDebugLogForTest() _, _, err := client.Login(th.TeamAdminUser.Email, th.TeamAdminUser.Password) if err != nil { panic(err) } - utils.EnableDebugLogForTest() } func (th *TestHelper) LoginSystemManagerWithClient(client *model.Client4) { - utils.DisableDebugLogForTest() _, _, err := client.Login(th.SystemManagerUser.Email, th.SystemManagerUser.Password) if err != nil { panic(err) } - utils.EnableDebugLogForTest() } func (th *TestHelper) LoginSystemAdminWithClient(client *model.Client4) { - utils.DisableDebugLogForTest() _, _, err := client.Login(th.SystemAdminUser.Email, th.SystemAdminUser.Password) if err != nil { panic(err) } - utils.EnableDebugLogForTest() } func (th *TestHelper) UpdateActiveUser(user *model.User, active bool) { - utils.DisableDebugLogForTest() - _, err := th.App.UpdateActive(th.Context, user, active) if err != nil { panic(err) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) { - utils.DisableDebugLogForTest() - _, err := th.App.JoinUserToTeam(th.Context, team, user, "") if err != nil { panic(err) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember { - utils.DisableDebugLogForTest() - member, err := th.App.AddUserToChannel(user, channel, false) if err != nil { panic(err) } - - utils.EnableDebugLogForTest() - return member } @@ -864,12 +832,10 @@ func (th *TestHelper) CreateGroup() *model.Group { RemoteId: "ri_" + id, } - utils.DisableDebugLogForTest() group, err := th.App.CreateGroup(group) if err != nil { panic(err) } - utils.EnableDebugLogForTest() return group } @@ -1107,59 +1073,39 @@ func (th *TestHelper) cleanupTestFile(info *model.FileInfo) error { } func (th *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Channel) { - utils.DisableDebugLogForTest() - if cm, err := th.App.Srv().Store.Channel().GetMember(context.Background(), channel.Id, user.Id); err == nil { cm.SchemeAdmin = true if _, err = th.App.Srv().Store.Channel().UpdateMember(cm); err != nil { - utils.EnableDebugLogForTest() panic(err) } } else { - utils.EnableDebugLogForTest() panic(err) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) { - utils.DisableDebugLogForTest() - if tm, err := th.App.Srv().Store.Team().GetMember(context.Background(), team.Id, user.Id); err == nil { tm.SchemeAdmin = true if _, err = th.App.Srv().Store.Team().UpdateMember(tm); err != nil { - utils.EnableDebugLogForTest() panic(err) } } else { - utils.EnableDebugLogForTest() panic(err) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) { - utils.DisableDebugLogForTest() - if tm, err := th.App.Srv().Store.Team().GetMember(context.Background(), team.Id, user.Id); err == nil { tm.SchemeAdmin = false if _, err = th.App.Srv().Store.Team().UpdateMember(tm); err != nil { - utils.EnableDebugLogForTest() panic(err) } } else { - utils.EnableDebugLogForTest() panic(err) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string { - utils.DisableDebugLogForTest() - results := make(map[string][]string) for _, roleName := range []string{ @@ -1172,24 +1118,18 @@ func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string { } { role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } results[roleName] = role.Permissions } - - utils.EnableDebugLogForTest() return results } func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) { - utils.DisableDebugLogForTest() - for roleName, permissions := range data { role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } @@ -1201,20 +1141,14 @@ func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) { _, err2 := th.App.UpdateRole(role) if err2 != nil { - utils.EnableDebugLogForTest() panic(err2) } } - - utils.EnableDebugLogForTest() } func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) { - utils.DisableDebugLogForTest() - role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } @@ -1226,7 +1160,6 @@ func (th *TestHelper) RemovePermissionFromRole(permission string, roleName strin } if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") { - utils.EnableDebugLogForTest() return } @@ -1234,25 +1167,18 @@ func (th *TestHelper) RemovePermissionFromRole(permission string, roleName strin _, err2 := th.App.UpdateRole(role) if err2 != nil { - utils.EnableDebugLogForTest() panic(err2) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) AddPermissionToRole(permission string, roleName string) { - utils.DisableDebugLogForTest() - role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } for _, existingPermission := range role.Permissions { if existingPermission == permission { - utils.EnableDebugLogForTest() return } } @@ -1261,11 +1187,8 @@ func (th *TestHelper) AddPermissionToRole(permission string, roleName string) { _, err2 := th.App.UpdateRole(role) if err2 != nil { - utils.EnableDebugLogForTest() panic(err2) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) SetupTeamScheme() *model.Scheme { diff --git a/api4/channel_test.go b/api4/channel_test.go index 4be43f519e..fa82aa5aa8 100644 --- a/api4/channel_test.go +++ b/api4/channel_test.go @@ -21,7 +21,6 @@ import ( "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock" "github.com/mattermost/mattermost-server/v6/store/storetest/mocks" - "github.com/mattermost/mattermost-server/v6/utils" ) func TestCreateChannel(t *testing.T) { @@ -3238,7 +3237,6 @@ func TestAutocompleteChannels(t *testing.T) { defer th.TearDown() // A private channel to make sure private channels are not used - utils.DisableDebugLogForTest() ptown, _, _ := th.Client.CreateChannel(&model.Channel{ DisplayName: "Town", Name: "town", @@ -3251,7 +3249,6 @@ func TestAutocompleteChannels(t *testing.T) { Type: model.ChannelTypeOpen, TeamId: th.BasicTeam.Id, }) - utils.EnableDebugLogForTest() defer func() { th.Client.DeleteChannel(ptown.Id) th.Client.DeleteChannel(tower.Id) @@ -3320,7 +3317,6 @@ func TestAutocompleteChannelsForSearch(t *testing.T) { defer th.App.PermanentDeleteUser(th.Context, u4) // A private channel to make sure private channels are not used - utils.DisableDebugLogForTest() ptown, _, _ := th.SystemAdminClient.CreateChannel(&model.Channel{ DisplayName: "Town", Name: "town", @@ -3339,7 +3335,6 @@ func TestAutocompleteChannelsForSearch(t *testing.T) { defer func() { th.Client.DeleteChannel(mypriv.Id) }() - utils.EnableDebugLogForTest() dc1, _, err := th.Client.CreateDirectChannel(th.BasicUser.Id, u1.Id) require.NoError(t, err) @@ -3450,7 +3445,6 @@ func TestAutocompleteChannelsForSearchGuestUsers(t *testing.T) { require.NoError(t, err) // A private channel to make sure private channels are not used - utils.DisableDebugLogForTest() town, _, _ := th.SystemAdminClient.CreateChannel(&model.Channel{ DisplayName: "Town", Name: "town", @@ -3475,8 +3469,6 @@ func TestAutocompleteChannelsForSearchGuestUsers(t *testing.T) { _, _, err = th.SystemAdminClient.AddChannelMember(mypriv.Id, guest.Id) require.NoError(t, err) - utils.EnableDebugLogForTest() - dc1, _, err := th.SystemAdminClient.CreateDirectChannel(th.BasicUser.Id, guest.Id) require.NoError(t, err) defer func() { diff --git a/api4/config_test.go b/api4/config_test.go index 53e9cf3405..3baaef649d 100644 --- a/api4/config_test.go +++ b/api4/config_test.go @@ -4,7 +4,6 @@ package api4 import ( - "context" "fmt" "io/ioutil" "net/http" @@ -468,7 +467,7 @@ func TestUpdateConfigDiffInAuditRecord(t *testing.T) { require.Equal(t, timeoutVal+1, *cfg.ServiceSettings.ReadTimeout) // Forcing a flush before attempting to read log's content. - err = th.Server.Log.Flush(context.Background()) + err = th.Server.Audit.Flush() require.NoError(t, err) require.NoError(t, logFile.Sync()) diff --git a/api4/main_test.go b/api4/main_test.go index 90862a5886..961db24f07 100644 --- a/api4/main_test.go +++ b/api4/main_test.go @@ -7,7 +7,6 @@ import ( "flag" "testing" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -25,8 +24,6 @@ func TestMain(m *testing.M) { WithReadReplica: replicaFlag, } - mlog.DisableZap() - mainHelper = testlib.NewMainHelperWithOptions(&options) defer mainHelper.Close() diff --git a/api4/system_test.go b/api4/system_test.go index 75e8474706..37c861c2e3 100644 --- a/api4/system_test.go +++ b/api4/system_test.go @@ -327,9 +327,12 @@ func TestGetLogs(t *testing.T) { mlog.Info(strconv.Itoa(i)) } + err := th.TestLogger.Flush() + require.NoError(t, err, "failed to flush log") + th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) { - logs, _, err := c.GetLogs(0, 10) - require.NoError(t, err) + logs, _, err2 := c.GetLogs(0, 10) + require.NoError(t, err2) require.Len(t, logs, 10) for i := 10; i < 20; i++ { @@ -347,8 +350,8 @@ func TestGetLogs(t *testing.T) { th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true }) - _, resp, err := th.Client.GetLogs(0, 10) - require.Error(t, err) + _, resp, err2 := th.Client.GetLogs(0, 10) + require.Error(t, err2) CheckForbiddenStatus(t, resp) }) diff --git a/app/admin.go b/app/admin.go index bee56f5dc4..4b3fb3bf34 100644 --- a/app/admin.go +++ b/app/admin.go @@ -4,7 +4,6 @@ package app import ( - "context" "fmt" "io" "io/ioutil" @@ -13,11 +12,11 @@ import ( "runtime/debug" "time" + "github.com/mattermost/mattermost-server/v6/config" "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/shared/i18n" "github.com/mattermost/mattermost-server/v6/shared/mail" "github.com/mattermost/mattermost-server/v6/shared/mlog" - "github.com/mattermost/mattermost-server/v6/utils" ) func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) { @@ -63,11 +62,8 @@ func (s *Server) GetLogsSkipSend(page, perPage int) ([]string, *model.AppError) var lines []string if *s.Config().LogSettings.EnableFile { - timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), mlog.DefaultFlushTimeout) - defer timeoutCancel() - mlog.Flush(timeoutCtx) - - logFile := utils.GetLogFileLocation(*s.Config().LogSettings.FileLocation) + s.Log.Flush() + logFile := config.GetLogFileLocation(*s.Config().LogSettings.FileLocation) file, err := os.Open(logFile) if err != nil { return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError) diff --git a/app/app_iface.go b/app/app_iface.go index 60a0f04a79..a39162fc3e 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -232,7 +232,7 @@ type AppIface interface { // LogAuditRec logs an audit record using default LvlAuditCLI. LogAuditRec(rec *audit.Record, err error) // LogAuditRecWithLevel logs an audit record using specified Level. - LogAuditRecWithLevel(rec *audit.Record, level mlog.LogLevel, err error) + LogAuditRecWithLevel(rec *audit.Record, level mlog.Level, err error) // MakeAuditRecord creates a audit record pre-populated with defaults. MakeAuditRecord(event string, initialStatus string) *audit.Record // MarkChanelAsUnreadFromPost will take a post and set the channel as unread from that one. diff --git a/app/audit.go b/app/audit.go index 845a2ee35e..a1a8837094 100644 --- a/app/audit.go +++ b/app/audit.go @@ -9,8 +9,6 @@ import ( "net/http" "os/user" - "github.com/hashicorp/go-multierror" - "github.com/mattermost/mattermost-server/v6/audit" "github.com/mattermost/mattermost-server/v6/config" "github.com/mattermost/mattermost-server/v6/model" @@ -66,7 +64,7 @@ func (a *App) LogAuditRec(rec *audit.Record, err error) { } // LogAuditRecWithLevel logs an audit record using specified Level. -func (a *App) LogAuditRecWithLevel(rec *audit.Record, level mlog.LogLevel, err error) { +func (a *App) LogAuditRecWithLevel(rec *audit.Record, level mlog.Level, err error) { if rec == nil { return } @@ -106,64 +104,27 @@ func (a *App) MakeAuditRecord(event string, initialStatus string) *audit.Record } func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) error { - var errs error - adt.OnQueueFull = s.onAuditTargetQueueFull adt.OnError = s.onAuditError - // Configure target for rotating file output (E0, E10) - if *s.Config().ExperimentalAuditSettings.FileEnabled { - opts := audit.FileOptions{ - Filename: *s.Config().ExperimentalAuditSettings.FileName, - MaxSize: *s.Config().ExperimentalAuditSettings.FileMaxSizeMB, - MaxAge: *s.Config().ExperimentalAuditSettings.FileMaxAgeDays, - MaxBackups: *s.Config().ExperimentalAuditSettings.FileMaxBackups, - Compress: *s.Config().ExperimentalAuditSettings.FileCompress, - } - - maxQueueSize := *s.Config().ExperimentalAuditSettings.FileMaxQueueSize - if maxQueueSize <= 0 { - maxQueueSize = audit.DefMaxQueueSize - } - - filter := adt.MakeFilter(LevelAPI, LevelContent, LevelPerms, LevelCLI) - formatter := adt.MakeJSONFormatter() - formatter.DisableTimestamp = false - target, err := audit.NewFileTarget(filter, formatter, opts, maxQueueSize) - if err != nil { - errs = multierror.Append(err) - } else { - mlog.Debug("File audit target created successfully", mlog.String("filename", opts.Filename)) - adt.AddTarget(target) - } - } - - // Advanced logging for audit requires license. + var logConfigSrc config.LogConfigSrc dsn := *s.Config().ExperimentalAuditSettings.AdvancedLoggingConfig - if !bAllowAdvancedLogging || dsn == "" { - return errs - } - cfg, err := config.NewLogConfigSrc(dsn, s.configStore) - if err != nil { - errs = multierror.Append(fmt.Errorf("invalid config for audit, %w", err)) - return errs - } - mlog.Debug("Loaded audit configuration", mlog.String("source", dsn)) - - for name, t := range cfg.Get() { - if len(t.Levels) == 0 { - t.Levels = mlog.MLvlAuditAll - } - target, err := mlog.NewLogrTarget(name, t) + if bAllowAdvancedLogging && dsn != "" { + var err error + logConfigSrc, err = config.NewLogConfigSrc(dsn, s.configStore) if err != nil { - errs = multierror.Append(err) - continue - } - if target != nil { - adt.AddTarget(target) + return fmt.Errorf("invalid config source for audit, %w", err) } + mlog.Debug("Loaded audit configuration", mlog.String("source", dsn)) } - return errs + + // ExperimentalAuditSettings provides basic file audit (E0, E10); logConfigSrc provides advanced config (E20). + cfg, err := config.MloggerConfigFromAuditConfig(s.Config().ExperimentalAuditSettings, logConfigSrc) + if err != nil { + return fmt.Errorf("invalid config for audit, %w", err) + } + + return adt.Configure(cfg) } func (s *Server) onAuditTargetQueueFull(qname string, maxQSize int) bool { diff --git a/app/email/helper_test.go b/app/email/helper_test.go index 4405e2e15f..f61401d91b 100644 --- a/app/email/helper_test.go +++ b/app/email/helper_test.go @@ -19,7 +19,6 @@ import ( "github.com/mattermost/mattermost-server/v6/store" "github.com/mattermost/mattermost-server/v6/store/storetest/mocks" "github.com/mattermost/mattermost-server/v6/testlib" - "github.com/mattermost/mattermost-server/v6/utils" ) type TestHelper struct { @@ -172,12 +171,10 @@ func (th *TestHelper) CreateTeam() *model.Team { Type: model.TeamOpen, } - utils.DisableDebugLogForTest() var err error if team, err = th.store.Team().Save(team); err != nil { panic(err) } - utils.EnableDebugLogForTest() return team } @@ -192,13 +189,11 @@ func (th *TestHelper) createChannel(team *model.Team, channelType string) *model CreatorId: th.BasicUser.Id, } - utils.DisableDebugLogForTest() var err error if channel, err = th.store.Channel().Save(channel, *th.configStore.Get().TeamSettings.MaxChannelsPerTeam); err != nil { panic(err) } - utils.EnableDebugLogForTest() return channel } diff --git a/app/email/main_test.go b/app/email/main_test.go index 587877f33d..ef8941c6d1 100644 --- a/app/email/main_test.go +++ b/app/email/main_test.go @@ -7,7 +7,6 @@ import ( "flag" "testing" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -26,8 +25,6 @@ func TestMain(m *testing.M) { WithReadReplica: replicaFlag, } - mlog.DisableZap() - mainHelper = testlib.NewMainHelperWithOptions(&options) defer mainHelper.Close() diff --git a/app/file_bench_test.go b/app/file_bench_test.go index 44c7516851..515f69cc03 100644 --- a/app/file_bench_test.go +++ b/app/file_bench_test.go @@ -16,7 +16,6 @@ import ( "time" "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/shared/mlog" ) var randomJPEG []byte @@ -59,8 +58,7 @@ func BenchmarkUploadFile(b *testing.B) { prepareTestImages(b) th := Setup(b).InitBasic() defer th.TearDown() - // disable logging in the benchmark, as best we can - th.App.Log().SetConsoleLevel(mlog.LevelError) + teamID := model.NewId() channelID := model.NewId() userID := model.NewId() diff --git a/app/helper_test.go b/app/helper_test.go index febb49fda7..c491eda57f 100644 --- a/app/helper_test.go +++ b/app/helper_test.go @@ -4,7 +4,6 @@ package app import ( - "bytes" "context" "io/ioutil" "os" @@ -27,7 +26,6 @@ import ( "github.com/mattermost/mattermost-server/v6/store/sqlstore" "github.com/mattermost/mattermost-server/v6/store/storetest/mocks" "github.com/mattermost/mattermost-server/v6/testlib" - "github.com/mattermost/mattermost-server/v6/utils" ) type TestHelper struct { @@ -41,7 +39,8 @@ type TestHelper struct { BasicPost *model.Post SystemAdminUser *model.User - LogBuffer *bytes.Buffer + LogBuffer *mlog.Buffer + TestLogger *mlog.Logger IncludeCacheLayer bool tempWorkspace string @@ -55,16 +54,16 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo configStore := config.NewTestMemoryStore() - config := configStore.Get() - *config.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins") - *config.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp") - *config.PluginSettings.AutomaticPrepackagedPlugins = false - *config.LogSettings.EnableSentry = false // disable error reporting during tests - *config.AnnouncementSettings.AdminNoticesEnabled = false - *config.AnnouncementSettings.UserNoticesEnabled = false - configStore.Set(config) + memoryConfig := configStore.Get() + *memoryConfig.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins") + *memoryConfig.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp") + *memoryConfig.PluginSettings.AutomaticPrepackagedPlugins = false + *memoryConfig.LogSettings.EnableSentry = false // disable error reporting during tests + *memoryConfig.AnnouncementSettings.AdminNoticesEnabled = false + *memoryConfig.AnnouncementSettings.UserNoticesEnabled = false + configStore.Set(memoryConfig) - buffer := &bytes.Buffer{} + buffer := &mlog.Buffer{} var options []Option options = append(options, ConfigStore(configStore)) @@ -80,7 +79,18 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo } else { options = append(options, StoreOverride(dbStore)) } - options = append(options, SetLogger(mlog.NewTestingLogger(tb, buffer))) + + testLogger, _ := mlog.NewLogger() + logCfg, _ := config.MloggerConfigFromLoggerConfig(&memoryConfig.LogSettings, nil, config.GetLogFileLocation) + if errCfg := testLogger.ConfigureTargets(logCfg); errCfg != nil { + panic("failed to configure test logger: " + errCfg.Error()) + } + if errW := mlog.AddWriterTarget(testLogger, buffer, true, mlog.StdAll...); errW != nil { + panic("failed to add writer target to test logger: " + errW.Error()) + } + // lock logger config so server init cannot override it during testing. + testLogger.LockConfiguration() + options = append(options, SetLogger(testLogger)) s, err := NewServer(options...) if err != nil { @@ -92,6 +102,7 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo Context: &request.Context{}, Server: s, LogBuffer: buffer, + TestLogger: testLogger, IncludeCacheLayer: includeCacheLayer, } @@ -240,12 +251,10 @@ func (th *TestHelper) CreateTeam() *model.Team { Type: model.TeamOpen, } - utils.DisableDebugLogForTest() var err *model.AppError if team, err = th.App.CreateTeam(th.Context, team); err != nil { panic(err) } - utils.EnableDebugLogForTest() return team } @@ -268,7 +277,6 @@ func (th *TestHelper) CreateUserOrGuest(guest bool) *model.User { EmailVerified: true, } - utils.DisableDebugLogForTest() var err *model.AppError if guest { if user, err = th.App.CreateGuest(th.Context, user); err != nil { @@ -279,7 +287,6 @@ func (th *TestHelper) CreateUserOrGuest(guest bool) *model.User { panic(err) } } - utils.EnableDebugLogForTest() return user } @@ -331,7 +338,6 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT option(channel) } - utils.DisableDebugLogForTest() var appErr *model.AppError if channel, appErr = th.App.CreateChannel(th.Context, channel, true); appErr != nil { panic(appErr) @@ -353,29 +359,24 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT panic(err) } } - utils.EnableDebugLogForTest() return channel } func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel { - utils.DisableDebugLogForTest() var err *model.AppError var channel *model.Channel if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil { panic(err) } - utils.EnableDebugLogForTest() return channel } func (th *TestHelper) CreateGroupChannel(user1 *model.User, user2 *model.User) *model.Channel { - utils.DisableDebugLogForTest() var err *model.AppError var channel *model.Channel if channel, err = th.App.CreateGroupChannel([]string{th.BasicUser.Id, user1.Id, user2.Id}, th.BasicUser.Id); err != nil { panic(err) } - utils.EnableDebugLogForTest() return channel } @@ -389,12 +390,10 @@ func (th *TestHelper) CreatePost(channel *model.Channel) *model.Post { CreateAt: model.GetMillis() - 10000, } - utils.DisableDebugLogForTest() var err *model.AppError if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil { panic(err) } - utils.EnableDebugLogForTest() return post } @@ -406,47 +405,32 @@ func (th *TestHelper) CreateMessagePost(channel *model.Channel, message string) CreateAt: model.GetMillis() - 10000, } - utils.DisableDebugLogForTest() var err *model.AppError if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil { panic(err) } - utils.EnableDebugLogForTest() return post } func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) { - utils.DisableDebugLogForTest() - _, err := th.App.JoinUserToTeam(th.Context, team, user, "") if err != nil { panic(err) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) RemoveUserFromTeam(user *model.User, team *model.Team) { - utils.DisableDebugLogForTest() - err := th.App.RemoveUserFromTeam(th.Context, team.Id, user.Id, "") if err != nil { panic(err) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember { - utils.DisableDebugLogForTest() - member, err := th.App.AddUserToChannel(user, channel, false) if err != nil { panic(err) } - - utils.EnableDebugLogForTest() - return member } @@ -456,8 +440,6 @@ func (th *TestHelper) CreateRole(roleName string) *model.Role { } func (th *TestHelper) CreateScheme() (*model.Scheme, []*model.Role) { - utils.DisableDebugLogForTest() - scheme, err := th.App.CreateScheme(&model.Scheme{ DisplayName: "Test Scheme Display Name", Name: model.NewId(), @@ -485,9 +467,6 @@ func (th *TestHelper) CreateScheme() (*model.Scheme, []*model.Role) { } roles = append(roles, role) } - - utils.EnableDebugLogForTest() - return scheme, roles } @@ -501,18 +480,14 @@ func (th *TestHelper) CreateGroup() *model.Group { RemoteId: model.NewId(), } - utils.DisableDebugLogForTest() var err *model.AppError if group, err = th.App.CreateGroup(group); err != nil { panic(err) } - utils.EnableDebugLogForTest() return group } func (th *TestHelper) CreateEmoji() *model.Emoji { - utils.DisableDebugLogForTest() - emoji, err := th.App.Srv().Store.Emoji().Save(&model.Emoji{ CreatorId: th.BasicUser.Id, Name: model.NewRandomString(10), @@ -520,15 +495,10 @@ func (th *TestHelper) CreateEmoji() *model.Emoji { if err != nil { panic(err) } - - utils.EnableDebugLogForTest() - return emoji } func (th *TestHelper) AddReactionToPost(post *model.Post, user *model.User, emojiName string) *model.Reaction { - utils.DisableDebugLogForTest() - reaction, err := th.App.SaveReactionForPost(th.Context, &model.Reaction{ UserId: user.Id, PostId: post.Id, @@ -537,9 +507,6 @@ func (th *TestHelper) AddReactionToPost(post *model.Post, user *model.User, emoj if err != nil { panic(err) } - - utils.EnableDebugLogForTest() - return reaction } @@ -668,11 +635,8 @@ func (th *TestHelper) SetupPluginAPI() *PluginAPI { } func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) { - utils.DisableDebugLogForTest() - role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } @@ -684,7 +648,6 @@ func (th *TestHelper) RemovePermissionFromRole(permission string, roleName strin } if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") { - utils.EnableDebugLogForTest() return } @@ -692,25 +655,18 @@ func (th *TestHelper) RemovePermissionFromRole(permission string, roleName strin _, err2 := th.App.UpdateRole(role) if err2 != nil { - utils.EnableDebugLogForTest() panic(err2) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) AddPermissionToRole(permission string, roleName string) { - utils.DisableDebugLogForTest() - role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } for _, existingPermission := range role.Permissions { if existingPermission == permission { - utils.EnableDebugLogForTest() return } } @@ -719,11 +675,8 @@ func (th *TestHelper) AddPermissionToRole(permission string, roleName string) { _, err2 := th.App.UpdateRole(role) if err2 != nil { - utils.EnableDebugLogForTest() panic(err2) } - - utils.EnableDebugLogForTest() } // This function is copy of storetest/NewTestId diff --git a/app/main_test.go b/app/main_test.go index 486e7d6b30..10af131a62 100644 --- a/app/main_test.go +++ b/app/main_test.go @@ -7,7 +7,6 @@ import ( "flag" "testing" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -26,8 +25,6 @@ func TestMain(m *testing.M) { WithReadReplica: replicaFlag, } - mlog.DisableZap() - mainHelper = testlib.NewMainHelperWithOptions(&options) defer mainHelper.Close() diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index e2377b2625..3947de1ec7 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -11251,7 +11251,7 @@ func (a *OpenTracingAppLayer) LogAuditRec(rec *audit.Record, err error) { a.app.LogAuditRec(rec, err) } -func (a *OpenTracingAppLayer) LogAuditRecWithLevel(rec *audit.Record, level mlog.LogLevel, err error) { +func (a *OpenTracingAppLayer) LogAuditRecWithLevel(rec *audit.Record, level mlog.Level, err error) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.LogAuditRecWithLevel") diff --git a/app/plugin_api.go b/app/plugin_api.go index c1594060ec..90d648d709 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -25,7 +25,7 @@ type PluginAPI struct { id string app *App ctx *request.Context - logger *mlog.SugarLogger + logger mlog.Sugar manifest *model.Manifest } @@ -35,7 +35,7 @@ func NewPluginAPI(a *App, c *request.Context, manifest *model.Manifest) *PluginA manifest: manifest, ctx: c, app: a, - logger: a.Log().With(mlog.String("plugin_id", manifest.Id)).Sugar(), + logger: a.Log().Sugar(mlog.String("plugin_id", manifest.Id)), } } diff --git a/app/plugin_test.go b/app/plugin_test.go index 4bea22c030..c85f4f61b2 100644 --- a/app/plugin_test.go +++ b/app/plugin_test.go @@ -724,11 +724,14 @@ func TestPluginPanicLogs(t *testing.T) { } _, err := th.App.CreatePost(th.Context, post, th.BasicChannel, false, true) assert.Nil(t, err) + + th.TestLogger.Flush() + // We shutdown plugins first so that the read on the log buffer is race-free. th.App.Srv().ShutDownPlugins() tearDown() - testlib.AssertLog(t, th.LogBuffer, mlog.LevelDebug, "panic: some text from panic") + testlib.AssertLog(t, th.LogBuffer, mlog.LvlDebug.Name, "panic: some text from panic") }) } diff --git a/app/post_test.go b/app/post_test.go index d60bfb89c7..0654f30667 100644 --- a/app/post_test.go +++ b/app/post_test.go @@ -970,7 +970,7 @@ func TestCreatePostAsUser(t *testing.T) { _, appErr := th.App.CreatePostAsUser(th.Context, post, "", true) require.Nil(t, appErr) - testlib.AssertLog(t, th.LogBuffer, mlog.LevelWarn, "Failed to get membership") + testlib.AssertLog(t, th.LogBuffer, mlog.LvlWarn.Name, "Failed to get membership") }) t.Run("does not log warning for bot user not in channel", func(t *testing.T) { @@ -993,7 +993,7 @@ func TestCreatePostAsUser(t *testing.T) { _, appErr = th.App.CreatePostAsUser(th.Context, post, "", true) require.Nil(t, appErr) - testlib.AssertNoLog(t, th.LogBuffer, mlog.LevelWarn, "Failed to get membership") + testlib.AssertNoLog(t, th.LogBuffer, mlog.LvlWarn.Name, "Failed to get membership") }) t.Run("marks channel as viewed for reply post when CRT is off", func(t *testing.T) { diff --git a/app/server.go b/app/server.go index a0f4e86b5c..edb3286726 100644 --- a/app/server.go +++ b/app/server.go @@ -138,7 +138,6 @@ type Server struct { statusCache cache.Cache configListenerId string licenseListenerId string - logListenerId string clusterLeaderListenerId string searchConfigListenerId string searchLicenseListenerId string @@ -146,8 +145,6 @@ type Server struct { configStore *config.Store postActionCookieSecret []byte - advancedLogListenerCleanup func() - pluginCommands []*PluginCommand pluginCommandsLock sync.RWMutex @@ -787,74 +784,79 @@ func (s *Server) DatabaseTypeAndMattermostVersion() (string, string) { return *s.Config().SqlSettings.DriverName, mattermostVersion.Value } -// initLogging initializes and configures the logger. This may be called more than once. +// initLogging initializes and configures the logger(s). This may be called more than once. func (s *Server) initLogging() error { + var err error + // create the app logger if needed if s.Log == nil { - s.Log = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(&s.Config().LogSettings, utils.GetLogFileLocation)) - } - - // Use this app logger as the global logger (eventually remove all instances of global logging). - // This is deferred because a copy is made of the logger and it must be fully configured before - // the copy is made. - defer mlog.InitGlobalLogger(s.Log) - - // Redirect default Go logger to this logger. - defer mlog.RedirectStdLog(s.Log) - - if s.NotificationsLog == nil { - notificationLogSettings := utils.GetLogSettingsFromNotificationsLogSettings(&s.Config().NotificationLogSettings) - s.NotificationsLog = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(notificationLogSettings, utils.GetNotificationsLogFileLocation)). - WithCallerSkip(1).With(mlog.String("logSource", "notifications")) - } - - if s.logListenerId != "" { - s.RemoveConfigListener(s.logListenerId) - } - s.logListenerId = s.AddConfigListener(func(_, after *model.Config) { - s.Log.ChangeLevels(utils.MloggerConfigFromLoggerConfig(&after.LogSettings, utils.GetLogFileLocation)) - - notificationLogSettings := utils.GetLogSettingsFromNotificationsLogSettings(&after.NotificationLogSettings) - s.NotificationsLog.ChangeLevels(utils.MloggerConfigFromLoggerConfig(notificationLogSettings, utils.GetNotificationsLogFileLocation)) - }) - - // Configure advanced logging. - // Advanced logging is E20 only, however logging must be initialized before the license - // file is loaded. If no valid E20 license exists then advanced logging will be - // shutdown once license is loaded/checked. - if *s.Config().LogSettings.AdvancedLoggingConfig != "" { - dsn := *s.Config().LogSettings.AdvancedLoggingConfig - - cfg, err := config.NewLogConfigSrc(dsn, s.configStore) + s.Log, err = mlog.NewLogger() if err != nil { - return fmt.Errorf("invalid advanced logging config, %w", err) + return err } + } - if err := s.Log.ConfigAdvancedLogging(cfg.Get()); err != nil { - return fmt.Errorf("error configuring advanced logging, %w", err) + // create notification logger if needed + if s.NotificationsLog == nil { + l, err := mlog.NewLogger() + if err != nil { + return err } + s.NotificationsLog = l.With(mlog.String("logSource", "notifications")) + } - mlog.Info("Loaded advanced logging config", mlog.String("source", dsn)) - - listenerId := cfg.AddListener(func(_, newCfg mlog.LogTargetCfg) { - if err := s.Log.ConfigAdvancedLogging(newCfg); err != nil { - mlog.Error("Error re-configuring advanced logging", mlog.Err(err)) - } else { - mlog.Info("Re-configured advanced logging") - } - }) - - // In case initLogging is called more than once. - if s.advancedLogListenerCleanup != nil { - s.advancedLogListenerCleanup() + if err := s.configureLogger("logging", s.Log, &s.Config().LogSettings, s.configStore, config.GetLogFileLocation); err != nil { + // if the config is locked then a unit test has already configured and locked the logger; not an error. + if !errors.Is(err, mlog.ErrConfigurationLock) { + // revert to default logger if the config is invalid + mlog.InitGlobalLogger(nil) + return err } + } - s.advancedLogListenerCleanup = func() { - cfg.RemoveListener(listenerId) + // Redirect default Go logger to app logger. + s.Log.RedirectStdLog(mlog.LvlStdLog) + + // Use the app logger as the global logger (eventually remove all instances of global logging). + mlog.InitGlobalLogger(s.Log) + + notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.Config().NotificationLogSettings) + if err := s.configureLogger("notification logging", s.NotificationsLog, notificationLogSettings, s.configStore, config.GetNotificationsLogFileLocation); err != nil { + if !errors.Is(err, mlog.ErrConfigurationLock) { + mlog.Error("Error configuring notification logger", mlog.Err(err)) + return err } } return nil } +// configureLogger applies the specified configuration to a logger. +func (s *Server) configureLogger(name string, logger *mlog.Logger, logSettings *model.LogSettings, configStore *config.Store, getPath func(string) string) error { + // Advanced logging is E20 only, however logging must be initialized before the license + // file is loaded. If no valid E20 license exists then advanced logging will be + // shutdown once license is loaded/checked. + var err error + dsn := *logSettings.AdvancedLoggingConfig + var logConfigSrc config.LogConfigSrc + if dsn != "" { + logConfigSrc, err = config.NewLogConfigSrc(dsn, configStore) + if err != nil { + return fmt.Errorf("invalid config source for %s, %w", name, err) + } + mlog.Info("Loaded configuration for "+name, mlog.String("source", dsn)) + } + + cfg, err := config.MloggerConfigFromLoggerConfig(logSettings, logConfigSrc, getPath) + if err != nil { + return fmt.Errorf("invalid config source for %s, %w", name, err) + } + + if err := logger.ConfigureTargets(cfg); err != nil { + return fmt.Errorf("invalid config for %s, %w", name, err) + } + return nil +} + +// removeUnlicensedLogTargets removes any unlicensed log target types. func (s *Server) removeUnlicensedLogTargets(license *model.License) { if license != nil && *license.Features.AdvancedLogging { // advanced logging enabled via license; no need to remove any targets @@ -864,8 +866,12 @@ func (s *Server) removeUnlicensedLogTargets(license *model.License) { timeoutCtx, cancelCtx := context.WithTimeout(context.Background(), time.Second*10) defer cancelCtx() - mlog.RemoveTargets(timeoutCtx, func(ti mlog.TargetInfo) bool { - return ti.Type != "*target.Writer" && ti.Type != "*target.File" + s.Log.RemoveTargets(timeoutCtx, func(ti mlog.TargetInfo) bool { + return ti.Type != "*targets.Writer" && ti.Type != "*targets.File" + }) + + s.NotificationsLog.RemoveTargets(timeoutCtx, func(ti mlog.TargetInfo) bool { + return ti.Type != "*targets.Writer" && ti.Type != "*targets.File" }) } @@ -939,11 +945,15 @@ func (s *Server) enableLoggingMetrics() { return } - if err := mlog.EnableMetrics(s.Metrics.GetLoggerMetricsCollector()); err != nil { - mlog.Error("Failed to enable advanced logging metrics", mlog.Err(err)) - } else { - mlog.Debug("Advanced logging metrics enabled") + s.Log.SetMetricsCollector(s.Metrics.GetLoggerMetricsCollector(), mlog.DefaultMetricsUpdateFreqMillis) + + // logging config needs to be reloaded when metrics collector is added or changed. + if err := s.initLogging(); err != nil { + mlog.Error("Error re-configuring logging for metrics") + return } + + mlog.Debug("Logging metrics enabled") } const TimeToWaitForConnectionsToCloseOnServerShutdown = time.Second @@ -1014,13 +1024,7 @@ func (s *Server) Shutdown() { s.WaitForGoroutines() - if s.advancedLogListenerCleanup != nil { - s.advancedLogListenerCleanup() - s.advancedLogListenerCleanup = nil - } - s.RemoveConfigListener(s.configListenerId) - s.RemoveConfigListener(s.logListenerId) s.stopSearchEngine() s.Audit.Shutdown() @@ -1058,12 +1062,6 @@ func (s *Server) Shutdown() { } } - timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), time.Second*15) - defer timeoutCancel() - if err := mlog.Flush(timeoutCtx); err != nil { - mlog.Warn("Error flushing logs", mlog.Err(err)) - } - s.dndTaskMut.Lock() if s.dndTask != nil { s.dndTask.Cancel() @@ -1072,10 +1070,15 @@ func (s *Server) Shutdown() { mlog.Info("Server stopped") - // this should just write the "server stopped" record, the rest are already flushed. - timeoutCtx2, timeoutCancel2 := context.WithTimeout(context.Background(), time.Second*5) - defer timeoutCancel2() - _ = mlog.ShutdownAdvancedLogging(timeoutCtx2) + // shutdown main and notification loggers which will flush any remaining log records. + timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), time.Second*15) + defer timeoutCancel() + if err = s.NotificationsLog.ShutdownWithTimeout(timeoutCtx); err != nil { + fmt.Fprintf(os.Stderr, "Error shutting down notification logger: %v", err) + } + if err = s.Log.ShutdownWithTimeout(timeoutCtx); err != nil { + fmt.Fprintf(os.Stderr, "Error shutting down main logger: %v", err) + } } func (s *Server) Restart() error { @@ -1202,7 +1205,7 @@ func (s *Server) Start() error { // If we have debugging of CORS turned on then forward messages to logs if debug { - corsWrapper.Log = s.Log.StdLog(mlog.String("source", "cors")) + corsWrapper.Log = s.Log.With(mlog.String("source", "cors")).StdLogger(mlog.LvlDebug) } handler = corsWrapper.Handler(handler) @@ -1222,10 +1225,7 @@ func (s *Server) Start() error { s.Busy = NewBusy(s.Cluster) // Creating a logger for logging errors from http.Server at error level - errStdLog, err := s.Log.StdLogAt(mlog.LevelError, mlog.String("source", "httpserver")) - if err != nil { - return err - } + errStdLog := s.Log.With(mlog.String("source", "httpserver")).StdLogger(mlog.LvlError) s.Server = &http.Server{ Handler: handler, @@ -1270,7 +1270,7 @@ func (s *Server) Start() error { server := &http.Server{ Addr: httpListenAddress, Handler: m.HTTPHandler(nil), - ErrorLog: s.Log.StdLog(mlog.String("source", "le_forwarder_server")), + ErrorLog: s.Log.With(mlog.String("source", "le_forwarder_server")).StdLogger(mlog.LvlError), } go server.ListenAndServe() } else { @@ -1284,7 +1284,7 @@ func (s *Server) Start() error { server := &http.Server{ Handler: http.HandlerFunc(handleHTTPRedirect), - ErrorLog: s.Log.StdLog(mlog.String("source", "forwarder_server")), + ErrorLog: s.Log.With(mlog.String("source", "forwarder_server")).StdLogger(mlog.LvlError), } server.Serve(redirectListener) }() @@ -2026,7 +2026,7 @@ func (a *App) getNotificationsLog() (*model.FileData, string) { // Getting notifications.log if *a.Srv().Config().NotificationLogSettings.EnableFile { // notifications.log - notificationsLog := utils.GetNotificationsLogFileLocation(*a.Srv().Config().LogSettings.FileLocation) + notificationsLog := config.GetNotificationsLogFileLocation(*a.Srv().Config().LogSettings.FileLocation) notificationsLogFileData, notificationsLogFileDataErr := ioutil.ReadFile(notificationsLog) @@ -2053,7 +2053,7 @@ func (a *App) getMattermostLog() (*model.FileData, string) { // Getting mattermost.log if *a.Srv().Config().LogSettings.EnableFile { // mattermost.log - mattermostLog := utils.GetLogFileLocation(*a.Srv().Config().LogSettings.FileLocation) + mattermostLog := config.GetLogFileLocation(*a.Srv().Config().LogSettings.FileLocation) mattermostLogFileData, mattermostLogFileDataErr := ioutil.ReadFile(mattermostLog) diff --git a/app/server_test.go b/app/server_test.go index 0c61209f32..4016847bdd 100644 --- a/app/server_test.go +++ b/app/server_test.go @@ -516,29 +516,29 @@ func checkEndpoint(t *testing.T, client *http.Client, url string) error { } func TestPanicLog(t *testing.T) { - // Creating a temp file to collect logs - tmpfile, err := ioutil.TempFile("", "mlog") - if err != nil { - require.NoError(t, err) - } - + // Creating a temp dir for log + tmpDir, err := os.MkdirTemp("", "mlog-test") + require.NoError(t, err, "cannot create tmp dir for log file") defer func() { - require.NoError(t, tmpfile.Close()) - require.NoError(t, os.Remove(tmpfile.Name())) + err2 := os.RemoveAll(tmpDir) + assert.NoError(t, err2) }() - // This test requires Zap file target for now. - mlog.EnableZap() - defer mlog.DisableZap() - // Creating logger to log to console and temp file - logger := mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - EnableFile: true, - FileLocation: tmpfile.Name(), - FileLevel: mlog.LevelInfo, - }) + logger, _ := mlog.NewLogger() + + logSettings := model.NewLogSettings() + logSettings.EnableConsole = model.NewBool(true) + logSettings.ConsoleJson = model.NewBool(true) + logSettings.EnableFile = model.NewBool(true) + logSettings.FileLocation = &tmpDir + logSettings.FileLevel = &mlog.LvlInfo.Name + + cfg, err := config.MloggerConfigFromLoggerConfig(logSettings, nil, config.GetLogFileLocation) + require.NoError(t, err) + err = logger.ConfigureTargets(cfg) + require.NoError(t, err) + logger.LockConfiguration() // Creating a server with logger s, err := NewServer(SetLogger(logger)) @@ -567,16 +567,22 @@ func TestPanicLog(t *testing.T) { client := &http.Client{Transport: tr} client.Get("https://localhost:" + strconv.Itoa(s.ListenAddr.Port) + "/panic") + + err = logger.Flush() + assert.NoError(t, err, "flush should succeed") s.Shutdown() // Checking whether panic was logged var panicLogged = false var infoLogged = false - _, err = tmpfile.Seek(0, 0) + logFile, err := os.Open(config.GetLogFileLocation(tmpDir)) + require.NoError(t, err, "cannot open log file") + + _, err = logFile.Seek(0, 0) require.NoError(t, err) - scanner := bufio.NewScanner(tmpfile) + scanner := bufio.NewScanner(logFile) for scanner.Scan() { if !infoLogged && strings.Contains(scanner.Text(), "inside panic handler") { infoLogged = true diff --git a/app/slashcommands/helper_test.go b/app/slashcommands/helper_test.go index 57faf1c805..8c3ff4e257 100644 --- a/app/slashcommands/helper_test.go +++ b/app/slashcommands/helper_test.go @@ -21,7 +21,6 @@ import ( "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/store" "github.com/mattermost/mattermost-server/v6/store/localcachelayer" - "github.com/mattermost/mattermost-server/v6/utils" ) type TestHelper struct { @@ -36,6 +35,7 @@ type TestHelper struct { SystemAdminUser *model.User LogBuffer *bytes.Buffer + TestLogger *mlog.Logger IncludeCacheLayer bool tempWorkspace string @@ -49,15 +49,15 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo memoryStore := config.NewTestMemoryStore() - config := memoryStore.Get() + memoryConfig := memoryStore.Get() if configSet != nil { - configSet(config) + configSet(memoryConfig) } - *config.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins") - *config.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp") - *config.PluginSettings.AutomaticPrepackagedPlugins = false - *config.LogSettings.EnableSentry = false // disable error reporting during tests - memoryStore.Set(config) + *memoryConfig.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins") + *memoryConfig.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp") + *memoryConfig.PluginSettings.AutomaticPrepackagedPlugins = false + *memoryConfig.LogSettings.EnableSentry = false // disable error reporting during tests + memoryStore.Set(memoryConfig) buffer := &bytes.Buffer{} @@ -74,7 +74,15 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo } else { options = append(options, app.StoreOverride(dbStore)) } - options = append(options, app.SetLogger(mlog.NewTestingLogger(tb, buffer))) + + testLogger, _ := mlog.NewLogger() + logCfg, _ := config.MloggerConfigFromLoggerConfig(&memoryConfig.LogSettings, nil, config.GetLogFileLocation) + if errCfg := testLogger.ConfigureTargets(logCfg); errCfg != nil { + panic("failed to configure test logger: " + errCfg.Error()) + } + // lock logger config so server init cannot override it during testing. + testLogger.LockConfiguration() + options = append(options, app.SetLogger(testLogger)) s, err := app.NewServer(options...) if err != nil { @@ -86,6 +94,7 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo Context: &request.Context{}, Server: s, LogBuffer: buffer, + TestLogger: testLogger, IncludeCacheLayer: includeCacheLayer, } @@ -188,12 +197,11 @@ func (th *TestHelper) createTeam() *model.Team { Type: model.TeamOpen, } - utils.DisableDebugLogForTest() var err *model.AppError if team, err = th.App.CreateTeam(th.Context, team); err != nil { panic(err) } - utils.EnableDebugLogForTest() + return team } @@ -216,7 +224,6 @@ func (th *TestHelper) createUserOrGuest(guest bool) *model.User { EmailVerified: true, } - utils.DisableDebugLogForTest() var err *model.AppError if guest { if user, err = th.App.CreateGuest(th.Context, user); err != nil { @@ -227,7 +234,6 @@ func (th *TestHelper) createUserOrGuest(guest bool) *model.User { panic(err) } } - utils.EnableDebugLogForTest() return user } @@ -262,7 +268,6 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT option(channel) } - utils.DisableDebugLogForTest() var err *model.AppError if channel, err = th.App.CreateChannel(th.Context, channel, true); err != nil { panic(err) @@ -284,7 +289,6 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT panic(err) } } - utils.EnableDebugLogForTest() return channel } @@ -299,34 +303,28 @@ func (th *TestHelper) createChannelWithAnotherUser(team *model.Team, channelType CreatorId: userID, } - utils.DisableDebugLogForTest() var err *model.AppError if channel, err = th.App.CreateChannel(th.Context, channel, true); err != nil { panic(err) } - utils.EnableDebugLogForTest() return channel } func (th *TestHelper) createDmChannel(user *model.User) *model.Channel { - utils.DisableDebugLogForTest() var err *model.AppError var channel *model.Channel if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil { panic(err) } - utils.EnableDebugLogForTest() return channel } func (th *TestHelper) createGroupChannel(user1 *model.User, user2 *model.User) *model.Channel { - utils.DisableDebugLogForTest() var err *model.AppError var channel *model.Channel if channel, err = th.App.CreateGroupChannel([]string{th.BasicUser.Id, user1.Id, user2.Id}, th.BasicUser.Id); err != nil { panic(err) } - utils.EnableDebugLogForTest() return channel } @@ -340,36 +338,25 @@ func (th *TestHelper) createPost(channel *model.Channel) *model.Post { CreateAt: model.GetMillis() - 10000, } - utils.DisableDebugLogForTest() var err *model.AppError if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil { panic(err) } - utils.EnableDebugLogForTest() return post } func (th *TestHelper) linkUserToTeam(user *model.User, team *model.Team) { - utils.DisableDebugLogForTest() - _, err := th.App.JoinUserToTeam(th.Context, team, user, "") if err != nil { panic(err) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) addUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember { - utils.DisableDebugLogForTest() - member, err := th.App.AddUserToChannel(user, channel, false) if err != nil { panic(err) } - - utils.EnableDebugLogForTest() - return member } @@ -401,11 +388,8 @@ func (th *TestHelper) tearDown() { } func (th *TestHelper) removePermissionFromRole(permission string, roleName string) { - utils.DisableDebugLogForTest() - role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } @@ -417,7 +401,6 @@ func (th *TestHelper) removePermissionFromRole(permission string, roleName strin } if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") { - utils.EnableDebugLogForTest() return } @@ -425,25 +408,18 @@ func (th *TestHelper) removePermissionFromRole(permission string, roleName strin _, err2 := th.App.UpdateRole(role) if err2 != nil { - utils.EnableDebugLogForTest() panic(err2) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) addPermissionToRole(permission string, roleName string) { - utils.DisableDebugLogForTest() - role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } for _, existingPermission := range role.Permissions { if existingPermission == permission { - utils.EnableDebugLogForTest() return } } @@ -452,9 +428,6 @@ func (th *TestHelper) addPermissionToRole(permission string, roleName string) { _, err2 := th.App.UpdateRole(role) if err2 != nil { - utils.EnableDebugLogForTest() panic(err2) } - - utils.EnableDebugLogForTest() } diff --git a/app/slashcommands/main_test.go b/app/slashcommands/main_test.go index 97029616bf..dd2d0d52f0 100644 --- a/app/slashcommands/main_test.go +++ b/app/slashcommands/main_test.go @@ -6,7 +6,6 @@ package slashcommands import ( "testing" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -18,8 +17,6 @@ func TestMain(m *testing.M) { EnableResources: true, } - mlog.DisableZap() - mainHelper = testlib.NewMainHelperWithOptions(&options) defer mainHelper.Close() diff --git a/audit/audit.go b/audit/audit.go index c63935f6f3..d0974fb4bf 100644 --- a/audit/audit.go +++ b/audit/audit.go @@ -5,17 +5,12 @@ package audit import ( "fmt" - "sort" - - "github.com/mattermost/logr" - "github.com/mattermost/logr/format" "github.com/mattermost/mattermost-server/v6/shared/mlog" ) type Audit struct { - lgr *logr.Logr - logger logr.Logger + logger *mlog.Logger // OnQueueFull is called on an attempt to add an audit record to a full queue. // Return true to drop record, or false to block until there is room in queue. @@ -26,56 +21,34 @@ type Audit struct { } func (a *Audit) Init(maxQueueSize int) { - a.lgr = &logr.Logr{MaxQueueSize: maxQueueSize} - a.logger = a.lgr.NewLogger() - - a.lgr.OnQueueFull = a.onQueueFull - a.lgr.OnTargetQueueFull = a.onTargetQueueFull - a.lgr.OnLoggerError = a.onLoggerError -} - -// MakeFilter creates a filter which only allows the specified audit levels to be output. -func (a *Audit) MakeFilter(level ...mlog.LogLevel) *logr.CustomFilter { - filter := &logr.CustomFilter{} - for _, l := range level { - filter.Add(logr.Level(l)) - } - return filter -} - -// MakeJSONFormatter creates a formatter that outputs JSON suitable for audit records. -func (a *Audit) MakeJSONFormatter() *format.JSON { - f := &format.JSON{ - DisableTimestamp: true, - DisableMsg: true, - DisableStacktrace: true, - DisableLevel: true, - ContextSorter: sortAuditFields, - } - return f + a.logger, _ = mlog.NewLogger( + mlog.MaxQueueSize(maxQueueSize), + mlog.OnLoggerError(a.onLoggerError), + mlog.OnQueueFull(a.onQueueFull), + mlog.OnTargetQueueFull(a.onTargetQueueFull), + ) } // LogRecord emits an audit record with complete info. -func (a *Audit) LogRecord(level mlog.LogLevel, rec Record) { - flds := logr.Fields{} - flds[KeyAPIPath] = rec.APIPath - flds[KeyEvent] = rec.Event - flds[KeyStatus] = rec.Status - flds[KeyUserID] = rec.UserID - flds[KeySessionID] = rec.SessionID - flds[KeyClient] = rec.Client - flds[KeyIPAddress] = rec.IPAddress - - for k, v := range rec.Meta { - flds[k] = v +func (a *Audit) LogRecord(level mlog.Level, rec Record) { + flds := []mlog.Field{ + mlog.String(KeyAPIPath, rec.APIPath), + mlog.String(KeyEvent, rec.Event), + mlog.String(KeyStatus, rec.Status), + mlog.String(KeyUserID, rec.UserID), + mlog.String(KeySessionID, rec.SessionID), + mlog.String(KeyClient, rec.Client), + mlog.String(KeyIPAddress, rec.IPAddress), } - l := a.logger.WithFields(flds) - l.Log(logr.Level(level)) + for k, v := range rec.Meta { + flds = append(flds, mlog.Any(k, v)) + } + a.logger.Log(level, "", flds...) } // Log emits an audit record based on minimum required info. -func (a *Audit) Log(level mlog.LogLevel, path string, evt string, status string, userID string, sessionID string, meta Meta) { +func (a *Audit) Log(level mlog.Level, path string, evt string, status string, userID string, sessionID string, meta Meta) { a.LogRecord(level, Record{ APIPath: path, Event: evt, @@ -86,20 +59,30 @@ func (a *Audit) Log(level mlog.LogLevel, path string, evt string, status string, }) } -// AddTarget adds a Logr target to the list of targets each audit record will be output to. -func (a *Audit) AddTarget(target logr.Target) { - a.lgr.AddTarget(target) +// Configure sets zero or more target to output audit logs to. +func (a *Audit) Configure(cfg mlog.LoggerConfiguration) error { + return a.logger.ConfigureTargets(cfg) } -// Shutdown cleanly stops the audit engine after making best efforts to flush all targets. -func (a *Audit) Shutdown() { - err := a.lgr.Shutdown() +// Flush attempts to write all queued audit records to all targets. +func (a *Audit) Flush() error { + err := a.logger.Flush() if err != nil { a.onLoggerError(err) } + return err } -func (a *Audit) onQueueFull(rec *logr.LogRec, maxQueueSize int) bool { +// Shutdown cleanly stops the audit engine after making best efforts to flush all targets. +func (a *Audit) Shutdown() error { + err := a.logger.Shutdown() + if err != nil { + a.onLoggerError(err) + } + return err +} + +func (a *Audit) onQueueFull(rec *mlog.LogRec, maxQueueSize int) bool { if a.OnQueueFull != nil { return a.OnQueueFull("main", maxQueueSize) } @@ -107,7 +90,7 @@ func (a *Audit) onQueueFull(rec *logr.LogRec, maxQueueSize int) bool { return true } -func (a *Audit) onTargetQueueFull(target logr.Target, rec *logr.LogRec, maxQueueSize int) bool { +func (a *Audit) onTargetQueueFull(target mlog.Target, rec *mlog.LogRec, maxQueueSize int) bool { if a.OnQueueFull != nil { return a.OnQueueFull(fmt.Sprintf("%v", target), maxQueueSize) } @@ -118,58 +101,7 @@ func (a *Audit) onTargetQueueFull(target logr.Target, rec *logr.LogRec, maxQueue func (a *Audit) onLoggerError(err error) { if a.OnError != nil { a.OnError(err) + return } -} - -// sortAuditFields sorts the context fields of an audit record such that some fields -// are prepended in order, some are appended in order, and the rest are sorted alphabetically. -// This is done to make reading the records easier since common fields will appear in the same order. -func sortAuditFields(fields logr.Fields) []format.ContextField { - prependKeys := []string{KeyEvent, KeyStatus, KeyUserID, KeySessionID, KeyIPAddress} - appendKeys := []string{KeyClusterID, KeyClient} - - // sort alphabetically any fields not in the prepend/append lists. - keys := make([]string, 0, len(fields)) - for k := range fields { - if !findIn(k, prependKeys, appendKeys) { - keys = append(keys, k) - } - } - sort.Strings(keys) - - allKeys := make([]string, 0, len(fields)) - - // add any prepends that exist in fields - for _, k := range prependKeys { - if _, ok := fields[k]; ok { - allKeys = append(allKeys, k) - } - } - - // sorted - allKeys = append(allKeys, keys...) - - // add any appends that exist in fields - for _, k := range appendKeys { - if _, ok := fields[k]; ok { - allKeys = append(allKeys, k) - } - } - - cfs := make([]format.ContextField, 0, len(allKeys)) - for _, k := range allKeys { - cfs = append(cfs, format.ContextField{Key: k, Val: fields[k]}) - } - return cfs -} - -func findIn(s string, arrs ...[]string) bool { - for _, list := range arrs { - for _, key := range list { - if s == key { - return true - } - } - } - return false + mlog.Error("Auditing error", mlog.Err(err)) } diff --git a/audit/audit_test.go b/audit/audit_test.go deleted file mode 100644 index 7900c78646..0000000000 --- a/audit/audit_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package audit - -import ( - "testing" - - "github.com/mattermost/logr" - "github.com/mattermost/logr/format" - "github.com/stretchr/testify/require" -) - -func Test_sortAuditFields(t *testing.T) { - type args struct { - fields logr.Fields - } - tests := []struct { - name string - args args - want []format.ContextField - }{ - {name: "empty list", - args: args{fields: logr.Fields{}}, - want: []format.ContextField{}, - }, - {name: "partial list", - args: args{fields: logr.Fields{"zProp": "x", "xProp": "x", "yProp": "x", KeyClusterID: "x", KeyEvent: "x"}}, - want: []format.ContextField{ - {Key: KeyEvent, Val: "x"}, - {Key: "xProp", Val: "x"}, - {Key: "yProp", Val: "x"}, - {Key: "zProp", Val: "x"}, - {Key: KeyClusterID, Val: "x"}, - }, - }, - {name: "append/prepend only list", - args: args{fields: logr.Fields{KeyClusterID: "x", KeyEvent: "x", KeySessionID: "x", KeyIPAddress: "x", KeyClient: "x", - KeyUserID: "x", KeyStatus: "x"}}, - want: []format.ContextField{ - // prepend: KeyEvent, KeyStatus, KeyUserID, KeySessionID, KeyIPAddress - // append: KeyClusterID, KeyClient - {Key: KeyEvent, Val: "x"}, - {Key: KeyStatus, Val: "x"}, - {Key: KeyUserID, Val: "x"}, - {Key: KeySessionID, Val: "x"}, - {Key: KeyIPAddress, Val: "x"}, - {Key: KeyClusterID, Val: "x"}, - {Key: KeyClient, Val: "x"}, - }, - }, - {name: "sortables only list", - args: args{fields: logr.Fields{"zProp": "x", "xProp": "x", "yProp": "x"}}, - want: []format.ContextField{ - {Key: "xProp", Val: "x"}, - {Key: "yProp", Val: "x"}, - {Key: "zProp", Val: "x"}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := sortAuditFields(tt.args.fields) - require.Equal(t, tt.want, got) - }) - } -} diff --git a/audit/file.go b/audit/file.go deleted file mode 100644 index 2d47b4c692..0000000000 --- a/audit/file.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package audit - -import ( - "os" - - "github.com/mattermost/logr" - "github.com/mattermost/logr/target" -) - -type FileOptions target.FileOptions - -// NewFileTarget creates a target capable of outputting log records to a rotated file. -func NewFileTarget(filter logr.Filter, formatter logr.Formatter, opts FileOptions, maxQSize int) (*target.File, error) { - fopts := target.FileOptions(opts) - err := checkFileWritable(fopts.Filename) - if err != nil { - return nil, err - } - target := target.NewFileTarget(filter, formatter, fopts, maxQSize) - return target, nil -} - -func checkFileWritable(filename string) error { - // try opening/creating the file for writing - file, err := os.OpenFile(filename, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) - if err != nil { - return err - } - file.Close() - return nil -} diff --git a/cmd/mattermost/commands/logs.go b/cmd/mattermost/commands/logs.go deleted file mode 100644 index bab0a12e6b..0000000000 --- a/cmd/mattermost/commands/logs.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package commands - -import ( - "io" - "os" - - "github.com/spf13/cobra" - - "github.com/mattermost/mattermost-server/v6/shared/mlog/human" -) - -var LogsCmd = &cobra.Command{ - Use: "logs", - Short: "Display logs in a human-readable format", - RunE: logsCmdF, -} - -func init() { - LogsCmd.Flags().Bool("logrus", false, "Use logrus for formatting.") - RootCmd.AddCommand(LogsCmd) -} - -func logsCmdF(command *cobra.Command, args []string) error { - // check stdin to see if we have a pipe - fi, err := os.Stdin.Stat() - if err != nil { - return err - } - - var input io.Reader - if fi.Size() == 0 && fi.Mode()&os.ModeNamedPipe == 0 { - file, err := os.Open("mattermost.log") - if err != nil { - return err - } - defer file.Close() - input = file - } else { - input = os.Stdin - } - var writer human.LogWriter - - if flag, _ := command.Flags().GetBool("logrus"); flag { - writer = human.NewLogrusWriter(os.Stdout) - } else { - writer = human.NewSimpleWriter(os.Stdout) - } - human.ProcessLogs(input, writer) - - return nil -} diff --git a/cmd/mattermost/commands/main_test.go b/cmd/mattermost/commands/main_test.go index 0905703763..035c15b2b1 100644 --- a/cmd/mattermost/commands/main_test.go +++ b/cmd/mattermost/commands/main_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/mattermost/mattermost-server/v6/api4" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -28,8 +27,6 @@ func TestMain(m *testing.M) { EnableResources: true, } - mlog.DisableZap() - mainHelper = testlib.NewMainHelperWithOptions(&options) defer mainHelper.Close() api4.SetMainHelper(mainHelper) diff --git a/config/emitter.go b/config/emitter.go index 9074868879..4431cba2dc 100644 --- a/config/emitter.go +++ b/config/emitter.go @@ -57,7 +57,7 @@ func (e *logSrcEmitter) RemoveListener(id string) { } // invokeConfigListeners synchronously notifies all listeners about the configuration change. -func (e *logSrcEmitter) invokeConfigListeners(oldCfg, newCfg mlog.LogTargetCfg) { +func (e *logSrcEmitter) invokeConfigListeners(oldCfg, newCfg mlog.LoggerConfiguration) { e.listeners.Range(func(key, value interface{}) bool { listener := value.(LogSrcListener) listener(oldCfg, newCfg) diff --git a/config/emitter_test.go b/config/emitter_test.go index 9906e851d3..364b7b2944 100644 --- a/config/emitter_test.go +++ b/config/emitter_test.go @@ -56,18 +56,18 @@ func TestEmitter(t *testing.T) { func TestLogSrcEmitter(t *testing.T) { var e logSrcEmitter - expectedOldCfg := make(mlog.LogTargetCfg) - expectedNewCfg := make(mlog.LogTargetCfg) + expectedOldCfg := make(mlog.LoggerConfiguration) + expectedNewCfg := make(mlog.LoggerConfiguration) listener1 := false - id1 := e.AddListener(func(oldCfg, newCfg mlog.LogTargetCfg) { + id1 := e.AddListener(func(oldCfg, newCfg mlog.LoggerConfiguration) { assert.Equal(t, expectedOldCfg, oldCfg) assert.Equal(t, expectedNewCfg, newCfg) listener1 = true }) listener2 := false - id2 := e.AddListener(func(oldCfg, newCfg mlog.LogTargetCfg) { + id2 := e.AddListener(func(oldCfg, newCfg mlog.LoggerConfiguration) { assert.Equal(t, expectedOldCfg, oldCfg) assert.Equal(t, expectedNewCfg, newCfg) listener2 = true diff --git a/config/logging.go b/config/logconfigsrc.go similarity index 74% rename from config/logging.go rename to config/logconfigsrc.go index 9e33daacc0..fa082caaf0 100644 --- a/config/logging.go +++ b/config/logconfigsrc.go @@ -6,7 +6,6 @@ package config import ( "encoding/json" "errors" - "os" "path/filepath" "strings" "sync" @@ -14,23 +13,17 @@ import ( "github.com/mattermost/mattermost-server/v6/shared/mlog" ) -type LogSrcListener func(old, new mlog.LogTargetCfg) +type LogSrcListener func(old, new mlog.LoggerConfiguration) // LogConfigSrc abstracts the Advanced Logging configuration so that implementations can // fetch from file, database, etc. type LogConfigSrc interface { // Get fetches the current, cached configuration. - Get() mlog.LogTargetCfg + Get() mlog.LoggerConfiguration // Set updates the dsn specifying the source and reloads Set(dsn string, configStore *Store) (err error) - // AddListener adds a callback function to invoke when the configuration is modified. - AddListener(listener LogSrcListener) string - - // RemoveListener removes a callback function using an id returned from AddListener. - RemoveListener(id string) - // Close cleans up resources. Close() error } @@ -38,6 +31,10 @@ type LogConfigSrc interface { // NewLogConfigSrc creates an advanced logging configuration source, backed by a // file, JSON string, or database. func NewLogConfigSrc(dsn string, configStore *Store) (LogConfigSrc, error) { + if dsn == "" { + return nil, errors.New("dsn should not be empty") + } + if configStore == nil { return nil, errors.New("configStore should not be nil") } @@ -63,7 +60,7 @@ func NewLogConfigSrc(dsn string, configStore *Store) (LogConfigSrc, error) { type jsonSrc struct { logSrcEmitter mutex sync.RWMutex - cfg mlog.LogTargetCfg + cfg mlog.LoggerConfiguration } func newJSONSrc(data string) (*jsonSrc, error) { @@ -72,7 +69,7 @@ func newJSONSrc(data string) (*jsonSrc, error) { } // Get fetches the current, cached configuration -func (src *jsonSrc) Get() mlog.LogTargetCfg { +func (src *jsonSrc) Get() mlog.LoggerConfiguration { src.mutex.RLock() defer src.mutex.RUnlock() return src.cfg @@ -89,7 +86,7 @@ func (src *jsonSrc) Set(data string, _ *Store) error { return nil } -func (src *jsonSrc) set(cfg mlog.LogTargetCfg) { +func (src *jsonSrc) set(cfg mlog.LoggerConfiguration) { src.mutex.Lock() defer src.mutex.Unlock() @@ -106,11 +103,9 @@ func (src *jsonSrc) Close() error { // fileSrc type fileSrc struct { - logSrcEmitter mutex sync.RWMutex - cfg mlog.LogTargetCfg - - path string + cfg mlog.LoggerConfiguration + path string } func newFileSrc(path string, configStore *Store) (*fileSrc, error) { @@ -124,7 +119,7 @@ func newFileSrc(path string, configStore *Store) (*fileSrc, error) { } // Get fetches the current, cached configuration -func (src *fileSrc) Get() mlog.LogTargetCfg { +func (src *fileSrc) Get() mlog.LoggerConfiguration { src.mutex.RLock() defer src.mutex.RUnlock() return src.cfg @@ -145,26 +140,14 @@ func (src *fileSrc) Set(path string, configStore *Store) error { } src.set(cfg) - - // If path is a real file and not just the name of a database resource then watch it for changes. - // Absolute paths are explicit and require no resolution. - if _, err = os.Stat(path); os.IsNotExist(err) { - return nil - } - - src.mutex.Lock() - defer src.mutex.Unlock() - return nil } -func (src *fileSrc) set(cfg mlog.LogTargetCfg) { +func (src *fileSrc) set(cfg mlog.LoggerConfiguration) { src.mutex.Lock() defer src.mutex.Unlock() - old := src.cfg src.cfg = cfg - src.invokeConfigListeners(old, cfg) } // Close cleans up resources. @@ -172,8 +155,8 @@ func (src *fileSrc) Close() error { return nil } -func logTargetCfgFromJSON(data []byte) (mlog.LogTargetCfg, error) { - cfg := make(mlog.LogTargetCfg) +func logTargetCfgFromJSON(data []byte) (mlog.LoggerConfiguration, error) { + cfg := make(mlog.LoggerConfiguration) err := json.Unmarshal(data, &cfg) if err != nil { return nil, err diff --git a/config/logging_test.go b/config/logconfigsrc_test.go similarity index 100% rename from config/logging_test.go rename to config/logconfigsrc_test.go diff --git a/config/logger.go b/config/logger.go new file mode 100644 index 0000000000..23c777d21b --- /dev/null +++ b/config/logger.go @@ -0,0 +1,216 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package config + +import ( + "encoding/json" + "fmt" + "path/filepath" + "strings" + + "github.com/mattermost/mattermost-server/v6/model" + "github.com/mattermost/mattermost-server/v6/shared/mlog" + "github.com/mattermost/mattermost-server/v6/utils/fileutils" +) + +const ( + LogRotateSize = 10000 + LogCompress = true + LogRotateMaxAge = 0 + LogRotateMaxBackups = 0 + LogFilename = "mattermost.log" + LogNotificationFilename = "notifications.log" + LogMinLevelLen = 5 + LogMinMsgLen = 45 + LogDelim = " " + LogEnableCaller = true +) + +type fileLocationFunc func(string) string + +func MloggerConfigFromLoggerConfig(s *model.LogSettings, configSrc LogConfigSrc, getFileFunc fileLocationFunc) (mlog.LoggerConfiguration, error) { + cfg := make(mlog.LoggerConfiguration) + + var targetCfg mlog.TargetCfg + var err error + + // add the simple logging config + if *s.EnableConsole { + targetCfg, err = makeSimpleConsoleTarget(*s.ConsoleLevel, *s.ConsoleJson, *s.EnableColor) + if err != nil { + return cfg, err + } + cfg["_defConsole"] = targetCfg + } + + if *s.EnableFile { + targetCfg, err = makeSimpleFileTarget(getFileFunc(*s.FileLocation), *s.FileLevel, *s.FileJson) + if err != nil { + return cfg, err + } + cfg["_defFile"] = targetCfg + } + + if configSrc == nil { + return cfg, nil + } + + // add advanced logging config + cfgAdv := configSrc.Get() + cfg.Append(cfgAdv) + + return cfg, nil +} + +func MloggerConfigFromAuditConfig(auditSettings model.ExperimentalAuditSettings, configSrc LogConfigSrc) (mlog.LoggerConfiguration, error) { + cfg := make(mlog.LoggerConfiguration) + + var targetCfg mlog.TargetCfg + var err error + + // add the simple audit config + if *auditSettings.FileEnabled { + targetCfg, err = makeSimpleFileTarget(*auditSettings.FileName, "error", true) + if err != nil { + return nil, err + } + + // apply audit specific levels + targetCfg.Levels = []mlog.Level{mlog.LvlAuditAPI, mlog.LvlAuditContent, mlog.LvlAuditPerms, mlog.LvlAuditCLI} + + // apply audit specific formatting + targetCfg.FormatOptions = json.RawMessage(`{"disable_timestamp": true, "disable_msg": true, "disable_stacktrace": true, "disable_level": true}`) + + cfg["_defAudit"] = targetCfg + } + + if configSrc == nil { + return cfg, nil + } + + // add advanced audit config + cfgAdv := configSrc.Get() + cfg.Append(cfgAdv) + + return cfg, nil +} + +func GetLogFileLocation(fileLocation string) string { + if fileLocation == "" { + fileLocation, _ = fileutils.FindDir("logs") + } + + return filepath.Join(fileLocation, LogFilename) +} + +func GetNotificationsLogFileLocation(fileLocation string) string { + if fileLocation == "" { + fileLocation, _ = fileutils.FindDir("logs") + } + + return filepath.Join(fileLocation, LogNotificationFilename) +} + +func GetLogSettingsFromNotificationsLogSettings(notificationLogSettings *model.NotificationLogSettings) *model.LogSettings { + settings := &model.LogSettings{} + settings.SetDefaults() + settings.ConsoleJson = notificationLogSettings.ConsoleJson + settings.ConsoleLevel = notificationLogSettings.ConsoleLevel + settings.EnableConsole = notificationLogSettings.EnableConsole + settings.EnableFile = notificationLogSettings.EnableFile + settings.FileJson = notificationLogSettings.FileJson + settings.FileLevel = notificationLogSettings.FileLevel + settings.FileLocation = notificationLogSettings.FileLocation + settings.AdvancedLoggingConfig = notificationLogSettings.AdvancedLoggingConfig + settings.EnableColor = notificationLogSettings.EnableColor + return settings +} + +func makeSimpleConsoleTarget(level string, outputJSON bool, color bool) (mlog.TargetCfg, error) { + levels, err := stdLevels(level) + if err != nil { + return mlog.TargetCfg{}, err + } + + target := mlog.TargetCfg{ + Type: "console", + Levels: levels, + Options: json.RawMessage(`{"out": "stdout"}`), + MaxQueueSize: 1000, + } + + if outputJSON { + target.Format = "json" + target.FormatOptions = makeJSONFormatOptions() + } else { + target.Format = "plain" + target.FormatOptions = makePlainFormatOptions(color) + } + return target, nil +} + +func makeSimpleFileTarget(filename string, level string, json bool) (mlog.TargetCfg, error) { + levels, err := stdLevels(level) + if err != nil { + return mlog.TargetCfg{}, err + } + + target := mlog.TargetCfg{ + Type: "file", + Levels: levels, + Options: makeFileOptions(filename), + MaxQueueSize: 1000, + } + + if json { + target.Format = "json" + target.FormatOptions = makeJSONFormatOptions() + } else { + target.Format = "plain" + target.FormatOptions = makePlainFormatOptions(false) + } + return target, nil +} + +func stdLevels(level string) ([]mlog.Level, error) { + stdLevel, err := stringToStdLevel(level) + if err != nil { + return nil, err + } + + var levels []mlog.Level + for _, l := range mlog.StdAll { + if l.ID <= stdLevel.ID { + levels = append(levels, l) + } + } + return levels, nil +} + +func stringToStdLevel(level string) (mlog.Level, error) { + level = strings.ToLower(level) + for _, l := range mlog.StdAll { + if l.Name == level { + return l, nil + } + } + return mlog.Level{}, fmt.Errorf("%s is not a standard level", level) +} + +func makeJSONFormatOptions() json.RawMessage { + str := fmt.Sprintf(`{"enable_caller": %t}`, LogEnableCaller) + return json.RawMessage(str) +} + +func makePlainFormatOptions(enableColor bool) json.RawMessage { + str := fmt.Sprintf(`{"delim": "%s", "min_level_len": %d, "min_msg_len": %d, "enable_color": %t, "enable_caller": %t}`, + LogDelim, LogMinLevelLen, LogMinMsgLen, enableColor, LogEnableCaller) + return json.RawMessage(str) +} + +func makeFileOptions(filename string) json.RawMessage { + str := fmt.Sprintf(`{"filename": "%s", "max_size": %d, "max_age": %d, "max_backups": %d, "compress": %t}`, + filename, LogRotateSize, LogRotateMaxAge, LogRotateMaxBackups, LogCompress) + return json.RawMessage(str) +} diff --git a/config/main_test.go b/config/main_test.go index 58e913108c..36c7f2e36f 100644 --- a/config/main_test.go +++ b/config/main_test.go @@ -12,7 +12,6 @@ import ( "github.com/stretchr/testify/require" "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -23,8 +22,6 @@ func TestMain(m *testing.M) { EnableStore: true, } - mlog.DisableZap() - mainHelper = testlib.NewMainHelperWithOptions(&options) defer mainHelper.Close() diff --git a/einterfaces/metrics.go b/einterfaces/metrics.go index 4815a35b01..56e6cb5aa0 100644 --- a/einterfaces/metrics.go +++ b/einterfaces/metrics.go @@ -4,8 +4,8 @@ package einterfaces import ( - "github.com/mattermost/logr" "github.com/mattermost/mattermost-server/v6/model" + "github.com/mattermost/mattermost-server/v6/shared/mlog" ) type MetricsInterface interface { @@ -66,7 +66,7 @@ type MetricsInterface interface { ObservePluginAPIDuration(pluginID, apiName string, success bool, elapsed float64) ObserveEnabledUsers(users int64) - GetLoggerMetricsCollector() logr.MetricsCollector + GetLoggerMetricsCollector() mlog.MetricsCollector IncrementRemoteClusterMsgSentCounter(remoteID string) IncrementRemoteClusterMsgReceivedCounter(remoteID string) diff --git a/einterfaces/mocks/MetricsInterface.go b/einterfaces/mocks/MetricsInterface.go index b854a567b2..db733f8b8f 100644 --- a/einterfaces/mocks/MetricsInterface.go +++ b/einterfaces/mocks/MetricsInterface.go @@ -5,7 +5,7 @@ package mocks import ( - logr "github.com/mattermost/logr" + logr "github.com/mattermost/logr/v2" mock "github.com/stretchr/testify/mock" model "github.com/mattermost/mattermost-server/v6/model" diff --git a/go.mod b/go.mod index 04111ac781..9ef0fafdb4 100644 --- a/go.mod +++ b/go.mod @@ -50,7 +50,7 @@ require ( github.com/hashicorp/go-hclog v0.16.1 github.com/hashicorp/go-immutable-radix v1.3.0 // indirect github.com/hashicorp/go-msgpack v1.1.5 // indirect - github.com/hashicorp/go-multierror v1.1.1 + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.4.2 github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect @@ -72,7 +72,8 @@ require ( github.com/mattermost/gosaml2 v0.3.3 github.com/mattermost/gziphandler v0.0.1 github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d - github.com/mattermost/logr v1.0.13 + github.com/mattermost/logr v1.0.13 // indirect + github.com/mattermost/logr/v2 v2.0.10 github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 github.com/mattn/go-isatty v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect @@ -102,7 +103,7 @@ require ( github.com/russellhaering/goxmldsig v1.1.0 github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect - github.com/sirupsen/logrus v1.8.1 + github.com/sirupsen/logrus v1.8.1 // indirect github.com/smartystreets/assertions v1.0.0 // indirect github.com/spf13/cobra v1.1.3 github.com/splitio/go-client/v6 v6.1.0 @@ -118,7 +119,7 @@ require ( github.com/ulikunitz/xz v0.5.10 // indirect github.com/vmihailenco/msgpack/v5 v5.3.4 github.com/wiggin77/merror v1.0.3 - github.com/wiggin77/srslog v1.0.1 + github.com/wiggin77/srslog v1.0.1 // indirect github.com/willf/bitset v1.1.11 // indirect github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c github.com/yuin/goldmark v1.3.8 @@ -126,7 +127,7 @@ require ( go.opentelemetry.io/otel/internal/metric v0.21.0 // indirect go.uber.org/atomic v1.8.0 // indirect go.uber.org/multierr v1.7.0 // indirect - go.uber.org/zap v1.17.0 + go.uber.org/zap v1.17.0 // indirect golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e golang.org/x/image v0.0.0-20210622092929-e6eecd499c2c golang.org/x/net v0.0.0-20210614182718-04defd469f4e @@ -138,7 +139,7 @@ require ( gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/mail.v2 v2.3.1 - gopkg.in/natefinch/lumberjack.v2 v2.0.0 + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/olivere/elastic.v6 v6.2.35 gopkg.in/yaml.v2 v2.4.0 willnorris.com/go/imageproxy v0.10.0 diff --git a/go.sum b/go.sum index 9e7ed86417..bd6ae9c061 100644 --- a/go.sum +++ b/go.sum @@ -655,6 +655,20 @@ github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d h1:/RJ/UV7M5c7L2TQ github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d/go.mod h1:HLbgMEI5K131jpxGazJ97AxfPDt31osq36YS1oxFQPQ= github.com/mattermost/logr v1.0.13 h1:6F/fM3csvH6Oy5sUpJuW7YyZSzZZAhJm5VcgKMxA2P8= github.com/mattermost/logr v1.0.13/go.mod h1:Mt4DPu1NXMe6JxPdwCC0XBoxXmN9eXOIRPoZarU2PXs= +github.com/mattermost/logr/v2 v2.0.4 h1:LiqvzNNfia23hlu1zmmCDmjqmSfwWNsEtFlL4/7jf8o= +github.com/mattermost/logr/v2 v2.0.4/go.mod h1:mpPp935r5dIkFDo2y9Q87cQWhFR/4xXpNh0k/y8Hmwg= +github.com/mattermost/logr/v2 v2.0.5 h1:qYuPn0FSDtsmxcwhGUsSDkiWCH17ZYts6BLAAgsp1ks= +github.com/mattermost/logr/v2 v2.0.5/go.mod h1:mpPp935r5dIkFDo2y9Q87cQWhFR/4xXpNh0k/y8Hmwg= +github.com/mattermost/logr/v2 v2.0.6 h1:Ewmtqm+o6y6yDSqdMZdrP5o0P7rCKbBNJQl+FbhcWHg= +github.com/mattermost/logr/v2 v2.0.6/go.mod h1:mpPp935r5dIkFDo2y9Q87cQWhFR/4xXpNh0k/y8Hmwg= +github.com/mattermost/logr/v2 v2.0.7 h1:rcwGa3faMWa/0k841FZhrcb8qZexfV8sk9ZVCV/8KUg= +github.com/mattermost/logr/v2 v2.0.7/go.mod h1:mpPp935r5dIkFDo2y9Q87cQWhFR/4xXpNh0k/y8Hmwg= +github.com/mattermost/logr/v2 v2.0.8 h1:HQuie671ZtmIp0Y6gY0u34UQvnBw8rPFLeLxWOxv5VM= +github.com/mattermost/logr/v2 v2.0.8/go.mod h1:mpPp935r5dIkFDo2y9Q87cQWhFR/4xXpNh0k/y8Hmwg= +github.com/mattermost/logr/v2 v2.0.9 h1:WZev0JYaWmRa/wmpXe8xGA+YDTj9iqbn25rM55Hgutw= +github.com/mattermost/logr/v2 v2.0.9/go.mod h1:mpPp935r5dIkFDo2y9Q87cQWhFR/4xXpNh0k/y8Hmwg= +github.com/mattermost/logr/v2 v2.0.10 h1:i6rJbuX/EkBM9maM8M0eJ3rxB+fsBKNslPvzSlA2w/M= +github.com/mattermost/logr/v2 v2.0.10/go.mod h1:mpPp935r5dIkFDo2y9Q87cQWhFR/4xXpNh0k/y8Hmwg= github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 h1:G9tL6JXRBMzjuD1kkBtcnd42kUiT6QDwxfFYu7adM6o= github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0/go.mod h1:nV5bfVpT//+B1RPD2JvRnxbkLmJEYXmRaaVl15fsXjs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= diff --git a/migrations/helper_test.go b/migrations/helper_test.go index 23a278cd31..cfc7d08e57 100644 --- a/migrations/helper_test.go +++ b/migrations/helper_test.go @@ -5,13 +5,14 @@ package migrations import ( "os" + "testing" "github.com/mattermost/mattermost-server/v6/app" "github.com/mattermost/mattermost-server/v6/app/request" "github.com/mattermost/mattermost-server/v6/config" "github.com/mattermost/mattermost-server/v6/model" + "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/store/localcachelayer" - "github.com/mattermost/mattermost-server/v6/utils" ) type TestHelper struct { @@ -27,6 +28,8 @@ type TestHelper struct { SystemAdminUser *model.User tempWorkspace string + + TestLogger *mlog.Logger } func setupTestHelper(enterprise bool) *TestHelper { @@ -44,6 +47,15 @@ func setupTestHelper(enterprise bool) *TestHelper { options = append(options, app.StoreOverride(mainHelper.Store)) options = append(options, app.SkipPostInitializiation()) + testLogger, _ := mlog.NewLogger() + logCfg, _ := config.MloggerConfigFromLoggerConfig(&newConfig.LogSettings, nil, config.GetLogFileLocation) + if errCfg := testLogger.ConfigureTargets(logCfg); errCfg != nil { + panic("failed to configure test logger: " + errCfg.Error()) + } + // lock logger config so server init cannot override it during testing. + testLogger.LockConfiguration() + options = append(options, app.SetLogger(testLogger)) + s, err := app.NewServer(options...) if err != nil { panic(err) @@ -55,9 +67,10 @@ func setupTestHelper(enterprise bool) *TestHelper { } th := &TestHelper{ - App: app.New(app.ServerConnector(s)), - Context: &request.Context{}, - Server: s, + App: app.New(app.ServerConnector(s)), + Context: &request.Context{}, + Server: s, + TestLogger: testLogger, } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxUsersPerTeam = 50 }) @@ -89,11 +102,11 @@ func setupTestHelper(enterprise bool) *TestHelper { return th } -func SetupEnterprise() *TestHelper { +func SetupEnterprise(tb testing.TB) *TestHelper { return setupTestHelper(true) } -func Setup() *TestHelper { +func Setup(tb testing.TB) *TestHelper { return setupTestHelper(false) } @@ -126,12 +139,10 @@ func (th *TestHelper) CreateTeam() *model.Team { Type: model.TeamOpen, } - utils.DisableDebugLogForTest() var err *model.AppError if team, err = th.App.CreateTeam(th.Context, team); err != nil { panic(err) } - utils.EnableDebugLogForTest() return team } @@ -146,12 +157,10 @@ func (th *TestHelper) CreateUser() *model.User { EmailVerified: true, } - utils.DisableDebugLogForTest() var err *model.AppError if user, err = th.App.CreateUser(th.Context, user); err != nil { panic(err) } - utils.EnableDebugLogForTest() return user } @@ -170,23 +179,19 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT CreatorId: th.BasicUser.Id, } - utils.DisableDebugLogForTest() var err *model.AppError if channel, err = th.App.CreateChannel(th.Context, channel, true); err != nil { panic(err) } - utils.EnableDebugLogForTest() return channel } func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel { - utils.DisableDebugLogForTest() var err *model.AppError var channel *model.Channel if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil { panic(err) } - utils.EnableDebugLogForTest() return channel } @@ -200,36 +205,25 @@ func (th *TestHelper) CreatePost(channel *model.Channel) *model.Post { CreateAt: model.GetMillis() - 10000, } - utils.DisableDebugLogForTest() var err *model.AppError if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil { panic(err) } - utils.EnableDebugLogForTest() return post } func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) { - utils.DisableDebugLogForTest() - _, err := th.App.JoinUserToTeam(th.Context, team, user, "") if err != nil { panic(err) } - - utils.EnableDebugLogForTest() } func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember { - utils.DisableDebugLogForTest() - member, err := th.App.AddUserToChannel(user, channel, false) if err != nil { panic(err) } - - utils.EnableDebugLogForTest() - return member } diff --git a/migrations/main_test.go b/migrations/main_test.go index 8826e085b5..372655a617 100644 --- a/migrations/main_test.go +++ b/migrations/main_test.go @@ -6,7 +6,6 @@ package migrations import ( "testing" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -18,8 +17,6 @@ func TestMain(m *testing.M) { EnableResources: true, } - mlog.DisableZap() - mainHelper = testlib.NewMainHelperWithOptions(&options) defer mainHelper.Close() diff --git a/migrations/migrations_test.go b/migrations/migrations_test.go index d88b63ade5..5adebfcdad 100644 --- a/migrations/migrations_test.go +++ b/migrations/migrations_test.go @@ -16,7 +16,7 @@ func TestGetMigrationState(t *testing.T) { if testing.Short() { t.SkipNow() } - th := Setup() + th := Setup(t) defer th.TearDown() migrationKey := model.NewId() diff --git a/model/config.go b/model/config.go index 5721bc1b92..82c6d6e433 100644 --- a/model/config.go +++ b/model/config.go @@ -1223,6 +1223,12 @@ type LogSettings struct { AdvancedLoggingConfig *string `access:"environment_logging,write_restrictable,cloud_restrictable"` } +func NewLogSettings() *LogSettings { + settings := &LogSettings{} + settings.SetDefaults() + return settings +} + func (s *LogSettings) SetDefaults() { if s.EnableConsole == nil { s.EnableConsole = NewBool(true) diff --git a/plugin/hclog_adapter.go b/plugin/hclog_adapter.go index d4e07d14df..4142298f48 100644 --- a/plugin/hclog_adapter.go +++ b/plugin/hclog_adapter.go @@ -115,7 +115,7 @@ func (h *hclogAdapter) ResetNamed(name string) hclog.Logger { } func (h *hclogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger { - return h.wrappedLogger.StdLog() + return h.wrappedLogger.StdLogger(mlog.LvlInfo) } func (h *hclogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer { diff --git a/plugin/health_check_test.go b/plugin/health_check_test.go index 12f8d95389..1854af0a9e 100644 --- a/plugin/health_check_test.go +++ b/plugin/health_check_test.go @@ -52,12 +52,8 @@ func testPluginHealthCheckSuccess(t *testing.T) { require.NoError(t, err) bundle := model.BundleInfoForPath(dir) - log := mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - }) + log := mlog.CreateConsoleTestLogger(true, mlog.LvlError) + defer log.Shutdown() supervisor, err := newSupervisor(bundle, nil, nil, log, nil) require.NoError(t, err) @@ -99,12 +95,8 @@ func testPluginHealthCheckPanic(t *testing.T) { require.NoError(t, err) bundle := model.BundleInfoForPath(dir) - log := mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - }) + log := mlog.CreateConsoleTestLogger(true, mlog.LvlError) + defer log.Shutdown() supervisor, err := newSupervisor(bundle, nil, nil, log, nil) require.NoError(t, err) diff --git a/plugin/supervisor.go b/plugin/supervisor.go index 883321fbcc..49dc70bc54 100644 --- a/plugin/supervisor.go +++ b/plugin/supervisor.go @@ -39,7 +39,7 @@ func newSupervisor(pluginInfo *model.BundleInfo, apiImpl API, driver Driver, par wrappedLogger := pluginInfo.WrapLogger(parentLogger) hclogAdaptedLogger := &hclogAdapter{ - wrappedLogger: wrappedLogger.WithCallerSkip(1), + wrappedLogger: wrappedLogger, extrasKey: "wrapped_extras", } diff --git a/plugin/supervisor_test.go b/plugin/supervisor_test.go index 9719b97eb6..81b8d6a912 100644 --- a/plugin/supervisor_test.go +++ b/plugin/supervisor_test.go @@ -35,12 +35,8 @@ func testSupervisorInvalidExecutablePath(t *testing.T) { ioutil.WriteFile(filepath.Join(dir, "plugin.json"), []byte(`{"id": "foo", "server": {"executable": "/foo/../../backend.exe"}}`), 0600) bundle := model.BundleInfoForPath(dir) - log := mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - }) + log := mlog.CreateConsoleTestLogger(true, mlog.LvlError) + defer log.Shutdown() supervisor, err := newSupervisor(bundle, nil, nil, log, nil) assert.Nil(t, supervisor) assert.Error(t, err) @@ -54,12 +50,8 @@ func testSupervisorNonExistentExecutablePath(t *testing.T) { ioutil.WriteFile(filepath.Join(dir, "plugin.json"), []byte(`{"id": "foo", "server": {"executable": "thisfileshouldnotexist"}}`), 0600) bundle := model.BundleInfoForPath(dir) - log := mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - }) + log := mlog.CreateConsoleTestLogger(true, mlog.LvlError) + defer log.Shutdown() supervisor, err := newSupervisor(bundle, nil, nil, log, nil) require.Error(t, err) require.Nil(t, supervisor) @@ -84,12 +76,8 @@ func testSupervisorStartTimeout(t *testing.T) { ioutil.WriteFile(filepath.Join(dir, "plugin.json"), []byte(`{"id": "foo", "server": {"executable": "backend.exe"}}`), 0600) bundle := model.BundleInfoForPath(dir) - log := mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - }) + log := mlog.CreateConsoleTestLogger(true, mlog.LvlError) + defer log.Shutdown() supervisor, err := newSupervisor(bundle, nil, nil, log, nil) require.Error(t, err) require.Nil(t, supervisor) diff --git a/services/imageproxy/local.go b/services/imageproxy/local.go index e3379d30d9..1b663723e3 100644 --- a/services/imageproxy/local.go +++ b/services/imageproxy/local.go @@ -46,12 +46,7 @@ func makeLocalBackend(proxy *ImageProxy) *LocalBackend { impl := imageproxy.NewProxy(proxy.HTTPService.MakeTransport(false), nil) if proxy.Logger != nil { - logger, err := proxy.Logger.StdLogAt(mlog.LevelDebug, mlog.String("image_proxy", "local")) - if err != nil { - mlog.Warn("Failed to initialize logger for image proxy", mlog.Err(err)) - } - - impl.Logger = logger + impl.Logger = proxy.Logger.With(mlog.String("image_proxy", "local")).StdLogger(mlog.LvlDebug) } baseURL, err := url.Parse(*proxy.ConfigService.Config().ServiceSettings.SiteURL) diff --git a/services/remotecluster/mocks_test.go b/services/remotecluster/mocks_test.go index 468396dfd1..4f102b5ac2 100644 --- a/services/remotecluster/mocks_test.go +++ b/services/remotecluster/mocks_test.go @@ -5,13 +5,8 @@ package remotecluster import ( "context" - "fmt" - "strings" - "sync" "testing" - "go.uber.org/zap/zapcore" - "github.com/mattermost/mattermost-server/v6/einterfaces" "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock" @@ -22,14 +17,16 @@ import ( type mockServer struct { remotes []*model.RemoteCluster - logger *mockLogger + logger *mlog.Logger user *model.User } func newMockServer(t *testing.T, remotes []*model.RemoteCluster) *mockServer { + testLogger := mlog.CreateTestLogger(t, nil, mlog.StdAll...) + return &mockServer{ remotes: remotes, - logger: &mockLogger{t: t}, + logger: testLogger, } } @@ -64,88 +61,3 @@ func (ms *mockServer) GetStore() store.Store { return storeMock } func (ms *mockServer) Shutdown() { ms.logger.Shutdown() } - -type mockLogger struct { - t *testing.T - mux sync.Mutex -} - -func (ml *mockLogger) IsLevelEnabled(level mlog.LogLevel) bool { - return true -} -func (ml *mockLogger) Debug(s string, flds ...mlog.Field) { - ml.mux.Lock() - defer ml.mux.Unlock() - if ml.t != nil { - ml.t.Log("debug", s, fieldsToStrings(flds)) - } -} -func (ml *mockLogger) Info(s string, flds ...mlog.Field) { - ml.mux.Lock() - defer ml.mux.Unlock() - if ml.t != nil { - ml.t.Log("info", s, fieldsToStrings(flds)) - } -} -func (ml *mockLogger) Warn(s string, flds ...mlog.Field) { - ml.mux.Lock() - defer ml.mux.Unlock() - if ml.t != nil { - ml.t.Log("warn", s, fieldsToStrings(flds)) - } -} -func (ml *mockLogger) Error(s string, flds ...mlog.Field) { - ml.mux.Lock() - defer ml.mux.Unlock() - if ml.t != nil { - ml.t.Log("error", s, fieldsToStrings(flds)) - } -} -func (ml *mockLogger) Critical(s string, flds ...mlog.Field) { - ml.mux.Lock() - defer ml.mux.Unlock() - if ml.t != nil { - ml.t.Log("crit", s, fieldsToStrings(flds)) - } -} -func (ml *mockLogger) Log(level mlog.LogLevel, s string, flds ...mlog.Field) { - ml.mux.Lock() - defer ml.mux.Unlock() - if ml.t != nil { - ml.t.Log(level.Name, s, fieldsToStrings(flds)) - } -} -func (ml *mockLogger) LogM(levels []mlog.LogLevel, s string, flds ...mlog.Field) { - ml.mux.Lock() - defer ml.mux.Unlock() - if ml.t != nil { - ml.t.Log(levelsToString(levels), s, fieldsToStrings(flds)) - } -} -func (ml *mockLogger) Shutdown() { - ml.mux.Lock() - defer ml.mux.Unlock() - ml.t = nil -} - -func levelsToString(levels []mlog.LogLevel) string { - sb := strings.Builder{} - for _, l := range levels { - sb.WriteString(l.Name) - sb.WriteString(",") - } - return sb.String() -} - -func fieldsToStrings(fields []mlog.Field) []string { - encoder := zapcore.NewMapObjectEncoder() - for _, zapField := range fields { - zapField.AddTo(encoder) - } - - var result []string - for k, v := range encoder.Fields { - result = append(result, fmt.Sprintf("%s:%v", k, v)) - } - return result -} diff --git a/services/sharedchannel/channelinvite_test.go b/services/sharedchannel/channelinvite_test.go index 3a58b10f29..d1ee9978ad 100644 --- a/services/sharedchannel/channelinvite_test.go +++ b/services/sharedchannel/channelinvite_test.go @@ -23,7 +23,7 @@ type mockLogger struct { mlog.LoggerIFace } -func (ml *mockLogger) Log(level mlog.LogLevel, s string, flds ...mlog.Field) {} +func (ml *mockLogger) Log(level mlog.Level, s string, flds ...mlog.Field) {} func TestOnReceiveChannelInvite(t *testing.T) { t.Run("when msg payload is empty, it does nothing", func(t *testing.T) { diff --git a/services/slackimport/main_test.go b/services/slackimport/main_test.go index 63e8c1ccc6..ad8ff61f40 100644 --- a/services/slackimport/main_test.go +++ b/services/slackimport/main_test.go @@ -7,8 +7,6 @@ import ( "fmt" "os" "testing" - - "github.com/mattermost/mattermost-server/v6/shared/mlog" ) func TestMain(m *testing.M) { @@ -22,8 +20,6 @@ func TestMain(m *testing.M) { panic(fmt.Sprintf("Failed to set current working directory to %s: %s", "../..", err.Error())) } - mlog.DisableZap() - defer func() { err := os.Chdir(prevDir) if err != nil { diff --git a/services/telemetry/telemetry.go b/services/telemetry/telemetry.go index d5678ac2b4..acea4b9071 100644 --- a/services/telemetry/telemetry.go +++ b/services/telemetry/telemetry.go @@ -1228,7 +1228,7 @@ func (ts *TelemetryService) trackChannelModeration() { func (ts *TelemetryService) initRudder(endpoint string, rudderKey string) { if ts.rudderClient == nil { config := rudder.Config{} - config.Logger = rudder.StdLogger(ts.log.StdLog(mlog.String("source", "rudder"))) + config.Logger = rudder.StdLogger(ts.log.With(mlog.String("source", "rudder")).StdLogger(mlog.LvlDebug)) config.Endpoint = endpoint // For testing if endpoint != RudderDataplaneURL { diff --git a/services/telemetry/telemetry_test.go b/services/telemetry/telemetry_test.go index c36a358e05..604b98d873 100644 --- a/services/telemetry/telemetry_test.go +++ b/services/telemetry/telemetry_test.go @@ -19,6 +19,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/mattermost/mattermost-server/v6/config" "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/plugin" "github.com/mattermost/mattermost-server/v6/plugin/plugintest" @@ -40,6 +41,7 @@ func (fcs *FakeConfigService) AsymmetricSigningKey() *ecdsa.PrivateKey func initializeMocks(cfg *model.Config) (*mocks.ServerIface, *storeMocks.Store, func(t *testing.T), func()) { serverIfaceMock := &mocks.ServerIface{} + logger, _ := mlog.NewLogger() configService := &FakeConfigService{cfg} serverIfaceMock.On("Config").Return(cfg) @@ -56,7 +58,7 @@ func initializeMocks(cfg *model.Config) (*mocks.ServerIface, *storeMocks.Store, func(m *model.Manifest) plugin.API { return pluginsAPIMock }, nil, pluginDir, webappPluginDir, - mlog.NewLogger(&mlog.LoggerConfiguration{}), + logger, nil) serverIfaceMock.On("GetPluginsEnvironment").Return(pluginEnv, nil) @@ -270,7 +272,14 @@ func TestRudderTelemetry(t *testing.T) { defer cleanUp() defer deferredAssertions(t) - telemetryService := New(serverIfaceMock, storeMock, searchengine.NewBroker(cfg, nil), mlog.NewLogger(&mlog.LoggerConfiguration{})) + testLogger, _ := mlog.NewLogger() + logCfg, _ := config.MloggerConfigFromLoggerConfig(&cfg.LogSettings, nil, config.GetLogFileLocation) + if errCfg := testLogger.ConfigureTargets(logCfg); errCfg != nil { + panic("failed to configure test logger: " + errCfg.Error()) + } + defer testLogger.Shutdown() + + telemetryService := New(serverIfaceMock, storeMock, searchengine.NewBroker(cfg, nil), testLogger) telemetryService.TelemetryID = telemetryID telemetryService.rudderClient = nil telemetryService.initRudder(server.URL, RudderKey) diff --git a/services/users/main_test.go b/services/users/main_test.go index 6a031adee5..f57d778116 100644 --- a/services/users/main_test.go +++ b/services/users/main_test.go @@ -7,7 +7,6 @@ import ( "flag" "testing" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -26,8 +25,6 @@ func TestMain(m *testing.M) { WithReadReplica: replicaFlag, } - mlog.DisableZap() - mainHelper = testlib.NewMainHelperWithOptions(&options) defer mainHelper.Close() diff --git a/shared/filestore/filesstore_test.go b/shared/filestore/filesstore_test.go index dd53768e08..da909c93d6 100644 --- a/shared/filestore/filesstore_test.go +++ b/shared/filestore/filesstore_test.go @@ -33,12 +33,10 @@ type FileBackendTestSuite struct { func TestLocalFileBackendTestSuite(t *testing.T) { // Setup a global logger to catch tests logging outside of app context // The global logger will be stomped by apps initializing but that's fine for testing. Ideally this won't happen. - mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - })) + logger := mlog.CreateConsoleTestLogger(true, mlog.LvlError) + defer logger.Shutdown() + + mlog.InitGlobalLogger(logger) dir, err := ioutil.TempDir("", "") require.NoError(t, err) diff --git a/shared/mlog/default.go b/shared/mlog/default.go index e7faa8c4c1..0567c01648 100644 --- a/shared/mlog/default.go +++ b/shared/mlog/default.go @@ -4,26 +4,32 @@ package mlog import ( - "context" + "bytes" "encoding/json" - "errors" "fmt" "os" - - "github.com/mattermost/logr" ) // defaultLog manually encodes the log to STDERR, providing a basic, default logging implementation // before mlog is fully configured. -func defaultLog(level, msg string, fields ...Field) { +func defaultLog(level Level, msg string, fields ...Field) { + mFields := make(map[string]string) + buf := &bytes.Buffer{} + + for _, fld := range fields { + buf.Reset() + fld.ValueString(buf, shouldQuote) + mFields[fld.Key] = buf.String() + } + log := struct { - Level string `json:"level"` - Message string `json:"msg"` - Fields []Field `json:"fields,omitempty"` + Level string `json:"level"` + Message string `json:"msg"` + Fields map[string]string `json:"fields,omitempty"` }{ - level, + level.Name, msg, - fields, + mFields, } if b, err := json.Marshal(log); err != nil { @@ -33,67 +39,25 @@ func defaultLog(level, msg string, fields ...Field) { } } -func defaultIsLevelEnabled(level LogLevel) bool { +func defaultIsLevelEnabled(level Level) bool { return true } -func defaultDebugLog(msg string, fields ...Field) { - defaultLog("debug", msg, fields...) +func defaultCustomMultiLog(lvl []Level, msg string, fields ...Field) { + for _, level := range lvl { + defaultLog(level, msg, fields...) + } } -func defaultInfoLog(msg string, fields ...Field) { - defaultLog("info", msg, fields...) -} - -func defaultWarnLog(msg string, fields ...Field) { - defaultLog("warn", msg, fields...) -} - -func defaultErrorLog(msg string, fields ...Field) { - defaultLog("error", msg, fields...) -} - -func defaultCriticalLog(msg string, fields ...Field) { - // We map critical to error in zap, so be consistent. - defaultLog("error", msg, fields...) -} - -func defaultCustomLog(lvl LogLevel, msg string, fields ...Field) { - // custom log levels are only output once log targets are configured. -} - -func defaultCustomMultiLog(lvl []LogLevel, msg string, fields ...Field) { - // custom log levels are only output once log targets are configured. -} - -func defaultFlush(ctx context.Context) error { - return nil -} - -func defaultAdvancedConfig(cfg LogTargetCfg) error { - // mlog.ConfigAdvancedConfig should not be called until default - // logger is replaced with mlog.Logger instance. - return errors.New("cannot config advanced logging on default logger") -} - -func defaultAdvancedShutdown(ctx context.Context) error { - return nil -} - -func defaultAddTarget(targets ...logr.Target) error { - // mlog.AddTarget should not be called until default - // logger is replaced with mlog.Logger instance. - return errors.New("cannot AddTarget on default logger") -} - -func defaultRemoveTargets(ctx context.Context, f func(TargetInfo) bool) error { - // mlog.RemoveTargets should not be called until default - // logger is replaced with mlog.Logger instance. - return errors.New("cannot RemoveTargets on default logger") -} - -func defaultEnableMetrics(collector logr.MetricsCollector) error { - // mlog.EnableMetrics should not be called until default - // logger is replaced with mlog.Logger instance. - return errors.New("cannot EnableMetrics on default logger") +// shouldQuote returns true if val contains any characters that require quotations. +func shouldQuote(val string) bool { + for _, c := range val { + if !((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '-' || c == '.' || c == '_' || c == '/' || c == '@' || c == '^' || c == '+') { + return true + } + } + return false } diff --git a/shared/mlog/errors.go b/shared/mlog/errors.go deleted file mode 100644 index 93762fda57..0000000000 --- a/shared/mlog/errors.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "github.com/mattermost/logr" -) - -// onLoggerError is called when the logging system encounters an error, -// such as a target not able to write records. The targets will keep trying -// however the error will be logged with a dedicated level that can be output -// to a safe/always available target for monitoring or alerting. -func onLoggerError(err error) { - Log(LvlLogError, "advanced logging error", Err(err)) -} - -// onQueueFull is called when the main logger queue is full, indicating the -// volume and frequency of log record creation is too high for the queue size -// and/or the target latencies. -func onQueueFull(rec *logr.LogRec, maxQueueSize int) bool { - Log(LvlLogError, "main queue full, dropping record", Any("rec", rec)) - return true // drop record -} - -// onTargetQueueFull is called when the main logger queue is full, indicating the -// volume and frequency of log record creation is too high for the target's queue size -// and/or the target latency. -func onTargetQueueFull(target logr.Target, rec *logr.LogRec, maxQueueSize int) bool { - Log(LvlLogError, "target queue full, dropping record", String("target", ""), Any("rec", rec)) - return true // drop record -} diff --git a/shared/mlog/global.go b/shared/mlog/global.go index aba0664672..30b88c9b28 100644 --- a/shared/mlog/global.go +++ b/shared/mlog/global.go @@ -4,95 +4,119 @@ package mlog import ( - "context" - "log" - "sync/atomic" - - "github.com/mattermost/logr" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" + "sync" ) -var globalLogger *Logger +var ( + globalLogger *Logger + muxGlobalLogger sync.RWMutex +) func InitGlobalLogger(logger *Logger) { - // Clean up previous instance. - if globalLogger != nil && globalLogger.logrLogger != nil { - globalLogger.logrLogger.Logr().Shutdown() + muxGlobalLogger.Lock() + defer muxGlobalLogger.Unlock() + + globalLogger = logger +} + +func getGlobalLogger() *Logger { + muxGlobalLogger.RLock() + defer muxGlobalLogger.RUnlock() + + return globalLogger +} + +// IsLevelEnabled returns true only if at least one log target is +// configured to emit the specified log level. Use this check when +// gathering the log info may be expensive. +// +// Note, transformations and serializations done via fields are already +// lazily evaluated and don't require this check beforehand. +func IsLevelEnabled(level Level) bool { + logger := getGlobalLogger() + if logger == nil { + return defaultIsLevelEnabled(level) } - glob := *logger - glob.zap = glob.zap.WithOptions(zap.AddCallerSkip(1)) - globalLogger = &glob - IsLevelEnabled = globalLogger.IsLevelEnabled - Debug = globalLogger.Debug - Info = globalLogger.Info - Warn = globalLogger.Warn - Error = globalLogger.Error - Critical = globalLogger.Critical - Log = globalLogger.Log - LogM = globalLogger.LogM - Flush = globalLogger.Flush - ConfigAdvancedLogging = globalLogger.ConfigAdvancedLogging - ShutdownAdvancedLogging = globalLogger.ShutdownAdvancedLogging - AddTarget = globalLogger.AddTarget - RemoveTargets = globalLogger.RemoveTargets - EnableMetrics = globalLogger.EnableMetrics + return logger.IsLevelEnabled(level) } -// logWriterFunc provides access to mlog via io.Writer, so the standard logger -// can be redirected to use mlog and whatever targets are defined. -type logWriterFunc func([]byte) (int, error) - -func (lw logWriterFunc) Write(p []byte) (int, error) { - return lw(p) -} - -func RedirectStdLog(logger *Logger) { - if atomic.LoadInt32(&disableZap) == 0 { - zap.RedirectStdLogAt(logger.zap.With(zap.String("source", "stdlog")).WithOptions(zap.AddCallerSkip(-2)), zapcore.ErrorLevel) +// Log emits the log record for any targets configured for the specified level. +func Log(level Level, msg string, fields ...Field) { + logger := getGlobalLogger() + if logger == nil { + defaultLog(level, msg, fields...) return } + logger.Log(level, msg, fields...) +} - writer := func(p []byte) (int, error) { - Log(LvlStdLog, string(p)) - return len(p), nil +// LogM emits the log record for any targets configured for the specified levels. +// Equivalent to calling `Log` once for each level. +func LogM(levels []Level, msg string, fields ...Field) { + logger := getGlobalLogger() + if logger == nil { + defaultCustomMultiLog(levels, msg, fields...) + return } - log.SetOutput(logWriterFunc(writer)) + logger.LogM(levels, msg, fields...) } -type IsLevelEnabledFunc func(LogLevel) bool -type LogFunc func(string, ...Field) -type LogFuncCustom func(LogLevel, string, ...Field) -type LogFuncCustomMulti func([]LogLevel, string, ...Field) -type FlushFunc func(context.Context) error -type ConfigFunc func(cfg LogTargetCfg) error -type ShutdownFunc func(context.Context) error -type AddTargetFunc func(...logr.Target) error -type RemoveTargetsFunc func(context.Context, func(TargetInfo) bool) error -type EnableMetricsFunc func(logr.MetricsCollector) error - -// DON'T USE THIS Modify the level on the app logger -func GloballyDisableDebugLogForTest() { - globalLogger.consoleLevel.SetLevel(zapcore.ErrorLevel) +// Convenience method equivalent to calling `Log` with the `Trace` level. +func Trace(msg string, fields ...Field) { + logger := getGlobalLogger() + if logger == nil { + defaultLog(LvlTrace, msg, fields...) + return + } + logger.Trace(msg, fields...) } -// DON'T USE THIS Modify the level on the app logger -func GloballyEnableDebugLogForTest() { - globalLogger.consoleLevel.SetLevel(zapcore.DebugLevel) +// Convenience method equivalent to calling `Log` with the `Debug` level. +func Debug(msg string, fields ...Field) { + logger := getGlobalLogger() + if logger == nil { + defaultLog(LvlDebug, msg, fields...) + return + } + logger.Debug(msg, fields...) } -var IsLevelEnabled IsLevelEnabledFunc = defaultIsLevelEnabled -var Debug LogFunc = defaultDebugLog -var Info LogFunc = defaultInfoLog -var Warn LogFunc = defaultWarnLog -var Error LogFunc = defaultErrorLog -var Critical LogFunc = defaultCriticalLog -var Log LogFuncCustom = defaultCustomLog -var LogM LogFuncCustomMulti = defaultCustomMultiLog -var Flush FlushFunc = defaultFlush +// Convenience method equivalent to calling `Log` with the `Info` level. +func Info(msg string, fields ...Field) { + logger := getGlobalLogger() + if logger == nil { + defaultLog(LvlInfo, msg, fields...) + return + } + logger.Info(msg, fields...) +} -var ConfigAdvancedLogging ConfigFunc = defaultAdvancedConfig -var ShutdownAdvancedLogging ShutdownFunc = defaultAdvancedShutdown -var AddTarget AddTargetFunc = defaultAddTarget -var RemoveTargets RemoveTargetsFunc = defaultRemoveTargets -var EnableMetrics EnableMetricsFunc = defaultEnableMetrics +// Convenience method equivalent to calling `Log` with the `Warn` level. +func Warn(msg string, fields ...Field) { + logger := getGlobalLogger() + if logger == nil { + defaultLog(LvlWarn, msg, fields...) + return + } + logger.Warn(msg, fields...) +} + +// Convenience method equivalent to calling `Log` with the `Error` level. +func Error(msg string, fields ...Field) { + logger := getGlobalLogger() + if logger == nil { + defaultLog(LvlError, msg, fields...) + return + } + logger.Error(msg, fields...) +} + +// Convenience method equivalent to calling `Log` with the `Critical` level. +func Critical(msg string, fields ...Field) { + logger := getGlobalLogger() + if logger == nil { + defaultLog(LvlCritical, msg, fields...) + return + } + logger.Critical(msg, fields...) +} diff --git a/shared/mlog/global_test.go b/shared/mlog/global_test.go index 8589d1a8b7..a3e47ebdce 100644 --- a/shared/mlog/global_test.go +++ b/shared/mlog/global_test.go @@ -4,6 +4,8 @@ package mlog_test import ( + "encoding/json" + "fmt" "io/ioutil" "os" "path/filepath" @@ -29,83 +31,86 @@ func TestLoggingBeforeInitialized(t *testing.T) { func TestLoggingAfterInitialized(t *testing.T) { testCases := []struct { - Description string - LoggerConfiguration *mlog.LoggerConfiguration - ExpectedLogs []string + description string + cfg mlog.TargetCfg + expectedLogs []string }{ { "file logging, json, debug", - &mlog.LoggerConfiguration{ - EnableConsole: false, - EnableFile: true, - FileJson: true, - FileLevel: mlog.LevelDebug, + mlog.TargetCfg{ + Type: "file", + Format: "json", + FormatOptions: json.RawMessage(`{"enable_caller":true}`), + Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError, mlog.LvlWarn, mlog.LvlInfo, mlog.LvlDebug}, }, []string{ - `{"level":"debug","ts":0,"caller":"mlog/global_test.go:0","msg":"real debug log"}`, - `{"level":"info","ts":0,"caller":"mlog/global_test.go:0","msg":"real info log"}`, - `{"level":"warn","ts":0,"caller":"mlog/global_test.go:0","msg":"real warning log"}`, - `{"level":"error","ts":0,"caller":"mlog/global_test.go:0","msg":"real error log"}`, - `{"level":"error","ts":0,"caller":"mlog/global_test.go:0","msg":"real critical log"}`, + `{"timestamp":0,"level":"debug","msg":"real debug log","caller":"mlog/global_test.go:0"}`, + `{"timestamp":0,"level":"info","msg":"real info log","caller":"mlog/global_test.go:0"}`, + `{"timestamp":0,"level":"warn","msg":"real warning log","caller":"mlog/global_test.go:0"}`, + `{"timestamp":0,"level":"error","msg":"real error log","caller":"mlog/global_test.go:0"}`, + `{"timestamp":0,"level":"critical","msg":"real critical log","caller":"mlog/global_test.go:0"}`, }, }, { "file logging, json, error", - &mlog.LoggerConfiguration{ - EnableConsole: false, - EnableFile: true, - FileJson: true, - FileLevel: mlog.LevelError, + mlog.TargetCfg{ + Type: "file", + Format: "json", + FormatOptions: json.RawMessage(`{"enable_caller":true}`), + Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError}, }, []string{ - `{"level":"error","ts":0,"caller":"mlog/global_test.go:0","msg":"real error log"}`, - `{"level":"error","ts":0,"caller":"mlog/global_test.go:0","msg":"real critical log"}`, + `{"timestamp":0,"level":"error","msg":"real error log","caller":"mlog/global_test.go:0"}`, + `{"timestamp":0,"level":"critical","msg":"real critical log","caller":"mlog/global_test.go:0"}`, }, }, { "file logging, non-json, debug", - &mlog.LoggerConfiguration{ - EnableConsole: false, - EnableFile: true, - FileJson: false, - FileLevel: mlog.LevelDebug, + mlog.TargetCfg{ + Type: "file", + Format: "plain", + FormatOptions: json.RawMessage(`{"delim":" | ", "enable_caller":true}`), + Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError, mlog.LvlWarn, mlog.LvlInfo, mlog.LvlDebug}, }, []string{ - `TIME debug mlog/global_test.go:0 real debug log`, - `TIME info mlog/global_test.go:0 real info log`, - `TIME warn mlog/global_test.go:0 real warning log`, - `TIME error mlog/global_test.go:0 real error log`, - `TIME error mlog/global_test.go:0 real critical log`, + `debug | TIME | real debug log | caller="mlog/global_test.go:0"`, + `info | TIME | real info log | caller="mlog/global_test.go:0"`, + `warn | TIME | real warning log | caller="mlog/global_test.go:0"`, + `error | TIME | real error log | caller="mlog/global_test.go:0"`, + `critical | TIME | real critical log | caller="mlog/global_test.go:0"`, }, }, { "file logging, non-json, error", - &mlog.LoggerConfiguration{ - EnableConsole: false, - EnableFile: true, - FileJson: false, - FileLevel: mlog.LevelError, + mlog.TargetCfg{ + Type: "file", + Format: "plain", + FormatOptions: json.RawMessage(`{"delim":" | ", "enable_caller":true}`), + Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError}, }, []string{ - `TIME error mlog/global_test.go:0 real error log`, - `TIME error mlog/global_test.go:0 real critical log`, + `error | TIME | real error log | caller="mlog/global_test.go:0"`, + `critical | TIME | real critical log | caller="mlog/global_test.go:0"`, }, }, } for _, testCase := range testCases { - t.Run(testCase.Description, func(t *testing.T) { + t.Run(testCase.description, func(t *testing.T) { var filePath string - if testCase.LoggerConfiguration.EnableFile { + if testCase.cfg.Type == "file" { tempDir, err := ioutil.TempDir(os.TempDir(), "TestLoggingAfterInitialized") require.NoError(t, err) defer os.Remove(tempDir) filePath = filepath.Join(tempDir, "file.log") - testCase.LoggerConfiguration.FileLocation = filePath + testCase.cfg.Options = json.RawMessage(fmt.Sprintf(`{"filename": "%s"}`, filePath)) } - logger := mlog.NewLogger(testCase.LoggerConfiguration) + logger, _ := mlog.NewLogger() + err := logger.ConfigureTargets(map[string]mlog.TargetCfg{testCase.description: testCase.cfg}) + require.NoError(t, err) + mlog.InitGlobalLogger(logger) mlog.Debug("real debug log") @@ -114,32 +119,26 @@ func TestLoggingAfterInitialized(t *testing.T) { mlog.Error("real error log") mlog.Critical("real critical log") - if testCase.LoggerConfiguration.EnableFile { + logger.Shutdown() + + if testCase.cfg.Type == "file" { logs, err := ioutil.ReadFile(filePath) require.NoError(t, err) actual := strings.TrimSpace(string(logs)) - if testCase.LoggerConfiguration.FileJson { - reTs := regexp.MustCompile(`"ts":[0-9\.]+`) + if testCase.cfg.Format == "json" { + reTs := regexp.MustCompile(`"timestamp":"[0-9\.\-\:\sZ]+"`) reCaller := regexp.MustCompile(`"caller":"([^"]+):[0-9\.]+"`) - actual = reTs.ReplaceAllString(actual, `"ts":0`) + actual = reTs.ReplaceAllString(actual, `"timestamp":0`) actual = reCaller.ReplaceAllString(actual, `"caller":"$1:0"`) } else { - actualRows := strings.Split(actual, "\n") - for i, actualRow := range actualRows { - actualFields := strings.Split(actualRow, "\t") - if len(actualFields) > 3 { - actualFields[0] = "TIME" - reCaller := regexp.MustCompile(`([^"]+):[0-9\.]+`) - actualFields[2] = reCaller.ReplaceAllString(actualFields[2], "$1:0") - actualRows[i] = strings.Join(actualFields, "\t") - } - } - - actual = strings.Join(actualRows, "\n") + reTs := regexp.MustCompile(`\[\d\d\d\d-\d\d-\d\d\s[0-9\:\.\s\-Z]+\]`) + reCaller := regexp.MustCompile(`caller="([^"]+):[0-9\.]+"`) + actual = reTs.ReplaceAllString(actual, "TIME") + actual = reCaller.ReplaceAllString(actual, `caller="$1:0"`) } - require.ElementsMatch(t, testCase.ExpectedLogs, strings.Split(actual, "\n")) + require.ElementsMatch(t, testCase.expectedLogs, strings.Split(actual, "\n")) } }) } diff --git a/shared/mlog/human/entry.go b/shared/mlog/human/entry.go deleted file mode 100644 index 1a49fdcaca..0000000000 --- a/shared/mlog/human/entry.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package human - -import ( - "fmt" - "strings" - "time" - - "github.com/mattermost/mattermost-server/v6/shared/mlog" -) - -type LogEntry struct { - Time time.Time - Level string - Message string - Caller string - Fields []mlog.Field -} - -// Provide default string representation. Used by SimpleWriter -func (f LogEntry) String() string { - var sb strings.Builder - if !f.Time.IsZero() { - sb.WriteString(f.Time.Format(time.RFC3339Nano)) - sb.WriteRune(' ') - } - if f.Level != "" { - sb.WriteString(f.Level) - sb.WriteRune(' ') - } - if f.Caller != "" { - sb.WriteString(f.Caller) - sb.WriteRune(' ') - } - for _, field := range f.Fields { - sb.WriteString(field.Key) - sb.WriteRune('=') - sb.WriteString(fmt.Sprint(field.Interface)) - sb.WriteRune(' ') - } - if f.Message != "" { - // If the message is multiple lines, start the whole message on a new line - if strings.ContainsRune(f.Message, '\n') { - sb.WriteRune('\n') - } - sb.WriteString(f.Message) - } - - return sb.String() -} diff --git a/shared/mlog/human/logrus_writer.go b/shared/mlog/human/logrus_writer.go deleted file mode 100644 index f086112fe0..0000000000 --- a/shared/mlog/human/logrus_writer.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package human - -import ( - "fmt" - "io" - "time" - - "github.com/sirupsen/logrus" -) - -type LogrusWriter struct { - logger *logrus.Logger -} - -func (w *LogrusWriter) Write(e LogEntry) { - if e.Level == "" { - fmt.Fprintln(w.logger.Out, e.Message) - return - } - - lvl, err := logrus.ParseLevel(e.Level) - if err != nil { - fmt.Fprintln(w.logger.Out, err) - lvl = logrus.TraceLevel + 1 // will invoke Println - } - - logger := w.logger.WithTime(e.Time) - - if e.Caller != "" { - // logrus has a system of reporting the caller, but there's no easy way to override it - logger = logger.WithField("caller", e.Caller) - } - - for _, field := range e.Fields { - logger = logger.WithField(field.Key, field.Interface) - } - - switch lvl { - case logrus.PanicLevel: - // Prevent panic from causing us to exit - defer func() { - recover() - }() - logger.Panic(e.Message) - case logrus.FatalLevel: - logger.Fatal(e.Message) - case logrus.ErrorLevel: - logger.Error(e.Message) - case logrus.WarnLevel: - logger.Warn(e.Message) - case logrus.InfoLevel: - logger.Info(e.Message) - case logrus.DebugLevel: - logger.Debug(e.Message) - case logrus.TraceLevel: - logger.Trace(e.Message) - default: - logger.Println(e.Message) - } -} - -func NewLogrusWriter(output io.Writer) *LogrusWriter { - w := new(LogrusWriter) - w.logger = logrus.New() - w.logger.SetLevel(logrus.TraceLevel) // don't filter any logs - w.logger.ExitFunc = func(int) {} // prevent Fatal from causing us to exit - w.logger.SetReportCaller(false) - w.logger.SetOutput(output) - var tf logrus.TextFormatter - tf.FullTimestamp = true - tf.TimestampFormat = time.RFC3339Nano - w.logger.SetFormatter(&tf) - return w -} diff --git a/shared/mlog/human/parser.go b/shared/mlog/human/parser.go deleted file mode 100644 index b4b45505df..0000000000 --- a/shared/mlog/human/parser.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package human - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "strconv" - "strings" - "time" - - "github.com/mattermost/mattermost-server/v6/shared/mlog" -) - -func ParseLogMessage(msg string) LogEntry { - result, err := parseLogMessage(msg) - if err != nil { - // If failed to parse, just output a LogEntry where all fields are blank, but Message is the original string - var result2 LogEntry - result2.Message = msg - return result2 - } - return result -} - -func parseLogMessage(msg string) (result LogEntry, err error) { - - // Note: This implementation uses a custom json decoding loop. - // The primary advantage of this versus decoding directly into a map is to - // preserve the order of the fields. This can be simplified if we end up - // having the formatter sort fields alphabetically (logrus does by default) - - dec := json.NewDecoder(strings.NewReader(msg)) - - // look for an initial "{" - token, err := dec.Token() - if err != nil { - return result, err - } - d, ok := token.(json.Delim) - if !ok || d != '{' { - return result, fmt.Errorf("input is not a JSON object, found: %v", token) - } - - // read all key-value pairs - for dec.More() { - key, err2 := dec.Token() - if err2 != nil { - return result, err2 - } - skey, ok2 := key.(string) - if !ok2 { - return result, errors.New("key is not a value string") - } - if !dec.More() { - return result, errors.New("missing value pair") - } - - switch skey { - case "ts": - var ts json.Number - if err2 := dec.Decode(&ts); err2 != nil { - return result, err2 - } - timeVal, err2 := numberToTime(ts) - if err2 != nil { - return result, err2 - } - result.Time = timeVal - - case "level": - s, err2 := decodeAsString(dec) - if err2 != nil { - return result, err2 - } - result.Level = s - - case "msg": - s, err2 := decodeAsString(dec) - if err2 != nil { - return result, err2 - } - result.Message = s - - case "caller": - s, err2 := decodeAsString(dec) - if err2 != nil { - return result, err2 - } - result.Caller = s - - default: - var p interface{} - if err2 := dec.Decode(&p); err2 != nil { - return result, err2 - } - var f mlog.Field - f.Key = skey - f.Interface = p - result.Fields = append(result.Fields, f) - } - } - - // read the "}" - token, err = dec.Token() - if err != nil { - return result, err - } - d, ok = token.(json.Delim) - if !ok || d != '}' { - return result, fmt.Errorf("failed to read '}', read: %v", token) - } - - // make sure nothing else trailing - if token, err := dec.Token(); err != io.EOF { - return result, err - } else if token != nil { - return result, errors.New("found trailing data") - } - - return result, nil -} - -// Translate a number into a time -func numberToTime(v json.Number) (time.Time, error) { - // Using floating point math to extract the nanoseconds leads to a time that doesn't exactly match the input - // Instead, parse out the components from the string representation - - var t time.Time - - // First make sure it is a number... - flt, err := v.Float64() - if err != nil { - return t, err - } - - s := v.String() - - if strings.ContainsAny(s, "eE") { - // input is in scientific notation. Convert to standard decimal notation - s = strconv.FormatFloat(flt, 'f', -1, 64) - } - - // extract the seconds and nanoseconds separately - var nanos, sec int64 - - parts := strings.SplitN(s, ".", 2) - sec, err = strconv.ParseInt(parts[0], 10, 64) - if err != nil { - return t, err - } - - if len(parts) == 2 { - nanosText := parts[1] + "000000000" - nanosText = nanosText[:9] - nanos, err = strconv.ParseInt(nanosText, 10, 64) - if err != nil { - return t, err - } - } - - t = time.Unix(sec, nanos) - return t, nil -} - -// Decodes a value from JSON, coercing it to a string value as necessary -func decodeAsString(dec *json.Decoder) (s string, err error) { - var v interface{} - if err = dec.Decode(&v); err != nil { - return s, err - } - var ok bool - if s, ok = v.(string); ok { - return s, err - } - s = fmt.Sprint(v) - return s, err -} diff --git a/shared/mlog/human/process.go b/shared/mlog/human/process.go deleted file mode 100644 index ae31acdad3..0000000000 --- a/shared/mlog/human/process.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package human - -import ( - "bufio" - "io" -) - -type LogWriter interface { - Write(e LogEntry) -} - -// Read JSON logs from input and write formatted logs to the output -func ProcessLogs(reader io.Reader, writer LogWriter) { - scanner := bufio.NewScanner(reader) - for scanner.Scan() { - s := scanner.Text() - e := ParseLogMessage(s) - writer.Write(e) - } -} diff --git a/shared/mlog/human/simple_writer.go b/shared/mlog/human/simple_writer.go deleted file mode 100644 index 760ed91596..0000000000 --- a/shared/mlog/human/simple_writer.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package human - -import ( - "fmt" - "io" -) - -type SimpleWriter struct { - out io.Writer -} - -func (w *SimpleWriter) Write(e LogEntry) { - fmt.Fprintln(w.out, e) -} - -func NewSimpleWriter(out io.Writer) *SimpleWriter { - w := new(SimpleWriter) - w.out = out - return w -} diff --git a/shared/mlog/levels.go b/shared/mlog/levels.go index 872b129687..c0b3099643 100644 --- a/shared/mlog/levels.go +++ b/shared/mlog/levels.go @@ -3,49 +3,56 @@ package mlog -// Standard levels +import "github.com/mattermost/logr/v2" + +// Standard levels. var ( - LvlPanic = LogLevel{ID: 0, Name: "panic", Stacktrace: true} - LvlFatal = LogLevel{ID: 1, Name: "fatal", Stacktrace: true} - LvlError = LogLevel{ID: 2, Name: "error"} - LvlWarn = LogLevel{ID: 3, Name: "warn"} - LvlInfo = LogLevel{ID: 4, Name: "info"} - LvlDebug = LogLevel{ID: 5, Name: "debug"} - LvlTrace = LogLevel{ID: 6, Name: "trace"} + LvlPanic = logr.Panic // ID = 0 + LvlFatal = logr.Fatal // ID = 1 + LvlError = logr.Error // ID = 2 + LvlWarn = logr.Warn // ID = 3 + LvlInfo = logr.Info // ID = 4 + LvlDebug = logr.Debug // ID = 5 + LvlTrace = logr.Trace // ID = 6 + StdAll = []Level{LvlPanic, LvlFatal, LvlError, LvlWarn, LvlInfo, LvlDebug, LvlTrace} + // non-standard "critical" level + LvlCritical = Level{ID: 7, Name: "critical"} // used by redirected standard logger - LvlStdLog = LogLevel{ID: 10, Name: "stdlog"} + LvlStdLog = Level{ID: 10, Name: "stdlog"} // used only by the logger - LvlLogError = LogLevel{ID: 11, Name: "logerror", Stacktrace: true} + LvlLogError = Level{ID: 11, Name: "logerror", Stacktrace: true} ) // Register custom (discrete) levels here. -// !!!!! ID's must not exceed 32,768 !!!!!! +// !!!!! Custom ID's must be between 20 and 32,768 !!!!!! var ( // used by the audit system - LvlAuditAPI = LogLevel{ID: 100, Name: "audit-api"} - LvlAuditContent = LogLevel{ID: 101, Name: "audit-content"} - LvlAuditPerms = LogLevel{ID: 102, Name: "audit-permissions"} - LvlAuditCLI = LogLevel{ID: 103, Name: "audit-cli"} + LvlAuditAPI = Level{ID: 100, Name: "audit-api"} + LvlAuditContent = Level{ID: 101, Name: "audit-content"} + LvlAuditPerms = Level{ID: 102, Name: "audit-permissions"} + LvlAuditCLI = Level{ID: 103, Name: "audit-cli"} // used by the TCP log target - LvlTCPLogTarget = LogLevel{ID: 120, Name: "TcpLogTarget"} + LvlTCPLogTarget = Level{ID: 120, Name: "TcpLogTarget"} // used by Remote Cluster Service - LvlRemoteClusterServiceDebug = LogLevel{ID: 130, Name: "RemoteClusterServiceDebug"} - LvlRemoteClusterServiceError = LogLevel{ID: 131, Name: "RemoteClusterServiceError"} - LvlRemoteClusterServiceWarn = LogLevel{ID: 132, Name: "RemoteClusterServiceWarn"} + LvlRemoteClusterServiceDebug = Level{ID: 130, Name: "RemoteClusterServiceDebug"} + LvlRemoteClusterServiceError = Level{ID: 131, Name: "RemoteClusterServiceError"} + LvlRemoteClusterServiceWarn = Level{ID: 132, Name: "RemoteClusterServiceWarn"} // used by Shared Channel Sync Service - LvlSharedChannelServiceDebug = LogLevel{ID: 200, Name: "SharedChannelServiceDebug"} - LvlSharedChannelServiceError = LogLevel{ID: 201, Name: "SharedChannelServiceError"} - LvlSharedChannelServiceWarn = LogLevel{ID: 202, Name: "SharedChannelServiceWarn"} - LvlSharedChannelServiceMessagesInbound = LogLevel{ID: 203, Name: "SharedChannelServiceMsgInbound"} - LvlSharedChannelServiceMessagesOutbound = LogLevel{ID: 204, Name: "SharedChannelServiceMsgOutbound"} + LvlSharedChannelServiceDebug = Level{ID: 200, Name: "SharedChannelServiceDebug"} + LvlSharedChannelServiceError = Level{ID: 201, Name: "SharedChannelServiceError"} + LvlSharedChannelServiceWarn = Level{ID: 202, Name: "SharedChannelServiceWarn"} + LvlSharedChannelServiceMessagesInbound = Level{ID: 203, Name: "SharedChannelServiceMsgInbound"} + LvlSharedChannelServiceMessagesOutbound = Level{ID: 204, Name: "SharedChannelServiceMsgOutbound"} - // add more here ... + // Focalboard + LvlFBTelemetry = Level{ID: 9000, Name: "telemetry"} + LvlFBMetrics = Level{ID: 9001, Name: "metrics"} ) -// Combinations for LogM (log multi) +// Combinations for LogM (log multi). var ( - MLvlAuditAll = []LogLevel{LvlAuditAPI, LvlAuditContent, LvlAuditPerms, LvlAuditCLI} + MLvlAuditAll = []Level{LvlAuditAPI, LvlAuditContent, LvlAuditPerms, LvlAuditCLI} ) diff --git a/shared/mlog/log.go b/shared/mlog/log.go deleted file mode 100644 index d50fc1230e..0000000000 --- a/shared/mlog/log.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "context" - "fmt" - "io" - "log" - "os" - "sync" - "sync/atomic" - "time" - - "github.com/mattermost/logr" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "gopkg.in/natefinch/lumberjack.v2" -) - -const ( - // Very verbose messages for debugging specific issues - LevelDebug = "debug" - // Default log level, informational - LevelInfo = "info" - // Warnings are messages about possible issues - LevelWarn = "warn" - // Errors are messages about things we know are problems - LevelError = "error" - - // DefaultFlushTimeout is the default amount of time mlog.Flush will wait - // before timing out. - DefaultFlushTimeout = time.Second * 5 -) - -var ( - // disableZap is set when Zap should be disabled and Logr used instead. - // This is needed for unit testing as Zap has no shutdown capabilities - // and holds file handles until process exit. Currently unit test create - // many server instances, and thus many Zap log files. - // This flag will be removed when Zap is permanently replaced. - disableZap int32 -) - -// Type and function aliases from zap to limit the libraries scope into MM code -type Field = zapcore.Field - -var Int64 = zap.Int64 -var Int32 = zap.Int32 -var Int = zap.Int -var Uint32 = zap.Uint32 -var String = zap.String -var Any = zap.Any -var Err = zap.Error -var NamedErr = zap.NamedError -var Bool = zap.Bool -var Duration = zap.Duration - -type LoggerIFace interface { - IsLevelEnabled(LogLevel) bool - Debug(string, ...Field) - Info(string, ...Field) - Warn(string, ...Field) - Error(string, ...Field) - Critical(string, ...Field) - Log(LogLevel, string, ...Field) - LogM([]LogLevel, string, ...Field) -} - -type TargetInfo logr.TargetInfo - -type LoggerConfiguration struct { - EnableConsole bool - ConsoleJson bool - EnableColor bool - ConsoleLevel string - EnableFile bool - FileJson bool - FileLevel string - FileLocation string -} - -type Logger struct { - zap *zap.Logger - consoleLevel zap.AtomicLevel - fileLevel zap.AtomicLevel - logrLogger *logr.Logger - mutex *sync.RWMutex -} - -func getZapLevel(level string) zapcore.Level { - switch level { - case LevelInfo: - return zapcore.InfoLevel - case LevelWarn: - return zapcore.WarnLevel - case LevelDebug: - return zapcore.DebugLevel - case LevelError: - return zapcore.ErrorLevel - default: - return zapcore.InfoLevel - } -} - -func makeEncoder(json, color bool) zapcore.Encoder { - encoderConfig := zap.NewProductionEncoderConfig() - if json { - return zapcore.NewJSONEncoder(encoderConfig) - } - - if color { - encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder - } - encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder - return zapcore.NewConsoleEncoder(encoderConfig) -} - -func NewLogger(config *LoggerConfiguration) *Logger { - cores := []zapcore.Core{} - logger := &Logger{ - consoleLevel: zap.NewAtomicLevelAt(getZapLevel(config.ConsoleLevel)), - fileLevel: zap.NewAtomicLevelAt(getZapLevel(config.FileLevel)), - logrLogger: newLogr(), - mutex: &sync.RWMutex{}, - } - - if config.EnableConsole { - writer := zapcore.Lock(os.Stderr) - core := zapcore.NewCore(makeEncoder(config.ConsoleJson, config.EnableColor), writer, logger.consoleLevel) - cores = append(cores, core) - } - - if config.EnableFile { - if atomic.LoadInt32(&disableZap) != 0 { - t := &LogTarget{ - Type: "file", - Format: "json", - Levels: mlogLevelToLogrLevels(config.FileLevel), - MaxQueueSize: DefaultMaxTargetQueue, - Options: []byte(fmt.Sprintf(`{"Filename":"%s", "MaxSizeMB":%d, "Compress":%t}`, - config.FileLocation, 100, true)), - } - if !config.FileJson { - t.Format = "plain" - } - if tgt, err := NewLogrTarget("mlogFile", t); err == nil { - logger.logrLogger.Logr().AddTarget(tgt) - } else { - Error("error creating mlogFile", Err(err)) - } - } else { - writer := zapcore.AddSync(&lumberjack.Logger{ - Filename: config.FileLocation, - MaxSize: 100, - Compress: true, - }) - - core := zapcore.NewCore(makeEncoder(config.FileJson, false), writer, logger.fileLevel) - cores = append(cores, core) - } - } - - combinedCore := zapcore.NewTee(cores...) - - logger.zap = zap.New(combinedCore, - zap.AddCaller(), - ) - return logger -} - -func (l *Logger) ChangeLevels(config *LoggerConfiguration) { - l.consoleLevel.SetLevel(getZapLevel(config.ConsoleLevel)) - l.fileLevel.SetLevel(getZapLevel(config.FileLevel)) -} - -func (l *Logger) SetConsoleLevel(level string) { - l.consoleLevel.SetLevel(getZapLevel(level)) -} - -func (l *Logger) With(fields ...Field) *Logger { - newLogger := *l - newLogger.zap = newLogger.zap.With(fields...) - if newLogger.getLogger() != nil { - ll := newLogger.getLogger().WithFields(zapToLogr(fields)) - newLogger.logrLogger = &ll - } - return &newLogger -} - -func (l *Logger) StdLog(fields ...Field) *log.Logger { - return zap.NewStdLog(l.With(fields...).zap.WithOptions(getStdLogOption())) -} - -// StdLogAt returns *log.Logger which writes to supplied zap logger at required level. -func (l *Logger) StdLogAt(level string, fields ...Field) (*log.Logger, error) { - return zap.NewStdLogAt(l.With(fields...).zap.WithOptions(getStdLogOption()), getZapLevel(level)) -} - -// StdLogWriter returns a writer that can be hooked up to the output of a golang standard logger -// anything written will be interpreted as log entries accordingly -func (l *Logger) StdLogWriter() io.Writer { - newLogger := *l - newLogger.zap = newLogger.zap.WithOptions(zap.AddCallerSkip(4), getStdLogOption()) - f := newLogger.Info - return &loggerWriter{f} -} - -func (l *Logger) WithCallerSkip(skip int) *Logger { - newLogger := *l - newLogger.zap = newLogger.zap.WithOptions(zap.AddCallerSkip(skip)) - return &newLogger -} - -// Made for the plugin interface, wraps mlog in a simpler interface -// at the cost of performance -func (l *Logger) Sugar() *SugarLogger { - return &SugarLogger{ - wrappedLogger: l, - zapSugar: l.zap.Sugar(), - } -} - -func (l *Logger) IsLevelEnabled(level LogLevel) bool { - return isLevelEnabled(l.getLogger(), logr.Level(level)) -} - -func (l *Logger) Debug(message string, fields ...Field) { - l.zap.Debug(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Debug) { - l.getLogger().WithFields(zapToLogr(fields)).Debug(message) - } -} - -func (l *Logger) Info(message string, fields ...Field) { - l.zap.Info(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Info) { - l.getLogger().WithFields(zapToLogr(fields)).Info(message) - } -} - -func (l *Logger) Warn(message string, fields ...Field) { - l.zap.Warn(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Warn) { - l.getLogger().WithFields(zapToLogr(fields)).Warn(message) - } -} - -func (l *Logger) Error(message string, fields ...Field) { - l.zap.Error(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Error) { - l.getLogger().WithFields(zapToLogr(fields)).Error(message) - } -} - -func (l *Logger) Critical(message string, fields ...Field) { - l.zap.Error(message, fields...) - if isLevelEnabled(l.getLogger(), logr.Error) { - l.getLogger().WithFields(zapToLogr(fields)).Error(message) - } -} - -func (l *Logger) Log(level LogLevel, message string, fields ...Field) { - l.getLogger().WithFields(zapToLogr(fields)).Log(logr.Level(level), message) -} - -func (l *Logger) LogM(levels []LogLevel, message string, fields ...Field) { - var logger *logr.Logger - for _, lvl := range levels { - if isLevelEnabled(l.getLogger(), logr.Level(lvl)) { - // don't create logger with fields unless at least one level is active. - if logger == nil { - l := l.getLogger().WithFields(zapToLogr(fields)) - logger = &l - } - logger.Log(logr.Level(lvl), message) - } - } -} - -func (l *Logger) Flush(cxt context.Context) error { - return l.getLogger().Logr().FlushWithTimeout(cxt) -} - -// ShutdownAdvancedLogging stops the logger from accepting new log records and tries to -// flush queues within the context timeout. Once complete all targets are shutdown -// and any resources released. -func (l *Logger) ShutdownAdvancedLogging(cxt context.Context) error { - err := l.getLogger().Logr().ShutdownWithTimeout(cxt) - l.setLogger(newLogr()) - return err -} - -// ConfigAdvancedLoggingConfig (re)configures advanced logging based on the -// specified log targets. This is the easiest way to get the advanced logger -// configured via a config source such as file. -func (l *Logger) ConfigAdvancedLogging(targets LogTargetCfg) error { - if err := l.ShutdownAdvancedLogging(context.Background()); err != nil { - Error("error shutting down previous logger", Err(err)) - } - - err := logrAddTargets(l.getLogger(), targets) - return err -} - -// AddTarget adds one or more logr.Target to the advanced logger. This is the preferred method -// to add custom targets or provide configuration that cannot be expressed via a -// config source. -func (l *Logger) AddTarget(targets ...logr.Target) error { - return l.getLogger().Logr().AddTarget(targets...) -} - -// RemoveTargets selectively removes targets that were previously added to this logger instance -// using the passed in filter function. The filter function should return true to remove the target -// and false to keep it. -func (l *Logger) RemoveTargets(ctx context.Context, f func(ti TargetInfo) bool) error { - // Use locally defined TargetInfo type so we don't spread Logr dependencies. - fc := func(tic logr.TargetInfo) bool { - return f(TargetInfo(tic)) - } - return l.getLogger().Logr().RemoveTargets(ctx, fc) -} - -// EnableMetrics enables metrics collection by supplying a MetricsCollector. -// The MetricsCollector provides counters and gauges that are updated by log targets. -func (l *Logger) EnableMetrics(collector logr.MetricsCollector) error { - return l.getLogger().Logr().SetMetricsCollector(collector) -} - -// getLogger is a concurrent safe getter of the logr logger -func (l *Logger) getLogger() *logr.Logger { - defer l.mutex.RUnlock() - l.mutex.RLock() - return l.logrLogger -} - -// setLogger is a concurrent safe setter of the logr logger -func (l *Logger) setLogger(logger *logr.Logger) { - defer l.mutex.Unlock() - l.mutex.Lock() - l.logrLogger = logger -} - -// DisableZap is called to disable Zap, and Logr will be used instead. Any Logger -// instances created after this call will only use Logr. -// -// This is needed for unit testing as Zap has no shutdown capabilities -// and holds file handles until process exit. Currently unit tests create -// many server instances, and thus many Zap log file handles. -// -// This method will be removed when Zap is permanently replaced. -func DisableZap() { - atomic.StoreInt32(&disableZap, 1) -} - -// EnableZap re-enables Zap such that any Logger instances created after this -// call will allow Zap targets. -func EnableZap() { - atomic.StoreInt32(&disableZap, 0) -} diff --git a/shared/mlog/log_test.go b/shared/mlog/log_test.go deleted file mode 100644 index cda0498625..0000000000 --- a/shared/mlog/log_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog_test - -import ( - "context" - "sync" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/mattermost/mattermost-server/v6/shared/mlog" -) - -// Test race condition when shutting down advanced logging. This test must run with the -race flag in order to verify -// that there is no race. -func TestLogger_ShutdownAdvancedLoggingRace(t *testing.T) { - logger := mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - EnableFile: false, - FileLevel: mlog.LevelInfo, - }) - started := make(chan bool) - ctx, cancel := context.WithCancel(context.Background()) - var wg sync.WaitGroup - - wg.Add(1) - go func() { - defer wg.Done() - started <- true - - for { - select { - case <-ctx.Done(): - return - default: - logger.Debug("testing...") - } - } - }() - - <-started - - err := logger.ShutdownAdvancedLogging(ctx) - require.NoError(t, err) - - cancel() - wg.Wait() -} diff --git a/shared/mlog/logr.go b/shared/mlog/logr.go deleted file mode 100644 index b253dfbc6c..0000000000 --- a/shared/mlog/logr.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "encoding/json" - "fmt" - "io" - "os" - - "github.com/hashicorp/go-multierror" - "github.com/mattermost/logr" - logrFmt "github.com/mattermost/logr/format" - "github.com/mattermost/logr/target" - "go.uber.org/zap/zapcore" -) - -const ( - DefaultMaxTargetQueue = 1000 - DefaultSysLogPort = 514 -) - -type LogLevel struct { - ID logr.LevelID - Name string - Stacktrace bool -} - -type LogTarget struct { - Type string // one of "console", "file", "tcp", "syslog", "none". - Format string // one of "json", "plain" - Levels []LogLevel - Options json.RawMessage - MaxQueueSize int -} - -type LogTargetCfg map[string]*LogTarget -type LogrCleanup func() error - -func newLogr() *logr.Logger { - lgr := &logr.Logr{} - lgr.OnExit = func(int) {} - lgr.OnPanic = func(interface{}) {} - lgr.OnLoggerError = onLoggerError - lgr.OnQueueFull = onQueueFull - lgr.OnTargetQueueFull = onTargetQueueFull - - logger := lgr.NewLogger() - return &logger -} - -func logrAddTargets(logger *logr.Logger, targets LogTargetCfg) error { - lgr := logger.Logr() - var errs error - for name, t := range targets { - target, err := NewLogrTarget(name, t) - if err != nil { - errs = multierror.Append(err) - continue - } - if target != nil { - target.SetName(name) - lgr.AddTarget(target) - } - } - return errs -} - -// NewLogrTarget creates a `logr.Target` based on a target config. -// Can be used when parsing custom config files, or when programmatically adding -// built-in targets. Use `mlog.AddTarget` to add custom targets. -func NewLogrTarget(name string, t *LogTarget) (logr.Target, error) { - formatter, err := newFormatter(name, t.Format) - if err != nil { - return nil, err - } - filter := newFilter(t.Levels) - - if t.MaxQueueSize == 0 { - t.MaxQueueSize = DefaultMaxTargetQueue - } - - switch t.Type { - case "console": - return newConsoleTarget(name, t, filter, formatter) - case "file": - return newFileTarget(name, t, filter, formatter) - case "syslog": - return newSyslogTarget(name, t, filter, formatter) - case "tcp": - return newTCPTarget(name, t, filter, formatter) - case "none": - return nil, nil - } - return nil, fmt.Errorf("invalid type '%s' for target %s", t.Type, name) -} - -func newFilter(levels []LogLevel) logr.Filter { - filter := &logr.CustomFilter{} - for _, lvl := range levels { - filter.Add(logr.Level(lvl)) - } - return filter -} - -func newFormatter(name string, format string) (logr.Formatter, error) { - switch format { - case "json", "": - return &logrFmt.JSON{}, nil - case "plain": - return &logrFmt.Plain{Delim: " | "}, nil - default: - return nil, fmt.Errorf("invalid format '%s' for target %s", format, name) - } -} - -func newConsoleTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - type consoleOptions struct { - Out string `json:"Out"` - } - options := &consoleOptions{} - if err := json.Unmarshal(t.Options, options); err != nil { - return nil, err - } - - var w io.Writer - switch options.Out { - case "stdout", "": - w = os.Stdout - case "stderr": - w = os.Stderr - default: - return nil, fmt.Errorf("invalid out '%s' for target %s", options.Out, name) - } - - newTarget := target.NewWriterTarget(filter, formatter, w, t.MaxQueueSize) - return newTarget, nil -} - -func newFileTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - type fileOptions struct { - Filename string `json:"Filename"` - MaxSize int `json:"MaxSizeMB"` - MaxAge int `json:"MaxAgeDays"` - MaxBackups int `json:"MaxBackups"` - Compress bool `json:"Compress"` - } - options := &fileOptions{} - if err := json.Unmarshal(t.Options, options); err != nil { - return nil, err - } - return newFileTargetWithOpts(name, t, target.FileOptions(*options), filter, formatter) -} - -func newFileTargetWithOpts(name string, t *LogTarget, opts target.FileOptions, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - if opts.Filename == "" { - return nil, fmt.Errorf("missing 'Filename' option for target %s", name) - } - if err := checkFileWritable(opts.Filename); err != nil { - return nil, fmt.Errorf("error writing to 'Filename' for target %s: %w", name, err) - } - - newTarget := target.NewFileTarget(filter, formatter, opts, t.MaxQueueSize) - return newTarget, nil -} - -func newSyslogTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - options := &SyslogParams{} - if err := json.Unmarshal(t.Options, options); err != nil { - return nil, err - } - - if options.IP == "" { - return nil, fmt.Errorf("missing 'IP' option for target %s", name) - } - if options.Port == 0 { - options.Port = DefaultSysLogPort - } - return NewSyslogTarget(filter, formatter, options, t.MaxQueueSize) -} - -func newTCPTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { - options := &TCPParams{} - if err := json.Unmarshal(t.Options, options); err != nil { - return nil, err - } - - if options.IP == "" { - return nil, fmt.Errorf("missing 'IP' option for target %s", name) - } - if options.Port == 0 { - return nil, fmt.Errorf("missing 'Port' option for target %s", name) - } - return NewTCPTarget(filter, formatter, options, t.MaxQueueSize) -} - -func checkFileWritable(filename string) error { - // try opening/creating the file for writing - file, err := os.OpenFile(filename, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) - if err != nil { - return err - } - file.Close() - return nil -} - -func isLevelEnabled(logger *logr.Logger, level logr.Level) bool { - if logger == nil || logger.Logr() == nil { - return false - } - - status := logger.Logr().IsLevelEnabled(level) - return status.Enabled -} - -// zapToLogr converts Zap fields to Logr fields. -// This will not be needed once Logr is used for all logging. -func zapToLogr(zapFields []Field) logr.Fields { - encoder := zapcore.NewMapObjectEncoder() - for _, zapField := range zapFields { - zapField.AddTo(encoder) - } - return logr.Fields(encoder.Fields) -} - -// mlogLevelToLogrLevel converts a mlog logger level to -// an array of discrete Logr levels. -func mlogLevelToLogrLevels(level string) []LogLevel { - levels := make([]LogLevel, 0) - levels = append(levels, LvlError, LvlPanic, LvlFatal, LvlStdLog) - - switch level { - case LevelDebug: - levels = append(levels, LvlDebug) - fallthrough - case LevelInfo: - levels = append(levels, LvlInfo) - fallthrough - case LevelWarn: - levels = append(levels, LvlWarn) - } - return levels -} diff --git a/shared/mlog/mlog.go b/shared/mlog/mlog.go new file mode 100644 index 0000000000..bfc2e5ab46 --- /dev/null +++ b/shared/mlog/mlog.go @@ -0,0 +1,407 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +// Package mlog provides a simple wrapper around Logr. +package mlog + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "strings" + "sync/atomic" + "time" + + "github.com/mattermost/logr/v2" + logrcfg "github.com/mattermost/logr/v2/config" +) + +const ( + ShutdownTimeout = time.Second * 15 + FlushTimeout = time.Second * 15 + DefaultMaxQueueSize = 1000 + DefaultMetricsUpdateFreqMillis = 15000 +) + +type LoggerIFace interface { + IsLevelEnabled(Level) bool + Debug(string, ...Field) + Info(string, ...Field) + Warn(string, ...Field) + Error(string, ...Field) + Critical(string, ...Field) + Log(Level, string, ...Field) + LogM([]Level, string, ...Field) +} + +// Type and function aliases from Logr to limit the spread of dependencies. +type Field = logr.Field +type Level = logr.Level +type Option = logr.Option +type Target = logr.Target +type TargetInfo = logr.TargetInfo +type LogRec = logr.LogRec +type LogCloner = logr.LogCloner +type MetricsCollector = logr.MetricsCollector +type TargetCfg = logrcfg.TargetCfg +type Sugar = logr.Sugar + +// LoggerConfiguration is a map of LogTarget configurations. +type LoggerConfiguration map[string]TargetCfg + +func (lc LoggerConfiguration) Append(cfg LoggerConfiguration) { + for k, v := range cfg { + lc[k] = v + } +} + +func (lc LoggerConfiguration) toTargetCfg() map[string]logrcfg.TargetCfg { + tcfg := make(map[string]logrcfg.TargetCfg) + for k, v := range lc { + tcfg[k] = v + } + return tcfg +} + +// Any picks the best supported field type based on type of val. +// For best performance when passing a struct (or struct pointer), +// implement `logr.LogWriter` on the struct, otherwise reflection +// will be used to generate a string representation. +var Any = logr.Any + +// Int64 constructs a field containing a key and Int64 value. +var Int64 = logr.Int64 + +// Int32 constructs a field containing a key and Int32 value. +var Int32 = logr.Int32 + +// Int constructs a field containing a key and Int value. +var Int = logr.Int + +// Uint64 constructs a field containing a key and Uint64 value. +var Uint64 = logr.Uint64 + +// Uint32 constructs a field containing a key and Uint32 value. +var Uint32 = logr.Uint32 + +// Uint constructs a field containing a key and Uint value. +var Uint = logr.Uint + +// Float64 constructs a field containing a key and Float64 value. +var Float64 = logr.Float64 + +// Float32 constructs a field containing a key and Float32 value. +var Float32 = logr.Float32 + +// String constructs a field containing a key and String value. +var String = logr.String + +// Stringer constructs a field containing a key and a fmt.Stringer value. +// The fmt.Stringer's `String` method is called lazily. +var Stringer = logr.Stringer + +// Err constructs a field containing a default key ("error") and error value. +var Err = logr.Err + +// NamedErr constructs a field containing a key and error value. +var NamedErr = logr.NamedErr + +// Bool constructs a field containing a key and bool value. +var Bool = logr.Bool + +// Time constructs a field containing a key and time.Time value. +var Time = logr.Time + +// Duration constructs a field containing a key and time.Duration value. +var Duration = logr.Duration + +// Millis constructs a field containing a key and timestamp value. +// The timestamp is expected to be milliseconds since Jan 1, 1970 UTC. +var Millis = logr.Millis + +// Array constructs a field containing a key and array value. +var Array = logr.Array + +// Map constructs a field containing a key and map value. +var Map = logr.Map + +// Logger provides a thin wrapper around a Logr instance. This is a struct instead of an interface +// so that there are no allocations on the heap each interface method invocation. Normally not +// something to be concerned about, but logging calls for disabled levels should have as little CPU +// and memory impact as possible. Most of these wrapper calls will be inlined as well. +type Logger struct { + log *logr.Logger + lockConfig *int32 +} + +// NewLogger creates a new Logger instance which can be configured via `(*Logger).Configure`. +// Some options with invalid values can cause an error to be returned, however `NewLogger()` +// using just defaults never errors. +func NewLogger(options ...Option) (*Logger, error) { + options = append(options, logr.StackFilter(logr.GetPackageName("NewLogger"))) + + lgr, err := logr.New(options...) + if err != nil { + return nil, err + } + + log := lgr.NewLogger() + var lockConfig int32 + + return &Logger{ + log: &log, + lockConfig: &lockConfig, + }, nil +} + +// Configure provides a new configuration for this logger. +// Zero or more sources of config can be provided: +// cfgFile - path to file containing JSON +// cfgEscaped - JSON string probably from ENV var +// +// For each case JSON containing log targets is provided. Target name collisions are resolved +// using the following precedence: +// cfgFile > cfgEscaped +func (l *Logger) Configure(cfgFile string, cfgEscaped string) error { + if atomic.LoadInt32(l.lockConfig) != 0 { + return ErrConfigurationLock + } + + cfgMap := make(LoggerConfiguration) + + // Add config from file + if cfgFile != "" { + b, err := ioutil.ReadFile(cfgFile) + if err != nil { + return fmt.Errorf("error reading logger config file %s: %w", cfgFile, err) + } + + var mapCfgFile LoggerConfiguration + if err := json.Unmarshal(b, &mapCfgFile); err != nil { + return fmt.Errorf("error decoding logger config file %s: %w", cfgFile, err) + } + cfgMap.Append(mapCfgFile) + } + + // Add config from escaped json string + if cfgEscaped != "" { + var mapCfgEscaped LoggerConfiguration + if err := json.Unmarshal([]byte(cfgEscaped), &mapCfgEscaped); err != nil { + return fmt.Errorf("error decoding logger config as escaped json: %w", err) + } + cfgMap.Append(mapCfgEscaped) + } + + if len(cfgMap) == 0 { + return nil + } + + return logrcfg.ConfigureTargets(l.log.Logr(), cfgMap.toTargetCfg(), nil) +} + +// ConfigureTargets provides a new configuration for this logger via a `LoggerConfig` map. +// Typically `mlog.Configure` is used instead which accepts JSON formatted configuration. +func (l *Logger) ConfigureTargets(cfg LoggerConfiguration) error { + if atomic.LoadInt32(l.lockConfig) != 0 { + return ErrConfigurationLock + } + return logrcfg.ConfigureTargets(l.log.Logr(), cfg.toTargetCfg(), nil) +} + +// LockConfiguration disallows further configuration changes until `UnlockConfiguration` +// is called. The previous locked stated is returned. +func (l *Logger) LockConfiguration() bool { + old := atomic.SwapInt32(l.lockConfig, 1) + return old != 0 +} + +// UnlockConfiguration allows configuration changes. The previous locked stated is returned. +func (l *Logger) UnlockConfiguration() bool { + old := atomic.SwapInt32(l.lockConfig, 0) + return old != 0 +} + +// IsConfigurationLocked returns the current state of the configuration lock. +func (l *Logger) IsConfigurationLocked() bool { + return atomic.LoadInt32(l.lockConfig) != 0 +} + +// With creates a new Logger with the specified fields. This is a light-weight +// operation and can be called on demand. +func (l *Logger) With(fields ...Field) *Logger { + logWith := l.log.With(fields...) + return &Logger{ + log: &logWith, + lockConfig: l.lockConfig, + } +} + +// IsLevelEnabled returns true only if at least one log target is +// configured to emit the specified log level. Use this check when +// gathering the log info may be expensive. +// +// Note, transformations and serializations done via fields are already +// lazily evaluated and don't require this check beforehand. +func (l *Logger) IsLevelEnabled(level Level) bool { + return l.log.IsLevelEnabled(level) +} + +// Log emits the log record for any targets configured for the specified level. +func (l *Logger) Log(level Level, msg string, fields ...Field) { + l.log.Log(level, msg, fields...) +} + +// LogM emits the log record for any targets configured for the specified levels. +// Equivalent to calling `Log` once for each level. +func (l *Logger) LogM(levels []Level, msg string, fields ...Field) { + l.log.LogM(levels, msg, fields...) +} + +// Convenience method equivalent to calling `Log` with the `Trace` level. +func (l *Logger) Trace(msg string, fields ...Field) { + l.log.Trace(msg, fields...) +} + +// Convenience method equivalent to calling `Log` with the `Debug` level. +func (l *Logger) Debug(msg string, fields ...Field) { + l.log.Debug(msg, fields...) +} + +// Convenience method equivalent to calling `Log` with the `Info` level. +func (l *Logger) Info(msg string, fields ...Field) { + l.log.Info(msg, fields...) +} + +// Convenience method equivalent to calling `Log` with the `Warn` level. +func (l *Logger) Warn(msg string, fields ...Field) { + l.log.Warn(msg, fields...) +} + +// Convenience method equivalent to calling `Log` with the `Error` level. +func (l *Logger) Error(msg string, fields ...Field) { + l.log.Error(msg, fields...) +} + +// Convenience method equivalent to calling `Log` with the `Critical` level. +func (l *Logger) Critical(msg string, fields ...Field) { + l.log.Log(LvlCritical, msg, fields...) +} + +// Convenience method equivalent to calling `Log` with the `Fatal` level, +// followed by `os.Exit(1)`. +func (l *Logger) Fatal(msg string, fields ...Field) { + l.log.Log(logr.Fatal, msg, fields...) + _ = l.Shutdown() + os.Exit(1) +} + +// HasTargets returns true if at least one log target has been added. +func (l *Logger) HasTargets() bool { + return l.log.Logr().HasTargets() +} + +// StdLogger creates a standard logger backed by this logger. +// All log records are output with the specified level. +func (l *Logger) StdLogger(level Level) *log.Logger { + return l.log.StdLogger(level) +} + +// StdLogWriter returns a writer that can be hooked up to the output of a golang standard logger +// anything written will be interpreted as log entries and passed to this logger. +func (l *Logger) StdLogWriter() io.Writer { + return &logWriter{ + logger: l, + } +} + +// RedirectStdLog redirects output from the standard library's package-global logger +// to this logger at the specified level and with zero or more Field's. Since this logger already +// handles caller annotations, timestamps, etc., it automatically disables the standard +// library's annotations and prefixing. +// A function is returned that restores the original prefix and flags and resets the standard +// library's output to os.Stdout. +func (l *Logger) RedirectStdLog(level Level, fields ...Field) func() { + return l.log.Logr().RedirectStdLog(level, fields...) +} + +// RemoveTargets safely removes one or more targets based on the filtering method. +// `f` should return true to delete the target, false to keep it. +// When removing a target, best effort is made to write any queued log records before +// closing, with cxt determining how much time can be spent in total. +// Note, keep the timeout short since this method blocks certain logging operations. +func (l *Logger) RemoveTargets(ctx context.Context, f func(ti TargetInfo) bool) error { + return l.log.Logr().RemoveTargets(ctx, f) +} + +// SetMetricsCollector sets (or resets) the metrics collector to be used for gathering +// metrics for all targets. Only targets added after this call will use the collector. +// +// To ensure all targets use a collector, use the `SetMetricsCollector` option when +// creating the Logger instead, or configure/reconfigure the Logger after calling this method. +func (l *Logger) SetMetricsCollector(collector MetricsCollector, updateFrequencyMillis int64) { + l.log.Logr().SetMetricsCollector(collector, updateFrequencyMillis) +} + +// Sugar creates a new `Logger` with a less structured API. Any fields are preserved. +func (l *Logger) Sugar(fields ...Field) Sugar { + return l.log.Sugar(fields...) +} + +// Flush forces all targets to write out any queued log records with a default timeout. +func (l *Logger) Flush() error { + ctx, cancel := context.WithTimeout(context.Background(), FlushTimeout) + defer cancel() + return l.log.Logr().FlushWithTimeout(ctx) +} + +// Flush forces all targets to write out any queued log records with the specfified timeout. +func (l *Logger) FlushWithTimeout(ctx context.Context) error { + return l.log.Logr().FlushWithTimeout(ctx) +} + +// Shutdown shuts down the logger after making best efforts to flush any +// remaining records. +func (l *Logger) Shutdown() error { + ctx, cancel := context.WithTimeout(context.Background(), ShutdownTimeout) + defer cancel() + return l.log.Logr().ShutdownWithTimeout(ctx) +} + +// Shutdown shuts down the logger after making best efforts to flush any +// remaining records. +func (l *Logger) ShutdownWithTimeout(ctx context.Context) error { + return l.log.Logr().ShutdownWithTimeout(ctx) +} + +// GetPackageName reduces a fully qualified function name to the package name +// By sirupsen: https://github.com/sirupsen/logrus/blob/master/entry.go +func GetPackageName(f string) string { + for { + lastPeriod := strings.LastIndex(f, ".") + lastSlash := strings.LastIndex(f, "/") + if lastPeriod > lastSlash { + f = f[:lastPeriod] + } else { + break + } + } + return f +} + +type logWriter struct { + logger *Logger +} + +func (lw *logWriter) Write(p []byte) (int, error) { + lw.logger.Info(string(p)) + return len(p), nil +} + +// ErrConfigurationLock is returned when one of a logger's configuration APIs is called +// while the configuration is locked. +var ErrConfigurationLock = errors.New("configuration is locked") diff --git a/shared/mlog/options.go b/shared/mlog/options.go new file mode 100644 index 0000000000..3a98b480b7 --- /dev/null +++ b/shared/mlog/options.go @@ -0,0 +1,55 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package mlog + +import "github.com/mattermost/logr/v2" + +// MaxQueueSize is the maximum number of log records that can be queued. +// If exceeded, `OnQueueFull` is called which determines if the log +// record will be dropped or block until add is successful. +// Defaults to DefaultMaxQueueSize. +func MaxQueueSize(size int) Option { + return logr.MaxQueueSize(size) +} + +// OnLoggerError, when not nil, is called any time an internal +// logging error occurs. For example, this can happen when a +// target cannot connect to its data sink. +func OnLoggerError(f func(error)) Option { + return logr.OnLoggerError(f) +} + +// OnQueueFull, when not nil, is called on an attempt to add +// a log record to a full Logr queue. +// `MaxQueueSize` can be used to modify the maximum queue size. +// This function should return quickly, with a bool indicating whether +// the log record should be dropped (true) or block until the log record +// is successfully added (false). If nil then blocking (false) is assumed. +func OnQueueFull(f func(rec *LogRec, maxQueueSize int) bool) Option { + return logr.OnQueueFull(f) +} + +// OnTargetQueueFull, when not nil, is called on an attempt to add +// a log record to a full target queue provided the target supports reporting +// this condition. +// This function should return quickly, with a bool indicating whether +// the log record should be dropped (true) or block until the log record +// is successfully added (false). If nil then blocking (false) is assumed. +func OnTargetQueueFull(f func(target Target, rec *LogRec, maxQueueSize int) bool) Option { + return logr.OnTargetQueueFull(f) +} + +// SetMetricsCollector enables metrics collection by supplying a MetricsCollector. +// The MetricsCollector provides counters and gauges that are updated by log targets. +// `updateFreqMillis` determines how often polled metrics are updated. Defaults to 15000 (15 seconds) +// and must be at least 250 so we don't peg the CPU. +func SetMetricsCollector(collector MetricsCollector, updateFreqMillis int64) Option { + return logr.SetMetricsCollector(collector, updateFreqMillis) +} + +// StackFilter provides a list of package names to exclude from the top of +// stack traces. The Logr packages are automatically filtered. +func StackFilter(pkg ...string) Option { + return logr.StackFilter(pkg...) +} diff --git a/shared/mlog/stdlog.go b/shared/mlog/stdlog.go deleted file mode 100644 index fd702abffa..0000000000 --- a/shared/mlog/stdlog.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "bytes" - "strings" - - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// Implementation of zapcore.Core to interpret log messages from a standard logger -// and translate the levels to zapcore levels. -type stdLogLevelInterpreterCore struct { - wrappedCore zapcore.Core -} - -func stdLogInterpretZapEntry(entry zapcore.Entry) zapcore.Entry { - message := entry.Message - if strings.Index(message, "[DEBUG]") == 0 { - entry.Level = zapcore.DebugLevel - entry.Message = message[7:] - } else if strings.Index(message, "[DEBG]") == 0 { - entry.Level = zapcore.DebugLevel - entry.Message = message[6:] - } else if strings.Index(message, "[WARN]") == 0 { - entry.Level = zapcore.WarnLevel - entry.Message = message[6:] - } else if strings.Index(message, "[ERROR]") == 0 { - entry.Level = zapcore.ErrorLevel - entry.Message = message[7:] - } else if strings.Index(message, "[EROR]") == 0 { - entry.Level = zapcore.ErrorLevel - entry.Message = message[6:] - } else if strings.Index(message, "[ERR]") == 0 { - entry.Level = zapcore.ErrorLevel - entry.Message = message[5:] - } else if strings.Index(message, "[INFO]") == 0 { - entry.Level = zapcore.InfoLevel - entry.Message = message[6:] - } - return entry -} - -func (s *stdLogLevelInterpreterCore) Enabled(lvl zapcore.Level) bool { - return s.wrappedCore.Enabled(lvl) -} - -func (s *stdLogLevelInterpreterCore) With(fields []zapcore.Field) zapcore.Core { - return s.wrappedCore.With(fields) -} - -func (s *stdLogLevelInterpreterCore) Check(entry zapcore.Entry, checkedEntry *zapcore.CheckedEntry) *zapcore.CheckedEntry { - entry = stdLogInterpretZapEntry(entry) - return s.wrappedCore.Check(entry, checkedEntry) -} - -func (s *stdLogLevelInterpreterCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { - entry = stdLogInterpretZapEntry(entry) - return s.wrappedCore.Write(entry, fields) -} - -func (s *stdLogLevelInterpreterCore) Sync() error { - return s.wrappedCore.Sync() -} - -func getStdLogOption() zap.Option { - return zap.WrapCore( - func(core zapcore.Core) zapcore.Core { - return &stdLogLevelInterpreterCore{core} - }, - ) -} - -type loggerWriter struct { - logFunc func(msg string, fields ...Field) -} - -func (l *loggerWriter) Write(p []byte) (int, error) { - trimmed := string(bytes.TrimSpace(p)) - for _, line := range strings.Split(trimmed, "\n") { - l.logFunc(line) - } - return len(p), nil -} diff --git a/shared/mlog/stdlog_test.go b/shared/mlog/stdlog_test.go deleted file mode 100644 index 139b5eab7f..0000000000 --- a/shared/mlog/stdlog_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "go.uber.org/zap/zapcore" -) - -func TestStdLogInterpretZapEntry(t *testing.T) { - for _, tc := range []struct { - testname string - message string - expectedMessage string - expectedLevel zapcore.Level - }{ - {"Debug Basic", "[DEBUG]My message", "My message", zapcore.DebugLevel}, - {"Debug Basic2", "[DEBG]My message", "My message", zapcore.DebugLevel}, - {"Warn Basic", "[WARN]My message", "My message", zapcore.WarnLevel}, - {"Error Basic", "[ERROR]My message", "My message", zapcore.ErrorLevel}, - {"Error Basic2", "[EROR]My message", "My message", zapcore.ErrorLevel}, - {"Error Basic3", "[ERR]My message", "My message", zapcore.ErrorLevel}, - {"Info Basic", "[INFO]My message", "My message", zapcore.InfoLevel}, - {"Unknown level", "[UNKNOWN]My message", "[UNKNOWN]My message", zapcore.PanicLevel}, - {"No level", "My message", "My message", zapcore.PanicLevel}, - {"Empty message", "", "", zapcore.PanicLevel}, - {"Malformed level", "INFO]My message", "INFO]My message", zapcore.PanicLevel}, - } { - t.Run(tc.testname, func(t *testing.T) { - inEntry := zapcore.Entry{ - Level: zapcore.PanicLevel, - Message: tc.message, - } - resultEntry := stdLogInterpretZapEntry(inEntry) - assert.Equal(t, tc.expectedMessage, resultEntry.Message) - assert.Equal(t, tc.expectedLevel, resultEntry.Level) - }) - } -} diff --git a/shared/mlog/sugar.go b/shared/mlog/sugar.go deleted file mode 100644 index 2368b085b7..0000000000 --- a/shared/mlog/sugar.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "go.uber.org/zap" -) - -// Made for the plugin interface, use the regular logger for other uses -type SugarLogger struct { - wrappedLogger *Logger - zapSugar *zap.SugaredLogger -} - -func (l *SugarLogger) Debug(msg string, keyValuePairs ...interface{}) { - l.zapSugar.Debugw(msg, keyValuePairs...) -} - -func (l *SugarLogger) Info(msg string, keyValuePairs ...interface{}) { - l.zapSugar.Infow(msg, keyValuePairs...) -} - -func (l *SugarLogger) Error(msg string, keyValuePairs ...interface{}) { - l.zapSugar.Errorw(msg, keyValuePairs...) -} - -func (l *SugarLogger) Warn(msg string, keyValuePairs ...interface{}) { - l.zapSugar.Warnw(msg, keyValuePairs...) -} diff --git a/shared/mlog/syslog.go b/shared/mlog/syslog.go deleted file mode 100644 index 8766a96476..0000000000 --- a/shared/mlog/syslog.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - - "github.com/mattermost/logr" - "github.com/wiggin77/merror" - syslog "github.com/wiggin77/srslog" -) - -// Syslog outputs log records to local or remote syslog. -type Syslog struct { - logr.Basic - w *syslog.Writer -} - -// SyslogParams provides parameters for dialing a syslog daemon. -type SyslogParams struct { - IP string `json:"IP"` - Port int `json:"Port"` - Tag string `json:"Tag"` - TLS bool `json:"TLS"` - Cert string `json:"Cert"` - Insecure bool `json:"Insecure"` -} - -// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS. -func NewSyslogTarget(filter logr.Filter, formatter logr.Formatter, params *SyslogParams, maxQueue int) (*Syslog, error) { - network := "tcp" - var config *tls.Config - - if params.TLS { - network = "tcp+tls" - config = &tls.Config{InsecureSkipVerify: params.Insecure} - if params.Cert != "" { - pool, err := getCertPool(params.Cert) - if err != nil { - return nil, err - } - config.RootCAs = pool - } - } - raddr := fmt.Sprintf("%s:%d", params.IP, params.Port) - - writer, err := syslog.DialWithTLSConfig(network, raddr, syslog.LOG_INFO, params.Tag, config) - if err != nil { - return nil, err - } - - s := &Syslog{w: writer} - s.Basic.Start(s, s, filter, formatter, maxQueue) - - return s, nil -} - -// Shutdown stops processing log records after making best effort to flush queue. -func (s *Syslog) Shutdown(ctx context.Context) error { - errs := merror.New() - - err := s.Basic.Shutdown(ctx) - errs.Append(err) - - err = s.w.Close() - errs.Append(err) - - return errs.ErrorOrNil() -} - -// getCertPool returns a x509.CertPool containing the cert(s) -// from `cert`, which can be a path to a .pem or .crt file, -// or a base64 encoded cert. -func getCertPool(cert string) (*x509.CertPool, error) { - if cert == "" { - return nil, errors.New("no cert provided") - } - - // first treat as a file and try to read. - serverCert, err := ioutil.ReadFile(cert) - if err != nil { - // maybe it's a base64 encoded cert - serverCert, err = base64.StdEncoding.DecodeString(cert) - if err != nil { - return nil, errors.New("cert cannot be read") - } - } - - pool := x509.NewCertPool() - if ok := pool.AppendCertsFromPEM(serverCert); ok { - return pool, nil - } - return nil, errors.New("cannot parse cert") -} - -// Write converts the log record to bytes, via the Formatter, -// and outputs to syslog. -func (s *Syslog) Write(rec *logr.LogRec) error { - _, stacktrace := s.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := s.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - txt := buf.String() - - switch rec.Level() { - case logr.Panic, logr.Fatal: - err = s.w.Crit(txt) - case logr.Error: - err = s.w.Err(txt) - case logr.Warn: - err = s.w.Warning(txt) - case logr.Debug, logr.Trace: - err = s.w.Debug(txt) - default: - // logr.Info plus all custom levels. - err = s.w.Info(txt) - } - - if err != nil { - reporter := rec.Logger().Logr().ReportError - reporter(fmt.Errorf("syslog write fail: %w", err)) - // syslog writer will try to reconnect. - } - return err -} - -// String returns a string representation of this target. -func (s *Syslog) String() string { - return "SyslogTarget" -} diff --git a/shared/mlog/syslog_test.go b/shared/mlog/syslog_test.go deleted file mode 100644 index d60bdc0721..0000000000 --- a/shared/mlog/syslog_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_getCertPool(t *testing.T) { - tests := []struct { - name string - cert string - wantErr bool - }{ - {name: "garbage in, garbage out", wantErr: true, cert: "THISISNOTACERT"}, - {name: "good cert base64", wantErr: false, cert: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURqekNDQW5lZ0F3SUJBZ0lSQVBZZlJTd2R6S29wQkt4WXhLcXNsSlV3RFFZSktvWklodmNOQVFFTEJRQXcKSnpFbE1DTUdBMVVFQXd3Y1RXRjBkR1Z5Ylc5emRDd2dTVzVqTGlCSmJuUmxjbTVoYkNCRFFUQWVGdzB4T1RBegpNakl3TURFME1UVmFGdzB5TWpBek1EWXdNREUwTVRWYU1Ec3hPVEEzQmdOVkJBTVRNRTFoZEhSbGNtMXZjM1FzCklFbHVZeTRnU1c1MFpYSnVZV3dnU1c1MFpYSnRaV1JwWVhSbElFRjFkR2h2Y21sMGVUQ0NBU0l3RFFZSktvWkkKaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNamxpUmRtdm5OTDR1L0pyL00yZFB3UW1USlhFQlkvVnE5UQp2QVU1MlgzdFJNQ1B4Y2FGeit4NmZ0dXZkTzJOZG9oWEdBbXR4OVFVNUxaY3ZGZVREcG9WRUJvOUErNGp0THZECkRaWWFUTkxwSm1vU29KSGFEYmRXWCtPQU9xeURpV1M3NDFMdWlNS1dIaGV3OVFPaXNhdDJaSU5QeGptQWQ5d0UKeHRoVE1nenN2N01VcW5NZXI4VTVPR1EwUXk3d0FtTlJjKzJLM3FQd2t4ZTJSVXZjdGU1MERVRk5neEVnaW5zaAp2cmtPWFIzODN2VUNaZnU3MnF1OG9nZ2ppUXB5VGxsdTVqZTJBcDZKTGpZTGtFTWlNcXJZQUR1V29yL1pId2E2CldyRnFWRVR4V2ZBVjV1OUVoMHdaTS9LS1l3UlF1dzl5K05hbnM3N0ZtVWwxdFZXV05OOENBd0VBQWFPQm9UQ0IKbmpBTUJnTlZIUk1FQlRBREFRSC9NQjBHQTFVZERnUVdCQlFZNFVxc3d5cjJoTy9IZXRadDJSRHhKZFRJUGpCaQpCZ05WSFNNRVd6QlpnQlJGWlhWZzJaNXROSXNXZVdqQkxFeTJ5ektiTUtFcnBDa3dKekVsTUNNR0ExVUVBd3djClRXRjBkR1Z5Ylc5emRDd2dTVzVqTGlCSmJuUmxjbTVoYkNCRFFZSVVFaWZHVU9NK2JJRlpvMXRralpCNVlHQnIKMHhFd0N3WURWUjBQQkFRREFnRUdNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUFFZGV4TDMwUTB6QkhtUEFIOApMaGRLN2RielcxQ21JTGJ4UlpsS0F3Uk4raEtSWGlNVzNNSElraE51b1Y5QWV2NjAyUStqYTRsV3NSaS9rdE9MCm5pMUZXeDVnU1NjZ2RHOEpHajQ3ZE9tb1QzdlhLWDcrdW1pdjRyUUxQRGw5L0RLTXV2MjA0T1lKcTZWVCt1TlUKNkM2a0wxNTdqR0pFTzc2SDRmTVo4b1lzRDdTcTB6amlOS3R1Q1lpaTBuZ0gzajNnQjFqQUNMcVJndmVVN01kVApwcU9WMktmWTMxK2g4VkJ0a1V2bGpOenRROXhOWThGam10MFNNZjdFM0ZhVWNhYXIzWkNyNzBHNWFVM2RLYmU3CjQ3dkdPQmE1dENxdzRZSzBqZ0RLaWQzSUpRdWw5YTNKMW1Tc0g4V3kzdG85Y0FWNEtHWkJRTG56Q1gxNWEvK3YKM3lWaAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tIAotLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KTUlJRGZqQ0NBbWFnQXdJQkFnSVVFaWZHVU9NK2JJRlpvMXRralpCNVlHQnIweEV3RFFZSktvWklodmNOQVFFTApCUUF3SnpFbE1DTUdBMVVFQXd3Y1RXRjBkR1Z5Ylc5emRDd2dTVzVqTGlCSmJuUmxjbTVoYkNCRFFUQWVGdzB4Ck9UQXpNakV5TVRJNE5ETmFGdzB5T1RBek1UZ3lNVEk0TkROYU1DY3hKVEFqQmdOVkJBTU1IRTFoZEhSbGNtMXYKYzNRc0lFbHVZeTRnU1c1MFpYSnVZV3dnUTBFd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFSwpBb0lCQVFESDBYcTVyTUJHcEtPVldUcGI1TW5hSklXRlAvdk90dkVrKzdoVnJmT2ZlMS81eDBLazNVZ0FIajg1Cm90YUVaRDFMaG4vSkxrRXFDaUUvVVhNSkZ3SkRsTmNPNENrZEtCU3BZWDRiS0FxeTVxL1gzUXdpb01TTnBKRzEKK1lZck5HQkgwc2dLY0tqeUNhTGhtcVlMRDB4WkRWT21XSVlCVTlqVVB5WHc1VTB0bnNWclRxR014VmttMXhDWQprckNXTjFab1VyTHZMME1DWmM1cXB4b1BUb3ByOVVPOWNxU0JTdXk2QlZXVnVFV0JaaHBxSHQrdWw4VnhoenpZCnExazRsN3IycXcrL3dtMWlKQmVkVGVCVmVXTmFnOEphVmZMZ3UrL1c3b0pWbFBPMzJQbzdwbnZIcDhpSjNiNEsKelh5VkhhVFg0UzZFbSs2TFY4ODU1VFlyU2h6bEFnTUJBQUdqZ2FFd2daNHdIUVlEVlIwT0JCWUVGRVZsZFdEWgpubTAwaXhaNWFNRXNUTGJMTXBzd01HSUdBMVVkSXdSYk1GbUFGRVZsZFdEWm5tMDBpeFo1YU1Fc1RMYkxNcHN3Cm9TdWtLVEFuTVNVd0l3WURWUVFEREJ4TllYUjBaWEp0YjNOMExDQkpibU11SUVsdWRHVnlibUZzSUVOQmdoUVMKSjhaUTR6NXNnVm1qVzJTTmtIbGdZR3ZURVRBTUJnTlZIUk1FQlRBREFRSC9NQXNHQTFVZER3UUVBd0lCQmpBTgpCZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFQaUNXRm1vcHlBa1kyVDNaeW80eWFSUGhYMStWT1RNS0p0WTZFVWhxCi9HSHo2a3pFeXZDVUJmME44OTJjaWJHeGVrckVvSXRZOU5xTzZSUVJmb3dnK0duNWtjMTN6NE55TDJXOC9lb1QKWHkwWnZmYVFiVSsrZlE2cFZ0V3RNYmxETVU5eGlZZDcvTUR2SnBPMzI4bDFWaGNkcDhrRWkrbEN2cHkwc0NSYwpQeHpQaGJnQ01BYlpFR3grNFRNUWQ0U1pLemxSeFcvMmZmbHBSZWg2djFEdjBWRFVTWVFXd3NVbmFMcGRLSGZoCmE1azB2dXlTWWNzekU0WUtsWTB6YWtlRmxKZnA3ZkJwMXhUd2NkVzhhVGZ3MTVFaWNQTXdUYzZ4eEE0SkpVSngKY2RkdTgxN24xbmF5SzV1NnI5UWgxb0lWa3IwbkM5WUVMTU15NGRwUGdKODhTQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"}, - {name: "good cert file", wantErr: false, cert: "test-tls-client-cert.pem"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - pool, err := getCertPool(tt.cert) - if tt.wantErr { - assert.Error(t, err) - assert.Nil(t, pool) - } else { - assert.NoError(t, err) - assert.NotNil(t, pool) - - // Test PEM has 2 certs. - subjects := pool.Subjects() - assert.Len(t, subjects, 2) - } - }) - } -} diff --git a/shared/mlog/tcp_test.go b/shared/mlog/tcp_test.go deleted file mode 100644 index 99fe2274f7..0000000000 --- a/shared/mlog/tcp_test.go +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "bytes" - "errors" - "fmt" - "io" - "net" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/require" - "github.com/wiggin77/merror" -) - -const ( - testPort = 18066 -) - -func TestNewTCPTarget(t *testing.T) { - target := LogTarget{ - Type: "tcp", - Format: "json", - Levels: []LogLevel{LvlInfo}, - Options: []byte(`{"IP": "localhost", "Port": 18066}`), - MaxQueueSize: 1000, - } - targets := map[string]*LogTarget{"tcp_test": &target} - - t.Run("logging", func(t *testing.T) { - buf := &buffer{} - server, err := newSocketServer(testPort, buf) - require.NoError(t, err) - - data := []string{"I drink your milkshake!", "We don't need no badges!", "You can't fight in here! This is the war room!"} - - logger := newLogr() - err = logrAddTargets(logger, targets) - require.NoError(t, err) - - for _, s := range data { - logger.Info(s) - } - err = logger.Logr().Flush() - require.NoError(t, err) - err = logger.Logr().Shutdown() - require.NoError(t, err) - - err = server.waitForAnyConnection() - require.NoError(t, err) - - err = server.stopServer(true) - require.NoError(t, err) - - sdata := buf.String() - for _, s := range data { - require.Contains(t, sdata, s) - } - }) -} - -// socketServer is a simple socket server used for testing TCP log targets. -// Note: There is more synchronization here than normally needed to avoid flaky tests. -// For example, it's possible for a unit test to create a socketServer, attempt -// writing to it, and stop the socket server before "go ss.listen()" gets scheduled. -type socketServer struct { - listener net.Listener - anyConn chan struct{} - buf *buffer - conns map[string]*socketServerConn - mux sync.Mutex -} - -type socketServerConn struct { - raddy string - conn net.Conn - done chan struct{} -} - -func newSocketServer(port int, buf *buffer) (*socketServer, error) { - ss := &socketServer{ - buf: buf, - conns: make(map[string]*socketServerConn), - anyConn: make(chan struct{}), - } - - addy := fmt.Sprintf(":%d", port) - l, err := net.Listen("tcp4", addy) - if err != nil { - return nil, err - } - ss.listener = l - - go ss.listen() - return ss, nil -} - -func (ss *socketServer) listen() { - for { - conn, err := ss.listener.Accept() - if err != nil { - return - } - sconn := &socketServerConn{raddy: conn.RemoteAddr().String(), conn: conn, done: make(chan struct{})} - ss.registerConnection(sconn) - go ss.handleConnection(sconn) - } -} - -func (ss *socketServer) waitForAnyConnection() error { - var err error - select { - case <-ss.anyConn: - case <-time.After(5 * time.Second): - err = errors.New("wait for any connection timed out") - } - return err -} - -func (ss *socketServer) handleConnection(sconn *socketServerConn) { - close(ss.anyConn) - defer ss.unregisterConnection(sconn) - buf := make([]byte, 1024) - - for { - n, err := sconn.conn.Read(buf) - if n > 0 { - ss.buf.Write(buf[:n]) - } - if err == io.EOF { - ss.signalDone(sconn) - return - } - } -} - -func (ss *socketServer) registerConnection(sconn *socketServerConn) { - ss.mux.Lock() - defer ss.mux.Unlock() - ss.conns[sconn.raddy] = sconn -} - -func (ss *socketServer) unregisterConnection(sconn *socketServerConn) { - ss.mux.Lock() - defer ss.mux.Unlock() - delete(ss.conns, sconn.raddy) -} - -func (ss *socketServer) signalDone(sconn *socketServerConn) { - ss.mux.Lock() - defer ss.mux.Unlock() - close(sconn.done) -} - -func (ss *socketServer) stopServer(wait bool) error { - errs := merror.New() - ss.listener.Close() - - ss.mux.Lock() - // defensive copy; no more connections can be accepted so copy will stay current. - conns := make(map[string]*socketServerConn, len(ss.conns)) - for k, v := range ss.conns { - conns[k] = v - } - ss.mux.Unlock() - - for _, sconn := range conns { - if wait { - select { - case <-sconn.done: - case <-time.After(time.Second * 5): - errs.Append(errors.New("timed out")) - } - } - } - return errs.ErrorOrNil() -} - -type buffer struct { - buf bytes.Buffer - mux sync.Mutex -} - -func (b *buffer) Write(p []byte) (n int, err error) { - b.mux.Lock() - defer b.mux.Unlock() - return b.buf.Write(p) -} - -func (b *buffer) String() string { - b.mux.Lock() - defer b.mux.Unlock() - return b.buf.String() -} diff --git a/shared/mlog/testing.go b/shared/mlog/testing.go deleted file mode 100644 index 6b41a7e4fb..0000000000 --- a/shared/mlog/testing.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package mlog - -import ( - "io" - "strings" - "sync" - "testing" - - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// testingWriter is an io.Writer that writes through t.Log -type testingWriter struct { - tb testing.TB -} - -func (tw *testingWriter) Write(b []byte) (int, error) { - tw.tb.Log(strings.TrimSpace(string(b))) - return len(b), nil -} - -// NewTestingLogger creates a Logger that proxies logs through a testing interface. -// This allows tests that spin up App instances to avoid spewing logs unless the test fails or -verbose is specified. -func NewTestingLogger(tb testing.TB, writer io.Writer) *Logger { - logWriter := &testingWriter{tb} - multiWriter := io.MultiWriter(logWriter, writer) - logWriterSync := zapcore.AddSync(multiWriter) - - testingLogger := &Logger{ - consoleLevel: zap.NewAtomicLevelAt(getZapLevel("debug")), - fileLevel: zap.NewAtomicLevelAt(getZapLevel("info")), - logrLogger: newLogr(), - mutex: &sync.RWMutex{}, - } - - logWriterCore := zapcore.NewCore(makeEncoder(true, false), zapcore.Lock(logWriterSync), testingLogger.consoleLevel) - - testingLogger.zap = zap.New(logWriterCore, - zap.AddCaller(), - ) - return testingLogger -} diff --git a/shared/mlog/tlog.go b/shared/mlog/tlog.go new file mode 100644 index 0000000000..89efe303cd --- /dev/null +++ b/shared/mlog/tlog.go @@ -0,0 +1,141 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package mlog + +import ( + "bytes" + "io" + "os" + "strings" + "sync" + "testing" + + "github.com/mattermost/logr/v2" + "github.com/mattermost/logr/v2/formatters" + "github.com/mattermost/logr/v2/targets" +) + +// CreateTestLogger creates a logger for unit tests, using the `TB.Log` +func CreateTestLogger(tb testing.TB, writer io.Writer, levels ...Level) *Logger { + logger, _ := NewLogger() + + filter := logr.NewCustomFilter(levels...) + formatter := &formatters.Plain{} + + if tb != nil { + testtarget := newTestingTarget(tb) + if err := logger.log.Logr().AddTarget(testtarget, "_testTB", filter, formatter, 1000); err != nil { + tb.Fail() + return nil + } + } + + if writer != nil { + target := targets.NewWriterTarget(writer) + if err := logger.log.Logr().AddTarget(target, "_testWriter", filter, formatter, 1000); err != nil { + tb.Fail() + return nil + } + } + return logger +} + +func AddWriterTarget(logger *Logger, w io.Writer, useJSON bool, levels ...Level) error { + filter := logr.NewCustomFilter(levels...) + + var formatter logr.Formatter + if useJSON { + formatter = &formatters.JSON{EnableCaller: true} + } else { + formatter = &formatters.Plain{EnableCaller: true} + } + + target := targets.NewWriterTarget(w) + return logger.log.Logr().AddTarget(target, "_testWriter", filter, formatter, 1000) +} + +// CreateConsoleTestLogger creates a logger for unit tests. Log records are output to `os.Stdout`. +// Logs can also be mirrored to the optional `io.Writer`. +func CreateConsoleTestLogger(useJSON bool, level Level) *Logger { + logger, _ := NewLogger() + + filter := logr.StdFilter{ + Lvl: level, + Stacktrace: LvlPanic, + } + + var formatter logr.Formatter + if useJSON { + formatter = &formatters.JSON{EnableCaller: true} + } else { + formatter = &formatters.Plain{EnableCaller: true} + } + + target := targets.NewWriterTarget(os.Stdout) + if err := logger.log.Logr().AddTarget(target, "_testcon", filter, formatter, 1000); err != nil { + panic(err) + } + return logger +} + +// testingTarget is a simple log target that writes to the testing log. +type testingTarget struct { + mux sync.Mutex + tb testing.TB +} + +func newTestingTarget(tb testing.TB) *testingTarget { + return &testingTarget{ + tb: tb, + } +} + +// Init is called once to initialize the target. +func (tt *testingTarget) Init() error { + return nil +} + +// Write outputs bytes to this file target. +func (tt *testingTarget) Write(p []byte, rec *logr.LogRec) (int, error) { + tt.mux.Lock() + defer tt.mux.Unlock() + + if tt.tb != nil { + tt.tb.Helper() + tt.tb.Log(strings.TrimSpace(string(p))) + } + return len(p), nil +} + +// Shutdown is called once to free/close any resources. +// Target queue is already drained when this is called. +func (tt *testingTarget) Shutdown() error { + tt.mux.Lock() + defer tt.mux.Unlock() + + tt.tb = nil + return nil +} + +// Buffer provides a thread-safe buffer useful for logging to memory in unit tests. +type Buffer struct { + buf bytes.Buffer + mux sync.Mutex +} + +func (b *Buffer) Read(p []byte) (n int, err error) { + b.mux.Lock() + defer b.mux.Unlock() + return b.buf.Read(p) +} +func (b *Buffer) Write(p []byte) (n int, err error) { + b.mux.Lock() + defer b.mux.Unlock() + return b.buf.Write(p) +} +func (b *Buffer) String() string { + b.mux.Lock() + defer b.mux.Unlock() + return b.buf.String() +} diff --git a/store/localcachelayer/main_test.go b/store/localcachelayer/main_test.go index d492bb3f92..ed82cb3d44 100644 --- a/store/localcachelayer/main_test.go +++ b/store/localcachelayer/main_test.go @@ -13,7 +13,6 @@ import ( "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/services/cache" cachemocks "github.com/mattermost/mattermost-server/v6/services/cache/mocks" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/store" "github.com/mattermost/mattermost-server/v6/store/sqlstore" "github.com/mattermost/mattermost-server/v6/store/storetest/mocks" @@ -172,7 +171,6 @@ func getMockStore() *mocks.Store { } func TestMain(m *testing.M) { - mlog.DisableZap() mainHelper = testlib.NewMainHelperWithOptions(nil) defer mainHelper.Close() diff --git a/store/sqlstore/main_test.go b/store/sqlstore/main_test.go index a47adda458..fe6b4c386a 100644 --- a/store/sqlstore/main_test.go +++ b/store/sqlstore/main_test.go @@ -6,7 +6,6 @@ package sqlstore_test import ( "testing" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/store/sqlstore" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -14,7 +13,6 @@ import ( var mainHelper *testlib.MainHelper func TestMain(m *testing.M) { - mlog.DisableZap() mainHelper = testlib.NewMainHelperWithOptions(nil) defer mainHelper.Close() diff --git a/testlib/assertions.go b/testlib/assertions.go index cb2ab5ce30..5b6afd7a97 100644 --- a/testlib/assertions.go +++ b/testlib/assertions.go @@ -4,14 +4,13 @@ package testlib import ( - "bytes" "encoding/json" "io" "testing" ) // AssertLog asserts that a JSON-encoded buffer of logs contains one with the given level and message. -func AssertLog(t *testing.T, logs *bytes.Buffer, level, message string) { +func AssertLog(t *testing.T, logs io.Reader, level, message string) { dec := json.NewDecoder(logs) for { var log struct { @@ -33,7 +32,7 @@ func AssertLog(t *testing.T, logs *bytes.Buffer, level, message string) { } // AssertNoLog asserts that a JSON-encoded buffer of logs does not contains one with the given level and message. -func AssertNoLog(t *testing.T, logs *bytes.Buffer, level, message string) { +func AssertNoLog(t *testing.T, logs io.Reader, level, message string) { dec := json.NewDecoder(logs) for { var log struct { diff --git a/testlib/helper.go b/testlib/helper.go index c8d53ace46..31c7b279c8 100644 --- a/testlib/helper.go +++ b/testlib/helper.go @@ -16,7 +16,6 @@ import ( "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/services/searchengine" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/store" "github.com/mattermost/mattermost-server/v6/store/searchlayer" "github.com/mattermost/mattermost-server/v6/store/sqlstore" @@ -53,16 +52,6 @@ func NewMainHelperWithOptions(options *HelperOptions) *MainHelper { var mainHelper MainHelper flag.Parse() - // Setup a global logger to catch tests logging outside of app context - // The global logger will be stomped by apps initializing but that's fine for testing. - // Ideally this won't happen. - mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{ - EnableConsole: true, - ConsoleJson: true, - ConsoleLevel: "error", - EnableFile: false, - })) - utils.TranslationsPreInit() if options != nil { diff --git a/utils/logger.go b/utils/logger.go deleted file mode 100644 index 0de3fdb240..0000000000 --- a/utils/logger.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package utils - -import ( - "path/filepath" - "strings" - - "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/shared/mlog" - "github.com/mattermost/mattermost-server/v6/utils/fileutils" -) - -const ( - LogRotateSize = 10000 - LogFilename = "mattermost.log" - LogNotificationFilename = "notifications.log" -) - -type fileLocationFunc func(string) string - -func MloggerConfigFromLoggerConfig(s *model.LogSettings, getFileFunc fileLocationFunc) *mlog.LoggerConfiguration { - return &mlog.LoggerConfiguration{ - EnableConsole: *s.EnableConsole, - ConsoleJson: *s.ConsoleJson, - ConsoleLevel: strings.ToLower(*s.ConsoleLevel), - EnableFile: *s.EnableFile, - FileJson: *s.FileJson, - FileLevel: strings.ToLower(*s.FileLevel), - FileLocation: getFileFunc(*s.FileLocation), - EnableColor: *s.EnableColor, - } -} - -func GetLogFileLocation(fileLocation string) string { - if fileLocation == "" { - fileLocation, _ = fileutils.FindDir("logs") - } - - return filepath.Join(fileLocation, LogFilename) -} - -func GetNotificationsLogFileLocation(fileLocation string) string { - if fileLocation == "" { - fileLocation, _ = fileutils.FindDir("logs") - } - - return filepath.Join(fileLocation, LogNotificationFilename) -} - -func GetLogSettingsFromNotificationsLogSettings(notificationLogSettings *model.NotificationLogSettings) *model.LogSettings { - return &model.LogSettings{ - ConsoleJson: notificationLogSettings.ConsoleJson, - ConsoleLevel: notificationLogSettings.ConsoleLevel, - EnableConsole: notificationLogSettings.EnableConsole, - EnableFile: notificationLogSettings.EnableFile, - FileJson: notificationLogSettings.FileJson, - FileLevel: notificationLogSettings.FileLevel, - FileLocation: notificationLogSettings.FileLocation, - AdvancedLoggingConfig: notificationLogSettings.AdvancedLoggingConfig, - EnableColor: notificationLogSettings.EnableColor, - } -} - -// DON'T USE THIS Modify the level on the app logger -func DisableDebugLogForTest() { - mlog.GloballyDisableDebugLogForTest() -} - -// DON'T USE THIS Modify the level on the app logger -func EnableDebugLogForTest() { - mlog.GloballyEnableDebugLogForTest() -} diff --git a/vendor/github.com/mattermost/logr/config.go b/vendor/github.com/mattermost/logr/config.go deleted file mode 100644 index 83d4b0c1c1..0000000000 --- a/vendor/github.com/mattermost/logr/config.go +++ /dev/null @@ -1,11 +0,0 @@ -package logr - -import ( - "fmt" - - "github.com/wiggin77/cfg" -) - -func ConfigLogger(config *cfg.Config) error { - return fmt.Errorf("Not implemented yet") -} diff --git a/vendor/github.com/mattermost/logr/filter.go b/vendor/github.com/mattermost/logr/filter.go deleted file mode 100644 index 6e654cd7b2..0000000000 --- a/vendor/github.com/mattermost/logr/filter.go +++ /dev/null @@ -1,26 +0,0 @@ -package logr - -// LevelID is the unique id of each level. -type LevelID uint - -// Level provides a mechanism to enable/disable specific log lines. -type Level struct { - ID LevelID - Name string - Stacktrace bool -} - -// String returns the name of this level. -func (level Level) String() string { - return level.Name -} - -// Filter allows targets to determine which Level(s) are active -// for logging and which Level(s) require a stack trace to be output. -// A default implementation using "panic, fatal..." is provided, and -// a more flexible alternative implementation is also provided that -// allows any number of custom levels. -type Filter interface { - IsEnabled(Level) bool - IsStacktraceEnabled(Level) bool -} diff --git a/vendor/github.com/mattermost/logr/format/json.go b/vendor/github.com/mattermost/logr/format/json.go deleted file mode 100644 index 8f56c6cb38..0000000000 --- a/vendor/github.com/mattermost/logr/format/json.go +++ /dev/null @@ -1,273 +0,0 @@ -package format - -import ( - "bytes" - "fmt" - "runtime" - "sort" - "sync" - "time" - - "github.com/francoispqt/gojay" - "github.com/mattermost/logr" -) - -// ContextField is a name/value pair within the context fields. -type ContextField struct { - Key string - Val interface{} -} - -// JSON formats log records as JSON. -type JSON struct { - // DisableTimestamp disables output of timestamp field. - DisableTimestamp bool - // DisableLevel disables output of level field. - DisableLevel bool - // DisableMsg disables output of msg field. - DisableMsg bool - // DisableContext disables output of all context fields. - DisableContext bool - // DisableStacktrace disables output of stack trace. - DisableStacktrace bool - - // TimestampFormat is an optional format for timestamps. If empty - // then DefTimestampFormat is used. - TimestampFormat string - - // Deprecated: this has no effect. - Indent string - - // EscapeHTML determines if certain characters (e.g. `<`, `>`, `&`) - // are escaped. - EscapeHTML bool - - // KeyTimestamp overrides the timestamp field key name. - KeyTimestamp string - - // KeyLevel overrides the level field key name. - KeyLevel string - - // KeyMsg overrides the msg field key name. - KeyMsg string - - // KeyContextFields when not empty will group all context fields - // under this key. - KeyContextFields string - - // KeyStacktrace overrides the stacktrace field key name. - KeyStacktrace string - - // ContextSorter allows custom sorting for the context fields. - ContextSorter func(fields logr.Fields) []ContextField - - once sync.Once -} - -// Format converts a log record to bytes in JSON format. -func (j *JSON) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) { - j.once.Do(j.applyDefaultKeyNames) - - if buf == nil { - buf = &bytes.Buffer{} - } - enc := gojay.BorrowEncoder(buf) - defer func() { - enc.Release() - }() - - sorter := j.ContextSorter - if sorter == nil { - sorter = j.defaultContextSorter - } - - jlr := JSONLogRec{ - LogRec: rec, - JSON: j, - stacktrace: stacktrace, - sorter: sorter, - } - - err := enc.EncodeObject(jlr) - if err != nil { - return nil, err - } - buf.WriteByte('\n') - return buf, nil -} - -func (j *JSON) applyDefaultKeyNames() { - if j.KeyTimestamp == "" { - j.KeyTimestamp = "timestamp" - } - if j.KeyLevel == "" { - j.KeyLevel = "level" - } - if j.KeyMsg == "" { - j.KeyMsg = "msg" - } - if j.KeyStacktrace == "" { - j.KeyStacktrace = "stacktrace" - } -} - -// defaultContextSorter sorts the context fields alphabetically by key. -func (j *JSON) defaultContextSorter(fields logr.Fields) []ContextField { - keys := make([]string, 0, len(fields)) - for k := range fields { - keys = append(keys, k) - } - sort.Strings(keys) - - cf := make([]ContextField, 0, len(keys)) - for _, k := range keys { - cf = append(cf, ContextField{Key: k, Val: fields[k]}) - } - return cf -} - -// JSONLogRec decorates a LogRec adding JSON encoding. -type JSONLogRec struct { - *logr.LogRec - *JSON - stacktrace bool - sorter func(fields logr.Fields) []ContextField -} - -// MarshalJSONObject encodes the LogRec as JSON. -func (rec JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) { - if !rec.DisableTimestamp { - timestampFmt := rec.TimestampFormat - if timestampFmt == "" { - timestampFmt = logr.DefTimestampFormat - } - time := rec.Time() - enc.AddTimeKey(rec.KeyTimestamp, &time, timestampFmt) - } - if !rec.DisableLevel { - enc.AddStringKey(rec.KeyLevel, rec.Level().Name) - } - if !rec.DisableMsg { - enc.AddStringKey(rec.KeyMsg, rec.Msg()) - } - if !rec.DisableContext { - ctxFields := rec.sorter(rec.Fields()) - if rec.KeyContextFields != "" { - enc.AddObjectKey(rec.KeyContextFields, jsonFields(ctxFields)) - } else { - if len(ctxFields) > 0 { - for _, cf := range ctxFields { - key := rec.prefixCollision(cf.Key) - encodeField(enc, key, cf.Val) - } - } - } - } - if rec.stacktrace && !rec.DisableStacktrace { - frames := rec.StackFrames() - if len(frames) > 0 { - enc.AddArrayKey(rec.KeyStacktrace, stackFrames(frames)) - } - } - -} - -// IsNil returns true if the LogRec pointer is nil. -func (rec JSONLogRec) IsNil() bool { - return rec.LogRec == nil -} - -func (rec JSONLogRec) prefixCollision(key string) string { - switch key { - case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace: - return rec.prefixCollision("_" + key) - } - return key -} - -type stackFrames []runtime.Frame - -// MarshalJSONArray encodes stackFrames slice as JSON. -func (s stackFrames) MarshalJSONArray(enc *gojay.Encoder) { - for _, frame := range s { - enc.AddObject(stackFrame(frame)) - } -} - -// IsNil returns true if stackFrames is empty slice. -func (s stackFrames) IsNil() bool { - return len(s) == 0 -} - -type stackFrame runtime.Frame - -// MarshalJSONArray encodes stackFrame as JSON. -func (f stackFrame) MarshalJSONObject(enc *gojay.Encoder) { - enc.AddStringKey("Function", f.Function) - enc.AddStringKey("File", f.File) - enc.AddIntKey("Line", f.Line) -} - -func (f stackFrame) IsNil() bool { - return false -} - -type jsonFields []ContextField - -// MarshalJSONObject encodes Fields map to JSON. -func (f jsonFields) MarshalJSONObject(enc *gojay.Encoder) { - for _, ctxField := range f { - encodeField(enc, ctxField.Key, ctxField.Val) - } -} - -// IsNil returns true if map is nil. -func (f jsonFields) IsNil() bool { - return f == nil -} - -func encodeField(enc *gojay.Encoder, key string, val interface{}) { - switch vt := val.(type) { - case gojay.MarshalerJSONObject: - enc.AddObjectKey(key, vt) - case gojay.MarshalerJSONArray: - enc.AddArrayKey(key, vt) - case string: - enc.AddStringKey(key, vt) - case error: - enc.AddStringKey(key, vt.Error()) - case bool: - enc.AddBoolKey(key, vt) - case int: - enc.AddIntKey(key, vt) - case int64: - enc.AddInt64Key(key, vt) - case int32: - enc.AddIntKey(key, int(vt)) - case int16: - enc.AddIntKey(key, int(vt)) - case int8: - enc.AddIntKey(key, int(vt)) - case uint64: - enc.AddIntKey(key, int(vt)) - case uint32: - enc.AddIntKey(key, int(vt)) - case uint16: - enc.AddIntKey(key, int(vt)) - case uint8: - enc.AddIntKey(key, int(vt)) - case float64: - enc.AddFloatKey(key, vt) - case float32: - enc.AddFloat32Key(key, vt) - case *gojay.EmbeddedJSON: - enc.AddEmbeddedJSONKey(key, vt) - case time.Time: - enc.AddTimeKey(key, &vt, logr.DefTimestampFormat) - case *time.Time: - enc.AddTimeKey(key, vt, logr.DefTimestampFormat) - default: - s := fmt.Sprintf("%v", vt) - enc.AddStringKey(key, s) - } -} diff --git a/vendor/github.com/mattermost/logr/format/plain.go b/vendor/github.com/mattermost/logr/format/plain.go deleted file mode 100644 index 3fa92b4900..0000000000 --- a/vendor/github.com/mattermost/logr/format/plain.go +++ /dev/null @@ -1,75 +0,0 @@ -package format - -import ( - "bytes" - "fmt" - - "github.com/mattermost/logr" -) - -// Plain is the simplest formatter, outputting only text with -// no colors. -type Plain struct { - // DisableTimestamp disables output of timestamp field. - DisableTimestamp bool - // DisableLevel disables output of level field. - DisableLevel bool - // DisableMsg disables output of msg field. - DisableMsg bool - // DisableContext disables output of all context fields. - DisableContext bool - // DisableStacktrace disables output of stack trace. - DisableStacktrace bool - - // Delim is an optional delimiter output between each log field. - // Defaults to a single space. - Delim string - - // TimestampFormat is an optional format for timestamps. If empty - // then DefTimestampFormat is used. - TimestampFormat string -} - -// Format converts a log record to bytes. -func (p *Plain) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) { - delim := p.Delim - if delim == "" { - delim = " " - } - if buf == nil { - buf = &bytes.Buffer{} - } - - timestampFmt := p.TimestampFormat - if timestampFmt == "" { - timestampFmt = logr.DefTimestampFormat - } - - if !p.DisableTimestamp { - var arr [128]byte - tbuf := rec.Time().AppendFormat(arr[:0], timestampFmt) - buf.Write(tbuf) - buf.WriteString(delim) - } - if !p.DisableLevel { - fmt.Fprintf(buf, "%v%s", rec.Level().Name, delim) - } - if !p.DisableMsg { - fmt.Fprint(buf, rec.Msg(), delim) - } - if !p.DisableContext { - ctx := rec.Fields() - if len(ctx) > 0 { - logr.WriteFields(buf, ctx, " ") - } - } - if stacktrace && !p.DisableStacktrace { - frames := rec.StackFrames() - if len(frames) > 0 { - buf.WriteString("\n") - logr.WriteStacktrace(buf, rec.StackFrames()) - } - } - buf.WriteString("\n") - return buf, nil -} diff --git a/vendor/github.com/mattermost/logr/formatter.go b/vendor/github.com/mattermost/logr/formatter.go deleted file mode 100644 index bb8df2d414..0000000000 --- a/vendor/github.com/mattermost/logr/formatter.go +++ /dev/null @@ -1,119 +0,0 @@ -package logr - -import ( - "bytes" - "fmt" - "io" - "runtime" - "sort" -) - -// Formatter turns a LogRec into a formatted string. -type Formatter interface { - // Format converts a log record to bytes. If buf is not nil then it will be - // be filled with the formatted results, otherwise a new buffer will be allocated. - Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) -} - -const ( - // DefTimestampFormat is the default time stamp format used by - // Plain formatter and others. - DefTimestampFormat = "2006-01-02 15:04:05.000 Z07:00" -) - -// DefaultFormatter is the default formatter, outputting only text with -// no colors and a space delimiter. Use `format.Plain` instead. -type DefaultFormatter struct { -} - -// Format converts a log record to bytes. -func (p *DefaultFormatter) Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) { - if buf == nil { - buf = &bytes.Buffer{} - } - delim := " " - timestampFmt := DefTimestampFormat - - fmt.Fprintf(buf, "%s%s", rec.Time().Format(timestampFmt), delim) - fmt.Fprintf(buf, "%v%s", rec.Level(), delim) - fmt.Fprint(buf, rec.Msg(), delim) - - ctx := rec.Fields() - if len(ctx) > 0 { - WriteFields(buf, ctx, " ") - } - - if stacktrace { - frames := rec.StackFrames() - if len(frames) > 0 { - buf.WriteString("\n") - WriteStacktrace(buf, rec.StackFrames()) - } - } - buf.WriteString("\n") - - return buf, nil -} - -// WriteFields writes zero or more name value pairs to the io.Writer. -// The pairs are sorted by key name and output in key=value format -// with optional separator between fields. -func WriteFields(w io.Writer, flds Fields, separator string) { - keys := make([]string, 0, len(flds)) - for k := range flds { - keys = append(keys, k) - } - sort.Strings(keys) - sep := "" - for _, key := range keys { - writeField(w, key, flds[key], sep) - sep = separator - } -} - -func writeField(w io.Writer, key string, val interface{}, sep string) { - var template string - switch v := val.(type) { - case error: - val := v.Error() - if shouldQuote(val) { - template = "%s%s=%q" - } else { - template = "%s%s=%s" - } - case string: - if shouldQuote(v) { - template = "%s%s=%q" - } else { - template = "%s%s=%s" - } - default: - template = "%s%s=%v" - } - fmt.Fprintf(w, template, sep, key, val) -} - -// shouldQuote returns true if val contains any characters that might be unsafe -// when injecting log output into an aggregator, viewer or report. -func shouldQuote(val string) bool { - for _, c := range val { - if !((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z')) { - return true - } - } - return false -} - -// WriteStacktrace formats and outputs a stack trace to an io.Writer. -func WriteStacktrace(w io.Writer, frames []runtime.Frame) { - for _, frame := range frames { - if frame.Function != "" { - fmt.Fprintf(w, " %s\n", frame.Function) - } - if frame.File != "" { - fmt.Fprintf(w, " %s:%d\n", frame.File, frame.Line) - } - } -} diff --git a/vendor/github.com/mattermost/logr/levelcustom.go b/vendor/github.com/mattermost/logr/levelcustom.go deleted file mode 100644 index 384fe4e9ed..0000000000 --- a/vendor/github.com/mattermost/logr/levelcustom.go +++ /dev/null @@ -1,45 +0,0 @@ -package logr - -import ( - "sync" -) - -// CustomFilter allows targets to enable logging via a list of levels. -type CustomFilter struct { - mux sync.RWMutex - levels map[LevelID]Level -} - -// IsEnabled returns true if the specified Level exists in this list. -func (st *CustomFilter) IsEnabled(level Level) bool { - st.mux.RLock() - defer st.mux.RUnlock() - _, ok := st.levels[level.ID] - return ok -} - -// IsStacktraceEnabled returns true if the specified Level requires a stack trace. -func (st *CustomFilter) IsStacktraceEnabled(level Level) bool { - st.mux.RLock() - defer st.mux.RUnlock() - lvl, ok := st.levels[level.ID] - if ok { - return lvl.Stacktrace - } - return false -} - -// Add adds one or more levels to the list. Adding a level enables logging for -// that level on any targets using this CustomFilter. -func (st *CustomFilter) Add(levels ...Level) { - st.mux.Lock() - defer st.mux.Unlock() - - if st.levels == nil { - st.levels = make(map[LevelID]Level) - } - - for _, s := range levels { - st.levels[s.ID] = s - } -} diff --git a/vendor/github.com/mattermost/logr/levelstd.go b/vendor/github.com/mattermost/logr/levelstd.go deleted file mode 100644 index f5e0fa4664..0000000000 --- a/vendor/github.com/mattermost/logr/levelstd.go +++ /dev/null @@ -1,37 +0,0 @@ -package logr - -// StdFilter allows targets to filter via classic log levels where any level -// beyond a certain verbosity/severity is enabled. -type StdFilter struct { - Lvl Level - Stacktrace Level -} - -// IsEnabled returns true if the specified Level is at or above this verbosity. Also -// determines if a stack trace is required. -func (lt StdFilter) IsEnabled(level Level) bool { - return level.ID <= lt.Lvl.ID -} - -// IsStacktraceEnabled returns true if the specified Level requires a stack trace. -func (lt StdFilter) IsStacktraceEnabled(level Level) bool { - return level.ID <= lt.Stacktrace.ID -} - -var ( - // Panic is the highest level of severity. Logs the message and then panics. - Panic = Level{ID: 0, Name: "panic"} - // Fatal designates a catastrophic error. Logs the message and then calls - // `logr.Exit(1)`. - Fatal = Level{ID: 1, Name: "fatal"} - // Error designates a serious but possibly recoverable error. - Error = Level{ID: 2, Name: "error"} - // Warn designates non-critical error. - Warn = Level{ID: 3, Name: "warn"} - // Info designates information regarding application events. - Info = Level{ID: 4, Name: "info"} - // Debug designates verbose information typically used for debugging. - Debug = Level{ID: 5, Name: "debug"} - // Trace designates the highest verbosity of log output. - Trace = Level{ID: 6, Name: "trace"} -) diff --git a/vendor/github.com/mattermost/logr/logger.go b/vendor/github.com/mattermost/logr/logger.go deleted file mode 100644 index c2386312f0..0000000000 --- a/vendor/github.com/mattermost/logr/logger.go +++ /dev/null @@ -1,218 +0,0 @@ -package logr - -import ( - "fmt" -) - -// Fields type, used to pass to `WithFields`. -type Fields map[string]interface{} - -// Logger provides context for logging via fields. -type Logger struct { - logr *Logr - fields Fields -} - -// Logr returns the `Logr` instance that created this `Logger`. -func (logger Logger) Logr() *Logr { - return logger.logr -} - -// WithField creates a new `Logger` with any existing fields -// plus the new one. -func (logger Logger) WithField(key string, value interface{}) Logger { - return logger.WithFields(Fields{key: value}) -} - -// WithFields creates a new `Logger` with any existing fields -// plus the new ones. -func (logger Logger) WithFields(fields Fields) Logger { - l := Logger{logr: logger.logr} - // if parent has no fields then avoid creating a new map. - oldLen := len(logger.fields) - if oldLen == 0 { - l.fields = fields - return l - } - - l.fields = make(Fields, len(fields)+oldLen) - for k, v := range logger.fields { - l.fields[k] = v - } - for k, v := range fields { - l.fields[k] = v - } - return l -} - -// Log checks that the level matches one or more targets, and -// if so, generates a log record that is added to the Logr queue. -// Arguments are handled in the manner of fmt.Print. -func (logger Logger) Log(lvl Level, args ...interface{}) { - status := logger.logr.IsLevelEnabled(lvl) - if status.Enabled { - rec := NewLogRec(lvl, logger, "", args, status.Stacktrace) - logger.logr.enqueue(rec) - } -} - -// Trace is a convenience method equivalent to `Log(TraceLevel, args...)`. -func (logger Logger) Trace(args ...interface{}) { - logger.Log(Trace, args...) -} - -// Debug is a convenience method equivalent to `Log(DebugLevel, args...)`. -func (logger Logger) Debug(args ...interface{}) { - logger.Log(Debug, args...) -} - -// Print ensures compatibility with std lib logger. -func (logger Logger) Print(args ...interface{}) { - logger.Info(args...) -} - -// Info is a convenience method equivalent to `Log(InfoLevel, args...)`. -func (logger Logger) Info(args ...interface{}) { - logger.Log(Info, args...) -} - -// Warn is a convenience method equivalent to `Log(WarnLevel, args...)`. -func (logger Logger) Warn(args ...interface{}) { - logger.Log(Warn, args...) -} - -// Error is a convenience method equivalent to `Log(ErrorLevel, args...)`. -func (logger Logger) Error(args ...interface{}) { - logger.Log(Error, args...) -} - -// Fatal is a convenience method equivalent to `Log(FatalLevel, args...)` -// followed by a call to os.Exit(1). -func (logger Logger) Fatal(args ...interface{}) { - logger.Log(Fatal, args...) - logger.logr.exit(1) -} - -// Panic is a convenience method equivalent to `Log(PanicLevel, args...)` -// followed by a call to panic(). -func (logger Logger) Panic(args ...interface{}) { - logger.Log(Panic, args...) - panic(fmt.Sprint(args...)) -} - -// -// Printf style -// - -// Logf checks that the level matches one or more targets, and -// if so, generates a log record that is added to the main -// queue (channel). Arguments are handled in the manner of fmt.Printf. -func (logger Logger) Logf(lvl Level, format string, args ...interface{}) { - status := logger.logr.IsLevelEnabled(lvl) - if status.Enabled { - rec := NewLogRec(lvl, logger, format, args, status.Stacktrace) - logger.logr.enqueue(rec) - } -} - -// Tracef is a convenience method equivalent to `Logf(TraceLevel, args...)`. -func (logger Logger) Tracef(format string, args ...interface{}) { - logger.Logf(Trace, format, args...) -} - -// Debugf is a convenience method equivalent to `Logf(DebugLevel, args...)`. -func (logger Logger) Debugf(format string, args ...interface{}) { - logger.Logf(Debug, format, args...) -} - -// Infof is a convenience method equivalent to `Logf(InfoLevel, args...)`. -func (logger Logger) Infof(format string, args ...interface{}) { - logger.Logf(Info, format, args...) -} - -// Printf ensures compatibility with std lib logger. -func (logger Logger) Printf(format string, args ...interface{}) { - logger.Infof(format, args...) -} - -// Warnf is a convenience method equivalent to `Logf(WarnLevel, args...)`. -func (logger Logger) Warnf(format string, args ...interface{}) { - logger.Logf(Warn, format, args...) -} - -// Errorf is a convenience method equivalent to `Logf(ErrorLevel, args...)`. -func (logger Logger) Errorf(format string, args ...interface{}) { - logger.Logf(Error, format, args...) -} - -// Fatalf is a convenience method equivalent to `Logf(FatalLevel, args...)` -// followed by a call to os.Exit(1). -func (logger Logger) Fatalf(format string, args ...interface{}) { - logger.Logf(Fatal, format, args...) - logger.logr.exit(1) -} - -// Panicf is a convenience method equivalent to `Logf(PanicLevel, args...)` -// followed by a call to panic(). -func (logger Logger) Panicf(format string, args ...interface{}) { - logger.Logf(Panic, format, args...) -} - -// -// Println style -// - -// Logln checks that the level matches one or more targets, and -// if so, generates a log record that is added to the main -// queue (channel). Arguments are handled in the manner of fmt.Println. -func (logger Logger) Logln(lvl Level, args ...interface{}) { - status := logger.logr.IsLevelEnabled(lvl) - if status.Enabled { - rec := NewLogRec(lvl, logger, "", args, status.Stacktrace) - rec.newline = true - logger.logr.enqueue(rec) - } -} - -// Traceln is a convenience method equivalent to `Logln(TraceLevel, args...)`. -func (logger Logger) Traceln(args ...interface{}) { - logger.Logln(Trace, args...) -} - -// Debugln is a convenience method equivalent to `Logln(DebugLevel, args...)`. -func (logger Logger) Debugln(args ...interface{}) { - logger.Logln(Debug, args...) -} - -// Infoln is a convenience method equivalent to `Logln(InfoLevel, args...)`. -func (logger Logger) Infoln(args ...interface{}) { - logger.Logln(Info, args...) -} - -// Println ensures compatibility with std lib logger. -func (logger Logger) Println(args ...interface{}) { - logger.Infoln(args...) -} - -// Warnln is a convenience method equivalent to `Logln(WarnLevel, args...)`. -func (logger Logger) Warnln(args ...interface{}) { - logger.Logln(Warn, args...) -} - -// Errorln is a convenience method equivalent to `Logln(ErrorLevel, args...)`. -func (logger Logger) Errorln(args ...interface{}) { - logger.Logln(Error, args...) -} - -// Fatalln is a convenience method equivalent to `Logln(FatalLevel, args...)` -// followed by a call to os.Exit(1). -func (logger Logger) Fatalln(args ...interface{}) { - logger.Logln(Fatal, args...) - logger.logr.exit(1) -} - -// Panicln is a convenience method equivalent to `Logln(PanicLevel, args...)` -// followed by a call to panic(). -func (logger Logger) Panicln(args ...interface{}) { - logger.Logln(Panic, args...) -} diff --git a/vendor/github.com/mattermost/logr/logr.go b/vendor/github.com/mattermost/logr/logr.go deleted file mode 100644 index 631366a570..0000000000 --- a/vendor/github.com/mattermost/logr/logr.go +++ /dev/null @@ -1,664 +0,0 @@ -package logr - -import ( - "bytes" - "context" - "errors" - "fmt" - "os" - "sync" - "time" - - "github.com/wiggin77/cfg" - "github.com/wiggin77/merror" -) - -// Logr maintains a list of log targets and accepts incoming -// log records. -type Logr struct { - tmux sync.RWMutex // target mutex - targets []Target - - mux sync.RWMutex - maxQueueSizeActual int - in chan *LogRec - done chan struct{} - once sync.Once - shutdown bool - lvlCache levelCache - - metricsInitOnce sync.Once - metricsCloseOnce sync.Once - metricsDone chan struct{} - metrics MetricsCollector - queueSizeGauge Gauge - loggedCounter Counter - errorCounter Counter - - bufferPool sync.Pool - - // MaxQueueSize is the maximum number of log records that can be queued. - // If exceeded, `OnQueueFull` is called which determines if the log - // record will be dropped or block until add is successful. - // If this is modified, it must be done before `Configure` or - // `AddTarget`. Defaults to DefaultMaxQueueSize. - MaxQueueSize int - - // OnLoggerError, when not nil, is called any time an internal - // logging error occurs. For example, this can happen when a - // target cannot connect to its data sink. - OnLoggerError func(error) - - // OnQueueFull, when not nil, is called on an attempt to add - // a log record to a full Logr queue. - // `MaxQueueSize` can be used to modify the maximum queue size. - // This function should return quickly, with a bool indicating whether - // the log record should be dropped (true) or block until the log record - // is successfully added (false). If nil then blocking (false) is assumed. - OnQueueFull func(rec *LogRec, maxQueueSize int) bool - - // OnTargetQueueFull, when not nil, is called on an attempt to add - // a log record to a full target queue provided the target supports reporting - // this condition. - // This function should return quickly, with a bool indicating whether - // the log record should be dropped (true) or block until the log record - // is successfully added (false). If nil then blocking (false) is assumed. - OnTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool - - // OnExit, when not nil, is called when a FatalXXX style log API is called. - // When nil, then the default behavior is to cleanly shut down this Logr and - // call `os.Exit(code)`. - OnExit func(code int) - - // OnPanic, when not nil, is called when a PanicXXX style log API is called. - // When nil, then the default behavior is to cleanly shut down this Logr and - // call `panic(err)`. - OnPanic func(err interface{}) - - // EnqueueTimeout is the amount of time a log record can take to be queued. - // This only applies to blocking enqueue which happen after `logr.OnQueueFull` - // is called and returns false. - EnqueueTimeout time.Duration - - // ShutdownTimeout is the amount of time `logr.Shutdown` can execute before - // timing out. - ShutdownTimeout time.Duration - - // FlushTimeout is the amount of time `logr.Flush` can execute before - // timing out. - FlushTimeout time.Duration - - // UseSyncMapLevelCache can be set to true before the first target is added - // when high concurrency (e.g. >32 cores) is expected. This may improve - // performance with large numbers of cores - benchmark for your use case. - UseSyncMapLevelCache bool - - // MaxPooledFormatBuffer determines the maximum size of a buffer that can be - // pooled. To reduce allocations, the buffers needed during formatting (etc) - // are pooled. A very large log item will grow a buffer that could stay in - // memory indefinitely. This settings lets you control how big a pooled buffer - // can be - anything larger will be garbage collected after use. - // Defaults to 1MB. - MaxPooledBuffer int - - // DisableBufferPool when true disables the buffer pool. See MaxPooledBuffer. - DisableBufferPool bool - - // MetricsUpdateFreqMillis determines how often polled metrics are updated - // when metrics are enabled. - MetricsUpdateFreqMillis int64 -} - -// Configure adds/removes targets via the supplied `Config`. -func (logr *Logr) Configure(config *cfg.Config) error { - // TODO - return fmt.Errorf("not implemented yet") -} - -func (logr *Logr) ensureInit() { - logr.once.Do(func() { - defer func() { - go logr.start() - }() - - logr.mux.Lock() - defer logr.mux.Unlock() - - logr.maxQueueSizeActual = logr.MaxQueueSize - if logr.maxQueueSizeActual == 0 { - logr.maxQueueSizeActual = DefaultMaxQueueSize - } - - if logr.maxQueueSizeActual < 0 { - logr.maxQueueSizeActual = 0 - } - - logr.in = make(chan *LogRec, logr.maxQueueSizeActual) - logr.done = make(chan struct{}) - - if logr.UseSyncMapLevelCache { - logr.lvlCache = &syncMapLevelCache{} - } else { - logr.lvlCache = &arrayLevelCache{} - } - - if logr.MaxPooledBuffer == 0 { - logr.MaxPooledBuffer = DefaultMaxPooledBuffer - } - logr.bufferPool = sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, - } - - logr.lvlCache.setup() - }) -} - -// AddTarget adds one or more targets to the logger which will receive -// log records for outputting. -func (logr *Logr) AddTarget(targets ...Target) error { - if logr.IsShutdown() { - return fmt.Errorf("AddTarget called after Logr shut down") - } - - logr.ensureInit() - metrics := logr.getMetricsCollector() - defer logr.ResetLevelCache() // call this after tmux is released - - logr.tmux.Lock() - defer logr.tmux.Unlock() - - errs := merror.New() - for _, t := range targets { - if t == nil { - continue - } - - logr.targets = append(logr.targets, t) - if metrics != nil { - if tm, ok := t.(TargetWithMetrics); ok { - if err := tm.EnableMetrics(metrics, logr.MetricsUpdateFreqMillis); err != nil { - errs.Append(err) - } - } - } - } - return errs.ErrorOrNil() -} - -// NewLogger creates a Logger using defaults. A `Logger` is light-weight -// enough to create on-demand, but typically one or more Loggers are -// created and re-used. -func (logr *Logr) NewLogger() Logger { - logger := Logger{logr: logr} - return logger -} - -var levelStatusDisabled = LevelStatus{} - -// IsLevelEnabled returns true if at least one target has the specified -// level enabled. The result is cached so that subsequent checks are fast. -func (logr *Logr) IsLevelEnabled(lvl Level) LevelStatus { - status, ok := logr.isLevelEnabledFromCache(lvl) - if ok { - return status - } - - // Check each target. - logr.tmux.RLock() - for _, t := range logr.targets { - e, s := t.IsLevelEnabled(lvl) - if e { - status.Enabled = true - if s { - status.Stacktrace = true - break // if both enabled then no sense checking more targets - } - } - } - logr.tmux.RUnlock() - - // Cache and return the result. - if err := logr.updateLevelCache(lvl.ID, status); err != nil { - logr.ReportError(err) - return LevelStatus{} - } - return status -} - -func (logr *Logr) isLevelEnabledFromCache(lvl Level) (LevelStatus, bool) { - logr.mux.RLock() - defer logr.mux.RUnlock() - - // Don't accept new log records after shutdown. - if logr.shutdown { - return levelStatusDisabled, true - } - - // Check cache. lvlCache may still be nil if no targets added. - if logr.lvlCache == nil { - return levelStatusDisabled, true - } - status, ok := logr.lvlCache.get(lvl.ID) - if ok { - return status, true - } - return LevelStatus{}, false -} - -func (logr *Logr) updateLevelCache(id LevelID, status LevelStatus) error { - logr.mux.RLock() - defer logr.mux.RUnlock() - if logr.lvlCache != nil { - return logr.lvlCache.put(id, status) - } - return nil -} - -// HasTargets returns true only if at least one target exists within the Logr. -func (logr *Logr) HasTargets() bool { - logr.tmux.RLock() - defer logr.tmux.RUnlock() - return len(logr.targets) > 0 -} - -// TargetInfo provides name and type for a Target. -type TargetInfo struct { - Name string - Type string -} - -// TargetInfos enumerates all the targets added to this Logr. -// The resulting slice represents a snapshot at time of calling. -func (logr *Logr) TargetInfos() []TargetInfo { - logr.tmux.RLock() - defer logr.tmux.RUnlock() - - infos := make([]TargetInfo, 0) - - for _, t := range logr.targets { - inf := TargetInfo{ - Name: fmt.Sprintf("%v", t), - Type: fmt.Sprintf("%T", t), - } - infos = append(infos, inf) - } - return infos -} - -// RemoveTargets safely removes one or more targets based on the filtering method. -// f should return true to delete the target, false to keep it. -// When removing a target, best effort is made to write any queued log records before -// closing, with cxt determining how much time can be spent in total. -// Note, keep the timeout short since this method blocks certain logging operations. -func (logr *Logr) RemoveTargets(cxt context.Context, f func(ti TargetInfo) bool) error { - var removed bool - defer func() { - if removed { - // call this after tmux is released since - // it will lock mux and we don't want to - // introduce possible deadlock. - logr.ResetLevelCache() - } - }() - - errs := merror.New() - - logr.tmux.Lock() - defer logr.tmux.Unlock() - - cp := make([]Target, 0) - - for _, t := range logr.targets { - inf := TargetInfo{ - Name: fmt.Sprintf("%v", t), - Type: fmt.Sprintf("%T", t), - } - if f(inf) { - if err := t.Shutdown(cxt); err != nil { - errs.Append(err) - } - removed = true - } else { - cp = append(cp, t) - } - } - logr.targets = cp - return errs.ErrorOrNil() -} - -// ResetLevelCache resets the cached results of `IsLevelEnabled`. This is -// called any time a Target is added or a target's level is changed. -func (logr *Logr) ResetLevelCache() { - // Write lock so that new cache entries cannot be stored while we - // clear the cache. - logr.mux.Lock() - defer logr.mux.Unlock() - logr.resetLevelCache() -} - -// resetLevelCache empties the level cache without locking. -// mux.Lock must be held before calling this function. -func (logr *Logr) resetLevelCache() { - // lvlCache may still be nil if no targets added. - if logr.lvlCache != nil { - logr.lvlCache.clear() - } -} - -// enqueue adds a log record to the logr queue. If the queue is full then -// this function either blocks or the log record is dropped, depending on -// the result of calling `OnQueueFull`. -func (logr *Logr) enqueue(rec *LogRec) { - if logr.in == nil { - logr.ReportError(fmt.Errorf("AddTarget or Configure must be called before enqueue")) - } - - select { - case logr.in <- rec: - default: - if logr.OnQueueFull != nil && logr.OnQueueFull(rec, logr.maxQueueSizeActual) { - return // drop the record - } - select { - case <-time.After(logr.enqueueTimeout()): - logr.ReportError(fmt.Errorf("enqueue timed out for log rec [%v]", rec)) - case logr.in <- rec: // block until success or timeout - } - } -} - -// exit is called by one of the FatalXXX style APIS. If `logr.OnExit` is not nil -// then that method is called, otherwise the default behavior is to shut down this -// Logr cleanly then call `os.Exit(code)`. -func (logr *Logr) exit(code int) { - if logr.OnExit != nil { - logr.OnExit(code) - return - } - - if err := logr.Shutdown(); err != nil { - logr.ReportError(err) - } - os.Exit(code) -} - -// panic is called by one of the PanicXXX style APIS. If `logr.OnPanic` is not nil -// then that method is called, otherwise the default behavior is to shut down this -// Logr cleanly then call `panic(err)`. -func (logr *Logr) panic(err interface{}) { - if logr.OnPanic != nil { - logr.OnPanic(err) - return - } - - if err := logr.Shutdown(); err != nil { - logr.ReportError(err) - } - panic(err) -} - -// Flush blocks while flushing the logr queue and all target queues, by -// writing existing log records to valid targets. -// Any attempts to add new log records will block until flush is complete. -// `logr.FlushTimeout` determines how long flush can execute before -// timing out. Use `IsTimeoutError` to determine if the returned error is -// due to a timeout. -func (logr *Logr) Flush() error { - ctx, cancel := context.WithTimeout(context.Background(), logr.flushTimeout()) - defer cancel() - return logr.FlushWithTimeout(ctx) -} - -// Flush blocks while flushing the logr queue and all target queues, by -// writing existing log records to valid targets. -// Any attempts to add new log records will block until flush is complete. -// Use `IsTimeoutError` to determine if the returned error is -// due to a timeout. -func (logr *Logr) FlushWithTimeout(ctx context.Context) error { - if !logr.HasTargets() { - return nil - } - - if logr.IsShutdown() { - return errors.New("Flush called on shut down Logr") - } - - rec := newFlushLogRec(logr.NewLogger()) - logr.enqueue(rec) - - select { - case <-ctx.Done(): - return newTimeoutError("logr queue shutdown timeout") - case <-rec.flush: - } - return nil -} - -// IsShutdown returns true if this Logr instance has been shut down. -// No further log records can be enqueued and no targets added after -// shutdown. -func (logr *Logr) IsShutdown() bool { - logr.mux.Lock() - defer logr.mux.Unlock() - return logr.shutdown -} - -// Shutdown cleanly stops the logging engine after making best efforts -// to flush all targets. Call this function right before application -// exit - logr cannot be restarted once shut down. -// `logr.ShutdownTimeout` determines how long shutdown can execute before -// timing out. Use `IsTimeoutError` to determine if the returned error is -// due to a timeout. -func (logr *Logr) Shutdown() error { - ctx, cancel := context.WithTimeout(context.Background(), logr.shutdownTimeout()) - defer cancel() - return logr.ShutdownWithTimeout(ctx) -} - -// Shutdown cleanly stops the logging engine after making best efforts -// to flush all targets. Call this function right before application -// exit - logr cannot be restarted once shut down. -// Use `IsTimeoutError` to determine if the returned error is due to a -// timeout. -func (logr *Logr) ShutdownWithTimeout(ctx context.Context) error { - logr.mux.Lock() - if logr.shutdown { - logr.mux.Unlock() - return errors.New("Shutdown called again after shut down") - } - logr.shutdown = true - logr.resetLevelCache() - logr.mux.Unlock() - - logr.metricsCloseOnce.Do(func() { - if logr.metricsDone != nil { - close(logr.metricsDone) - } - }) - - errs := merror.New() - - // close the incoming channel and wait for read loop to exit. - if logr.in != nil { - close(logr.in) - select { - case <-ctx.Done(): - errs.Append(newTimeoutError("logr queue shutdown timeout")) - case <-logr.done: - } - } - - // logr.in channel should now be drained to targets and no more log records - // can be added. - logr.tmux.RLock() - defer logr.tmux.RUnlock() - for _, t := range logr.targets { - err := t.Shutdown(ctx) - if err != nil { - errs.Append(err) - } - } - return errs.ErrorOrNil() -} - -// ReportError is used to notify the host application of any internal logging errors. -// If `OnLoggerError` is not nil, it is called with the error, otherwise the error is -// output to `os.Stderr`. -func (logr *Logr) ReportError(err interface{}) { - logr.incErrorCounter() - - if logr.OnLoggerError == nil { - fmt.Fprintln(os.Stderr, err) - return - } - logr.OnLoggerError(fmt.Errorf("%v", err)) -} - -// BorrowBuffer borrows a buffer from the pool. Release the buffer to reduce garbage collection. -func (logr *Logr) BorrowBuffer() *bytes.Buffer { - if logr.DisableBufferPool { - return &bytes.Buffer{} - } - return logr.bufferPool.Get().(*bytes.Buffer) -} - -// ReleaseBuffer returns a buffer to the pool to reduce garbage collection. The buffer is only -// retained if less than MaxPooledBuffer. -func (logr *Logr) ReleaseBuffer(buf *bytes.Buffer) { - if !logr.DisableBufferPool && buf.Cap() < logr.MaxPooledBuffer { - buf.Reset() - logr.bufferPool.Put(buf) - } -} - -// enqueueTimeout returns amount of time a log record can take to be queued. -// This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called -// and returns false. -func (logr *Logr) enqueueTimeout() time.Duration { - if logr.EnqueueTimeout == 0 { - return DefaultEnqueueTimeout - } - return logr.EnqueueTimeout -} - -// shutdownTimeout returns the timeout duration for `logr.Shutdown`. -func (logr *Logr) shutdownTimeout() time.Duration { - if logr.ShutdownTimeout == 0 { - return DefaultShutdownTimeout - } - return logr.ShutdownTimeout -} - -// flushTimeout returns the timeout duration for `logr.Flush`. -func (logr *Logr) flushTimeout() time.Duration { - if logr.FlushTimeout == 0 { - return DefaultFlushTimeout - } - return logr.FlushTimeout -} - -// start selects on incoming log records until done channel signals. -// Incoming log records are fanned out to all log targets. -func (logr *Logr) start() { - defer func() { - if r := recover(); r != nil { - logr.ReportError(r) - go logr.start() - } - }() - - for rec := range logr.in { - if rec.flush != nil { - logr.flush(rec.flush) - } else { - rec.prep() - logr.fanout(rec) - } - } - close(logr.done) -} - -// startMetricsUpdater updates the metrics for any polled values every `MetricsUpdateFreqSecs` seconds until -// logr is closed. -func (logr *Logr) startMetricsUpdater() { - for { - updateFreq := logr.getMetricsUpdateFreqMillis() - if updateFreq == 0 { - updateFreq = DefMetricsUpdateFreqMillis - } - if updateFreq < 250 { - updateFreq = 250 // don't peg the CPU - } - - select { - case <-logr.metricsDone: - return - case <-time.After(time.Duration(updateFreq) * time.Millisecond): - logr.setQueueSizeGauge(float64(len(logr.in))) - } - } -} - -func (logr *Logr) getMetricsUpdateFreqMillis() int64 { - logr.mux.RLock() - defer logr.mux.RUnlock() - return logr.MetricsUpdateFreqMillis -} - -// fanout pushes a LogRec to all targets. -func (logr *Logr) fanout(rec *LogRec) { - var target Target - defer func() { - if r := recover(); r != nil { - logr.ReportError(fmt.Errorf("fanout failed for target %s, %v", target, r)) - } - }() - - var logged bool - defer func() { - if logged { - logr.incLoggedCounter() // call this after tmux is released - } - }() - - logr.tmux.RLock() - defer logr.tmux.RUnlock() - for _, target = range logr.targets { - if enabled, _ := target.IsLevelEnabled(rec.Level()); enabled { - target.Log(rec) - logged = true - } - } -} - -// flush drains the queue and notifies when done. -func (logr *Logr) flush(done chan<- struct{}) { - // first drain the logr queue. -loop: - for { - var rec *LogRec - select { - case rec = <-logr.in: - if rec.flush == nil { - rec.prep() - logr.fanout(rec) - } - default: - break loop - } - } - - logger := logr.NewLogger() - - // drain all the targets; block until finished. - logr.tmux.RLock() - defer logr.tmux.RUnlock() - for _, target := range logr.targets { - rec := newFlushLogRec(logger) - target.Log(rec) - <-rec.flush - } - done <- struct{}{} -} diff --git a/vendor/github.com/mattermost/logr/target.go b/vendor/github.com/mattermost/logr/target.go deleted file mode 100644 index f8e7bf752c..0000000000 --- a/vendor/github.com/mattermost/logr/target.go +++ /dev/null @@ -1,299 +0,0 @@ -package logr - -import ( - "context" - "fmt" - "os" - "sync" - "time" -) - -// Target represents a destination for log records such as file, -// database, TCP socket, etc. -type Target interface { - // SetName provides an optional name for the target. - SetName(name string) - - // IsLevelEnabled returns true if this target should emit - // logs for the specified level. Also determines if - // a stack trace is required. - IsLevelEnabled(Level) (enabled bool, stacktrace bool) - - // Formatter returns the Formatter associated with this Target. - Formatter() Formatter - - // Log outputs the log record to this target's destination. - Log(rec *LogRec) - - // Shutdown makes best effort to flush target queue and - // frees/closes all resources. - Shutdown(ctx context.Context) error -} - -// RecordWriter can convert a LogRecord to bytes and output to some data sink. -type RecordWriter interface { - Write(rec *LogRec) error -} - -// Basic provides the basic functionality of a Target that can be used -// to more easily compose your own Targets. To use, just embed Basic -// in your target type, implement `RecordWriter`, and call `(*Basic).Start`. -type Basic struct { - target Target - - filter Filter - formatter Formatter - - in chan *LogRec - done chan struct{} - w RecordWriter - - mux sync.RWMutex - name string - - metrics bool - queueSizeGauge Gauge - loggedCounter Counter - errorCounter Counter - droppedCounter Counter - blockedCounter Counter - - metricsUpdateFreqMillis int64 -} - -// Start initializes this target helper and starts accepting log records for processing. -func (b *Basic) Start(target Target, rw RecordWriter, filter Filter, formatter Formatter, maxQueued int) { - if filter == nil { - filter = &StdFilter{Lvl: Fatal} - } - if formatter == nil { - formatter = &DefaultFormatter{} - } - - b.target = target - b.filter = filter - b.formatter = formatter - b.in = make(chan *LogRec, maxQueued) - b.done = make(chan struct{}, 1) - b.w = rw - go b.start() - - if b.hasMetrics() { - go b.startMetricsUpdater() - } -} - -func (b *Basic) SetName(name string) { - b.mux.Lock() - defer b.mux.Unlock() - b.name = name -} - -// IsLevelEnabled returns true if this target should emit -// logs for the specified level. Also determines if -// a stack trace is required. -func (b *Basic) IsLevelEnabled(lvl Level) (enabled bool, stacktrace bool) { - return b.filter.IsEnabled(lvl), b.filter.IsStacktraceEnabled(lvl) -} - -// Formatter returns the Formatter associated with this Target. -func (b *Basic) Formatter() Formatter { - return b.formatter -} - -// Shutdown stops processing log records after making best -// effort to flush queue. -func (b *Basic) Shutdown(ctx context.Context) error { - // close the incoming channel and wait for read loop to exit. - close(b.in) - select { - case <-ctx.Done(): - case <-b.done: - } - - // b.in channel should now be drained. - return nil -} - -// Log outputs the log record to this targets destination. -func (b *Basic) Log(rec *LogRec) { - lgr := rec.Logger().Logr() - select { - case b.in <- rec: - default: - handler := lgr.OnTargetQueueFull - if handler != nil && handler(b.target, rec, cap(b.in)) { - b.incDroppedCounter() - return // drop the record - } - b.incBlockedCounter() - - select { - case <-time.After(lgr.enqueueTimeout()): - lgr.ReportError(fmt.Errorf("target enqueue timeout for log rec [%v]", rec)) - case b.in <- rec: // block until success or timeout - } - } -} - -// Metrics enables metrics collection using the provided MetricsCollector. -func (b *Basic) EnableMetrics(collector MetricsCollector, updateFreqMillis int64) error { - name := fmt.Sprintf("%v", b) - - b.mux.Lock() - defer b.mux.Unlock() - - b.metrics = true - b.metricsUpdateFreqMillis = updateFreqMillis - - var err error - - if b.queueSizeGauge, err = collector.QueueSizeGauge(name); err != nil { - return err - } - if b.loggedCounter, err = collector.LoggedCounter(name); err != nil { - return err - } - if b.errorCounter, err = collector.ErrorCounter(name); err != nil { - return err - } - if b.droppedCounter, err = collector.DroppedCounter(name); err != nil { - return err - } - if b.blockedCounter, err = collector.BlockedCounter(name); err != nil { - return err - } - return nil -} - -func (b *Basic) hasMetrics() bool { - b.mux.RLock() - defer b.mux.RUnlock() - return b.metrics -} - -func (b *Basic) setQueueSizeGauge(val float64) { - b.mux.RLock() - defer b.mux.RUnlock() - if b.queueSizeGauge != nil { - b.queueSizeGauge.Set(val) - } -} - -func (b *Basic) incLoggedCounter() { - b.mux.RLock() - defer b.mux.RUnlock() - if b.loggedCounter != nil { - b.loggedCounter.Inc() - } -} - -func (b *Basic) incErrorCounter() { - b.mux.RLock() - defer b.mux.RUnlock() - if b.errorCounter != nil { - b.errorCounter.Inc() - } -} - -func (b *Basic) incDroppedCounter() { - b.mux.RLock() - defer b.mux.RUnlock() - if b.droppedCounter != nil { - b.droppedCounter.Inc() - } -} - -func (b *Basic) incBlockedCounter() { - b.mux.RLock() - defer b.mux.RUnlock() - if b.blockedCounter != nil { - b.blockedCounter.Inc() - } -} - -// String returns a name for this target. Use `SetName` to specify a name. -func (b *Basic) String() string { - b.mux.RLock() - defer b.mux.RUnlock() - - if b.name != "" { - return b.name - } - return fmt.Sprintf("%T", b.target) -} - -// Start accepts log records via In channel and writes to the -// supplied writer, until Done channel signaled. -func (b *Basic) start() { - defer func() { - if r := recover(); r != nil { - fmt.Fprintln(os.Stderr, "Basic.start -- ", r) - go b.start() - } - }() - - for rec := range b.in { - if rec.flush != nil { - b.flush(rec.flush) - } else { - err := b.w.Write(rec) - if err != nil { - b.incErrorCounter() - rec.Logger().Logr().ReportError(err) - } else { - b.incLoggedCounter() - } - } - } - close(b.done) -} - -// startMetricsUpdater updates the metrics for any polled values every `MetricsUpdateFreqSecs` seconds until -// target is closed. -func (b *Basic) startMetricsUpdater() { - for { - updateFreq := b.getMetricsUpdateFreqMillis() - if updateFreq == 0 { - updateFreq = DefMetricsUpdateFreqMillis - } - if updateFreq < 250 { - updateFreq = 250 // don't peg the CPU - } - - select { - case <-b.done: - return - case <-time.After(time.Duration(updateFreq) * time.Millisecond): - b.setQueueSizeGauge(float64(len(b.in))) - } - } -} - -func (b *Basic) getMetricsUpdateFreqMillis() int64 { - b.mux.RLock() - defer b.mux.RUnlock() - return b.metricsUpdateFreqMillis -} - -// flush drains the queue and notifies when done. -func (b *Basic) flush(done chan<- struct{}) { - for { - var rec *LogRec - var err error - select { - case rec = <-b.in: - // ignore any redundant flush records. - if rec.flush == nil { - err = b.w.Write(rec) - if err != nil { - b.incErrorCounter() - rec.Logger().Logr().ReportError(err) - } - } - default: - done <- struct{}{} - return - } - } -} diff --git a/vendor/github.com/mattermost/logr/target/syslog.go b/vendor/github.com/mattermost/logr/target/syslog.go deleted file mode 100644 index 1d2013b681..0000000000 --- a/vendor/github.com/mattermost/logr/target/syslog.go +++ /dev/null @@ -1,89 +0,0 @@ -// +build !windows,!nacl,!plan9 - -package target - -import ( - "context" - "fmt" - "log/syslog" - - "github.com/mattermost/logr" - "github.com/wiggin77/merror" -) - -// Syslog outputs log records to local or remote syslog. -type Syslog struct { - logr.Basic - w *syslog.Writer -} - -// SyslogParams provides parameters for dialing a syslog daemon. -type SyslogParams struct { - Network string - Raddr string - Priority syslog.Priority - Tag string -} - -// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog. -func NewSyslogTarget(filter logr.Filter, formatter logr.Formatter, params *SyslogParams, maxQueue int) (*Syslog, error) { - writer, err := syslog.Dial(params.Network, params.Raddr, params.Priority, params.Tag) - if err != nil { - return nil, err - } - - s := &Syslog{w: writer} - s.Basic.Start(s, s, filter, formatter, maxQueue) - - return s, nil -} - -// Shutdown stops processing log records after making best -// effort to flush queue. -func (s *Syslog) Shutdown(ctx context.Context) error { - errs := merror.New() - - err := s.Basic.Shutdown(ctx) - errs.Append(err) - - err = s.w.Close() - errs.Append(err) - - return errs.ErrorOrNil() -} - -// Write converts the log record to bytes, via the Formatter, -// and outputs to syslog. -func (s *Syslog) Write(rec *logr.LogRec) error { - _, stacktrace := s.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := s.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - txt := buf.String() - - switch rec.Level() { - case logr.Panic, logr.Fatal: - err = s.w.Crit(txt) - case logr.Error: - err = s.w.Err(txt) - case logr.Warn: - err = s.w.Warning(txt) - case logr.Debug, logr.Trace: - err = s.w.Debug(txt) - default: - // logr.Info plus all custom levels. - err = s.w.Info(txt) - } - - if err != nil { - reporter := rec.Logger().Logr().ReportError - reporter(fmt.Errorf("syslog write fail: %w", err)) - // syslog writer will try to reconnect. - } - return err -} diff --git a/vendor/github.com/mattermost/logr/target/writer.go b/vendor/github.com/mattermost/logr/target/writer.go deleted file mode 100644 index 2250da5138..0000000000 --- a/vendor/github.com/mattermost/logr/target/writer.go +++ /dev/null @@ -1,40 +0,0 @@ -package target - -import ( - "io" - "io/ioutil" - - "github.com/mattermost/logr" -) - -// Writer outputs log records to any `io.Writer`. -type Writer struct { - logr.Basic - out io.Writer -} - -// NewWriterTarget creates a target capable of outputting log records to an io.Writer. -func NewWriterTarget(filter logr.Filter, formatter logr.Formatter, out io.Writer, maxQueue int) *Writer { - if out == nil { - out = ioutil.Discard - } - w := &Writer{out: out} - w.Basic.Start(w, w, filter, formatter, maxQueue) - return w -} - -// Write converts the log record to bytes, via the Formatter, -// and outputs to the io.Writer. -func (w *Writer) Write(rec *logr.LogRec) error { - _, stacktrace := w.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := w.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - _, err = w.out.Write(buf.Bytes()) - return err -} diff --git a/vendor/github.com/mattermost/logr/.gitignore b/vendor/github.com/mattermost/logr/v2/.gitignore similarity index 96% rename from vendor/github.com/mattermost/logr/.gitignore rename to vendor/github.com/mattermost/logr/v2/.gitignore index c2c0a9e2e5..bac5e1c16a 100644 --- a/vendor/github.com/mattermost/logr/.gitignore +++ b/vendor/github.com/mattermost/logr/v2/.gitignore @@ -34,3 +34,4 @@ logs # test apps test/cmd/testapp1/testapp1 test/cmd/simple/simple +test/cmd/gelf/gelf diff --git a/vendor/github.com/mattermost/logr/.travis.yml b/vendor/github.com/mattermost/logr/v2/.travis.yml similarity index 100% rename from vendor/github.com/mattermost/logr/.travis.yml rename to vendor/github.com/mattermost/logr/v2/.travis.yml diff --git a/vendor/github.com/mattermost/logr/LICENSE b/vendor/github.com/mattermost/logr/v2/LICENSE similarity index 100% rename from vendor/github.com/mattermost/logr/LICENSE rename to vendor/github.com/mattermost/logr/v2/LICENSE diff --git a/vendor/github.com/mattermost/logr/README.md b/vendor/github.com/mattermost/logr/v2/README.md similarity index 83% rename from vendor/github.com/mattermost/logr/README.md rename to vendor/github.com/mattermost/logr/v2/README.md index a25d6de0a2..9ee0f17c37 100644 --- a/vendor/github.com/mattermost/logr/README.md +++ b/vendor/github.com/mattermost/logr/v2/README.md @@ -16,9 +16,9 @@ It is very much inspired by [Logrus](https://github.com/sirupsen/logrus) but add | entity | description | | ------ | ----------- | -| Logr | Engine instance typically instantiated once; used to configure logging.
```lgr := &Logr{}```| +| Logr | Engine instance typically instantiated once; used to configure logging.
```lgr,_ := logr.New()```| | Logger | Provides contextual logging via fields; lightweight, can be created once and accessed globally or create on demand.
```logger := lgr.NewLogger()```
```logger2 := logger.WithField("user", "Sam")```| -| Target | A destination for log items such as console, file, database or just about anything that can be written to. Each target has its own filter/level and formatter, and any number of targets can be added to a Logr. Targets for syslog and any io.Writer are built-in and it is easy to create your own. You can also use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr).| +| Target | A destination for log items such as console, file, database or just about anything that can be written to. Each target has its own filter/level and formatter, and any number of targets can be added to a Logr. Targets for file, syslog and any io.Writer are built-in and it is easy to create your own. You can also use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr).| | Filter | Determines which logging calls get written versus filtered out. Also determines which logging calls generate a stack trace.
```filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Fatal}```| | Formatter | Formats the output. Logr includes built-in formatters for JSON and plain text with delimiters. It is easy to create your own formatters or you can also use any [Logrus formatters](https://github.com/sirupsen/logrus#formatters) via a simple [adapter](https://github.com/wiggin77/logrus4logr).
```formatter := &format.Plain{Delim: " \| "}```| @@ -26,15 +26,15 @@ It is very much inspired by [Logrus](https://github.com/sirupsen/logrus) but add ```go // Create Logr instance. -lgr := &logr.Logr{} +lgr,_ := logr.New() // Create a filter and formatter. Both can be shared by multiple // targets. filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Error} -formatter := &format.Plain{Delim: " | "} +formatter := &formatters.Plain{Delim: " | "} // WriterTarget outputs to any io.Writer -t := target.NewWriterTarget(filter, formatter, os.StdOut, 1000) +t := targets.NewWriterTarget(filter, formatter, os.StdOut, 1000) lgr.AddTarget(t) // One or more Loggers can be created, shared, used concurrently, @@ -56,7 +56,7 @@ Fields allow for contextual logging, meaning information can be added to log sta Fields are added via Loggers: ```go -lgr := &Logr{} +lgr,_ := logr.New() // ... add targets ... logger := lgr.NewLogger().WithFields(logr.Fields{ "user": user, @@ -88,14 +88,14 @@ Logr also supports custom filters (logr.CustomFilter) which allow fine grained i LoginLevel := logr.Level{ID: 100, Name: "login ", Stacktrace: false} LogoutLevel := logr.Level{ID: 101, Name: "logout", Stacktrace: false} - lgr := &logr.Logr{} + lgr,_ := logr.New() // create a custom filter with custom levels. filter := &logr.CustomFilter{} filter.Add(LoginLevel, LogoutLevel) - formatter := &format.Plain{Delim: " | "} - tgr := target.NewWriterTarget(filter, formatter, os.StdOut, 1000) + formatter := &formatters.Plain{Delim: " | "} + tgr := targets.NewWriterTarget(filter, formatter, os.StdOut, 1000) lgr.AddTarget(tgr) logger := lgr.NewLogger().WithFields(logr.Fields{"user": "Bob", "role": "admin"}) @@ -113,36 +113,31 @@ You can use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) vi You can create your own target by implementing the [Target](./target.go) interface. -An easier method is to use the [logr.Basic](./target.go) type target and build your functionality on that. Basic handles all the queuing and other plumbing so you only need to implement two methods. Example target that outputs to `io.Writer`: +Example target that outputs to `io.Writer`: ```go type Writer struct { - logr.Basic out io.Writer } -func NewWriterTarget(filter logr.Filter, formatter logr.Formatter, out io.Writer, maxQueue int) *Writer { +func NewWriterTarget(out io.Writer) *Writer { w := &Writer{out: out} - w.Basic.Start(w, w, filter, formatter, maxQueue) return w } +// Called once to initialize target. +func (w *Writer) Init() error { + return nil +} + // Write will always be called by a single goroutine, so no locking needed. -// Just convert a log record to a []byte using the formatter and output the -// bytes to your sink. -func (w *Writer) Write(rec *logr.LogRec) error { - _, stacktrace := w.IsLevelEnabled(rec.Level()) +func (w *Writer) Write(p []byte, rec *logr.LogRec) (int, error) { + return w.out.Write(buf.Bytes()) +} - // take a buffer from the pool to avoid allocations or just allocate a new one. - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := w.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - _, err = w.out.Write(buf.Bytes()) - return err +// Called once to cleanup/free resources for target. +func (w *Writer) Shutdown() error { + return nil } ``` diff --git a/vendor/github.com/mattermost/logr/v2/config/config.go b/vendor/github.com/mattermost/logr/v2/config/config.go new file mode 100644 index 0000000000..a93b7a25a2 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/config/config.go @@ -0,0 +1,209 @@ +package config + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "strings" + + "github.com/mattermost/logr/v2" + "github.com/mattermost/logr/v2/formatters" + "github.com/mattermost/logr/v2/targets" +) + +type TargetCfg struct { + Type string `json:"type"` // one of "console", "file", "tcp", "syslog", "none". + Options json.RawMessage `json:"options,omitempty"` + Format string `json:"format"` // one of "json", "plain", "gelf" + FormatOptions json.RawMessage `json:"format_options,omitempty"` + Levels []logr.Level `json:"levels"` + MaxQueueSize int `json:"maxqueuesize,omitempty"` +} + +type ConsoleOptions struct { + Out string `json:"out"` // one of "stdout", "stderr" +} + +type TargetFactory func(targetType string, options json.RawMessage) (logr.Target, error) +type FormatterFactory func(format string, options json.RawMessage) (logr.Formatter, error) + +type Factories struct { + targetFactory TargetFactory // can be nil + formatterFactory FormatterFactory // can be nil +} + +var removeAll = func(ti logr.TargetInfo) bool { return true } + +// ConfigureTargets replaces the current list of log targets with a new one based on a map +// of name->TargetCfg. The map of TargetCfg's would typically be serialized from a JSON +// source or can be programmatically created. +// +// An optional set of factories can be provided which will be called to create any target +// types or formatters not built-in. +// +// To append log targets to an existing config, use `(*Logr).AddTarget` or +// `(*Logr).AddTargetFromConfig` instead. +func ConfigureTargets(lgr *logr.Logr, config map[string]TargetCfg, factories *Factories) error { + if err := lgr.RemoveTargets(context.Background(), removeAll); err != nil { + return fmt.Errorf("error removing existing log targets: %w", err) + } + + if factories == nil { + factories = &Factories{nil, nil} + } + + for name, tcfg := range config { + target, err := newTarget(tcfg.Type, tcfg.Options, factories.targetFactory) + if err != nil { + return fmt.Errorf("error creating log target %s: %w", name, err) + } + + if target == nil { + continue + } + + formatter, err := newFormatter(tcfg.Format, tcfg.FormatOptions, factories.formatterFactory) + if err != nil { + return fmt.Errorf("error creating formatter for log target %s: %w", name, err) + } + + filter := newFilter(tcfg.Levels) + qSize := tcfg.MaxQueueSize + if qSize == 0 { + qSize = logr.DefaultMaxQueueSize + } + + if err = lgr.AddTarget(target, name, filter, formatter, qSize); err != nil { + return fmt.Errorf("error adding log target %s: %w", name, err) + } + } + return nil +} + +func newFilter(levels []logr.Level) logr.Filter { + filter := &logr.CustomFilter{} + for _, lvl := range levels { + filter.Add(lvl) + } + return filter +} + +func newTarget(targetType string, options json.RawMessage, factory TargetFactory) (logr.Target, error) { + switch strings.ToLower(targetType) { + case "console": + c := ConsoleOptions{} + if len(options) != 0 { + if err := json.Unmarshal(options, &c); err != nil { + return nil, fmt.Errorf("error decoding console target options: %w", err) + } + } + var w io.Writer + switch c.Out { + case "stderr": + w = os.Stderr + case "stdout", "": + w = os.Stdout + default: + return nil, fmt.Errorf("invalid console target option '%s'", c.Out) + } + return targets.NewWriterTarget(w), nil + case "file": + fo := targets.FileOptions{} + if len(options) == 0 { + return nil, errors.New("missing file target options") + } + if err := json.Unmarshal(options, &fo); err != nil { + return nil, fmt.Errorf("error decoding file target options: %w", err) + } + if err := fo.CheckValid(); err != nil { + return nil, fmt.Errorf("invalid file target options: %w", err) + } + return targets.NewFileTarget(fo), nil + case "tcp": + to := targets.TcpOptions{} + if len(options) == 0 { + return nil, errors.New("missing TCP target options") + } + if err := json.Unmarshal(options, &to); err != nil { + return nil, fmt.Errorf("error decoding TCP target options: %w", err) + } + if err := to.CheckValid(); err != nil { + return nil, fmt.Errorf("invalid TCP target options: %w", err) + } + return targets.NewTcpTarget(&to), nil + case "syslog": + so := targets.SyslogOptions{} + if len(options) == 0 { + return nil, errors.New("missing SysLog target options") + } + if err := json.Unmarshal(options, &so); err != nil { + return nil, fmt.Errorf("error decoding Syslog target options: %w", err) + } + if err := so.CheckValid(); err != nil { + return nil, fmt.Errorf("invalid SysLog target options: %w", err) + } + return targets.NewSyslogTarget(&so) + case "none": + return nil, nil + default: + if factory != nil { + t, err := factory(targetType, options) + if err != nil || t == nil { + return nil, fmt.Errorf("error from target factory: %w", err) + } + return t, nil + } + } + return nil, fmt.Errorf("target type '%s' is unrecogized", targetType) +} + +func newFormatter(format string, options json.RawMessage, factory FormatterFactory) (logr.Formatter, error) { + switch strings.ToLower(format) { + case "json": + j := formatters.JSON{} + if len(options) != 0 { + if err := json.Unmarshal(options, &j); err != nil { + return nil, fmt.Errorf("error decoding JSON formatter options: %w", err) + } + if err := j.CheckValid(); err != nil { + return nil, fmt.Errorf("invalid JSON formatter options: %w", err) + } + } + return &j, nil + case "plain": + p := formatters.Plain{} + if len(options) != 0 { + if err := json.Unmarshal(options, &p); err != nil { + return nil, fmt.Errorf("error decoding Plain formatter options: %w", err) + } + if err := p.CheckValid(); err != nil { + return nil, fmt.Errorf("invalid plain formatter options: %w", err) + } + } + return &p, nil + case "gelf": + g := formatters.Gelf{} + if len(options) != 0 { + if err := json.Unmarshal(options, &g); err != nil { + return nil, fmt.Errorf("error decoding Gelf formatter options: %w", err) + } + if err := g.CheckValid(); err != nil { + return nil, fmt.Errorf("invalid GELF formatter options: %w", err) + } + } + return &g, nil + + default: + if factory != nil { + f, err := factory(format, options) + if err != nil || f == nil { + return nil, fmt.Errorf("error from formatter factory: %w", err) + } + return f, nil + } + } + return nil, fmt.Errorf("format '%s' is unrecogized", format) +} diff --git a/vendor/github.com/mattermost/logr/v2/config/sample-config.json b/vendor/github.com/mattermost/logr/v2/config/sample-config.json new file mode 100644 index 0000000000..540bafbb8d --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/config/sample-config.json @@ -0,0 +1,90 @@ +{ + "sample-console": { + "type": "console", + "options": { + "out": "stdout" + }, + "format": "plain", + "format_options": { + "delim": " | " + }, + "levels": [ + {"id": 5, "name": "debug"}, + {"id": 4, "name": "info"}, + {"id": 3, "name": "warn"}, + {"id": 2, "name": "error", "stacktrace": true}, + {"id": 1, "name": "fatal", "stacktrace": true}, + {"id": 0, "name": "panic", "stacktrace": true} + ], + "maxqueuesize": 1000 + }, + "sample-file": { + "type": "file", + "options": { + "filename": "test.log", + "max_size": 1000000, + "max_age": 1, + "max_backups": 10, + "compress": true + }, + "format": "json", + "format_options": { + }, + "levels": [ + {"id": 5, "name": "debug"}, + {"id": 4, "name": "info"}, + {"id": 3, "name": "warn"}, + {"id": 2, "name": "error", "stacktrace": true}, + {"id": 1, "name": "fatal", "stacktrace": true}, + {"id": 0, "name": "panic", "stacktrace": true} + ], + "maxqueuesize": 1000 + }, + "sample-tcp": { + "type": "tcp", + "options": { + "host": "localhost", + "port": 18066, + "tls": false, + "cert": "", + "insecure": false + }, + "format": "gelf", + "format_options": { + "hostname": "server01" + }, + "levels": [ + {"id": 5, "name": "debug"}, + {"id": 4, "name": "info"}, + {"id": 3, "name": "warn"}, + {"id": 2, "name": "error", "stacktrace": true}, + {"id": 1, "name": "fatal", "stacktrace": true}, + {"id": 0, "name": "panic", "stacktrace": true} + ], + "maxqueuesize": 1000 + }, + "sample-syslog": { + "type": "syslog", + "options": { + "host": "localhost", + "port": 18066, + "tls": false, + "cert": "", + "insecure": false, + "tag": "testapp" + }, + "format": "plain", + "format_options": { + "delim": " " + }, + "levels": [ + {"id": 5, "name": "debug"}, + {"id": 4, "name": "info"}, + {"id": 3, "name": "warn"}, + {"id": 2, "name": "error", "stacktrace": true}, + {"id": 1, "name": "fatal", "stacktrace": true}, + {"id": 0, "name": "panic", "stacktrace": true} + ], + "maxqueuesize": 1000 + } +} diff --git a/vendor/github.com/mattermost/logr/const.go b/vendor/github.com/mattermost/logr/v2/const.go similarity index 98% rename from vendor/github.com/mattermost/logr/const.go rename to vendor/github.com/mattermost/logr/v2/const.go index 704d050756..29d9224112 100644 --- a/vendor/github.com/mattermost/logr/const.go +++ b/vendor/github.com/mattermost/logr/v2/const.go @@ -13,7 +13,7 @@ const ( // MaxLevelID is the maximum value of a level ID. Some level cache implementations will // allocate a cache of this size. Cannot exceed uint. - MaxLevelID = 256 + MaxLevelID = 65535 // DefaultEnqueueTimeout is the default amount of time a log record can take to be queued. // This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called diff --git a/vendor/github.com/mattermost/logr/v2/field.go b/vendor/github.com/mattermost/logr/v2/field.go new file mode 100644 index 0000000000..5725d0a10e --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/field.go @@ -0,0 +1,403 @@ +package logr + +import ( + "errors" + "fmt" + "io" + "reflect" + "strconv" + "time" +) + +var ( + Comma = []byte{','} + Equals = []byte{'='} + Space = []byte{' '} + Newline = []byte{'\n'} + Quote = []byte{'"'} + Colon = []byte{'"'} +) + +// LogCloner is implemented by `Any` types that require a clone to be provided +// to the logger because the original may mutate. +type LogCloner interface { + LogClone() interface{} +} + +// LogWriter is implemented by `Any` types that provide custom formatting for +// log output. A string representation of the type should be written directly to +// the `io.Writer`. +type LogWriter interface { + LogWrite(w io.Writer) error +} + +type FieldType uint8 + +const ( + UnknownType FieldType = iota + StringType + StringerType + StructType + ErrorType + BoolType + TimestampMillisType + TimeType + DurationType + Int64Type + Int32Type + IntType + Uint64Type + Uint32Type + UintType + Float64Type + Float32Type + BinaryType + ArrayType + MapType +) + +type Field struct { + Key string + Type FieldType + Integer int64 + Float float64 + String string + Interface interface{} +} + +func quoteString(w io.Writer, s string, shouldQuote func(s string) bool) error { + b := shouldQuote(s) + if b { + if _, err := w.Write(Quote); err != nil { + return err + } + } + + if _, err := w.Write([]byte(s)); err != nil { + return err + } + + if b { + if _, err := w.Write(Quote); err != nil { + return err + } + } + return nil +} + +// ValueString converts a known type to a string using default formatting. +// This is called lazily by a formatter. +// Formatters can provide custom formatting or types passed via `Any` can implement +// the `LogString` interface to generate output for logging. +// If the optional shouldQuote callback is provided, then it will be called for any +// string output that could potentially need to be quoted. +func (f Field) ValueString(w io.Writer, shouldQuote func(s string) bool) error { + if shouldQuote == nil { + shouldQuote = func(s string) bool { return false } + } + var err error + switch f.Type { + case StringType: + err = quoteString(w, f.String, shouldQuote) + + case StringerType: + s, ok := f.Interface.(fmt.Stringer) + if ok { + err = quoteString(w, s.String(), shouldQuote) + } else if f.Interface == nil { + err = quoteString(w, "", shouldQuote) + } else { + err = fmt.Errorf("invalid fmt.Stringer for key %s", f.Key) + } + + case StructType: + s, ok := f.Interface.(LogWriter) + if ok { + err = s.LogWrite(w) + break + } + // structs that do not implement LogWriter fall back to reflection via Printf. + // TODO: create custom reflection-based encoder. + _, err = fmt.Fprintf(w, "%v", f.Interface) + + case ErrorType: + // TODO: create custom error encoder. + err = quoteString(w, fmt.Sprintf("%v", f.Interface), shouldQuote) + + case BoolType: + var b bool + if f.Integer != 0 { + b = true + } + _, err = io.WriteString(w, strconv.FormatBool(b)) + + case TimestampMillisType: + ts := time.Unix(f.Integer/1000, (f.Integer%1000)*int64(time.Millisecond)) + err = quoteString(w, ts.UTC().Format(TimestampMillisFormat), shouldQuote) + + case TimeType: + t, ok := f.Interface.(time.Time) + if !ok { + err = errors.New("invalid time") + break + } + err = quoteString(w, t.Format(DefTimestampFormat), shouldQuote) + + case DurationType: + _, err = fmt.Fprintf(w, "%s", time.Duration(f.Integer)) + + case Int64Type, Int32Type, IntType: + _, err = io.WriteString(w, strconv.FormatInt(f.Integer, 10)) + + case Uint64Type, Uint32Type, UintType: + _, err = io.WriteString(w, strconv.FormatUint(uint64(f.Integer), 10)) + + case Float64Type, Float32Type: + size := 64 + if f.Type == Float32Type { + size = 32 + } + err = quoteString(w, strconv.FormatFloat(f.Float, 'f', -1, size), shouldQuote) + + case BinaryType: + b, ok := f.Interface.([]byte) + if ok { + _, err = fmt.Fprintf(w, "[%X]", b) + break + } + _, err = fmt.Fprintf(w, "[%v]", f.Interface) + + case ArrayType: + a := reflect.ValueOf(f.Interface) + arr: + for i := 0; i < a.Len(); i++ { + item := a.Index(i) + switch v := item.Interface().(type) { + case LogWriter: + if err = v.LogWrite(w); err != nil { + break arr + } + case fmt.Stringer: + if err = quoteString(w, v.String(), shouldQuote); err != nil { + break arr + } + default: + s := fmt.Sprintf("%v", v) + if err = quoteString(w, s, shouldQuote); err != nil { + break arr + } + } + if _, err = w.Write(Comma); err != nil { + break arr + } + } + + case MapType: + a := reflect.ValueOf(f.Interface) + iter := a.MapRange() + it: + for iter.Next() { + if _, err = io.WriteString(w, iter.Key().String()); err != nil { + break it + } + if _, err = w.Write(Equals); err != nil { + break it + } + val := iter.Value().Interface() + switch v := val.(type) { + case LogWriter: + if err = v.LogWrite(w); err != nil { + break it + } + case fmt.Stringer: + if err = quoteString(w, v.String(), shouldQuote); err != nil { + break it + } + default: + s := fmt.Sprintf("%v", v) + if err = quoteString(w, s, shouldQuote); err != nil { + break it + } + } + if _, err = w.Write(Comma); err != nil { + break it + } + } + + case UnknownType: + _, err = fmt.Fprintf(w, "%v", f.Interface) + + default: + err = fmt.Errorf("invalid type %d", f.Type) + } + return err +} + +func nilField(key string) Field { + return String(key, "") +} + +func fieldForAny(key string, val interface{}) Field { + switch v := val.(type) { + case LogCloner: + if v == nil { + return nilField(key) + } + c := v.LogClone() + return Field{Key: key, Type: StructType, Interface: c} + case *LogCloner: + if v == nil { + return nilField(key) + } + c := (*v).LogClone() + return Field{Key: key, Type: StructType, Interface: c} + case LogWriter: + if v == nil { + return nilField(key) + } + return Field{Key: key, Type: StructType, Interface: v} + case *LogWriter: + if v == nil { + return nilField(key) + } + return Field{Key: key, Type: StructType, Interface: *v} + case bool: + return Bool(key, v) + case *bool: + if v == nil { + return nilField(key) + } + return Bool(key, *v) + case float64: + return Float64(key, v) + case *float64: + if v == nil { + return nilField(key) + } + return Float64(key, *v) + case float32: + return Float32(key, v) + case *float32: + if v == nil { + return nilField(key) + } + return Float32(key, *v) + case int: + return Int(key, v) + case *int: + if v == nil { + return nilField(key) + } + return Int(key, *v) + case int64: + return Int64(key, v) + case *int64: + if v == nil { + return nilField(key) + } + return Int64(key, *v) + case int32: + return Int32(key, v) + case *int32: + if v == nil { + return nilField(key) + } + return Int32(key, *v) + case int16: + return Int32(key, int32(v)) + case *int16: + if v == nil { + return nilField(key) + } + return Int32(key, int32(*v)) + case int8: + return Int32(key, int32(v)) + case *int8: + if v == nil { + return nilField(key) + } + return Int32(key, int32(*v)) + case string: + return String(key, v) + case *string: + if v == nil { + return nilField(key) + } + return String(key, *v) + case uint: + return Uint(key, v) + case *uint: + if v == nil { + return nilField(key) + } + return Uint(key, *v) + case uint64: + return Uint64(key, v) + case *uint64: + if v == nil { + return nilField(key) + } + return Uint64(key, *v) + case uint32: + return Uint32(key, v) + case *uint32: + if v == nil { + return nilField(key) + } + return Uint32(key, *v) + case uint16: + return Uint32(key, uint32(v)) + case *uint16: + if v == nil { + return nilField(key) + } + return Uint32(key, uint32(*v)) + case uint8: + return Uint32(key, uint32(v)) + case *uint8: + if v == nil { + return nilField(key) + } + return Uint32(key, uint32(*v)) + case []byte: + if v == nil { + return nilField(key) + } + return Field{Key: key, Type: BinaryType, Interface: v} + case time.Time: + return Time(key, v) + case *time.Time: + if v == nil { + return nilField(key) + } + return Time(key, *v) + case time.Duration: + return Duration(key, v) + case *time.Duration: + if v == nil { + return nilField(key) + } + return Duration(key, *v) + case error: + return NamedErr(key, v) + case fmt.Stringer: + if v == nil { + return nilField(key) + } + return Field{Key: key, Type: StringerType, Interface: v} + case *fmt.Stringer: + if v == nil { + return nilField(key) + } + return Field{Key: key, Type: StringerType, Interface: *v} + default: + return Field{Key: key, Type: UnknownType, Interface: val} + } +} + +// FieldSorter provides sorting of an array of fields by key. +type FieldSorter []Field + +func (fs FieldSorter) Len() int { return len(fs) } +func (fs FieldSorter) Less(i, j int) bool { return fs[i].Key < fs[j].Key } +func (fs FieldSorter) Swap(i, j int) { fs[i], fs[j] = fs[j], fs[i] } diff --git a/vendor/github.com/mattermost/logr/v2/fieldapi.go b/vendor/github.com/mattermost/logr/v2/fieldapi.go new file mode 100644 index 0000000000..58b12280f2 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/fieldapi.go @@ -0,0 +1,110 @@ +package logr + +import ( + "fmt" + "time" +) + +// Any picks the best supported field type based on type of val. +// For best performance when passing a struct (or struct pointer), +// implement `logr.LogWriter` on the struct, otherwise reflection +// will be used to generate a string representation. +func Any(key string, val interface{}) Field { + return fieldForAny(key, val) +} + +// Int64 constructs a field containing a key and Int64 value. +func Int64(key string, val int64) Field { + return Field{Key: key, Type: Int64Type, Integer: val} +} + +// Int32 constructs a field containing a key and Int32 value. +func Int32(key string, val int32) Field { + return Field{Key: key, Type: Int32Type, Integer: int64(val)} +} + +// Int constructs a field containing a key and Int value. +func Int(key string, val int) Field { + return Field{Key: key, Type: IntType, Integer: int64(val)} +} + +// Uint64 constructs a field containing a key and Uint64 value. +func Uint64(key string, val uint64) Field { + return Field{Key: key, Type: Uint64Type, Integer: int64(val)} +} + +// Uint32 constructs a field containing a key and Uint32 value. +func Uint32(key string, val uint32) Field { + return Field{Key: key, Type: Uint32Type, Integer: int64(val)} +} + +// Uint constructs a field containing a key and Uint value. +func Uint(key string, val uint) Field { + return Field{Key: key, Type: UintType, Integer: int64(val)} +} + +// Float64 constructs a field containing a key and Float64 value. +func Float64(key string, val float64) Field { + return Field{Key: key, Type: Float64Type, Float: val} +} + +// Float32 constructs a field containing a key and Float32 value. +func Float32(key string, val float32) Field { + return Field{Key: key, Type: Float32Type, Float: float64(val)} +} + +// String constructs a field containing a key and String value. +func String(key string, val string) Field { + return Field{Key: key, Type: StringType, String: val} +} + +// Stringer constructs a field containing a key and a `fmt.Stringer` value. +// The `String` method will be called in lazy fashion. +func Stringer(key string, val fmt.Stringer) Field { + return Field{Key: key, Type: StringerType, Interface: val} +} + +// Err constructs a field containing a default key ("error") and error value. +func Err(err error) Field { + return NamedErr("error", err) +} + +// NamedErr constructs a field containing a key and error value. +func NamedErr(key string, err error) Field { + return Field{Key: key, Type: ErrorType, Interface: err} +} + +// Bool constructs a field containing a key and bool value. +func Bool(key string, val bool) Field { + var b int64 + if val { + b = 1 + } + return Field{Key: key, Type: BoolType, Integer: b} +} + +// Time constructs a field containing a key and time.Time value. +func Time(key string, val time.Time) Field { + return Field{Key: key, Type: TimeType, Interface: val} +} + +// Duration constructs a field containing a key and time.Duration value. +func Duration(key string, val time.Duration) Field { + return Field{Key: key, Type: DurationType, Integer: int64(val)} +} + +// Millis constructs a field containing a key and timestamp value. +// The timestamp is expected to be milliseconds since Jan 1, 1970 UTC. +func Millis(key string, val int64) Field { + return Field{Key: key, Type: TimestampMillisType, Integer: val} +} + +// Array constructs a field containing a key and array value. +func Array(key string, val interface{}) Field { + return Field{Key: key, Type: ArrayType, Interface: val} +} + +// Map constructs a field containing a key and map value. +func Map(key string, val interface{}) Field { + return Field{Key: key, Type: MapType, Interface: val} +} diff --git a/vendor/github.com/mattermost/logr/v2/filter.go b/vendor/github.com/mattermost/logr/v2/filter.go new file mode 100644 index 0000000000..a52a7cf4a5 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/filter.go @@ -0,0 +1,10 @@ +package logr + +// Filter allows targets to determine which Level(s) are active +// for logging and which Level(s) require a stack trace to be output. +// A default implementation using "panic, fatal..." is provided, and +// a more flexible alternative implementation is also provided that +// allows any number of custom levels. +type Filter interface { + GetEnabledLevel(level Level) (Level, bool) +} diff --git a/vendor/github.com/mattermost/logr/v2/filtercustom.go b/vendor/github.com/mattermost/logr/v2/filtercustom.go new file mode 100644 index 0000000000..c20f2811b2 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/filtercustom.go @@ -0,0 +1,47 @@ +package logr + +import ( + "sync" +) + +// CustomFilter allows targets to enable logging via a list of discrete levels. +type CustomFilter struct { + mux sync.RWMutex + levels map[LevelID]Level +} + +// NewCustomFilter creates a filter supporting discrete log levels. +func NewCustomFilter(levels ...Level) *CustomFilter { + filter := &CustomFilter{} + filter.Add(levels...) + return filter +} + +// GetEnabledLevel returns the Level with the specified Level.ID and whether the level +// is enabled for this filter. +func (cf *CustomFilter) GetEnabledLevel(level Level) (Level, bool) { + cf.mux.RLock() + defer cf.mux.RUnlock() + levelEnabled, ok := cf.levels[level.ID] + + if ok && levelEnabled.Name == "" { + levelEnabled.Name = level.Name + } + + return levelEnabled, ok +} + +// Add adds one or more levels to the list. Adding a level enables logging for +// that level on any targets using this CustomFilter. +func (cf *CustomFilter) Add(levels ...Level) { + cf.mux.Lock() + defer cf.mux.Unlock() + + if cf.levels == nil { + cf.levels = make(map[LevelID]Level) + } + + for _, s := range levels { + cf.levels[s.ID] = s + } +} diff --git a/vendor/github.com/mattermost/logr/v2/filterstd.go b/vendor/github.com/mattermost/logr/v2/filterstd.go new file mode 100644 index 0000000000..7f38a33228 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/filterstd.go @@ -0,0 +1,65 @@ +package logr + +// StdFilter allows targets to filter via classic log levels where any level +// beyond a certain verbosity/severity is enabled. +type StdFilter struct { + Lvl Level + Stacktrace Level +} + +// GetEnabledLevel returns the Level with the specified Level.ID and whether the level +// is enabled for this filter. +func (lt StdFilter) GetEnabledLevel(level Level) (Level, bool) { + enabled := level.ID <= lt.Lvl.ID + var levelEnabled Level + + if enabled { + switch level.ID { + case Panic.ID: + levelEnabled = Panic + case Fatal.ID: + levelEnabled = Fatal + case Error.ID: + levelEnabled = Error + case Warn.ID: + levelEnabled = Warn + case Info.ID: + levelEnabled = Info + case Debug.ID: + levelEnabled = Debug + case Trace.ID: + levelEnabled = Trace + default: + levelEnabled = level + } + } + return levelEnabled, enabled +} + +// IsEnabled returns true if the specified Level is at or above this verbosity. Also +// determines if a stack trace is required. +func (lt StdFilter) IsEnabled(level Level) bool { + return level.ID <= lt.Lvl.ID +} + +// IsStacktraceEnabled returns true if the specified Level requires a stack trace. +func (lt StdFilter) IsStacktraceEnabled(level Level) bool { + return level.ID <= lt.Stacktrace.ID +} + +var ( + // Panic is the highest level of severity. + Panic = Level{ID: 0, Name: "panic", Color: Red} + // Fatal designates a catastrophic error. + Fatal = Level{ID: 1, Name: "fatal", Color: Red} + // Error designates a serious but possibly recoverable error. + Error = Level{ID: 2, Name: "error", Color: Red} + // Warn designates non-critical error. + Warn = Level{ID: 3, Name: "warn", Color: Yellow} + // Info designates information regarding application events. + Info = Level{ID: 4, Name: "info", Color: Cyan} + // Debug designates verbose information typically used for debugging. + Debug = Level{ID: 5, Name: "debug", Color: NoColor} + // Trace designates the highest verbosity of log output. + Trace = Level{ID: 6, Name: "trace", Color: NoColor} +) diff --git a/vendor/github.com/mattermost/logr/v2/formatter.go b/vendor/github.com/mattermost/logr/v2/formatter.go new file mode 100644 index 0000000000..c8bb9b703d --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/formatter.go @@ -0,0 +1,184 @@ +package logr + +import ( + "bytes" + "io" + "runtime" + "strconv" +) + +// Formatter turns a LogRec into a formatted string. +type Formatter interface { + // IsStacktraceNeeded returns true if this formatter requires a stacktrace to be + // generated for each LogRecord. Enabling features such as `Caller` field require + // a stacktrace. + IsStacktraceNeeded() bool + + // Format converts a log record to bytes. If buf is not nil then it will be + // be filled with the formatted results, otherwise a new buffer will be allocated. + Format(rec *LogRec, level Level, buf *bytes.Buffer) (*bytes.Buffer, error) +} + +const ( + // DefTimestampFormat is the default time stamp format used by Plain formatter and others. + DefTimestampFormat = "2006-01-02 15:04:05.000 Z07:00" + + // TimestampMillisFormat is the format for logging milliseconds UTC + TimestampMillisFormat = "Jan _2 15:04:05.000" +) + +type Writer struct { + io.Writer +} + +func (w Writer) Writes(elems ...[]byte) (int, error) { + var count int + for _, e := range elems { + if c, err := w.Write(e); err != nil { + return count + c, err + } else { + count += c + } + } + return count, nil +} + +// DefaultFormatter is the default formatter, outputting only text with +// no colors and a space delimiter. Use `format.Plain` instead. +type DefaultFormatter struct { +} + +// IsStacktraceNeeded always returns false for default formatter since the +// `Caller` field is not supported. +func (p *DefaultFormatter) IsStacktraceNeeded() bool { + return false +} + +// Format converts a log record to bytes. +func (p *DefaultFormatter) Format(rec *LogRec, level Level, buf *bytes.Buffer) (*bytes.Buffer, error) { + if buf == nil { + buf = &bytes.Buffer{} + } + timestampFmt := DefTimestampFormat + + buf.WriteString(rec.Time().Format(timestampFmt)) + buf.Write(Space) + + buf.WriteString(level.Name) + buf.Write(Space) + + buf.WriteString(rec.Msg()) + buf.Write(Space) + + fields := rec.Fields() + if len(fields) > 0 { + if err := WriteFields(buf, fields, Space, NoColor); err != nil { + return nil, err + } + } + + if level.Stacktrace { + frames := rec.StackFrames() + if len(frames) > 0 { + buf.Write(Newline) + if err := WriteStacktrace(buf, rec.StackFrames()); err != nil { + return nil, err + } + } + } + buf.Write(Newline) + + return buf, nil +} + +// WriteFields writes zero or more name value pairs to the io.Writer. +// The pairs output in key=value format with optional separator between fields. +func WriteFields(w io.Writer, fields []Field, separator []byte, color Color) error { + ws := Writer{w} + + sep := []byte{} + for _, field := range fields { + if err := writeField(ws, field, sep, color); err != nil { + return err + } + sep = separator + } + return nil +} + +func writeField(ws Writer, field Field, sep []byte, color Color) error { + if len(sep) != 0 { + if _, err := ws.Write(sep); err != nil { + return err + } + } + if err := WriteWithColor(ws, field.Key, color); err != nil { + return err + } + if _, err := ws.Write(Equals); err != nil { + return err + } + return field.ValueString(ws, shouldQuote) +} + +// shouldQuote returns true if val contains any characters that might be unsafe +// when injecting log output into an aggregator, viewer or report. +func shouldQuote(val string) bool { + for _, c := range val { + if !((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '-' || c == '.' || c == '_' || c == '/' || c == '@' || c == '^' || c == '+') { + return true + } + } + return false +} + +// WriteStacktrace formats and outputs a stack trace to an io.Writer. +func WriteStacktrace(w io.Writer, frames []runtime.Frame) error { + ws := Writer{w} + for _, frame := range frames { + if frame.Function != "" { + if _, err := ws.Writes(Space, Space, []byte(frame.Function), Newline); err != nil { + return err + } + } + if frame.File != "" { + s := strconv.FormatInt(int64(frame.Line), 10) + if _, err := ws.Writes([]byte{' ', ' ', ' ', ' ', ' ', ' '}, []byte(frame.File), Colon, []byte(s), Newline); err != nil { + return err + } + } + } + return nil +} + +// WriteWithColor outputs a string with the specified ANSI color. +func WriteWithColor(w io.Writer, s string, color Color) error { + var err error + + writer := func(buf []byte) { + if err != nil { + return + } + _, err = w.Write(buf) + } + + if color != NoColor { + writer(AnsiColorPrefix) + writer([]byte(strconv.FormatInt(int64(color), 10))) + writer(AnsiColorSuffix) + } + + if err == nil { + _, err = io.WriteString(w, s) + } + + if color != NoColor { + writer(AnsiColorPrefix) + writer([]byte(strconv.FormatInt(int64(NoColor), 10))) + writer(AnsiColorSuffix) + } + return err +} diff --git a/vendor/github.com/mattermost/logr/v2/formatters/gelf.go b/vendor/github.com/mattermost/logr/v2/formatters/gelf.go new file mode 100644 index 0000000000..9dece13ca7 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/formatters/gelf.go @@ -0,0 +1,152 @@ +package formatters + +import ( + "bytes" + "fmt" + "net" + "os" + "strings" + + "github.com/francoispqt/gojay" + "github.com/mattermost/logr/v2" +) + +const ( + GelfVersion = "1.1" + GelfVersionKey = "version" + GelfHostKey = "host" + GelfShortKey = "short_message" + GelfFullKey = "full_message" + GelfTimestampKey = "timestamp" + GelfLevelKey = "level" +) + +// Gelf formats log records as GELF rcords (https://docs.graylog.org/en/4.0/pages/gelf.html). +type Gelf struct { + // Hostname allows a custom hostname, otherwise os.Hostname is used + Hostname string `json:"hostname"` + + // EnableCaller enables output of the file and line number that emitted a log record. + EnableCaller bool `json:"enable_caller"` + + // FieldSorter allows custom sorting for the context fields. + FieldSorter func(fields []logr.Field) []logr.Field `json:"-"` +} + +func (g *Gelf) CheckValid() error { + return nil +} + +// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field. +func (g *Gelf) IsStacktraceNeeded() bool { + return g.EnableCaller +} + +// Format converts a log record to bytes in GELF format. +func (g *Gelf) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) { + if buf == nil { + buf = &bytes.Buffer{} + } + enc := gojay.BorrowEncoder(buf) + defer func() { + enc.Release() + }() + + gr := gelfRecord{ + LogRec: rec, + Gelf: g, + level: level, + sorter: g.FieldSorter, + } + + err := enc.EncodeObject(gr) + if err != nil { + return nil, err + } + + buf.WriteByte(0) + return buf, nil +} + +type gelfRecord struct { + *logr.LogRec + *Gelf + level logr.Level + sorter func(fields []logr.Field) []logr.Field +} + +// MarshalJSONObject encodes the LogRec as JSON. +func (gr gelfRecord) MarshalJSONObject(enc *gojay.Encoder) { + enc.AddStringKey(GelfVersionKey, GelfVersion) + enc.AddStringKey(GelfHostKey, gr.getHostname()) + enc.AddStringKey(GelfShortKey, gr.Msg()) + + if gr.level.Stacktrace { + frames := gr.StackFrames() + if len(frames) != 0 { + var sbuf strings.Builder + for _, frame := range frames { + fmt.Fprintf(&sbuf, "%s\n %s:%d\n", frame.Function, frame.File, frame.Line) + } + enc.AddStringKey(GelfFullKey, sbuf.String()) + } + } + + secs := float64(gr.Time().UTC().Unix()) + millis := float64(gr.Time().Nanosecond() / 1000000) + ts := secs + (millis / 1000) + enc.AddFloat64Key(GelfTimestampKey, ts) + + enc.AddUint32Key(GelfLevelKey, uint32(gr.level.ID)) + + var fields []logr.Field + if gr.EnableCaller { + caller := logr.Field{ + Key: "_caller", + Type: logr.StringType, + String: gr.LogRec.Caller(), + } + fields = append(fields, caller) + } + + fields = append(fields, gr.Fields()...) + if gr.sorter != nil { + fields = gr.sorter(fields) + } + + if len(fields) > 0 { + for _, field := range fields { + if !strings.HasPrefix("_", field.Key) { + field.Key = "_" + field.Key + } + if err := encodeField(enc, field); err != nil { + enc.AddStringKey(field.Key, fmt.Sprintf("", err)) + } + } + } +} + +// IsNil returns true if the gelf record pointer is nil. +func (gr gelfRecord) IsNil() bool { + return gr.LogRec == nil +} + +func (g *Gelf) getHostname() string { + if g.Hostname != "" { + return g.Hostname + } + h, err := os.Hostname() + if err == nil { + return h + } + + // get the egress IP by fake dialing any address. UDP ensures no dial. + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + return "unknown" + } + defer conn.Close() + + local := conn.LocalAddr().(*net.UDPAddr) + return local.IP.String() +} diff --git a/vendor/github.com/mattermost/logr/v2/formatters/json.go b/vendor/github.com/mattermost/logr/v2/formatters/json.go new file mode 100644 index 0000000000..172b9612dc --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/formatters/json.go @@ -0,0 +1,273 @@ +package formatters + +import ( + "bytes" + "encoding/json" + "fmt" + "runtime" + "strings" + "sync" + + "github.com/francoispqt/gojay" + "github.com/mattermost/logr/v2" +) + +// JSON formats log records as JSON. +type JSON struct { + // DisableTimestamp disables output of timestamp field. + DisableTimestamp bool `json:"disable_timestamp"` + // DisableLevel disables output of level field. + DisableLevel bool `json:"disable_level"` + // DisableMsg disables output of msg field. + DisableMsg bool `json:"disable_msg"` + // DisableFields disables output of all fields. + DisableFields bool `json:"disable_fields"` + // DisableStacktrace disables output of stack trace. + DisableStacktrace bool `json:"disable_stacktrace"` + // EnableCaller enables output of the file and line number that emitted a log record. + EnableCaller bool `json:"enable_caller"` + + // TimestampFormat is an optional format for timestamps. If empty + // then DefTimestampFormat is used. + TimestampFormat string `json:"timestamp_format"` + + // KeyTimestamp overrides the timestamp field key name. + KeyTimestamp string `json:"key_timestamp"` + + // KeyLevel overrides the level field key name. + KeyLevel string `json:"key_level"` + + // KeyMsg overrides the msg field key name. + KeyMsg string `json:"key_msg"` + + // KeyGroupFields when not empty will group all context fields + // under this key. + KeyGroupFields string `json:"key_group_fields"` + + // KeyStacktrace overrides the stacktrace field key name. + KeyStacktrace string `json:"key_stacktrace"` + + // KeyCaller overrides the caller field key name. + KeyCaller string `json:"key_caller"` + + // FieldSorter allows custom sorting of the fields. If nil then + // no sorting is done. + FieldSorter func(fields []logr.Field) []logr.Field `json:"-"` + + once sync.Once +} + +func (j *JSON) CheckValid() error { + return nil +} + +// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field. +func (j *JSON) IsStacktraceNeeded() bool { + return j.EnableCaller +} + +// Format converts a log record to bytes in JSON format. +func (j *JSON) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) { + j.once.Do(j.applyDefaultKeyNames) + + if buf == nil { + buf = &bytes.Buffer{} + } + enc := gojay.BorrowEncoder(buf) + defer func() { + enc.Release() + }() + + jlr := JSONLogRec{ + LogRec: rec, + JSON: j, + level: level, + sorter: j.FieldSorter, + } + + err := enc.EncodeObject(jlr) + if err != nil { + return nil, err + } + buf.WriteByte('\n') + return buf, nil +} + +func (j *JSON) applyDefaultKeyNames() { + if j.KeyTimestamp == "" { + j.KeyTimestamp = "timestamp" + } + if j.KeyLevel == "" { + j.KeyLevel = "level" + } + if j.KeyMsg == "" { + j.KeyMsg = "msg" + } + if j.KeyStacktrace == "" { + j.KeyStacktrace = "stacktrace" + } + if j.KeyCaller == "" { + j.KeyCaller = "caller" + } +} + +// JSONLogRec decorates a LogRec adding JSON encoding. +type JSONLogRec struct { + *logr.LogRec + *JSON + level logr.Level + sorter func(fields []logr.Field) []logr.Field +} + +// MarshalJSONObject encodes the LogRec as JSON. +func (jlr JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) { + if !jlr.DisableTimestamp { + timestampFmt := jlr.TimestampFormat + if timestampFmt == "" { + timestampFmt = logr.DefTimestampFormat + } + time := jlr.Time() + enc.AddTimeKey(jlr.KeyTimestamp, &time, timestampFmt) + } + if !jlr.DisableLevel { + enc.AddStringKey(jlr.KeyLevel, jlr.level.Name) + } + if !jlr.DisableMsg { + enc.AddStringKey(jlr.KeyMsg, jlr.Msg()) + } + if jlr.EnableCaller { + enc.AddStringKey(jlr.KeyCaller, jlr.Caller()) + } + if !jlr.DisableFields { + fields := jlr.Fields() + if jlr.sorter != nil { + fields = jlr.sorter(fields) + } + if jlr.KeyGroupFields != "" { + enc.AddObjectKey(jlr.KeyGroupFields, FieldArray(fields)) + } else { + if len(fields) > 0 { + for _, field := range fields { + field = jlr.prefixCollision(field) + if err := encodeField(enc, field); err != nil { + enc.AddStringKey(field.Key, "") + } + } + } + } + } + if jlr.level.Stacktrace && !jlr.DisableStacktrace { + frames := jlr.StackFrames() + if len(frames) > 0 { + enc.AddArrayKey(jlr.KeyStacktrace, stackFrames(frames)) + } + } +} + +// IsNil returns true if the LogRec pointer is nil. +func (rec JSONLogRec) IsNil() bool { + return rec.LogRec == nil +} + +func (rec JSONLogRec) prefixCollision(field logr.Field) logr.Field { + switch field.Key { + case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace: + f := field + f.Key = "_" + field.Key + return rec.prefixCollision(f) + } + return field +} + +type stackFrames []runtime.Frame + +// MarshalJSONArray encodes stackFrames slice as JSON. +func (s stackFrames) MarshalJSONArray(enc *gojay.Encoder) { + for _, frame := range s { + enc.AddObject(stackFrame(frame)) + } +} + +// IsNil returns true if stackFrames is empty slice. +func (s stackFrames) IsNil() bool { + return len(s) == 0 +} + +type stackFrame runtime.Frame + +// MarshalJSONArray encodes stackFrame as JSON. +func (f stackFrame) MarshalJSONObject(enc *gojay.Encoder) { + enc.AddStringKey("Function", f.Function) + enc.AddStringKey("File", f.File) + enc.AddIntKey("Line", f.Line) +} + +func (f stackFrame) IsNil() bool { + return false +} + +type FieldArray []logr.Field + +// MarshalJSONObject encodes Fields map to JSON. +func (fa FieldArray) MarshalJSONObject(enc *gojay.Encoder) { + for _, fld := range fa { + if err := encodeField(enc, fld); err != nil { + enc.AddStringKey(fld.Key, "") + } + } +} + +// IsNil returns true if map is nil. +func (fa FieldArray) IsNil() bool { + return fa == nil +} + +func encodeField(enc *gojay.Encoder, field logr.Field) error { + // first check if the value has a marshaller already. + switch vt := field.Interface.(type) { + case gojay.MarshalerJSONObject: + enc.AddObjectKey(field.Key, vt) + return nil + case gojay.MarshalerJSONArray: + enc.AddArrayKey(field.Key, vt) + return nil + } + + switch field.Type { + case logr.StringType: + enc.AddStringKey(field.Key, field.String) + + case logr.BoolType: + var b bool + if field.Integer != 0 { + b = true + } + enc.AddBoolKey(field.Key, b) + + case logr.StructType, logr.ArrayType, logr.MapType, logr.UnknownType: + b, err := json.Marshal(field.Interface) + if err != nil { + return err + } + embed := gojay.EmbeddedJSON(b) + enc.AddEmbeddedJSONKey(field.Key, &embed) + + case logr.StringerType, logr.ErrorType, logr.TimestampMillisType, logr.TimeType, logr.DurationType, logr.BinaryType: + var buf strings.Builder + _ = field.ValueString(&buf, nil) + enc.AddStringKey(field.Key, buf.String()) + + case logr.Int64Type, logr.Int32Type, logr.IntType: + enc.AddInt64Key(field.Key, field.Integer) + + case logr.Uint64Type, logr.Uint32Type, logr.UintType: + enc.AddUint64Key(field.Key, uint64(field.Integer)) + + case logr.Float64Type, logr.Float32Type: + enc.AddFloat64Key(field.Key, field.Float) + + default: + return fmt.Errorf("invalid field type: %d", field.Type) + } + return nil +} diff --git a/vendor/github.com/mattermost/logr/v2/formatters/plain.go b/vendor/github.com/mattermost/logr/v2/formatters/plain.go new file mode 100644 index 0000000000..4d8af643b9 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/formatters/plain.go @@ -0,0 +1,146 @@ +package formatters + +import ( + "bytes" + "fmt" + "strings" + + "github.com/mattermost/logr/v2" +) + +// Plain is the simplest formatter, outputting only text with +// no colors. +type Plain struct { + // DisableTimestamp disables output of timestamp field. + DisableTimestamp bool `json:"disable_timestamp"` + // DisableLevel disables output of level field. + DisableLevel bool `json:"disable_level"` + // DisableMsg disables output of msg field. + DisableMsg bool `json:"disable_msg"` + // DisableFields disables output of all fields. + DisableFields bool `json:"disable_fields"` + // DisableStacktrace disables output of stack trace. + DisableStacktrace bool `json:"disable_stacktrace"` + // EnableCaller enables output of the file and line number that emitted a log record. + EnableCaller bool `json:"enable_caller"` + + // Delim is an optional delimiter output between each log field. + // Defaults to a single space. + Delim string `json:"delim"` + + // MinLevelLen sets the minimum level name length. If the level name is less + // than the minimum it will be padded with spaces. + MinLevelLen int `json:"min_level_len"` + + // MinMessageLen sets the minimum msg length. If the msg text is less + // than the minimum it will be padded with spaces. + MinMessageLen int `json:"min_msg_len"` + + // TimestampFormat is an optional format for timestamps. If empty + // then DefTimestampFormat is used. + TimestampFormat string `json:"timestamp_format"` + + // LineEnd sets the end of line character(s). Defaults to '\n'. + LineEnd string `json:"line_end"` + + // EnableColor sets whether output should include color. + EnableColor bool `json:"enable_color"` +} + +func (p *Plain) CheckValid() error { + if p.MinMessageLen < 0 || p.MinMessageLen > 1024 { + return fmt.Errorf("min_msg_len is invalid(%d)", p.MinMessageLen) + } + return nil +} + +// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field. +func (p *Plain) IsStacktraceNeeded() bool { + return p.EnableCaller +} + +// Format converts a log record to bytes. +func (p *Plain) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) { + delim := p.Delim + if delim == "" { + delim = " " + } + if buf == nil { + buf = &bytes.Buffer{} + } + + timestampFmt := p.TimestampFormat + if timestampFmt == "" { + timestampFmt = logr.DefTimestampFormat + } + + color := logr.NoColor + if p.EnableColor { + color = level.Color + } + + if !p.DisableLevel { + _ = logr.WriteWithColor(buf, level.Name, color) + count := len(level.Name) + if p.MinLevelLen > count { + _, _ = buf.WriteString(strings.Repeat(" ", p.MinLevelLen-count)) + } + buf.WriteString(delim) + } + + if !p.DisableTimestamp { + var arr [128]byte + tbuf := rec.Time().AppendFormat(arr[:0], timestampFmt) + buf.WriteByte('[') + buf.Write(tbuf) + buf.WriteByte(']') + buf.WriteString(delim) + } + + if !p.DisableMsg { + count, _ := buf.WriteString(rec.Msg()) + if p.MinMessageLen > count { + _, _ = buf.WriteString(strings.Repeat(" ", p.MinMessageLen-count)) + } + _, _ = buf.WriteString(delim) + } + + var fields []logr.Field + + if p.EnableCaller { + fld := logr.Field{ + Key: "caller", + Type: logr.StringType, + String: rec.Caller(), + } + fields = append(fields, fld) + } + + if !p.DisableFields { + fields = append(fields, rec.Fields()...) + } + + if len(fields) > 0 { + if err := logr.WriteFields(buf, fields, logr.Space, color); err != nil { + return nil, err + } + } + + if level.Stacktrace && !p.DisableStacktrace { + frames := rec.StackFrames() + if len(frames) > 0 { + buf.WriteString("\n") + if err := logr.WriteStacktrace(buf, rec.StackFrames()); err != nil { + return nil, err + } + } + } + + if p.LineEnd == "" { + buf.WriteString("\n") + } else { + buf.WriteString(p.LineEnd) + } + + return buf, nil +} diff --git a/vendor/github.com/mattermost/logr/go.mod b/vendor/github.com/mattermost/logr/v2/go.mod similarity index 55% rename from vendor/github.com/mattermost/logr/go.mod rename to vendor/github.com/mattermost/logr/v2/go.mod index e8e8acfb2f..4cbe375e7d 100644 --- a/vendor/github.com/mattermost/logr/go.mod +++ b/vendor/github.com/mattermost/logr/v2/go.mod @@ -1,11 +1,11 @@ -module github.com/mattermost/logr +module github.com/mattermost/logr/v2 go 1.12 require ( github.com/francoispqt/gojay v1.2.13 - github.com/stretchr/testify v1.2.2 - github.com/wiggin77/cfg v1.0.2 + github.com/stretchr/testify v1.4.0 github.com/wiggin77/merror v1.0.2 + github.com/wiggin77/srslog v1.0.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) diff --git a/vendor/github.com/mattermost/logr/go.sum b/vendor/github.com/mattermost/logr/v2/go.sum similarity index 97% rename from vendor/github.com/mattermost/logr/go.sum rename to vendor/github.com/mattermost/logr/v2/go.sum index ea688513e9..ae50449220 100644 --- a/vendor/github.com/mattermost/logr/go.sum +++ b/vendor/github.com/mattermost/logr/v2/go.sum @@ -15,6 +15,7 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -93,15 +94,18 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/wiggin77/cfg v1.0.2 h1:NBUX+iJRr+RTncTqTNvajHwzduqbhCQjEqxLHr6Fk7A= -github.com/wiggin77/cfg v1.0.2/go.mod h1:b3gotba2e5bXTqTW48DwIFoLc+4lWKP7WPi/CdvZ4aE= github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw= github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg= +github.com/wiggin77/srslog v1.0.1 h1:gA2XjSMy3DrRdX9UqLuDtuVAAshb8bE1NhX1YK0Qe+8= +github.com/wiggin77/srslog v1.0.1/go.mod h1:fehkyYDq1QfuYn60TDPu9YdY2bB85VUW2mvN1WynEls= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= diff --git a/vendor/github.com/mattermost/logr/v2/level.go b/vendor/github.com/mattermost/logr/v2/level.go new file mode 100644 index 0000000000..643d68e34a --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/level.go @@ -0,0 +1,34 @@ +package logr + +var AnsiColorPrefix = []byte("\u001b[") +var AnsiColorSuffix = []byte("m") + +// Color for formatters that support color output. +type Color uint8 + +const ( + NoColor Color = 0 + Red Color = 31 + Green Color = 32 + Yellow Color = 33 + Blue Color = 34 + Magenta Color = 35 + Cyan Color = 36 + White Color = 37 +) + +// LevelID is the unique id of each level. +type LevelID uint + +// Level provides a mechanism to enable/disable specific log lines. +type Level struct { + ID LevelID `json:"id"` + Name string `json:"name"` + Stacktrace bool `json:"stacktrace,omitempty"` + Color Color `json:"color,omitempty"` +} + +// String returns the name of this level. +func (level Level) String() string { + return level.Name +} diff --git a/vendor/github.com/mattermost/logr/levelcache.go b/vendor/github.com/mattermost/logr/v2/levelcache.go similarity index 100% rename from vendor/github.com/mattermost/logr/levelcache.go rename to vendor/github.com/mattermost/logr/v2/levelcache.go diff --git a/vendor/github.com/mattermost/logr/v2/logger.go b/vendor/github.com/mattermost/logr/v2/logger.go new file mode 100644 index 0000000000..6ce9c9f06f --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/logger.go @@ -0,0 +1,99 @@ +package logr + +import "log" + +// Logger provides context for logging via fields. +type Logger struct { + lgr *Logr + fields []Field +} + +// Logr returns the `Logr` instance that created this `Logger`. +func (logger Logger) Logr() *Logr { + return logger.lgr +} + +// With creates a new `Logger` with any existing fields plus the new ones. +func (logger Logger) With(fields ...Field) Logger { + l := Logger{lgr: logger.lgr} + size := len(logger.fields) + len(fields) + if size > 0 { + l.fields = make([]Field, 0, size) + l.fields = append(l.fields, logger.fields...) + l.fields = append(l.fields, fields...) + } + return l +} + +// StdLogger creates a standard logger backed by this `Logr.Logger` instance. +// All log records are emitted with the specified log level. +func (logger Logger) StdLogger(level Level) *log.Logger { + return NewStdLogger(level, logger) +} + +// IsLevelEnabled determines if the specified level is enabled for at least +// one log target. +func (logger Logger) IsLevelEnabled(level Level) bool { + status := logger.Logr().IsLevelEnabled(level) + return status.Enabled +} + +// Sugar creates a new `Logger` with a less structured API. Any fields are preserved. +func (logger Logger) Sugar(fields ...Field) Sugar { + return Sugar{ + logger: logger.With(fields...), + } +} + +// Log checks that the level matches one or more targets, and +// if so, generates a log record that is added to the Logr queue. +// Arguments are handled in the manner of fmt.Print. +func (logger Logger) Log(lvl Level, msg string, fields ...Field) { + status := logger.lgr.IsLevelEnabled(lvl) + if status.Enabled { + rec := NewLogRec(lvl, logger, msg, fields, status.Stacktrace) + logger.lgr.enqueue(rec) + } +} + +// LogM calls `Log` multiple times, one for each level provided. +func (logger Logger) LogM(levels []Level, msg string, fields ...Field) { + for _, lvl := range levels { + logger.Log(lvl, msg, fields...) + } +} + +// Trace is a convenience method equivalent to `Log(TraceLevel, msg, fields...)`. +func (logger Logger) Trace(msg string, fields ...Field) { + logger.Log(Trace, msg, fields...) +} + +// Debug is a convenience method equivalent to `Log(DebugLevel, msg, fields...)`. +func (logger Logger) Debug(msg string, fields ...Field) { + logger.Log(Debug, msg, fields...) +} + +// Info is a convenience method equivalent to `Log(InfoLevel, msg, fields...)`. +func (logger Logger) Info(msg string, fields ...Field) { + logger.Log(Info, msg, fields...) +} + +// Warn is a convenience method equivalent to `Log(WarnLevel, msg, fields...)`. +func (logger Logger) Warn(msg string, fields ...Field) { + logger.Log(Warn, msg, fields...) +} + +// Error is a convenience method equivalent to `Log(ErrorLevel, msg, fields...)`. +func (logger Logger) Error(msg string, fields ...Field) { + logger.Log(Error, msg, fields...) +} + +// Fatal is a convenience method equivalent to `Log(FatalLevel, msg, fields...)` +func (logger Logger) Fatal(msg string, fields ...Field) { + logger.Log(Fatal, msg, fields...) +} + +// Panic is a convenience method equivalent to `Log(PanicLevel, msg, fields...)` +func (logger Logger) Panic(msg string, fields ...Field) { + logger.Log(Panic, msg, fields...) +} diff --git a/vendor/github.com/mattermost/logr/v2/logr.go b/vendor/github.com/mattermost/logr/v2/logr.go new file mode 100644 index 0000000000..82b2a835f1 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/logr.go @@ -0,0 +1,471 @@ +package logr + +import ( + "bytes" + "context" + "errors" + "fmt" + "os" + "sync" + "sync/atomic" + "time" + + "github.com/wiggin77/merror" +) + +// Logr maintains a list of log targets and accepts incoming +// log records. Use `New` to create instances. +type Logr struct { + tmux sync.RWMutex // targetHosts mutex + targetHosts []*TargetHost + + in chan *LogRec + quit chan struct{} // closed by Shutdown to exit read loop + done chan struct{} // closed when read loop exited + lvlCache levelCache + bufferPool sync.Pool + options *options + + metricsMux sync.RWMutex + metrics *metrics + + shutdown int32 +} + +// New creates a new Logr instance with one or more options specified. +// Some options with invalid values can cause an error to be returned, +// however `logr.New()` using just defaults never errors. +func New(opts ...Option) (*Logr, error) { + options := &options{ + maxQueueSize: DefaultMaxQueueSize, + enqueueTimeout: DefaultEnqueueTimeout, + shutdownTimeout: DefaultShutdownTimeout, + flushTimeout: DefaultFlushTimeout, + maxPooledBuffer: DefaultMaxPooledBuffer, + } + + lgr := &Logr{options: options} + + // apply the options + for _, opt := range opts { + if err := opt(lgr); err != nil { + return nil, err + } + } + pkgName := GetLogrPackageName() + if pkgName != "" { + opt := StackFilter(pkgName, pkgName+"/targets", pkgName+"/formatters") + _ = opt(lgr) + } + + lgr.in = make(chan *LogRec, lgr.options.maxQueueSize) + lgr.quit = make(chan struct{}) + lgr.done = make(chan struct{}) + + if lgr.options.useSyncMapLevelCache { + lgr.lvlCache = &syncMapLevelCache{} + } else { + lgr.lvlCache = &arrayLevelCache{} + } + lgr.lvlCache.setup() + + lgr.bufferPool = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, + } + + lgr.initMetrics(lgr.options.metricsCollector, lgr.options.metricsUpdateFreqMillis) + + go lgr.start() + + return lgr, nil +} + +// AddTarget adds a target to the logger which will receive +// log records for outputting. +func (lgr *Logr) AddTarget(target Target, name string, filter Filter, formatter Formatter, maxQueueSize int) error { + if lgr.IsShutdown() { + return fmt.Errorf("AddTarget called after Logr shut down") + } + + lgr.metricsMux.RLock() + metrics := lgr.metrics + lgr.metricsMux.RUnlock() + + hostOpts := targetHostOptions{ + name: name, + filter: filter, + formatter: formatter, + maxQueueSize: maxQueueSize, + metrics: metrics, + } + + host, err := newTargetHost(target, hostOpts) + if err != nil { + return err + } + + lgr.tmux.Lock() + defer lgr.tmux.Unlock() + + lgr.targetHosts = append(lgr.targetHosts, host) + + lgr.ResetLevelCache() + + return nil +} + +// NewLogger creates a Logger using defaults. A `Logger` is light-weight +// enough to create on-demand, but typically one or more Loggers are +// created and re-used. +func (lgr *Logr) NewLogger() Logger { + logger := Logger{lgr: lgr} + return logger +} + +var levelStatusDisabled = LevelStatus{} + +// IsLevelEnabled returns true if at least one target has the specified +// level enabled. The result is cached so that subsequent checks are fast. +func (lgr *Logr) IsLevelEnabled(lvl Level) LevelStatus { + // No levels enabled after shutdown + if atomic.LoadInt32(&lgr.shutdown) != 0 { + return levelStatusDisabled + } + + // Check cache. + status, ok := lgr.lvlCache.get(lvl.ID) + if ok { + return status + } + + status = LevelStatus{} + + // Cache miss; check each target. + lgr.tmux.RLock() + defer lgr.tmux.RUnlock() + for _, host := range lgr.targetHosts { + enabled, level := host.IsLevelEnabled(lvl) + if enabled { + status.Enabled = true + if level.Stacktrace || host.formatter.IsStacktraceNeeded() { + status.Stacktrace = true + break // if both level and stacktrace enabled then no sense checking more targets + } + } + } + + // Cache and return the result. + if err := lgr.lvlCache.put(lvl.ID, status); err != nil { + lgr.ReportError(err) + return LevelStatus{} + } + return status +} + +// HasTargets returns true only if at least one target exists within the lgr. +func (lgr *Logr) HasTargets() bool { + lgr.tmux.RLock() + defer lgr.tmux.RUnlock() + return len(lgr.targetHosts) > 0 +} + +// TargetInfo provides name and type for a Target. +type TargetInfo struct { + Name string + Type string +} + +// TargetInfos enumerates all the targets added to this lgr. +// The resulting slice represents a snapshot at time of calling. +func (lgr *Logr) TargetInfos() []TargetInfo { + infos := make([]TargetInfo, 0) + + lgr.tmux.RLock() + defer lgr.tmux.RUnlock() + + for _, host := range lgr.targetHosts { + inf := TargetInfo{ + Name: host.String(), + Type: fmt.Sprintf("%T", host.target), + } + infos = append(infos, inf) + } + return infos +} + +// RemoveTargets safely removes one or more targets based on the filtering method. +// f should return true to delete the target, false to keep it. +// When removing a target, best effort is made to write any queued log records before +// closing, with cxt determining how much time can be spent in total. +// Note, keep the timeout short since this method blocks certain logging operations. +func (lgr *Logr) RemoveTargets(cxt context.Context, f func(ti TargetInfo) bool) error { + errs := merror.New() + hosts := make([]*TargetHost, 0) + + lgr.tmux.Lock() + defer lgr.tmux.Unlock() + + for _, host := range lgr.targetHosts { + inf := TargetInfo{ + Name: host.String(), + Type: fmt.Sprintf("%T", host.target), + } + if f(inf) { + if err := host.Shutdown(cxt); err != nil { + errs.Append(err) + } + } else { + hosts = append(hosts, host) + } + } + + lgr.targetHosts = hosts + lgr.ResetLevelCache() + + return errs.ErrorOrNil() +} + +// ResetLevelCache resets the cached results of `IsLevelEnabled`. This is +// called any time a Target is added or a target's level is changed. +func (lgr *Logr) ResetLevelCache() { + lgr.lvlCache.clear() +} + +// SetMetricsCollector sets (or resets) the metrics collector to be used for gathering +// metrics for all targets. Only targets added after this call will use the collector. +// +// To ensure all targets use a collector, use the `SetMetricsCollector` option when +// creating the Logr instead, or configure/reconfigure the Logr after calling this method. +func (lgr *Logr) SetMetricsCollector(collector MetricsCollector, updateFreqMillis int64) { + lgr.initMetrics(collector, updateFreqMillis) +} + +// enqueue adds a log record to the logr queue. If the queue is full then +// this function either blocks or the log record is dropped, depending on +// the result of calling `OnQueueFull`. +func (lgr *Logr) enqueue(rec *LogRec) { + select { + case lgr.in <- rec: + default: + if lgr.options.onQueueFull != nil && lgr.options.onQueueFull(rec, cap(lgr.in)) { + return // drop the record + } + select { + case <-time.After(lgr.options.enqueueTimeout): + lgr.ReportError(fmt.Errorf("enqueue timed out for log rec [%v]", rec)) + case lgr.in <- rec: // block until success or timeout + } + } +} + +// Flush blocks while flushing the logr queue and all target queues, by +// writing existing log records to valid targets. +// Any attempts to add new log records will block until flush is complete. +// `logr.FlushTimeout` determines how long flush can execute before +// timing out. Use `IsTimeoutError` to determine if the returned error is +// due to a timeout. +func (lgr *Logr) Flush() error { + ctx, cancel := context.WithTimeout(context.Background(), lgr.options.flushTimeout) + defer cancel() + return lgr.FlushWithTimeout(ctx) +} + +// Flush blocks while flushing the logr queue and all target queues, by +// writing existing log records to valid targets. +// Any attempts to add new log records will block until flush is complete. +// Use `IsTimeoutError` to determine if the returned error is +// due to a timeout. +func (lgr *Logr) FlushWithTimeout(ctx context.Context) error { + if !lgr.HasTargets() { + return nil + } + + if lgr.IsShutdown() { + return errors.New("Flush called on shut down Logr") + } + + rec := newFlushLogRec(lgr.NewLogger()) + lgr.enqueue(rec) + + select { + case <-ctx.Done(): + return newTimeoutError("logr queue flush timeout") + case <-rec.flush: + } + return nil +} + +// IsShutdown returns true if this Logr instance has been shut down. +// No further log records can be enqueued and no targets added after +// shutdown. +func (lgr *Logr) IsShutdown() bool { + return atomic.LoadInt32(&lgr.shutdown) != 0 +} + +// Shutdown cleanly stops the logging engine after making best efforts +// to flush all targets. Call this function right before application +// exit - logr cannot be restarted once shut down. +// `logr.ShutdownTimeout` determines how long shutdown can execute before +// timing out. Use `IsTimeoutError` to determine if the returned error is +// due to a timeout. +func (lgr *Logr) Shutdown() error { + ctx, cancel := context.WithTimeout(context.Background(), lgr.options.shutdownTimeout) + defer cancel() + return lgr.ShutdownWithTimeout(ctx) +} + +// Shutdown cleanly stops the logging engine after making best efforts +// to flush all targets. Call this function right before application +// exit - logr cannot be restarted once shut down. +// Use `IsTimeoutError` to determine if the returned error is due to a +// timeout. +func (lgr *Logr) ShutdownWithTimeout(ctx context.Context) error { + if err := lgr.FlushWithTimeout(ctx); err != nil { + return err + } + + if atomic.SwapInt32(&lgr.shutdown, 1) != 0 { + return errors.New("Shutdown called again after shut down") + } + + lgr.ResetLevelCache() + lgr.stopMetricsUpdater() + + close(lgr.quit) + + errs := merror.New() + + // Wait for read loop to exit + select { + case <-ctx.Done(): + errs.Append(newTimeoutError("logr queue shutdown timeout")) + case <-lgr.done: + } + + // logr.in channel should now be drained to targets and no more log records + // can be added. + lgr.tmux.RLock() + defer lgr.tmux.RUnlock() + for _, host := range lgr.targetHosts { + err := host.Shutdown(ctx) + if err != nil { + errs.Append(err) + } + } + return errs.ErrorOrNil() +} + +// ReportError is used to notify the host application of any internal logging errors. +// If `OnLoggerError` is not nil, it is called with the error, otherwise the error is +// output to `os.Stderr`. +func (lgr *Logr) ReportError(err interface{}) { + lgr.incErrorCounter() + + if lgr.options.onLoggerError == nil { + fmt.Fprintln(os.Stderr, err) + return + } + lgr.options.onLoggerError(fmt.Errorf("%v", err)) +} + +// BorrowBuffer borrows a buffer from the pool. Release the buffer to reduce garbage collection. +func (lgr *Logr) BorrowBuffer() *bytes.Buffer { + if lgr.options.disableBufferPool { + return &bytes.Buffer{} + } + return lgr.bufferPool.Get().(*bytes.Buffer) +} + +// ReleaseBuffer returns a buffer to the pool to reduce garbage collection. The buffer is only +// retained if less than MaxPooledBuffer. +func (lgr *Logr) ReleaseBuffer(buf *bytes.Buffer) { + if !lgr.options.disableBufferPool && buf.Cap() < lgr.options.maxPooledBuffer { + buf.Reset() + lgr.bufferPool.Put(buf) + } +} + +// start selects on incoming log records until shutdown record is received. +// Incoming log records are fanned out to all log targets. +func (lgr *Logr) start() { + defer func() { + if r := recover(); r != nil { + lgr.ReportError(r) + go lgr.start() + } else { + close(lgr.done) + } + }() + + for { + var rec *LogRec + select { + case rec = <-lgr.in: + if rec.flush != nil { + lgr.flush(rec.flush) + } else { + rec.prep() + lgr.fanout(rec) + } + case <-lgr.quit: + return + } + } +} + +// fanout pushes a LogRec to all targets. +func (lgr *Logr) fanout(rec *LogRec) { + var host *TargetHost + defer func() { + if r := recover(); r != nil { + lgr.ReportError(fmt.Errorf("fanout failed for target %s, %v", host.String(), r)) + } + }() + + var logged bool + + lgr.tmux.RLock() + defer lgr.tmux.RUnlock() + for _, host = range lgr.targetHosts { + if enabled, _ := host.IsLevelEnabled(rec.Level()); enabled { + host.Log(rec) + logged = true + } + } + + if logged { + lgr.incLoggedCounter() + } +} + +// flush drains the queue and notifies when done. +func (lgr *Logr) flush(done chan<- struct{}) { + // first drain the logr queue. +loop: + for { + var rec *LogRec + select { + case rec = <-lgr.in: + if rec.flush == nil { + rec.prep() + lgr.fanout(rec) + } + default: + break loop + } + } + + logger := lgr.NewLogger() + + // drain all the targets; block until finished. + lgr.tmux.RLock() + defer lgr.tmux.RUnlock() + for _, host := range lgr.targetHosts { + rec := newFlushLogRec(logger) + host.Log(rec) + <-rec.flush + } + done <- struct{}{} +} diff --git a/vendor/github.com/mattermost/logr/logrec.go b/vendor/github.com/mattermost/logr/v2/logrec.go similarity index 60% rename from vendor/github.com/mattermost/logr/logrec.go rename to vendor/github.com/mattermost/logr/v2/logrec.go index 9428aaec75..76d51b9e16 100644 --- a/vendor/github.com/mattermost/logr/logrec.go +++ b/vendor/github.com/mattermost/logr/v2/logrec.go @@ -2,24 +2,13 @@ package logr import ( "fmt" + "path/filepath" "runtime" "strings" "sync" "time" ) -var ( - logrPkg string -) - -func init() { - // Calc current package name - pcs := make([]uintptr, 2) - _ = runtime.Callers(0, pcs) - tmp := runtime.FuncForPC(pcs[1]).Name() - logrPkg = getPackageName(tmp) -} - // LogRec collects raw, unformatted data to be logged. // TODO: pool these? how to reliably know when targets are done with them? Copy for each target? type LogRec struct { @@ -29,9 +18,9 @@ type LogRec struct { level Level logger Logger - template string - newline bool - args []interface{} + msg string + newline bool + fields []Field stackPC []uintptr stackCount int @@ -40,13 +29,14 @@ type LogRec struct { flush chan struct{} // remaining fields calculated by `prep` - msg string - frames []runtime.Frame + frames []runtime.Frame + fieldsAll []Field + caller string } // NewLogRec creates a new LogRec with the current time and optional stack trace. -func NewLogRec(lvl Level, logger Logger, template string, args []interface{}, incStacktrace bool) *LogRec { - rec := &LogRec{time: time.Now(), logger: logger, level: lvl, template: template, args: args} +func NewLogRec(lvl Level, logger Logger, msg string, fields []Field, incStacktrace bool) *LogRec { + rec := &LogRec{time: time.Now(), logger: logger, level: lvl, msg: msg, fields: fields} if incStacktrace { rec.stackPC = make([]uintptr, DefaultMaxStackFrames) rec.stackCount = runtime.Callers(2, rec.stackPC) @@ -60,44 +50,40 @@ func newFlushLogRec(logger Logger) *LogRec { return &LogRec{logger: logger, flush: make(chan struct{})} } -// prep resolves all args and field values to strings, and -// resolves stack trace to frames. +// prep resolves stack trace to frames. func (rec *LogRec) prep() { rec.mux.Lock() defer rec.mux.Unlock() - // resolve args - if rec.template == "" { - if rec.newline { - rec.msg = fmt.Sprintln(rec.args...) - } else { - rec.msg = fmt.Sprint(rec.args...) - } - } else { - rec.msg = fmt.Sprintf(rec.template, rec.args...) - } + // include log rec fields and logger fields added via "With" + rec.fieldsAll = make([]Field, 0, len(rec.fields)+len(rec.logger.fields)) + rec.fieldsAll = append(rec.fieldsAll, rec.logger.fields...) + rec.fieldsAll = append(rec.fieldsAll, rec.fields...) + + filter := rec.logger.lgr.options.stackFilter // resolve stack trace if rec.stackCount > 0 { + rec.frames = make([]runtime.Frame, 0, rec.stackCount) frames := runtime.CallersFrames(rec.stackPC[:rec.stackCount]) for { - f, more := frames.Next() - rec.frames = append(rec.frames, f) + frame, more := frames.Next() + + // remove all package entries that are in filter. + pkg := ResolvePackageName(frame.Function) + if _, ok := filter[pkg]; !ok && pkg != "" { + rec.frames = append(rec.frames, frame) + } + if !more { break } } + } - // remove leading logr package entries. - var start int - for i, frame := range rec.frames { - pkg := getPackageName(frame.Function) - if pkg != "" && pkg != logrPkg { - start = i - break - } - } - rec.frames = rec.frames[start:] + // calc caller if stack trace provided + if len(rec.frames) > 0 { + rec.caller = calcCaller(rec.frames) } } @@ -112,10 +98,9 @@ func (rec *LogRec) WithTime(time time.Time) *LogRec { time: time, level: rec.level, logger: rec.logger, - template: rec.template, - newline: rec.newline, - args: rec.args, msg: rec.msg, + newline: rec.newline, + fields: rec.fields, stackPC: rec.stackPC, stackCount: rec.stackCount, frames: rec.frames, @@ -140,9 +125,9 @@ func (rec *LogRec) Level() Level { } // Fields returns this log record's Fields. -func (rec *LogRec) Fields() Fields { +func (rec *LogRec) Fields() []Field { // no locking needed as this field is not mutated. - return rec.logger.fields + return rec.fieldsAll } // Msg returns this log record's message text. @@ -160,6 +145,15 @@ func (rec *LogRec) StackFrames() []runtime.Frame { return rec.frames } +// Caller returns this log record's caller info, meaning the file and line +// number where this log record was emitted. Returns empty string if no +// stack trace was provided. +func (rec *LogRec) Caller() string { + rec.mux.RLock() + defer rec.mux.RUnlock() + return rec.caller +} + // String returns a string representation of this log record. func (rec *LogRec) String() string { if rec.flush != nil { @@ -167,23 +161,22 @@ func (rec *LogRec) String() string { } f := &DefaultFormatter{} - buf := rec.logger.logr.BorrowBuffer() - defer rec.logger.logr.ReleaseBuffer(buf) - buf, _ = f.Format(rec, true, buf) + buf := rec.logger.lgr.BorrowBuffer() + defer rec.logger.lgr.ReleaseBuffer(buf) + buf, _ = f.Format(rec, rec.Level(), buf) return strings.TrimSpace(buf.String()) } -// getPackageName reduces a fully qualified function name to the package name -// By sirupsen: https://github.com/sirupsen/logrus/blob/master/entry.go -func getPackageName(f string) string { - for { - lastPeriod := strings.LastIndex(f, ".") - lastSlash := strings.LastIndex(f, "/") - if lastPeriod > lastSlash { - f = f[:lastPeriod] - } else { - break +func calcCaller(frames []runtime.Frame) string { + for _, frame := range frames { + if frame.File == "" { + continue } + + dir, file := filepath.Split(frame.File) + base := filepath.Base(dir) + + return fmt.Sprintf("%s/%s:%d", base, file, frame.Line) } - return f + return "" } diff --git a/vendor/github.com/mattermost/logr/metrics.go b/vendor/github.com/mattermost/logr/v2/metrics.go similarity index 52% rename from vendor/github.com/mattermost/logr/metrics.go rename to vendor/github.com/mattermost/logr/v2/metrics.go index 24fe22b6e5..f4f4d67fbb 100644 --- a/vendor/github.com/mattermost/logr/metrics.go +++ b/vendor/github.com/mattermost/logr/v2/metrics.go @@ -1,10 +1,6 @@ package logr -import ( - "errors" - - "github.com/wiggin77/merror" -) +import "time" const ( DefMetricsUpdateFreqMillis = 15000 // 15 seconds @@ -52,66 +48,93 @@ type TargetWithMetrics interface { EnableMetrics(collector MetricsCollector, updateFreqMillis int64) error } -func (logr *Logr) getMetricsCollector() MetricsCollector { - logr.mux.RLock() - defer logr.mux.RUnlock() - return logr.metrics +type metrics struct { + collector MetricsCollector + updateFreqMillis int64 + queueSizeGauge Gauge + loggedCounter Counter + errorCounter Counter + done chan struct{} } -// SetMetricsCollector enables metrics collection by supplying a MetricsCollector. -// The MetricsCollector provides counters and gauges that are updated by log targets. -func (logr *Logr) SetMetricsCollector(collector MetricsCollector) error { +// initMetrics initializes metrics collection. +func (lgr *Logr) initMetrics(collector MetricsCollector, updatefreq int64) { + lgr.stopMetricsUpdater() + if collector == nil { - return errors.New("collector cannot be nil") + lgr.metricsMux.Lock() + lgr.metrics = nil + lgr.metricsMux.Unlock() + return } - logr.mux.Lock() - logr.metrics = collector - logr.queueSizeGauge, _ = collector.QueueSizeGauge("_logr") - logr.loggedCounter, _ = collector.LoggedCounter("_logr") - logr.errorCounter, _ = collector.ErrorCounter("_logr") - logr.mux.Unlock() + metrics := &metrics{ + collector: collector, + updateFreqMillis: updatefreq, + done: make(chan struct{}), + } + metrics.queueSizeGauge, _ = collector.QueueSizeGauge("_logr") + metrics.loggedCounter, _ = collector.LoggedCounter("_logr") + metrics.errorCounter, _ = collector.ErrorCounter("_logr") - logr.metricsInitOnce.Do(func() { - logr.metricsDone = make(chan struct{}) - go logr.startMetricsUpdater() - }) + lgr.metricsMux.Lock() + lgr.metrics = metrics + lgr.metricsMux.Unlock() - merr := merror.New() + go lgr.startMetricsUpdater() +} - logr.tmux.RLock() - defer logr.tmux.RUnlock() - for _, target := range logr.targets { - if tm, ok := target.(TargetWithMetrics); ok { - if err := tm.EnableMetrics(collector, logr.MetricsUpdateFreqMillis); err != nil { - merr.Append(err) - } +func (lgr *Logr) setQueueSizeGauge(val float64) { + lgr.metricsMux.RLock() + defer lgr.metricsMux.RUnlock() + + if lgr.metrics != nil { + lgr.metrics.queueSizeGauge.Set(val) + } +} + +func (lgr *Logr) incLoggedCounter() { + lgr.metricsMux.RLock() + defer lgr.metricsMux.RUnlock() + + if lgr.metrics != nil { + lgr.metrics.loggedCounter.Inc() + } +} + +func (lgr *Logr) incErrorCounter() { + lgr.metricsMux.RLock() + defer lgr.metricsMux.RUnlock() + + if lgr.metrics != nil { + lgr.metrics.errorCounter.Inc() + } +} + +// startMetricsUpdater updates the metrics for any polled values every `metricsUpdateFreqSecs` seconds until +// logr is closed. +func (lgr *Logr) startMetricsUpdater() { + for { + lgr.metricsMux.RLock() + metrics := lgr.metrics + c := metrics.done + lgr.metricsMux.RUnlock() + + select { + case <-c: + return + case <-time.After(time.Duration(metrics.updateFreqMillis) * time.Millisecond): + lgr.setQueueSizeGauge(float64(len(lgr.in))) } - - } - return merr.ErrorOrNil() -} - -func (logr *Logr) setQueueSizeGauge(val float64) { - logr.mux.RLock() - defer logr.mux.RUnlock() - if logr.queueSizeGauge != nil { - logr.queueSizeGauge.Set(val) } } -func (logr *Logr) incLoggedCounter() { - logr.mux.RLock() - defer logr.mux.RUnlock() - if logr.loggedCounter != nil { - logr.loggedCounter.Inc() - } -} +func (lgr *Logr) stopMetricsUpdater() { + lgr.metricsMux.Lock() + defer lgr.metricsMux.Unlock() -func (logr *Logr) incErrorCounter() { - logr.mux.RLock() - defer logr.mux.RUnlock() - if logr.errorCounter != nil { - logr.errorCounter.Inc() + if lgr.metrics != nil && lgr.metrics.done != nil { + close(lgr.metrics.done) + lgr.metrics.done = nil } } diff --git a/vendor/github.com/mattermost/logr/v2/options.go b/vendor/github.com/mattermost/logr/v2/options.go new file mode 100644 index 0000000000..638f638ae6 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/options.go @@ -0,0 +1,192 @@ +package logr + +import ( + "errors" + "time" +) + +type Option func(*Logr) error + +type options struct { + maxQueueSize int + onLoggerError func(error) + onQueueFull func(rec *LogRec, maxQueueSize int) bool + onTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool + onExit func(code int) + onPanic func(err interface{}) + enqueueTimeout time.Duration + shutdownTimeout time.Duration + flushTimeout time.Duration + useSyncMapLevelCache bool + maxPooledBuffer int + disableBufferPool bool + metricsCollector MetricsCollector + metricsUpdateFreqMillis int64 + stackFilter map[string]struct{} +} + +// MaxQueueSize is the maximum number of log records that can be queued. +// If exceeded, `OnQueueFull` is called which determines if the log +// record will be dropped or block until add is successful. +// Defaults to DefaultMaxQueueSize. +func MaxQueueSize(size int) Option { + return func(l *Logr) error { + if size < 0 { + return errors.New("size cannot be less than zero") + } + l.options.maxQueueSize = size + return nil + } +} + +// OnLoggerError, when not nil, is called any time an internal +// logging error occurs. For example, this can happen when a +// target cannot connect to its data sink. +func OnLoggerError(f func(error)) Option { + return func(l *Logr) error { + l.options.onLoggerError = f + return nil + } +} + +// OnQueueFull, when not nil, is called on an attempt to add +// a log record to a full Logr queue. +// `MaxQueueSize` can be used to modify the maximum queue size. +// This function should return quickly, with a bool indicating whether +// the log record should be dropped (true) or block until the log record +// is successfully added (false). If nil then blocking (false) is assumed. +func OnQueueFull(f func(rec *LogRec, maxQueueSize int) bool) Option { + return func(l *Logr) error { + l.options.onQueueFull = f + return nil + } +} + +// OnTargetQueueFull, when not nil, is called on an attempt to add +// a log record to a full target queue provided the target supports reporting +// this condition. +// This function should return quickly, with a bool indicating whether +// the log record should be dropped (true) or block until the log record +// is successfully added (false). If nil then blocking (false) is assumed. +func OnTargetQueueFull(f func(target Target, rec *LogRec, maxQueueSize int) bool) Option { + return func(l *Logr) error { + l.options.onTargetQueueFull = f + return nil + } +} + +// OnExit, when not nil, is called when a FatalXXX style log API is called. +// When nil, then the default behavior is to cleanly shut down this Logr and +// call `os.Exit(code)`. +func OnExit(f func(code int)) Option { + return func(l *Logr) error { + l.options.onExit = f + return nil + } +} + +// OnPanic, when not nil, is called when a PanicXXX style log API is called. +// When nil, then the default behavior is to cleanly shut down this Logr and +// call `panic(err)`. +func OnPanic(f func(err interface{})) Option { + return func(l *Logr) error { + l.options.onPanic = f + return nil + } +} + +// EnqueueTimeout is the amount of time a log record can take to be queued. +// This only applies to blocking enqueue which happen after `logr.OnQueueFull` +// is called and returns false. +func EnqueueTimeout(dur time.Duration) Option { + return func(l *Logr) error { + l.options.enqueueTimeout = dur + return nil + } +} + +// ShutdownTimeout is the amount of time `logr.Shutdown` can execute before +// timing out. An alternative is to use `logr.ShutdownWithContext` and supply +// a timeout. +func ShutdownTimeout(dur time.Duration) Option { + return func(l *Logr) error { + l.options.shutdownTimeout = dur + return nil + } +} + +// FlushTimeout is the amount of time `logr.Flush` can execute before +// timing out. An alternative is to use `logr.FlushWithContext` and supply +// a timeout. +func FlushTimeout(dur time.Duration) Option { + return func(l *Logr) error { + l.options.flushTimeout = dur + return nil + } +} + +// UseSyncMapLevelCache can be set to true when high concurrency (e.g. >32 cores) +// is expected. This may improve performance with large numbers of cores - benchmark +// for your use case. +func UseSyncMapLevelCache(use bool) Option { + return func(l *Logr) error { + l.options.useSyncMapLevelCache = use + return nil + } +} + +// MaxPooledBufferSize determines the maximum size of a buffer that can be +// pooled. To reduce allocations, the buffers needed during formatting (etc) +// are pooled. A very large log item will grow a buffer that could stay in +// memory indefinitely. This setting lets you control how big a pooled buffer +// can be - anything larger will be garbage collected after use. +// Defaults to 1MB. +func MaxPooledBufferSize(size int) Option { + return func(l *Logr) error { + l.options.maxPooledBuffer = size + return nil + } +} + +// DisableBufferPool when true disables the buffer pool. See MaxPooledBuffer. +func DisableBufferPool(disable bool) Option { + return func(l *Logr) error { + l.options.disableBufferPool = disable + return nil + } +} + +// SetMetricsCollector enables metrics collection by supplying a MetricsCollector. +// The MetricsCollector provides counters and gauges that are updated by log targets. +// `updateFreqMillis` determines how often polled metrics are updated. Defaults to 15000 (15 seconds) +// and must be at least 250 so we don't peg the CPU. +func SetMetricsCollector(collector MetricsCollector, updateFreqMillis int64) Option { + return func(l *Logr) error { + if collector == nil { + return errors.New("collector cannot be nil") + } + if updateFreqMillis < 250 { + return errors.New("updateFreqMillis cannot be less than 250") + } + l.options.metricsCollector = collector + l.options.metricsUpdateFreqMillis = updateFreqMillis + return nil + } +} + +// StackFilter provides a list of package names to exclude from the top of +// stack traces. The Logr packages are automatically filtered. +func StackFilter(pkg ...string) Option { + return func(l *Logr) error { + if l.options.stackFilter == nil { + l.options.stackFilter = make(map[string]struct{}) + } + + for _, p := range pkg { + if p != "" { + l.options.stackFilter[p] = struct{}{} + } + } + return nil + } +} diff --git a/vendor/github.com/mattermost/logr/v2/pkg.go b/vendor/github.com/mattermost/logr/v2/pkg.go new file mode 100644 index 0000000000..873b2e953e --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/pkg.go @@ -0,0 +1,57 @@ +package logr + +import ( + "runtime" + "strings" + "sync" +) + +const ( + maximumStackDepth int = 30 +) + +var ( + logrPkg string + pkgCalcOnce sync.Once +) + +// GetPackageName returns the root package name of Logr. +func GetLogrPackageName() string { + pkgCalcOnce.Do(func() { + logrPkg = GetPackageName("GetLogrPackageName") + }) + return logrPkg +} + +// GetPackageName returns the package name of the caller. +// `callingFuncName` should be the name of the calling function and +// should be unique enough not to collide with any runtime methods. +func GetPackageName(callingFuncName string) string { + var pkgName string + + pcs := make([]uintptr, maximumStackDepth) + _ = runtime.Callers(0, pcs) + + for _, pc := range pcs { + funcName := runtime.FuncForPC(pc).Name() + if strings.Contains(funcName, callingFuncName) { + pkgName = ResolvePackageName(funcName) + break + } + } + return pkgName +} + +// ResolvePackageName reduces a fully qualified function name to the package name +func ResolvePackageName(f string) string { + for { + lastPeriod := strings.LastIndex(f, ".") + lastSlash := strings.LastIndex(f, "/") + if lastPeriod > lastSlash { + f = f[:lastPeriod] + } else { + break + } + } + return f +} diff --git a/vendor/github.com/mattermost/logr/v2/stdlogger.go b/vendor/github.com/mattermost/logr/v2/stdlogger.go new file mode 100644 index 0000000000..50171b3df8 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/stdlogger.go @@ -0,0 +1,56 @@ +package logr + +import ( + "log" + "os" + "strings" +) + +// NewStdLogger creates a standard logger backed by a Logr instance. +// All log records are emitted with the specified log level. +func NewStdLogger(level Level, logger Logger) *log.Logger { + adapter := newStdLogAdapter(logger, level) + return log.New(adapter, "", 0) +} + +// RedirectStdLog redirects output from the standard library's package-global logger +// to this logger at the specified level and with zero or more Field's. Since Logr already +// handles caller annotations, timestamps, etc., it automatically disables the standard +// library's annotations and prefixing. +// A function is returned that restores the original prefix and flags and resets the standard +// library's output to os.Stderr. +func (lgr *Logr) RedirectStdLog(level Level, fields ...Field) func() { + flags := log.Flags() + prefix := log.Prefix() + log.SetFlags(0) + log.SetPrefix("") + + logger := lgr.NewLogger().With(fields...) + adapter := newStdLogAdapter(logger, level) + log.SetOutput(adapter) + + return func() { + log.SetFlags(flags) + log.SetPrefix(prefix) + log.SetOutput(os.Stderr) + } +} + +type stdLogAdapter struct { + logger Logger + level Level +} + +func newStdLogAdapter(logger Logger, level Level) *stdLogAdapter { + return &stdLogAdapter{ + logger: logger, + level: level, + } +} + +// Write implements io.Writer +func (a *stdLogAdapter) Write(p []byte) (int, error) { + s := strings.TrimSpace(string(p)) + a.logger.Log(a.level, s) + return len(p), nil +} diff --git a/vendor/github.com/mattermost/logr/v2/sugar.go b/vendor/github.com/mattermost/logr/v2/sugar.go new file mode 100644 index 0000000000..f4f300eeac --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/sugar.go @@ -0,0 +1,119 @@ +package logr + +import ( + "fmt" +) + +// Sugar provides a less structured API for logging. +type Sugar struct { + logger Logger +} + +func (s Sugar) sugarLog(lvl Level, msg string, args ...interface{}) { + if s.logger.IsLevelEnabled(lvl) { + fields := make([]Field, 0, len(args)) + for _, arg := range args { + fields = append(fields, Any("", arg)) + } + s.logger.Log(lvl, msg, fields...) + } +} + +// Trace is a convenience method equivalent to `Log(TraceLevel, msg, args...)`. +func (s Sugar) Trace(msg string, args ...interface{}) { + s.sugarLog(Trace, msg, args...) +} + +// Debug is a convenience method equivalent to `Log(DebugLevel, msg, args...)`. +func (s Sugar) Debug(msg string, args ...interface{}) { + s.sugarLog(Debug, msg, args...) +} + +// Print ensures compatibility with std lib logger. +func (s Sugar) Print(msg string, args ...interface{}) { + s.Info(msg, args...) +} + +// Info is a convenience method equivalent to `Log(InfoLevel, msg, args...)`. +func (s Sugar) Info(msg string, args ...interface{}) { + s.sugarLog(Info, msg, args...) +} + +// Warn is a convenience method equivalent to `Log(WarnLevel, msg, args...)`. +func (s Sugar) Warn(msg string, args ...interface{}) { + s.sugarLog(Warn, msg, args...) +} + +// Error is a convenience method equivalent to `Log(ErrorLevel, msg, args...)`. +func (s Sugar) Error(msg string, args ...interface{}) { + s.sugarLog(Error, msg, args...) +} + +// Fatal is a convenience method equivalent to `Log(FatalLevel, msg, args...)` +func (s Sugar) Fatal(msg string, args ...interface{}) { + s.sugarLog(Fatal, msg, args...) +} + +// Panic is a convenience method equivalent to `Log(PanicLevel, msg, args...)` +func (s Sugar) Panic(msg string, args ...interface{}) { + s.sugarLog(Panic, msg, args...) +} + +// +// Printf style +// + +// Logf checks that the level matches one or more targets, and +// if so, generates a log record that is added to the main +// queue (channel). Arguments are handled in the manner of fmt.Printf. +func (s Sugar) Logf(lvl Level, format string, args ...interface{}) { + if s.logger.IsLevelEnabled(lvl) { + var msg string + if format == "" { + msg = fmt.Sprint(args...) + } else { + msg = fmt.Sprintf(format, args...) + } + s.logger.Log(lvl, msg) + } +} + +// Tracef is a convenience method equivalent to `Logf(TraceLevel, args...)`. +func (s Sugar) Tracef(format string, args ...interface{}) { + s.Logf(Trace, format, args...) +} + +// Debugf is a convenience method equivalent to `Logf(DebugLevel, args...)`. +func (s Sugar) Debugf(format string, args ...interface{}) { + s.Logf(Debug, format, args...) +} + +// Infof is a convenience method equivalent to `Logf(InfoLevel, args...)`. +func (s Sugar) Infof(format string, args ...interface{}) { + s.Logf(Info, format, args...) +} + +// Printf ensures compatibility with std lib logger. +func (s Sugar) Printf(format string, args ...interface{}) { + s.Infof(format, args...) +} + +// Warnf is a convenience method equivalent to `Logf(WarnLevel, args...)`. +func (s Sugar) Warnf(format string, args ...interface{}) { + s.Logf(Warn, format, args...) +} + +// Errorf is a convenience method equivalent to `Logf(ErrorLevel, args...)`. +func (s Sugar) Errorf(format string, args ...interface{}) { + s.Logf(Error, format, args...) +} + +// Fatalf is a convenience method equivalent to `Logf(FatalLevel, args...)` +func (s Sugar) Fatalf(format string, args ...interface{}) { + s.Logf(Fatal, format, args...) +} + +// Panicf is a convenience method equivalent to `Logf(PanicLevel, args...)` +func (s Sugar) Panicf(format string, args ...interface{}) { + s.Logf(Panic, format, args...) +} diff --git a/vendor/github.com/mattermost/logr/v2/target.go b/vendor/github.com/mattermost/logr/v2/target.go new file mode 100644 index 0000000000..fa0a9320cd --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/target.go @@ -0,0 +1,304 @@ +package logr + +import ( + "context" + "errors" + "fmt" + "os" + "sync/atomic" + "time" +) + +// Target represents a destination for log records such as file, +// database, TCP socket, etc. +type Target interface { + // Init is called once to initialize the target. + Init() error + + // Write outputs to this target's destination. + Write(p []byte, rec *LogRec) (int, error) + + // Shutdown is called once to free/close any resources. + // Target queue is already drained when this is called. + Shutdown() error +} + +type targetMetrics struct { + queueSizeGauge Gauge + loggedCounter Counter + errorCounter Counter + droppedCounter Counter + blockedCounter Counter +} + +type targetHostOptions struct { + name string + filter Filter + formatter Formatter + maxQueueSize int + metrics *metrics +} + +// TargetHost hosts and manages the lifecycle of a target. +// Incoming log records are queued and formatted before +// being passed to the target. +type TargetHost struct { + target Target + name string + + filter Filter + formatter Formatter + + in chan *LogRec + quit chan struct{} // closed by Shutdown to exit read loop + done chan struct{} // closed when read loop exited + targetMetrics *targetMetrics + + shutdown int32 +} + +func newTargetHost(target Target, options targetHostOptions) (*TargetHost, error) { + host := &TargetHost{ + target: target, + name: options.name, + filter: options.filter, + formatter: options.formatter, + in: make(chan *LogRec, options.maxQueueSize), + quit: make(chan struct{}), + done: make(chan struct{}), + } + + if host.name == "" { + host.name = fmt.Sprintf("%T", target) + } + + if host.filter == nil { + host.filter = &StdFilter{Lvl: Fatal} + } + if host.formatter == nil { + host.formatter = &DefaultFormatter{} + } + + err := host.initMetrics(options.metrics) + if err != nil { + return nil, err + } + + err = target.Init() + if err != nil { + return nil, err + } + + go host.start() + + return host, nil +} + +func (h *TargetHost) initMetrics(metrics *metrics) error { + if metrics == nil { + return nil + } + + var err error + tmetrics := &targetMetrics{} + + if tmetrics.queueSizeGauge, err = metrics.collector.QueueSizeGauge(h.name); err != nil { + return err + } + if tmetrics.loggedCounter, err = metrics.collector.LoggedCounter(h.name); err != nil { + return err + } + if tmetrics.errorCounter, err = metrics.collector.ErrorCounter(h.name); err != nil { + return err + } + if tmetrics.droppedCounter, err = metrics.collector.DroppedCounter(h.name); err != nil { + return err + } + if tmetrics.blockedCounter, err = metrics.collector.BlockedCounter(h.name); err != nil { + return err + } + h.targetMetrics = tmetrics + + updateFreqMillis := metrics.updateFreqMillis + if updateFreqMillis == 0 { + updateFreqMillis = DefMetricsUpdateFreqMillis + } + if updateFreqMillis < 250 { + updateFreqMillis = 250 // don't peg the CPU + } + + go h.startMetricsUpdater(updateFreqMillis) + return nil +} + +// IsLevelEnabled returns true if this target should emit logs for the specified level. +func (h *TargetHost) IsLevelEnabled(lvl Level) (enabled bool, level Level) { + level, enabled = h.filter.GetEnabledLevel(lvl) + return enabled, level +} + +// Shutdown stops processing log records after making best +// effort to flush queue. +func (h *TargetHost) Shutdown(ctx context.Context) error { + if atomic.SwapInt32(&h.shutdown, 1) != 0 { + return errors.New("targetHost shutdown called more than once") + } + + close(h.quit) + + // No more records can be accepted; now wait for read loop to exit. + select { + case <-ctx.Done(): + case <-h.done: + } + + // b.in channel should now be drained. + return h.target.Shutdown() +} + +// Log queues a log record to be output to this target's destination. +func (h *TargetHost) Log(rec *LogRec) { + if atomic.LoadInt32(&h.shutdown) != 0 { + return + } + + lgr := rec.Logger().Logr() + select { + case h.in <- rec: + default: + handler := lgr.options.onTargetQueueFull + if handler != nil && handler(h.target, rec, cap(h.in)) { + h.incDroppedCounter() + return // drop the record + } + h.incBlockedCounter() + + select { + case <-time.After(lgr.options.enqueueTimeout): + lgr.ReportError(fmt.Errorf("target enqueue timeout for log rec [%v]", rec)) + case h.in <- rec: // block until success or timeout + } + } +} + +func (h *TargetHost) setQueueSizeGauge(val float64) { + if h.targetMetrics != nil { + h.targetMetrics.queueSizeGauge.Set(val) + } +} + +func (h *TargetHost) incLoggedCounter() { + if h.targetMetrics != nil { + h.targetMetrics.loggedCounter.Inc() + } +} + +func (h *TargetHost) incErrorCounter() { + if h.targetMetrics != nil { + h.targetMetrics.errorCounter.Inc() + } +} + +func (h *TargetHost) incDroppedCounter() { + if h.targetMetrics != nil { + h.targetMetrics.droppedCounter.Inc() + } +} + +func (h *TargetHost) incBlockedCounter() { + if h.targetMetrics != nil { + h.targetMetrics.blockedCounter.Inc() + } +} + +// String returns a name for this target. +func (h *TargetHost) String() string { + return h.name +} + +// start accepts log records via In channel and writes to the +// supplied target, until Done channel signaled. +func (h *TargetHost) start() { + defer func() { + if r := recover(); r != nil { + fmt.Fprintln(os.Stderr, "TargetHost.start -- ", r) + go h.start() + } else { + close(h.done) + } + }() + + for { + var rec *LogRec + select { + case rec = <-h.in: + if rec.flush != nil { + h.flush(rec.flush) + } else { + err := h.writeRec(rec) + if err != nil { + h.incErrorCounter() + rec.Logger().Logr().ReportError(err) + } else { + h.incLoggedCounter() + } + } + case <-h.quit: + return + } + } +} + +func (h *TargetHost) writeRec(rec *LogRec) error { + level, enabled := h.filter.GetEnabledLevel(rec.Level()) + if !enabled { + // how did we get here? + return fmt.Errorf("level %s not enabled for target %s", rec.Level().Name, h.name) + } + + buf := rec.logger.lgr.BorrowBuffer() + defer rec.logger.lgr.ReleaseBuffer(buf) + + buf, err := h.formatter.Format(rec, level, buf) + if err != nil { + return err + } + + _, err = h.target.Write(buf.Bytes(), rec) + return err +} + +// startMetricsUpdater updates the metrics for any polled values every `updateFreqMillis` seconds until +// target is shut down. +func (h *TargetHost) startMetricsUpdater(updateFreqMillis int64) { + for { + select { + case <-h.done: + return + case <-time.After(time.Duration(updateFreqMillis) * time.Millisecond): + h.setQueueSizeGauge(float64(len(h.in))) + } + } +} + +// flush drains the queue and notifies when done. +func (h *TargetHost) flush(done chan<- struct{}) { + for { + var rec *LogRec + var err error + select { + case rec = <-h.in: + // ignore any redundant flush records. + if rec.flush == nil { + err = h.writeRec(rec) + if err != nil { + h.incErrorCounter() + rec.Logger().Logr().ReportError(err) + } + } + default: + done <- struct{}{} + return + } + } +} diff --git a/vendor/github.com/mattermost/logr/target/file.go b/vendor/github.com/mattermost/logr/v2/targets/file.go similarity index 60% rename from vendor/github.com/mattermost/logr/target/file.go rename to vendor/github.com/mattermost/logr/v2/targets/file.go index 0fd50768da..71133fac94 100644 --- a/vendor/github.com/mattermost/logr/target/file.go +++ b/vendor/github.com/mattermost/logr/v2/targets/file.go @@ -1,11 +1,10 @@ -package target +package targets import ( - "context" + "errors" "io" - "github.com/mattermost/logr" - "github.com/wiggin77/merror" + "github.com/mattermost/logr/v2" "gopkg.in/natefinch/lumberjack.v2" ) @@ -13,38 +12,44 @@ type FileOptions struct { // Filename is the file to write logs to. Backup log files will be retained // in the same directory. It uses -lumberjack.log in // os.TempDir() if empty. - Filename string + Filename string `json:"filename"` // MaxSize is the maximum size in megabytes of the log file before it gets // rotated. It defaults to 100 megabytes. - MaxSize int + MaxSize int `json:"max_size"` // MaxAge is the maximum number of days to retain old log files based on the // timestamp encoded in their filename. Note that a day is defined as 24 // hours and may not exactly correspond to calendar days due to daylight // savings, leap seconds, etc. The default is not to remove old log files // based on age. - MaxAge int + MaxAge int `json:"max_age"` // MaxBackups is the maximum number of old log files to retain. The default // is to retain all old log files (though MaxAge may still cause them to get // deleted.) - MaxBackups int + MaxBackups int `json:"max_backups"` // Compress determines if the rotated log files should be compressed // using gzip. The default is not to perform compression. - Compress bool + Compress bool `json:"compress"` +} + +func (fo FileOptions) CheckValid() error { + if fo.Filename == "" { + return errors.New("filename cannot be empty") + } + return nil } // File outputs log records to a file which can be log rotated based on size or age. // Uses `https://github.com/natefinch/lumberjack` for rotation. type File struct { - logr.Basic out io.WriteCloser } // NewFileTarget creates a target capable of outputting log records to a rotated file. -func NewFileTarget(filter logr.Filter, formatter logr.Formatter, opts FileOptions, maxQueue int) *File { +func NewFileTarget(opts FileOptions) *File { lumber := &lumberjack.Logger{ Filename: opts.Filename, MaxSize: opts.MaxSize, @@ -53,35 +58,21 @@ func NewFileTarget(filter logr.Filter, formatter logr.Formatter, opts FileOption Compress: opts.Compress, } f := &File{out: lumber} - f.Basic.Start(f, f, filter, formatter, maxQueue) return f } -// Write converts the log record to bytes, via the Formatter, -// and outputs to a file. -func (f *File) Write(rec *logr.LogRec) error { - _, stacktrace := f.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := f.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - _, err = f.out.Write(buf.Bytes()) - return err +// Init is called once to initialize the target. +func (f *File) Init() error { + return nil } -// Shutdown flushes any remaining log records and closes the file. -func (f *File) Shutdown(ctx context.Context) error { - errs := merror.New() - - err := f.Basic.Shutdown(ctx) - errs.Append(err) - - err = f.out.Close() - errs.Append(err) - - return errs.ErrorOrNil() +// Write outputs bytes to this file target. +func (f *File) Write(p []byte, rec *logr.LogRec) (int, error) { + return f.out.Write(p) +} + +// Shutdown is called once to free/close any resources. +// Target queue is already drained when this is called. +func (f *File) Shutdown() error { + return f.out.Close() } diff --git a/vendor/github.com/mattermost/logr/v2/targets/syslog.go b/vendor/github.com/mattermost/logr/v2/targets/syslog.go new file mode 100644 index 0000000000..fc3fcc5fee --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/targets/syslog.go @@ -0,0 +1,112 @@ +// +build !windows,!nacl,!plan9 + +package targets + +import ( + "crypto/tls" + "errors" + "fmt" + + "github.com/mattermost/logr/v2" + syslog "github.com/wiggin77/srslog" +) + +// Syslog outputs log records to local or remote syslog. +type Syslog struct { + params *SyslogOptions + writer *syslog.Writer +} + +// SyslogOptions provides parameters for dialing a syslog daemon. +type SyslogOptions struct { + IP string `json:"ip,omitempty"` // deprecated + Host string `json:"host"` + Port int `json:"port"` + TLS bool `json:"tls"` + Cert string `json:"cert"` + Insecure bool `json:"insecure"` + Tag string `json:"tag"` +} + +func (so SyslogOptions) CheckValid() error { + if so.Host == "" && so.IP == "" { + return errors.New("missing host") + } + if so.Port == 0 { + return errors.New("missing port") + } + return nil +} + +// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS. +func NewSyslogTarget(params *SyslogOptions) (*Syslog, error) { + if params == nil { + return nil, errors.New("params cannot be nil") + } + + s := &Syslog{ + params: params, + } + return s, nil +} + +// Init is called once to initialize the target. +func (s *Syslog) Init() error { + network := "tcp" + var config *tls.Config + + if s.params.TLS { + network = "tcp+tls" + config = &tls.Config{InsecureSkipVerify: s.params.Insecure} + if s.params.Cert != "" { + pool, err := GetCertPool(s.params.Cert) + if err != nil { + return err + } + config.RootCAs = pool + } + } + raddr := fmt.Sprintf("%s:%d", s.params.IP, s.params.Port) + if raddr == ":0" { + // If no IP:port provided then connect to local syslog. + raddr = "" + network = "" + } + + var err error + s.writer, err = syslog.DialWithTLSConfig(network, raddr, syslog.LOG_INFO, s.params.Tag, config) + return err +} + +// Write outputs bytes to this file target. +func (s *Syslog) Write(p []byte, rec *logr.LogRec) (int, error) { + txt := string(p) + n := len(txt) + var err error + + switch rec.Level() { + case logr.Panic, logr.Fatal: + err = s.writer.Crit(txt) + case logr.Error: + err = s.writer.Err(txt) + case logr.Warn: + err = s.writer.Warning(txt) + case logr.Debug, logr.Trace: + err = s.writer.Debug(txt) + default: + // logr.Info plus all custom levels. + err = s.writer.Info(txt) + } + + if err != nil { + n = 0 + // syslog writer will try to reconnect. + } + return n, err +} + +// Shutdown is called once to free/close any resources. +// Target queue is already drained when this is called. +func (s *Syslog) Shutdown() error { + return s.writer.Close() +} diff --git a/vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go b/vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go new file mode 100644 index 0000000000..e4086e9669 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go @@ -0,0 +1,56 @@ +// +build windows nacl plan9 + +package targets + +import ( + "errors" + + "github.com/mattermost/logr/v2" + syslog "github.com/wiggin77/srslog" +) + +const ( + unsupported = "Syslog target is not supported on this platform." +) + +// Syslog outputs log records to local or remote syslog. +type Syslog struct { + params *SyslogOptions + writer *syslog.Writer +} + +// SyslogOptions provides parameters for dialing a syslog daemon. +type SyslogOptions struct { + IP string `json:"ip,omitempty"` // deprecated + Host string `json:"host"` + Port int `json:"port"` + TLS bool `json:"tls"` + Cert string `json:"cert"` + Insecure bool `json:"insecure"` + Tag string `json:"tag"` +} + +func (so SyslogOptions) CheckValid() error { + return errors.New(unsupported) +} + +// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS. +func NewSyslogTarget(params *SyslogOptions) (*Syslog, error) { + return nil, errors.New(unsupported) +} + +// Init is called once to initialize the target. +func (s *Syslog) Init() error { + return errors.New(unsupported) +} + +// Write outputs bytes to this file target. +func (s *Syslog) Write(p []byte, rec *logr.LogRec) (int, error) { + return 0, errors.New(unsupported) +} + +// Shutdown is called once to free/close any resources. +// Target queue is already drained when this is called. +func (s *Syslog) Shutdown() error { + return errors.New(unsupported) +} diff --git a/shared/mlog/tcp.go b/vendor/github.com/mattermost/logr/v2/targets/tcp.go similarity index 54% rename from shared/mlog/tcp.go rename to vendor/github.com/mattermost/logr/v2/targets/tcp.go index 7a6e6d1327..ce73e03406 100644 --- a/shared/mlog/tcp.go +++ b/vendor/github.com/mattermost/logr/v2/targets/tcp.go @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -package mlog +package targets import ( "context" @@ -9,12 +9,10 @@ import ( "errors" "fmt" "net" - _ "net/http/pprof" "sync" "time" - "github.com/hashicorp/go-multierror" - "github.com/mattermost/logr" + "github.com/mattermost/logr/v2" ) const ( @@ -24,12 +22,10 @@ const ( MaxRetryBackoffMillis int64 = 30 * 1000 // 30 seconds ) -// TCP outputs log records to raw socket server. -type TCP struct { - logr.Basic - - params *TCPParams - addy string +// Tcp outputs log records to raw socket server. +type Tcp struct { + options *TcpOptions + addy string mutex sync.Mutex conn net.Conn @@ -37,39 +33,49 @@ type TCP struct { shutdown chan struct{} } -// TCPParams provides parameters for dialing a socket server. -type TCPParams struct { - IP string `json:"IP"` - Port int `json:"Port"` - TLS bool `json:"TLS"` - Cert string `json:"Cert"` - Insecure bool `json:"Insecure"` +// TcpOptions provides parameters for dialing a socket server. +type TcpOptions struct { + IP string `json:"ip,omitempty"` // deprecated + Host string `json:"host"` + Port int `json:"port"` + TLS bool `json:"tls"` + Cert string `json:"cert"` + Insecure bool `json:"insecure"` } -// NewTPCTarget creates a target capable of outputting log records to a raw socket, with or without TLS. -func NewTCPTarget(filter logr.Filter, formatter logr.Formatter, params *TCPParams, maxQueue int) (*TCP, error) { - tcp := &TCP{ - params: params, - addy: fmt.Sprintf("%s:%d", params.IP, params.Port), +func (to TcpOptions) CheckValid() error { + if to.Host == "" && to.IP == "" { + return errors.New("missing host") + } + if to.Port == 0 { + return errors.New("missing port") + } + return nil +} + +// NewTcpTarget creates a target capable of outputting log records to a raw socket, with or without TLS. +func NewTcpTarget(options *TcpOptions) *Tcp { + tcp := &Tcp{ + options: options, + addy: fmt.Sprintf("%s:%d", options.IP, options.Port), monitor: make(chan struct{}), shutdown: make(chan struct{}), } - tcp.Basic.Start(tcp, tcp, filter, formatter, maxQueue) + return tcp +} - return tcp, nil +// Init is called once to initialize the target. +func (tcp *Tcp) Init() error { + return nil } // getConn provides a net.Conn. If a connection already exists, it is returned immediately, // otherwise this method blocks until a new connection is created, timeout or shutdown. -func (tcp *TCP) getConn() (net.Conn, error) { +func (tcp *Tcp) getConn(reporter func(err interface{})) (net.Conn, error) { tcp.mutex.Lock() defer tcp.mutex.Unlock() - Log(LvlTCPLogTarget, "getConn enter", String("addy", tcp.addy)) - defer Log(LvlTCPLogTarget, "getConn exit", String("addy", tcp.addy)) - if tcp.conn != nil { - Log(LvlTCPLogTarget, "reusing existing conn", String("addy", tcp.addy)) // use "With" once Zap is removed return tcp.conn, nil } @@ -83,13 +89,14 @@ func (tcp *TCP) getConn() (net.Conn, error) { defer cancel() go func(ctx context.Context, ch chan result) { - Log(LvlTCPLogTarget, "dailing", String("addy", tcp.addy)) conn, err := tcp.dial(ctx) - if err == nil { - tcp.conn = conn - tcp.monitor = make(chan struct{}) - go monitor(tcp.conn, tcp.monitor, Log) + if err != nil { + reporter(fmt.Errorf("log target %s connection error: %w", tcp.String(), err)) + return } + tcp.conn = conn + tcp.monitor = make(chan struct{}) + go monitor(tcp.conn, tcp.monitor) ch <- result{conn: conn, err: err} }(ctx, connChan) @@ -103,26 +110,24 @@ func (tcp *TCP) getConn() (net.Conn, error) { // dial connects to a TCP socket, and optionally performs a TLS handshake. // A non-nil context must be provided which can cancel the dial. -func (tcp *TCP) dial(ctx context.Context) (net.Conn, error) { +func (tcp *Tcp) dial(ctx context.Context) (net.Conn, error) { var dialer net.Dialer dialer.Timeout = time.Second * DialTimeoutSecs - conn, err := dialer.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", tcp.params.IP, tcp.params.Port)) + conn, err := dialer.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", tcp.options.IP, tcp.options.Port)) if err != nil { return nil, err } - if !tcp.params.TLS { + if !tcp.options.TLS { return conn, nil } - Log(LvlTCPLogTarget, "TLS handshake", String("addy", tcp.addy)) - tlsconfig := &tls.Config{ - ServerName: tcp.params.IP, - InsecureSkipVerify: tcp.params.Insecure, + ServerName: tcp.options.IP, + InsecureSkipVerify: tcp.options.Insecure, } - if tcp.params.Cert != "" { - pool, err := getCertPool(tcp.params.Cert) + if tcp.options.Cert != "" { + pool, err := GetCertPool(tcp.options.Cert) if err != nil { return nil, err } @@ -136,13 +141,12 @@ func (tcp *TCP) dial(ctx context.Context) (net.Conn, error) { return tlsConn, nil } -func (tcp *TCP) close() error { +func (tcp *Tcp) close() error { tcp.mutex.Lock() defer tcp.mutex.Unlock() var err error if tcp.conn != nil { - Log(LvlTCPLogTarget, "closing connection", String("addy", tcp.addy)) close(tcp.monitor) err = tcp.conn.Close() tcp.conn = nil @@ -151,69 +155,49 @@ func (tcp *TCP) close() error { } // Shutdown stops processing log records after making best effort to flush queue. -func (tcp *TCP) Shutdown(ctx context.Context) error { - errs := &multierror.Error{} - - Log(LvlTCPLogTarget, "shutting down", String("addy", tcp.addy)) - - if err := tcp.Basic.Shutdown(ctx); err != nil { - errs = multierror.Append(errs, err) - } - - if err := tcp.close(); err != nil { - errs = multierror.Append(errs, err) - } - +func (tcp *Tcp) Shutdown() error { + err := tcp.close() close(tcp.shutdown) - return errs.ErrorOrNil() + return err } // Write converts the log record to bytes, via the Formatter, and outputs to the socket. // Called by dedicated target goroutine and will block until success or shutdown. -func (tcp *TCP) Write(rec *logr.LogRec) error { - _, stacktrace := tcp.IsLevelEnabled(rec.Level()) - - buf := rec.Logger().Logr().BorrowBuffer() - defer rec.Logger().Logr().ReleaseBuffer(buf) - - buf, err := tcp.Formatter().Format(rec, stacktrace, buf) - if err != nil { - return err - } - +func (tcp *Tcp) Write(p []byte, rec *logr.LogRec) (int, error) { try := 1 backoff := RetryBackoffMillis for { select { case <-tcp.shutdown: - return err + return 0, nil default: } - conn, err := tcp.getConn() + reporter := rec.Logger().Logr().ReportError + + conn, err := tcp.getConn(reporter) if err != nil { - Log(LvlTCPLogTarget, "failed getting connection", String("addy", tcp.addy), Err(err)) - reporter := rec.Logger().Logr().ReportError reporter(fmt.Errorf("log target %s connection error: %w", tcp.String(), err)) backoff = tcp.sleep(backoff) continue } - conn.SetWriteDeadline(time.Now().Add(time.Second * WriteTimeoutSecs)) - _, err = buf.WriteTo(conn) - if err == nil { - return nil + err = conn.SetWriteDeadline(time.Now().Add(time.Second * WriteTimeoutSecs)) + if err != nil { + reporter(fmt.Errorf("log target %s set write deadline error: %w", tcp.String(), err)) + } + + count, err := conn.Write(p) + if err == nil { + return count, nil } - Log(LvlTCPLogTarget, "write error", String("addy", tcp.addy), Err(err)) - reporter := rec.Logger().Logr().ReportError reporter(fmt.Errorf("log target %s write error: %w", tcp.String(), err)) _ = tcp.close() backoff = tcp.sleep(backoff) try++ - Log(LvlTCPLogTarget, "retrying write", String("addy", tcp.addy), Int("try", try)) } } @@ -221,14 +205,9 @@ func (tcp *TCP) Write(rec *logr.LogRec) error { // This is needed because TCP target uses a write only socket and Linux systems // take a long time to detect a loss of connectivity on a socket when only writing; // the writes simply fail without an error returned. -func monitor(conn net.Conn, done <-chan struct{}, logFunc LogFuncCustom) { - addy := conn.RemoteAddr().String() - defer logFunc(LvlTCPLogTarget, "monitor exiting", String("addy", addy)) - +func monitor(conn net.Conn, done <-chan struct{}) { buf := make([]byte, 1) for { - logFunc(LvlTCPLogTarget, "monitor loop", String("addy", addy)) - select { case <-done: return @@ -248,18 +227,17 @@ func monitor(conn net.Conn, done <-chan struct{}, logFunc LogFuncCustom) { } // Any other error closes the connection, forcing a reconnect. - logFunc(LvlTCPLogTarget, "monitor closing connection", Err(err)) conn.Close() return } } // String returns a string representation of this target. -func (tcp *TCP) String() string { - return fmt.Sprintf("TcpTarget[%s:%d]", tcp.params.IP, tcp.params.Port) +func (tcp *Tcp) String() string { + return fmt.Sprintf("TcpTarget[%s:%d]", tcp.options.IP, tcp.options.Port) } -func (tcp *TCP) sleep(backoff int64) int64 { +func (tcp *Tcp) sleep(backoff int64) int64 { select { case <-tcp.shutdown: case <-time.After(time.Millisecond * time.Duration(backoff)): diff --git a/shared/mlog/test-tls-client-cert.pem b/vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem similarity index 100% rename from shared/mlog/test-tls-client-cert.pem rename to vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem diff --git a/vendor/github.com/mattermost/logr/v2/targets/utils.go b/vendor/github.com/mattermost/logr/v2/targets/utils.go new file mode 100644 index 0000000000..6e605af282 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/targets/utils.go @@ -0,0 +1,33 @@ +package targets + +import ( + "crypto/x509" + "encoding/base64" + "errors" + "io/ioutil" +) + +// GetCertPool returns a x509.CertPool containing the cert(s) +// from `cert`, which can be a path to a .pem or .crt file, +// or a base64 encoded cert. +func GetCertPool(cert string) (*x509.CertPool, error) { + if cert == "" { + return nil, errors.New("no cert provided") + } + + // first treat as a file and try to read. + serverCert, err := ioutil.ReadFile(cert) + if err != nil { + // maybe it's a base64 encoded cert + serverCert, err = base64.StdEncoding.DecodeString(cert) + if err != nil { + return nil, errors.New("cert cannot be read") + } + } + + pool := x509.NewCertPool() + if ok := pool.AppendCertsFromPEM(serverCert); ok { + return pool, nil + } + return nil, errors.New("cannot parse cert") +} diff --git a/vendor/github.com/mattermost/logr/v2/targets/writer.go b/vendor/github.com/mattermost/logr/v2/targets/writer.go new file mode 100644 index 0000000000..d9f64d7688 --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/targets/writer.go @@ -0,0 +1,38 @@ +package targets + +import ( + "io" + "io/ioutil" + + "github.com/mattermost/logr/v2" +) + +// Writer outputs log records to any `io.Writer`. +type Writer struct { + out io.Writer +} + +// NewWriterTarget creates a target capable of outputting log records to an io.Writer. +func NewWriterTarget(out io.Writer) *Writer { + if out == nil { + out = ioutil.Discard + } + w := &Writer{out: out} + return w +} + +// Init is called once to initialize the target. +func (w *Writer) Init() error { + return nil +} + +// Write outputs bytes to this file target. +func (w *Writer) Write(p []byte, rec *logr.LogRec) (int, error) { + return w.out.Write(p) +} + +// Shutdown is called once to free/close any resources. +// Target queue is already drained when this is called. +func (w *Writer) Shutdown() error { + return nil +} diff --git a/vendor/github.com/mattermost/logr/timeout.go b/vendor/github.com/mattermost/logr/v2/timeout.go similarity index 100% rename from vendor/github.com/mattermost/logr/timeout.go rename to vendor/github.com/mattermost/logr/v2/timeout.go diff --git a/vendor/github.com/wiggin77/cfg/.gitignore b/vendor/github.com/wiggin77/cfg/.gitignore deleted file mode 100644 index f1c181ec9c..0000000000 --- a/vendor/github.com/wiggin77/cfg/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out diff --git a/vendor/github.com/wiggin77/cfg/.travis.yml b/vendor/github.com/wiggin77/cfg/.travis.yml deleted file mode 100644 index 9899b387da..0000000000 --- a/vendor/github.com/wiggin77/cfg/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: go -sudo: false -before_script: - - go vet ./... - \ No newline at end of file diff --git a/vendor/github.com/wiggin77/cfg/LICENSE b/vendor/github.com/wiggin77/cfg/LICENSE deleted file mode 100644 index 2b0bf7efa1..0000000000 --- a/vendor/github.com/wiggin77/cfg/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 wiggin77 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/wiggin77/cfg/README.md b/vendor/github.com/wiggin77/cfg/README.md deleted file mode 100644 index 583a82cb19..0000000000 --- a/vendor/github.com/wiggin77/cfg/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# cfg - -[![GoDoc](https://godoc.org/github.com/wiggin77/cfg?status.svg)](https://godoc.org/github.com/wiggin77/cfg) -[![Build Status](https://travis-ci.org/wiggin77/cfg.svg?branch=master)](https://travis-ci.org/wiggin77/cfg) - -Go package for app configuration. Supports chained configuration sources for multiple levels of defaults. -Includes APIs for loading Linux style configuration files (name/value pairs) or INI files, map based properties, -or easily create new configuration sources (e.g. load from database). - -Supports monitoring configuration sources for changes, hot loading properties, and notifying listeners of changes. - -## Usage - -```Go -config := &cfg.Config{} -defer config.Shutdown() // stops monitoring - -// load file via filespec string, os.File -src, err := Config.NewSrcFileFromFilespec("./myfile.conf") -if err != nil { - return err -} -// add src to top of chain, meaning first searched -cfg.PrependSource(src) - -// fetch prop 'retries', default to 3 if not found -val := config.Int("retries", 3) -``` - -See [example](./example_test.go) for more complete example, including listening for configuration changes. - -Config API parses the following data types: - -| type | method | example property values | -| ------- | ------ | -------- | -| string | Config.String | test, "" | -| int | Config.Int | -1, 77, 0 | -| int64 | Config.Int64 | -9223372036854775, 372036854775808 | -| float64 | Config.Float64 | -77.3456, 95642331.1 | -| bool | Config.Bool | T,t,true,True,1,0,False,false,f,F | -| time.Duration | Config.Duration | "10ms", "2 hours", "5 min" * | - -\* Units of measure supported: ms, sec, min, hour, day, week, year. diff --git a/vendor/github.com/wiggin77/cfg/config.go b/vendor/github.com/wiggin77/cfg/config.go deleted file mode 100644 index 0e958102e7..0000000000 --- a/vendor/github.com/wiggin77/cfg/config.go +++ /dev/null @@ -1,366 +0,0 @@ -package cfg - -import ( - "errors" - "fmt" - "strconv" - "strings" - "sync" - "time" - - "github.com/wiggin77/cfg/timeconv" -) - -// ErrNotFound returned when an operation is attempted on a -// resource that doesn't exist, such as fetching a non-existing -// property name. -var ErrNotFound = errors.New("not found") - -type sourceEntry struct { - src Source - props map[string]string -} - -// Config provides methods for retrieving property values from one or more -// configuration sources. -type Config struct { - mutexSrc sync.RWMutex - mutexListeners sync.RWMutex - srcs []*sourceEntry - chgListeners []ChangedListener - shutdown chan interface{} - wantPanicOnError bool -} - -// PrependSource inserts one or more `Sources` at the beginning of -// the list of sources such that the first source will be the -// source checked first when resolving a property value. -func (config *Config) PrependSource(srcs ...Source) { - arr := config.wrapSources(srcs...) - - config.mutexSrc.Lock() - if config.shutdown == nil { - config.shutdown = make(chan interface{}) - } - config.srcs = append(arr, config.srcs...) - config.mutexSrc.Unlock() - - for _, se := range arr { - if _, ok := se.src.(SourceMonitored); ok { - config.monitor(se) - } - } -} - -// AppendSource appends one or more `Sources` at the end of -// the list of sources such that the last source will be the -// source checked last when resolving a property value. -func (config *Config) AppendSource(srcs ...Source) { - arr := config.wrapSources(srcs...) - - config.mutexSrc.Lock() - if config.shutdown == nil { - config.shutdown = make(chan interface{}) - } - config.srcs = append(config.srcs, arr...) - config.mutexSrc.Unlock() - - for _, se := range arr { - if _, ok := se.src.(SourceMonitored); ok { - config.monitor(se) - } - } -} - -// wrapSources wraps one or more Source's and returns -// them as an array of `sourceEntry`. -func (config *Config) wrapSources(srcs ...Source) []*sourceEntry { - arr := make([]*sourceEntry, 0, len(srcs)) - for _, src := range srcs { - se := &sourceEntry{src: src} - config.reloadProps(se) - arr = append(arr, se) - } - return arr -} - -// SetWantPanicOnError sets the flag determining if Config -// should panic when `GetProps` or `GetLastModified` errors -// for a `Source`. -func (config *Config) SetWantPanicOnError(b bool) { - config.mutexSrc.Lock() - config.wantPanicOnError = b - config.mutexSrc.Unlock() -} - -// ShouldPanicOnError gets the flag determining if Config -// should panic when `GetProps` or `GetLastModified` errors -// for a `Source`. -func (config *Config) ShouldPanicOnError() (b bool) { - config.mutexSrc.RLock() - b = config.wantPanicOnError - config.mutexSrc.RUnlock() - return b -} - -// getProp returns the value of a named property. -// Each `Source` is checked, in the order created by adding via -// `AppendSource` and `PrependSource`, until a value for the -// property is found. -func (config *Config) getProp(name string) (val string, ok bool) { - config.mutexSrc.RLock() - defer config.mutexSrc.RUnlock() - - var s string - for _, se := range config.srcs { - if se.props != nil { - if s, ok = se.props[name]; ok { - val = strings.TrimSpace(s) - return - } - } - } - return -} - -// String returns the value of the named prop as a string. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -func (config *Config) String(name string, def string) (val string, err error) { - if v, ok := config.getProp(name); ok { - val = v - err = nil - return - } - - err = ErrNotFound - val = def - return -} - -// Int returns the value of the named prop as an `int`. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// See config.String -func (config *Config) Int(name string, def int) (val int, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - var i int64 - if i, err = strconv.ParseInt(s, 10, 32); err == nil { - val = int(i) - } - } - if err != nil { - val = def - } - return -} - -// Int64 returns the value of the named prop as an `int64`. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// See config.String -func (config *Config) Int64(name string, def int64) (val int64, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - val, err = strconv.ParseInt(s, 10, 64) - } - if err != nil { - val = def - } - return -} - -// Float64 returns the value of the named prop as a `float64`. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// See config.String -func (config *Config) Float64(name string, def float64) (val float64, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - val, err = strconv.ParseFloat(s, 64) - } - if err != nil { - val = def - } - return -} - -// Bool returns the value of the named prop as a `bool`. -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// Supports (t, true, 1, y, yes) for true, and (f, false, 0, n, no) for false, -// all case-insensitive. -// -// See config.String -func (config *Config) Bool(name string, def bool) (val bool, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - switch strings.ToLower(s) { - case "t", "true", "1", "y", "yes": - val = true - case "f", "false", "0", "n", "no": - val = false - default: - err = errors.New("invalid syntax") - } - } - if err != nil { - val = def - } - return -} - -// Duration returns the value of the named prop as a `time.Duration`, representing -// a span of time. -// -// Units of measure are supported: ms, sec, min, hour, day, week, year. -// See config.UnitsToMillis for a complete list of units supported. -// -// If the property is not found then the supplied default `def` -// and `ErrNotFound` are returned. -// -// See config.String -func (config *Config) Duration(name string, def time.Duration) (val time.Duration, err error) { - var s string - if s, err = config.String(name, ""); err == nil { - var ms int64 - ms, err = timeconv.ParseMilliseconds(s) - val = time.Duration(ms) * time.Millisecond - } - if err != nil { - val = def - } - return -} - -// AddChangedListener adds a listener that will receive notifications -// whenever one or more property values change within the config. -func (config *Config) AddChangedListener(l ChangedListener) { - config.mutexListeners.Lock() - defer config.mutexListeners.Unlock() - - config.chgListeners = append(config.chgListeners, l) -} - -// RemoveChangedListener removes all instances of a ChangedListener. -// Returns `ErrNotFound` if the listener was not present. -func (config *Config) RemoveChangedListener(l ChangedListener) error { - config.mutexListeners.Lock() - defer config.mutexListeners.Unlock() - - dest := make([]ChangedListener, 0, len(config.chgListeners)) - err := ErrNotFound - - // Remove all instances of the listener by - // copying list while filtering. - for _, s := range config.chgListeners { - if s != l { - dest = append(dest, s) - } else { - err = nil - } - } - config.chgListeners = dest - return err -} - -// Shutdown can be called to stop monitoring of all config sources. -func (config *Config) Shutdown() { - config.mutexSrc.RLock() - defer config.mutexSrc.RUnlock() - if config.shutdown != nil { - close(config.shutdown) - } -} - -// onSourceChanged is called whenever one or more properties of a -// config source has changed. -func (config *Config) onSourceChanged(src SourceMonitored) { - defer func() { - if p := recover(); p != nil { - fmt.Println(p) - } - }() - config.mutexListeners.RLock() - defer config.mutexListeners.RUnlock() - for _, l := range config.chgListeners { - l.ConfigChanged(config, src) - } -} - -// monitor periodically checks a config source for changes. -func (config *Config) monitor(se *sourceEntry) { - go func(se *sourceEntry, shutdown <-chan interface{}) { - var src SourceMonitored - var ok bool - if src, ok = se.src.(SourceMonitored); !ok { - return - } - paused := false - last := time.Time{} - freq := src.GetMonitorFreq() - if freq <= 0 { - paused = true - freq = 10 - last, _ = src.GetLastModified() - } - timer := time.NewTimer(freq) - for { - select { - case <-timer.C: - if !paused { - if latest, err := src.GetLastModified(); err != nil { - if config.ShouldPanicOnError() { - panic(fmt.Sprintf("error <%v> getting last modified for %v", err, src)) - } - } else { - if last.Before(latest) { - last = latest - config.reloadProps(se) - // TODO: calc diff and provide detailed changes - config.onSourceChanged(src) - } - } - } - freq = src.GetMonitorFreq() - if freq <= 0 { - paused = true - freq = 10 - } else { - paused = false - } - timer.Reset(freq) - case <-shutdown: - // stop the timer and exit - if !timer.Stop() { - <-timer.C - } - return - } - } - }(se, config.shutdown) -} - -// reloadProps causes a Source to reload its properties. -func (config *Config) reloadProps(se *sourceEntry) { - config.mutexSrc.Lock() - defer config.mutexSrc.Unlock() - - m, err := se.src.GetProps() - if err != nil { - if config.wantPanicOnError { - panic(fmt.Sprintf("GetProps error for %v", se.src)) - } - return - } - - se.props = make(map[string]string) - for k, v := range m { - se.props[k] = v - } -} diff --git a/vendor/github.com/wiggin77/cfg/go.mod b/vendor/github.com/wiggin77/cfg/go.mod deleted file mode 100644 index 2e5a038edb..0000000000 --- a/vendor/github.com/wiggin77/cfg/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/wiggin77/cfg - -go 1.12 - -require github.com/wiggin77/merror v1.0.2 diff --git a/vendor/github.com/wiggin77/cfg/go.sum b/vendor/github.com/wiggin77/cfg/go.sum deleted file mode 100644 index 30fd3b5809..0000000000 --- a/vendor/github.com/wiggin77/cfg/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw= -github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg= diff --git a/vendor/github.com/wiggin77/cfg/ini/ini.go b/vendor/github.com/wiggin77/cfg/ini/ini.go deleted file mode 100644 index d28d7444dd..0000000000 --- a/vendor/github.com/wiggin77/cfg/ini/ini.go +++ /dev/null @@ -1,167 +0,0 @@ -package ini - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "sync" - "time" -) - -// Ini provides parsing and querying of INI format or simple name/value pairs -// such as a simple config file. -// A name/value pair format is just an INI with no sections, and properties can -// be queried using an empty section name. -type Ini struct { - mutex sync.RWMutex - m map[string]*Section - lm time.Time -} - -// LoadFromFilespec loads an INI file from string containing path and filename. -func (ini *Ini) LoadFromFilespec(filespec string) error { - f, err := os.Open(filespec) - if err != nil { - return err - } - return ini.LoadFromFile(f) -} - -// LoadFromFile loads an INI file from `os.File`. -func (ini *Ini) LoadFromFile(file *os.File) error { - - fi, err := file.Stat() - if err != nil { - return err - } - lm := fi.ModTime() - - if err := ini.LoadFromReader(file); err != nil { - return err - } - ini.lm = lm - return nil -} - -// LoadFromReader loads an INI file from an `io.Reader`. -func (ini *Ini) LoadFromReader(reader io.Reader) error { - data, err := ioutil.ReadAll(reader) - if err != nil { - return err - } - return ini.LoadFromString(string(data)) -} - -// LoadFromString parses an INI from a string . -func (ini *Ini) LoadFromString(s string) error { - m, err := getSections(s) - if err != nil { - return err - } - ini.mutex.Lock() - ini.m = m - ini.lm = time.Now() - ini.mutex.Unlock() - return nil -} - -// GetLastModified returns the last modified timestamp of the -// INI contents. -func (ini *Ini) GetLastModified() time.Time { - return ini.lm -} - -// GetSectionNames returns the names of all sections in this INI. -// Note, the returned section names are a snapshot in time, meaning -// other goroutines may change the contents of this INI as soon as -// the method returns. -func (ini *Ini) GetSectionNames() []string { - ini.mutex.RLock() - defer ini.mutex.RUnlock() - - arr := make([]string, 0, len(ini.m)) - for key := range ini.m { - arr = append(arr, key) - } - return arr -} - -// GetKeys returns the names of all keys in the specified section. -// Note, the returned key names are a snapshot in time, meaning other -// goroutines may change the contents of this INI as soon as the -// method returns. -func (ini *Ini) GetKeys(sectionName string) ([]string, error) { - sec, err := ini.getSection(sectionName) - if err != nil { - return nil, err - } - return sec.getKeys(), nil -} - -// getSection returns the named section. -func (ini *Ini) getSection(sectionName string) (*Section, error) { - ini.mutex.RLock() - defer ini.mutex.RUnlock() - - sec, ok := ini.m[sectionName] - if !ok { - return nil, fmt.Errorf("section '%s' not found", sectionName) - } - return sec, nil -} - -// GetFlattenedKeys returns all section names plus keys as one -// flattened array. -func (ini *Ini) GetFlattenedKeys() []string { - ini.mutex.RLock() - defer ini.mutex.RUnlock() - - arr := make([]string, 0, len(ini.m)*2) - for _, section := range ini.m { - keys := section.getKeys() - for _, key := range keys { - name := section.GetName() - if name != "" { - key = name + "." + key - } - arr = append(arr, key) - } - } - return arr -} - -// GetProp returns the value of the specified key in the named section. -func (ini *Ini) GetProp(section string, key string) (val string, ok bool) { - sec, err := ini.getSection(section) - if err != nil { - return val, false - } - return sec.GetProp(key) -} - -// ToMap returns a flattened map of the section name plus keys mapped -// to values. -func (ini *Ini) ToMap() map[string]string { - m := make(map[string]string) - - ini.mutex.RLock() - defer ini.mutex.RUnlock() - - for _, section := range ini.m { - for _, key := range section.getKeys() { - val, ok := section.GetProp(key) - if ok { - name := section.GetName() - var mapkey string - if name != "" { - mapkey = name + "." + key - } else { - mapkey = key - } - m[mapkey] = val - } - } - } - return m -} diff --git a/vendor/github.com/wiggin77/cfg/ini/parser.go b/vendor/github.com/wiggin77/cfg/ini/parser.go deleted file mode 100644 index 28916409ae..0000000000 --- a/vendor/github.com/wiggin77/cfg/ini/parser.go +++ /dev/null @@ -1,142 +0,0 @@ -package ini - -import ( - "fmt" - "strings" - - "github.com/wiggin77/merror" -) - -// LF is linefeed -const LF byte = 0x0A - -// CR is carriage return -const CR byte = 0x0D - -// getSections parses an INI formatted string, or string containing just name/value pairs, -// returns map of `Section`'s. -// -// Any name/value pairs appearing before a section name are added to the section named -// with an empty string (""). Also true for Linux-style config files where all props -// are outside a named section. -// -// Any errors encountered are aggregated and returned, along with the partially parsed -// sections. -func getSections(str string) (map[string]*Section, error) { - merr := merror.New() - mapSections := make(map[string]*Section) - lines := buildLineArray(str) - section := newSection("") - - for _, line := range lines { - name, ok := parseSection(line) - if ok { - // A section name encountered. Stop processing the current one. - // Don't add the current section to the map if the section name is blank - // and the prop map is empty. - nameCurr := section.GetName() - if nameCurr != "" || section.hasKeys() { - mapSections[nameCurr] = section - } - // Start processing a new section. - section = newSection(name) - } else { - // Parse the property and add to the current section, or ignore if comment. - if k, v, comment, err := parseProp(line); !comment && err == nil { - section.setProp(k, v) - } else if err != nil { - merr.Append(err) // aggregate errors - } - } - - } - // If the current section is not empty, add it. - if section.hasKeys() { - mapSections[section.GetName()] = section - } - return mapSections, merr.ErrorOrNil() -} - -// buildLineArray parses the given string buffer and creates a list of strings, -// one for each line in the string buffer. -// -// A line is considered to be terminated by any one of a line feed ('\n'), -// a carriage return ('\r'), or a carriage return followed immediately by a -// linefeed. -// -// Lines prefixed with ';' or '#' are considered comments and skipped. -func buildLineArray(str string) []string { - arr := make([]string, 0, 10) - str = str + "\n" - - iLen := len(str) - iPos, iBegin := 0, 0 - var ch byte - - for iPos < iLen { - ch = str[iPos] - if ch == LF || ch == CR { - sub := str[iBegin:iPos] - sub = strings.TrimSpace(sub) - if sub != "" && !strings.HasPrefix(sub, ";") && !strings.HasPrefix(sub, "#") { - arr = append(arr, sub) - } - iPos++ - if ch == CR && iPos < iLen && str[iPos] == LF { - iPos++ - } - iBegin = iPos - } else { - iPos++ - } - } - return arr -} - -// parseSection parses the specified string for a section name enclosed in square brackets. -// Returns the section name found, or `ok=false` if `str` is not a section header. -func parseSection(str string) (name string, ok bool) { - str = strings.TrimSpace(str) - if !strings.HasPrefix(str, "[") { - return "", false - } - iCloser := strings.Index(str, "]") - if iCloser == -1 { - return "", false - } - return strings.TrimSpace(str[1:iCloser]), true -} - -// parseProp parses the specified string and extracts a key/value pair. -// -// If the string is a comment (prefixed with ';' or '#') then `comment=true` -// and key will be empty. -func parseProp(str string) (key string, val string, comment bool, err error) { - iLen := len(str) - iEqPos := strings.Index(str, "=") - if iEqPos == -1 { - return "", "", false, fmt.Errorf("not a key/value pair:'%s'", str) - } - - key = str[0:iEqPos] - key = strings.TrimSpace(key) - if iEqPos+1 < iLen { - val = str[iEqPos+1:] - val = strings.TrimSpace(val) - } - - // Check that the key has at least 1 char. - if key == "" { - return "", "", false, fmt.Errorf("key is empty for '%s'", str) - } - - // Check if this line is a comment that just happens - // to have an equals sign in it. Not an error, but not a - // useable line either. - if strings.HasPrefix(key, ";") || strings.HasPrefix(key, "#") { - key = "" - val = "" - comment = true - } - return key, val, comment, err -} diff --git a/vendor/github.com/wiggin77/cfg/ini/section.go b/vendor/github.com/wiggin77/cfg/ini/section.go deleted file mode 100644 index 18c4c25403..0000000000 --- a/vendor/github.com/wiggin77/cfg/ini/section.go +++ /dev/null @@ -1,109 +0,0 @@ -package ini - -import ( - "fmt" - "strings" - "sync" -) - -// Section represents a section in an INI file. The section has a name, which is -// enclosed in square brackets in the file. The section also has an array of -// key/value pairs. -type Section struct { - name string - props map[string]string - mtx sync.RWMutex -} - -func newSection(name string) *Section { - sec := &Section{} - sec.name = name - sec.props = make(map[string]string) - return sec -} - -// addLines addes an array of strings containing name/value pairs -// of the format `key=value`. -//func addLines(lines []string) { -// TODO -//} - -// GetName returns the name of the section. -func (sec *Section) GetName() (name string) { - sec.mtx.RLock() - name = sec.name - sec.mtx.RUnlock() - return -} - -// GetProp returns the value associated with the given key, or -// `ok=false` if key does not exist. -func (sec *Section) GetProp(key string) (val string, ok bool) { - sec.mtx.RLock() - val, ok = sec.props[key] - sec.mtx.RUnlock() - return -} - -// SetProp sets the value associated with the given key. -func (sec *Section) setProp(key string, val string) { - sec.mtx.Lock() - sec.props[key] = val - sec.mtx.Unlock() -} - -// hasKeys returns true if there are one or more properties in -// this section. -func (sec *Section) hasKeys() (b bool) { - sec.mtx.RLock() - b = len(sec.props) > 0 - sec.mtx.RUnlock() - return -} - -// getKeys returns an array containing all keys in this section. -func (sec *Section) getKeys() []string { - sec.mtx.RLock() - defer sec.mtx.RUnlock() - - arr := make([]string, len(sec.props)) - idx := 0 - for k := range sec.props { - arr[idx] = k - idx++ - } - return arr -} - -// combine the given section with this one. -func (sec *Section) combine(sec2 *Section) { - sec.mtx.Lock() - sec2.mtx.RLock() - defer sec.mtx.Unlock() - defer sec2.mtx.RUnlock() - - for k, v := range sec2.props { - sec.props[k] = v - } -} - -// String returns a string representation of this section. -func (sec *Section) String() string { - return fmt.Sprintf("[%s]\n%s", sec.GetName(), sec.StringPropsOnly()) -} - -// StringPropsOnly returns a string representation of this section -// without the section header. -func (sec *Section) StringPropsOnly() string { - sec.mtx.RLock() - defer sec.mtx.RUnlock() - sb := &strings.Builder{} - - for k, v := range sec.props { - sb.WriteString(k) - sb.WriteString("=") - sb.WriteString(v) - sb.WriteString("\n") - } - return sb.String() -} diff --git a/vendor/github.com/wiggin77/cfg/listener.go b/vendor/github.com/wiggin77/cfg/listener.go deleted file mode 100644 index 12ea4e45d6..0000000000 --- a/vendor/github.com/wiggin77/cfg/listener.go +++ /dev/null @@ -1,11 +0,0 @@ -package cfg - -// ChangedListener interface is for receiving notifications -// when one or more properties within monitored config sources -// (SourceMonitored) have changed values. -type ChangedListener interface { - - // Changed is called when one or more properties in a `SourceMonitored` has a - // changed value. - ConfigChanged(cfg *Config, src SourceMonitored) -} diff --git a/vendor/github.com/wiggin77/cfg/nocopy.go b/vendor/github.com/wiggin77/cfg/nocopy.go deleted file mode 100644 index f2450c0b23..0000000000 --- a/vendor/github.com/wiggin77/cfg/nocopy.go +++ /dev/null @@ -1,11 +0,0 @@ -package cfg - -// noCopy may be embedded into structs which must not be copied -// after the first use. -// -// See https://golang.org/issues/8005#issuecomment-190753527 -// for details. -type noCopy struct{} - -// Lock is a no-op used by -copylocks checker from `go vet`. -func (*noCopy) Lock() {} diff --git a/vendor/github.com/wiggin77/cfg/source.go b/vendor/github.com/wiggin77/cfg/source.go deleted file mode 100644 index 09083e970e..0000000000 --- a/vendor/github.com/wiggin77/cfg/source.go +++ /dev/null @@ -1,58 +0,0 @@ -package cfg - -import ( - "sync" - "time" -) - -// Source is the interface required for any source of name/value pairs. -type Source interface { - - // GetProps fetches all the properties from a source and returns - // them as a map. - GetProps() (map[string]string, error) -} - -// SourceMonitored is the interface required for any config source that is -// monitored for changes. -type SourceMonitored interface { - Source - - // GetLastModified returns the time of the latest modification to any - // property value within the source. If a source does not support - // modifying properties at runtime then the zero value for `Time` - // should be returned to ensure reload events are not generated. - GetLastModified() (time.Time, error) - - // GetMonitorFreq returns the frequency as a `time.Duration` between - // checks for changes to this config source. - // - // Returning zero (or less) will temporarily suspend calls to `GetLastModified` - // and `GetMonitorFreq` will be called every 10 seconds until resumed, after which - // `GetMontitorFreq` will be called at a frequency roughly equal to the `time.Duration` - // returned. - GetMonitorFreq() time.Duration -} - -// AbstractSourceMonitor can be embedded in a custom `Source` to provide the -// basic plumbing for monitor frequency. -type AbstractSourceMonitor struct { - mutex sync.RWMutex - freq time.Duration -} - -// GetMonitorFreq returns the frequency as a `time.Duration` between -// checks for changes to this config source. -func (asm *AbstractSourceMonitor) GetMonitorFreq() (freq time.Duration) { - asm.mutex.RLock() - freq = asm.freq - asm.mutex.RUnlock() - return -} - -// SetMonitorFreq sets the frequency between checks for changes to this config source. -func (asm *AbstractSourceMonitor) SetMonitorFreq(freq time.Duration) { - asm.mutex.Lock() - asm.freq = freq - asm.mutex.Unlock() -} diff --git a/vendor/github.com/wiggin77/cfg/srcfile.go b/vendor/github.com/wiggin77/cfg/srcfile.go deleted file mode 100644 index f42c69fac7..0000000000 --- a/vendor/github.com/wiggin77/cfg/srcfile.go +++ /dev/null @@ -1,63 +0,0 @@ -package cfg - -import ( - "os" - "time" - - "github.com/wiggin77/cfg/ini" -) - -// SrcFile is a configuration `Source` backed by a file containing -// name/value pairs or INI format. -type SrcFile struct { - AbstractSourceMonitor - ini ini.Ini - file *os.File -} - -// NewSrcFileFromFilespec creates a new SrcFile with the specified filespec. -func NewSrcFileFromFilespec(filespec string) (*SrcFile, error) { - file, err := os.Open(filespec) - if err != nil { - return nil, err - } - return NewSrcFile(file) -} - -// NewSrcFile creates a new SrcFile with the specified os.File. -func NewSrcFile(file *os.File) (*SrcFile, error) { - sf := &SrcFile{} - sf.freq = time.Minute - sf.file = file - if err := sf.ini.LoadFromFile(file); err != nil { - return nil, err - } - return sf, nil -} - -// GetProps fetches all the properties from a source and returns -// them as a map. -func (sf *SrcFile) GetProps() (map[string]string, error) { - lm, err := sf.GetLastModified() - if err != nil { - return nil, err - } - - // Check if we need to reload. - if sf.ini.GetLastModified() != lm { - if err := sf.ini.LoadFromFile(sf.file); err != nil { - return nil, err - } - } - return sf.ini.ToMap(), nil -} - -// GetLastModified returns the time of the latest modification to any -// property value within the source. -func (sf *SrcFile) GetLastModified() (time.Time, error) { - fi, err := sf.file.Stat() - if err != nil { - return time.Now(), err - } - return fi.ModTime(), nil -} diff --git a/vendor/github.com/wiggin77/cfg/srcmap.go b/vendor/github.com/wiggin77/cfg/srcmap.go deleted file mode 100644 index 321db27ac9..0000000000 --- a/vendor/github.com/wiggin77/cfg/srcmap.go +++ /dev/null @@ -1,78 +0,0 @@ -package cfg - -import ( - "time" -) - -// SrcMap is a configuration `Source` backed by a simple map. -type SrcMap struct { - AbstractSourceMonitor - m map[string]string - lm time.Time -} - -// NewSrcMap creates an empty `SrcMap`. -func NewSrcMap() *SrcMap { - sm := &SrcMap{} - sm.m = make(map[string]string) - sm.lm = time.Now() - sm.freq = time.Minute - return sm -} - -// NewSrcMapFromMap creates a `SrcMap` containing a copy of the -// specified map. -func NewSrcMapFromMap(mapIn map[string]string) *SrcMap { - sm := NewSrcMap() - sm.PutAll(mapIn) - return sm -} - -// Put inserts or updates a value in the `SrcMap`. -func (sm *SrcMap) Put(key string, val string) { - sm.mutex.Lock() - sm.m[key] = val - sm.lm = time.Now() - sm.mutex.Unlock() -} - -// PutAll inserts a copy of `mapIn` into the `SrcMap` -func (sm *SrcMap) PutAll(mapIn map[string]string) { - sm.mutex.Lock() - defer sm.mutex.Unlock() - - for k, v := range mapIn { - sm.m[k] = v - } - sm.lm = time.Now() -} - -// GetProps fetches all the properties from a source and returns -// them as a map. -func (sm *SrcMap) GetProps() (m map[string]string, err error) { - sm.mutex.RLock() - m = sm.m - sm.mutex.RUnlock() - return -} - -// GetLastModified returns the time of the latest modification to any -// property value within the source. If a source does not support -// modifying properties at runtime then the zero value for `Time` -// should be returned to ensure reload events are not generated. -func (sm *SrcMap) GetLastModified() (last time.Time, err error) { - sm.mutex.RLock() - last = sm.lm - sm.mutex.RUnlock() - return -} - -// GetMonitorFreq returns the frequency as a `time.Duration` between -// checks for changes to this config source. Defaults to 1 minute -// unless changed with `SetMonitorFreq`. -func (sm *SrcMap) GetMonitorFreq() (freq time.Duration) { - sm.mutex.RLock() - freq = sm.freq - sm.mutex.RUnlock() - return -} diff --git a/vendor/github.com/wiggin77/cfg/timeconv/parse.go b/vendor/github.com/wiggin77/cfg/timeconv/parse.go deleted file mode 100644 index 218ef43a04..0000000000 --- a/vendor/github.com/wiggin77/cfg/timeconv/parse.go +++ /dev/null @@ -1,108 +0,0 @@ -package timeconv - -import ( - "fmt" - "math" - "regexp" - "strconv" - "strings" -) - -// MillisPerSecond is the number of millseconds per second. -const MillisPerSecond int64 = 1000 - -// MillisPerMinute is the number of millseconds per minute. -const MillisPerMinute int64 = MillisPerSecond * 60 - -// MillisPerHour is the number of millseconds per hour. -const MillisPerHour int64 = MillisPerMinute * 60 - -// MillisPerDay is the number of millseconds per day. -const MillisPerDay int64 = MillisPerHour * 24 - -// MillisPerWeek is the number of millseconds per week. -const MillisPerWeek int64 = MillisPerDay * 7 - -// MillisPerYear is the approximate number of millseconds per year. -const MillisPerYear int64 = MillisPerDay*365 + int64((float64(MillisPerDay) * 0.25)) - -// ParseMilliseconds parses a string containing a number plus -// a unit of measure for time and returns the number of milliseconds -// it represents. -// -// Example: -// * "1 second" returns 1000 -// * "1 minute" returns 60000 -// * "1 hour" returns 3600000 -// -// See config.UnitsToMillis for a list of supported units of measure. -func ParseMilliseconds(str string) (int64, error) { - s := strings.TrimSpace(str) - reg := regexp.MustCompile("([0-9\\.\\-+]*)(.*)") - matches := reg.FindStringSubmatch(s) - if matches == nil || len(matches) < 1 || matches[1] == "" { - return 0, fmt.Errorf("invalid syntax - '%s'", s) - } - digits := matches[1] - units := "ms" - if len(matches) > 1 && matches[2] != "" { - units = matches[2] - } - - fDigits, err := strconv.ParseFloat(digits, 64) - if err != nil { - return 0, err - } - - msPerUnit, err := UnitsToMillis(units) - if err != nil { - return 0, err - } - - // Check for overflow. - fms := float64(msPerUnit) * fDigits - if fms > math.MaxInt64 || fms < math.MinInt64 { - return 0, fmt.Errorf("out of range - '%s' overflows", s) - } - ms := int64(fms) - return ms, nil -} - -// UnitsToMillis returns the number of milliseconds represented by the specified unit of measure. -// -// Example: -// * "second" returns 1000
-// * "minute" returns 60000
-// * "hour" returns 3600000
-// -// Supported units of measure: -// * "milliseconds", "millis", "ms", "millisecond" -// * "seconds", "sec", "s", "second" -// * "minutes", "mins", "min", "m", "minute" -// * "hours", "h", "hour" -// * "days", "d", "day" -// * "weeks", "w", "week" -// * "years", "y", "year" -func UnitsToMillis(units string) (ms int64, err error) { - u := strings.TrimSpace(units) - u = strings.ToLower(u) - switch u { - case "milliseconds", "millisecond", "millis", "ms": - ms = 1 - case "seconds", "second", "sec", "s": - ms = MillisPerSecond - case "minutes", "minute", "mins", "min", "m": - ms = MillisPerMinute - case "hours", "hour", "h": - ms = MillisPerHour - case "days", "day", "d": - ms = MillisPerDay - case "weeks", "week", "w": - ms = MillisPerWeek - case "years", "year", "y": - ms = MillisPerYear - default: - err = fmt.Errorf("invalid syntax - '%s' not a supported unit of measure", u) - } - return -} diff --git a/vendor/go.uber.org/multierr/.codecov.yml b/vendor/go.uber.org/multierr/.codecov.yml deleted file mode 100644 index 6d4d1be7b5..0000000000 --- a/vendor/go.uber.org/multierr/.codecov.yml +++ /dev/null @@ -1,15 +0,0 @@ -coverage: - range: 80..100 - round: down - precision: 2 - - status: - project: # measuring the overall project coverage - default: # context, you can create multiple ones with custom titles - enabled: yes # must be yes|true to enable this status - target: 100 # specify the target coverage for each commit status - # option: "auto" (must increase from parent commit or pull request base) - # option: "X%" a static target percentage to hit - if_not_found: success # if parent is not found report status as success, error, or failure - if_ci_failed: error # if ci fails report status as success, error, or failure - diff --git a/vendor/go.uber.org/multierr/.gitignore b/vendor/go.uber.org/multierr/.gitignore deleted file mode 100644 index b9a05e3da0..0000000000 --- a/vendor/go.uber.org/multierr/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/vendor -cover.html -cover.out -/bin diff --git a/vendor/go.uber.org/multierr/CHANGELOG.md b/vendor/go.uber.org/multierr/CHANGELOG.md deleted file mode 100644 index b0814e7c9b..0000000000 --- a/vendor/go.uber.org/multierr/CHANGELOG.md +++ /dev/null @@ -1,66 +0,0 @@ -Releases -======== - -v1.7.0 (2021-05-06) -=================== - -- Add `AppendInvoke` to append into errors from `defer` blocks. - - -v1.6.0 (2020-09-14) -=================== - -- Actually drop library dependency on development-time tooling. - - -v1.5.0 (2020-02-24) -=================== - -- Drop library dependency on development-time tooling. - - -v1.4.0 (2019-11-04) -=================== - -- Add `AppendInto` function to more ergonomically build errors inside a - loop. - - -v1.3.0 (2019-10-29) -=================== - -- Switch to Go modules. - - -v1.2.0 (2019-09-26) -=================== - -- Support extracting and matching against wrapped errors with `errors.As` - and `errors.Is`. - - -v1.1.0 (2017-06-30) -=================== - -- Added an `Errors(error) []error` function to extract the underlying list of - errors for a multierr error. - - -v1.0.0 (2017-05-31) -=================== - -No changes since v0.2.0. This release is committing to making no breaking -changes to the current API in the 1.X series. - - -v0.2.0 (2017-04-11) -=================== - -- Repeatedly appending to the same error is now faster due to fewer - allocations. - - -v0.1.0 (2017-31-03) -=================== - -- Initial release diff --git a/vendor/go.uber.org/multierr/LICENSE.txt b/vendor/go.uber.org/multierr/LICENSE.txt deleted file mode 100644 index 413e30f7ce..0000000000 --- a/vendor/go.uber.org/multierr/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2017-2021 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/go.uber.org/multierr/Makefile b/vendor/go.uber.org/multierr/Makefile deleted file mode 100644 index dcb6fe723c..0000000000 --- a/vendor/go.uber.org/multierr/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -# Directory to put `go install`ed binaries in. -export GOBIN ?= $(shell pwd)/bin - -GO_FILES := $(shell \ - find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ - -o -name '*.go' -print | cut -b3-) - -.PHONY: build -build: - go build ./... - -.PHONY: test -test: - go test -race ./... - -.PHONY: gofmt -gofmt: - $(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX)) - @gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true - @[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false) - -.PHONY: golint -golint: - @cd tools && go install golang.org/x/lint/golint - @$(GOBIN)/golint ./... - -.PHONY: staticcheck -staticcheck: - @cd tools && go install honnef.co/go/tools/cmd/staticcheck - @$(GOBIN)/staticcheck ./... - -.PHONY: lint -lint: gofmt golint staticcheck - -.PHONY: cover -cover: - go test -race -coverprofile=cover.out -coverpkg=./... -v ./... - go tool cover -html=cover.out -o cover.html diff --git a/vendor/go.uber.org/multierr/README.md b/vendor/go.uber.org/multierr/README.md deleted file mode 100644 index 70aacecd71..0000000000 --- a/vendor/go.uber.org/multierr/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# multierr [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] - -`multierr` allows combining one or more Go `error`s together. - -## Installation - - go get -u go.uber.org/multierr - -## Status - -Stable: No breaking changes will be made before 2.0. - -------------------------------------------------------------------------------- - -Released under the [MIT License]. - -[MIT License]: LICENSE.txt -[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr -[doc]: https://pkg.go.dev/go.uber.org/multierr -[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg -[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg -[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml -[cov]: https://codecov.io/gh/uber-go/multierr diff --git a/vendor/go.uber.org/multierr/error.go b/vendor/go.uber.org/multierr/error.go deleted file mode 100644 index faa0a05946..0000000000 --- a/vendor/go.uber.org/multierr/error.go +++ /dev/null @@ -1,639 +0,0 @@ -// Copyright (c) 2017-2021 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package multierr allows combining one or more errors together. -// -// Overview -// -// Errors can be combined with the use of the Combine function. -// -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// conn.Close(), -// ) -// -// If only two errors are being combined, the Append function may be used -// instead. -// -// err = multierr.Append(reader.Close(), writer.Close()) -// -// The underlying list of errors for a returned error object may be retrieved -// with the Errors function. -// -// errors := multierr.Errors(err) -// if len(errors) > 0 { -// fmt.Println("The following errors occurred:", errors) -// } -// -// Appending from a loop -// -// You sometimes need to append into an error from a loop. -// -// var err error -// for _, item := range items { -// err = multierr.Append(err, process(item)) -// } -// -// Cases like this may require knowledge of whether an individual instance -// failed. This usually requires introduction of a new variable. -// -// var err error -// for _, item := range items { -// if perr := process(item); perr != nil { -// log.Warn("skipping item", item) -// err = multierr.Append(err, perr) -// } -// } -// -// multierr includes AppendInto to simplify cases like this. -// -// var err error -// for _, item := range items { -// if multierr.AppendInto(&err, process(item)) { -// log.Warn("skipping item", item) -// } -// } -// -// This will append the error into the err variable, and return true if that -// individual error was non-nil. -// -// See AppendInto for more information. -// -// Deferred Functions -// -// Go makes it possible to modify the return value of a function in a defer -// block if the function was using named returns. This makes it possible to -// record resource cleanup failures from deferred blocks. -// -// func sendRequest(req Request) (err error) { -// conn, err := openConnection() -// if err != nil { -// return err -// } -// defer func() { -// err = multierr.Append(err, conn.Close()) -// }() -// // ... -// } -// -// multierr provides the Invoker type and AppendInvoke function to make cases -// like the above simpler and obviate the need for a closure. The following is -// roughly equivalent to the example above. -// -// func sendRequest(req Request) (err error) { -// conn, err := openConnection() -// if err != nil { -// return err -// } -// defer multierr.AppendInvoke(err, multierr.Close(conn)) -// // ... -// } -// -// See AppendInvoke and Invoker for more information. -// -// Advanced Usage -// -// Errors returned by Combine and Append MAY implement the following -// interface. -// -// type errorGroup interface { -// // Returns a slice containing the underlying list of errors. -// // -// // This slice MUST NOT be modified by the caller. -// Errors() []error -// } -// -// Note that if you need access to list of errors behind a multierr error, you -// should prefer using the Errors function. That said, if you need cheap -// read-only access to the underlying errors slice, you can attempt to cast -// the error to this interface. You MUST handle the failure case gracefully -// because errors returned by Combine and Append are not guaranteed to -// implement this interface. -// -// var errors []error -// group, ok := err.(errorGroup) -// if ok { -// errors = group.Errors() -// } else { -// errors = []error{err} -// } -package multierr // import "go.uber.org/multierr" - -import ( - "bytes" - "errors" - "fmt" - "io" - "strings" - "sync" - - "go.uber.org/atomic" -) - -var ( - // Separator for single-line error messages. - _singlelineSeparator = []byte("; ") - - // Prefix for multi-line messages - _multilinePrefix = []byte("the following errors occurred:") - - // Prefix for the first and following lines of an item in a list of - // multi-line error messages. - // - // For example, if a single item is: - // - // foo - // bar - // - // It will become, - // - // - foo - // bar - _multilineSeparator = []byte("\n - ") - _multilineIndent = []byte(" ") -) - -// _bufferPool is a pool of bytes.Buffers. -var _bufferPool = sync.Pool{ - New: func() interface{} { - return &bytes.Buffer{} - }, -} - -type errorGroup interface { - Errors() []error -} - -// Errors returns a slice containing zero or more errors that the supplied -// error is composed of. If the error is nil, a nil slice is returned. -// -// err := multierr.Append(r.Close(), w.Close()) -// errors := multierr.Errors(err) -// -// If the error is not composed of other errors, the returned slice contains -// just the error that was passed in. -// -// Callers of this function are free to modify the returned slice. -func Errors(err error) []error { - if err == nil { - return nil - } - - // Note that we're casting to multiError, not errorGroup. Our contract is - // that returned errors MAY implement errorGroup. Errors, however, only - // has special behavior for multierr-specific error objects. - // - // This behavior can be expanded in the future but I think it's prudent to - // start with as little as possible in terms of contract and possibility - // of misuse. - eg, ok := err.(*multiError) - if !ok { - return []error{err} - } - - errors := eg.Errors() - result := make([]error, len(errors)) - copy(result, errors) - return result -} - -// multiError is an error that holds one or more errors. -// -// An instance of this is guaranteed to be non-empty and flattened. That is, -// none of the errors inside multiError are other multiErrors. -// -// multiError formats to a semi-colon delimited list of error messages with -// %v and with a more readable multi-line format with %+v. -type multiError struct { - copyNeeded atomic.Bool - errors []error -} - -var _ errorGroup = (*multiError)(nil) - -// Errors returns the list of underlying errors. -// -// This slice MUST NOT be modified. -func (merr *multiError) Errors() []error { - if merr == nil { - return nil - } - return merr.errors -} - -// As attempts to find the first error in the error list that matches the type -// of the value that target points to. -// -// This function allows errors.As to traverse the values stored on the -// multierr error. -func (merr *multiError) As(target interface{}) bool { - for _, err := range merr.Errors() { - if errors.As(err, target) { - return true - } - } - return false -} - -// Is attempts to match the provided error against errors in the error list. -// -// This function allows errors.Is to traverse the values stored on the -// multierr error. -func (merr *multiError) Is(target error) bool { - for _, err := range merr.Errors() { - if errors.Is(err, target) { - return true - } - } - return false -} - -func (merr *multiError) Error() string { - if merr == nil { - return "" - } - - buff := _bufferPool.Get().(*bytes.Buffer) - buff.Reset() - - merr.writeSingleline(buff) - - result := buff.String() - _bufferPool.Put(buff) - return result -} - -func (merr *multiError) Format(f fmt.State, c rune) { - if c == 'v' && f.Flag('+') { - merr.writeMultiline(f) - } else { - merr.writeSingleline(f) - } -} - -func (merr *multiError) writeSingleline(w io.Writer) { - first := true - for _, item := range merr.errors { - if first { - first = false - } else { - w.Write(_singlelineSeparator) - } - io.WriteString(w, item.Error()) - } -} - -func (merr *multiError) writeMultiline(w io.Writer) { - w.Write(_multilinePrefix) - for _, item := range merr.errors { - w.Write(_multilineSeparator) - writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item)) - } -} - -// Writes s to the writer with the given prefix added before each line after -// the first. -func writePrefixLine(w io.Writer, prefix []byte, s string) { - first := true - for len(s) > 0 { - if first { - first = false - } else { - w.Write(prefix) - } - - idx := strings.IndexByte(s, '\n') - if idx < 0 { - idx = len(s) - 1 - } - - io.WriteString(w, s[:idx+1]) - s = s[idx+1:] - } -} - -type inspectResult struct { - // Number of top-level non-nil errors - Count int - - // Total number of errors including multiErrors - Capacity int - - // Index of the first non-nil error in the list. Value is meaningless if - // Count is zero. - FirstErrorIdx int - - // Whether the list contains at least one multiError - ContainsMultiError bool -} - -// Inspects the given slice of errors so that we can efficiently allocate -// space for it. -func inspect(errors []error) (res inspectResult) { - first := true - for i, err := range errors { - if err == nil { - continue - } - - res.Count++ - if first { - first = false - res.FirstErrorIdx = i - } - - if merr, ok := err.(*multiError); ok { - res.Capacity += len(merr.errors) - res.ContainsMultiError = true - } else { - res.Capacity++ - } - } - return -} - -// fromSlice converts the given list of errors into a single error. -func fromSlice(errors []error) error { - res := inspect(errors) - switch res.Count { - case 0: - return nil - case 1: - // only one non-nil entry - return errors[res.FirstErrorIdx] - case len(errors): - if !res.ContainsMultiError { - // already flat - return &multiError{errors: errors} - } - } - - nonNilErrs := make([]error, 0, res.Capacity) - for _, err := range errors[res.FirstErrorIdx:] { - if err == nil { - continue - } - - if nested, ok := err.(*multiError); ok { - nonNilErrs = append(nonNilErrs, nested.errors...) - } else { - nonNilErrs = append(nonNilErrs, err) - } - } - - return &multiError{errors: nonNilErrs} -} - -// Combine combines the passed errors into a single error. -// -// If zero arguments were passed or if all items are nil, a nil error is -// returned. -// -// Combine(nil, nil) // == nil -// -// If only a single error was passed, it is returned as-is. -// -// Combine(err) // == err -// -// Combine skips over nil arguments so this function may be used to combine -// together errors from operations that fail independently of each other. -// -// multierr.Combine( -// reader.Close(), -// writer.Close(), -// pipe.Close(), -// ) -// -// If any of the passed errors is a multierr error, it will be flattened along -// with the other errors. -// -// multierr.Combine(multierr.Combine(err1, err2), err3) -// // is the same as -// multierr.Combine(err1, err2, err3) -// -// The returned error formats into a readable multi-line error message if -// formatted with %+v. -// -// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) -func Combine(errors ...error) error { - return fromSlice(errors) -} - -// Append appends the given errors together. Either value may be nil. -// -// This function is a specialization of Combine for the common case where -// there are only two errors. -// -// err = multierr.Append(reader.Close(), writer.Close()) -// -// The following pattern may also be used to record failure of deferred -// operations without losing information about the original error. -// -// func doSomething(..) (err error) { -// f := acquireResource() -// defer func() { -// err = multierr.Append(err, f.Close()) -// }() -func Append(left error, right error) error { - switch { - case left == nil: - return right - case right == nil: - return left - } - - if _, ok := right.(*multiError); !ok { - if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) { - // Common case where the error on the left is constantly being - // appended to. - errs := append(l.errors, right) - return &multiError{errors: errs} - } else if !ok { - // Both errors are single errors. - return &multiError{errors: []error{left, right}} - } - } - - // Either right or both, left and right, are multiErrors. Rely on usual - // expensive logic. - errors := [2]error{left, right} - return fromSlice(errors[0:]) -} - -// AppendInto appends an error into the destination of an error pointer and -// returns whether the error being appended was non-nil. -// -// var err error -// multierr.AppendInto(&err, r.Close()) -// multierr.AppendInto(&err, w.Close()) -// -// The above is equivalent to, -// -// err := multierr.Append(r.Close(), w.Close()) -// -// As AppendInto reports whether the provided error was non-nil, it may be -// used to build a multierr error in a loop more ergonomically. For example: -// -// var err error -// for line := range lines { -// var item Item -// if multierr.AppendInto(&err, parse(line, &item)) { -// continue -// } -// items = append(items, item) -// } -// -// Compare this with a version that relies solely on Append: -// -// var err error -// for line := range lines { -// var item Item -// if parseErr := parse(line, &item); parseErr != nil { -// err = multierr.Append(err, parseErr) -// continue -// } -// items = append(items, item) -// } -func AppendInto(into *error, err error) (errored bool) { - if into == nil { - // We panic if 'into' is nil. This is not documented above - // because suggesting that the pointer must be non-nil may - // confuse users into thinking that the error that it points - // to must be non-nil. - panic("misuse of multierr.AppendInto: into pointer must not be nil") - } - - if err == nil { - return false - } - *into = Append(*into, err) - return true -} - -// Invoker is an operation that may fail with an error. Use it with -// AppendInvoke to append the result of calling the function into an error. -// This allows you to conveniently defer capture of failing operations. -// -// See also, Close and Invoke. -type Invoker interface { - Invoke() error -} - -// Invoke wraps a function which may fail with an error to match the Invoker -// interface. Use it to supply functions matching this signature to -// AppendInvoke. -// -// For example, -// -// func processReader(r io.Reader) (err error) { -// scanner := bufio.NewScanner(r) -// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) -// for scanner.Scan() { -// // ... -// } -// // ... -// } -// -// In this example, the following line will construct the Invoker right away, -// but defer the invocation of scanner.Err() until the function returns. -// -// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) -type Invoke func() error - -// Invoke calls the supplied function and returns its result. -func (i Invoke) Invoke() error { return i() } - -// Close builds an Invoker that closes the provided io.Closer. Use it with -// AppendInvoke to close io.Closers and append their results into an error. -// -// For example, -// -// func processFile(path string) (err error) { -// f, err := os.Open(path) -// if err != nil { -// return err -// } -// defer multierr.AppendInvoke(&err, multierr.Close(f)) -// return processReader(f) -// } -// -// In this example, multierr.Close will construct the Invoker right away, but -// defer the invocation of f.Close until the function returns. -// -// defer multierr.AppendInvoke(&err, multierr.Close(f)) -func Close(closer io.Closer) Invoker { - return Invoke(closer.Close) -} - -// AppendInvoke appends the result of calling the given Invoker into the -// provided error pointer. Use it with named returns to safely defer -// invocation of fallible operations until a function returns, and capture the -// resulting errors. -// -// func doSomething(...) (err error) { -// // ... -// f, err := openFile(..) -// if err != nil { -// return err -// } -// -// // multierr will call f.Close() when this function returns and -// // if the operation fails, its append its error into the -// // returned error. -// defer multierr.AppendInvoke(&err, multierr.Close(f)) -// -// scanner := bufio.NewScanner(f) -// // Similarly, this scheduled scanner.Err to be called and -// // inspected when the function returns and append its error -// // into the returned error. -// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err)) -// -// // ... -// } -// -// Without defer, AppendInvoke behaves exactly like AppendInto. -// -// err := // ... -// multierr.AppendInvoke(&err, mutltierr.Invoke(foo)) -// -// // ...is roughly equivalent to... -// -// err := // ... -// multierr.AppendInto(&err, foo()) -// -// The advantage of the indirection introduced by Invoker is to make it easy -// to defer the invocation of a function. Without this indirection, the -// invoked function will be evaluated at the time of the defer block rather -// than when the function returns. -// -// // BAD: This is likely not what the caller intended. This will evaluate -// // foo() right away and append its result into the error when the -// // function returns. -// defer multierr.AppendInto(&err, foo()) -// -// // GOOD: This will defer invocation of foo unutil the function returns. -// defer multierr.AppendInvoke(&err, multierr.Invoke(foo)) -// -// multierr provides a few Invoker implementations out of the box for -// convenience. See Invoker for more information. -func AppendInvoke(into *error, invoker Invoker) { - AppendInto(into, invoker.Invoke()) -} diff --git a/vendor/go.uber.org/multierr/glide.yaml b/vendor/go.uber.org/multierr/glide.yaml deleted file mode 100644 index 6ef084ec24..0000000000 --- a/vendor/go.uber.org/multierr/glide.yaml +++ /dev/null @@ -1,8 +0,0 @@ -package: go.uber.org/multierr -import: -- package: go.uber.org/atomic - version: ^1 -testImport: -- package: github.com/stretchr/testify - subpackages: - - assert diff --git a/vendor/go.uber.org/multierr/go.mod b/vendor/go.uber.org/multierr/go.mod deleted file mode 100644 index 398d6c99e7..0000000000 --- a/vendor/go.uber.org/multierr/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module go.uber.org/multierr - -go 1.14 - -require ( - github.com/stretchr/testify v1.7.0 - go.uber.org/atomic v1.7.0 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect -) diff --git a/vendor/go.uber.org/multierr/go.sum b/vendor/go.uber.org/multierr/go.sum deleted file mode 100644 index 75edd735e0..0000000000 --- a/vendor/go.uber.org/multierr/go.sum +++ /dev/null @@ -1,16 +0,0 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/go.uber.org/zap/.codecov.yml b/vendor/go.uber.org/zap/.codecov.yml deleted file mode 100644 index 8e5ca7d3e2..0000000000 --- a/vendor/go.uber.org/zap/.codecov.yml +++ /dev/null @@ -1,17 +0,0 @@ -coverage: - range: 80..100 - round: down - precision: 2 - - status: - project: # measuring the overall project coverage - default: # context, you can create multiple ones with custom titles - enabled: yes # must be yes|true to enable this status - target: 95% # specify the target coverage for each commit status - # option: "auto" (must increase from parent commit or pull request base) - # option: "X%" a static target percentage to hit - if_not_found: success # if parent is not found report status as success, error, or failure - if_ci_failed: error # if ci fails report status as success, error, or failure -ignore: - - internal/readme/readme.go - diff --git a/vendor/go.uber.org/zap/.gitignore b/vendor/go.uber.org/zap/.gitignore deleted file mode 100644 index da9d9d00b4..0000000000 --- a/vendor/go.uber.org/zap/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test -vendor - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof -*.pprof -*.out -*.log - -/bin -cover.out -cover.html diff --git a/vendor/go.uber.org/zap/.readme.tmpl b/vendor/go.uber.org/zap/.readme.tmpl deleted file mode 100644 index 3154a1e64c..0000000000 --- a/vendor/go.uber.org/zap/.readme.tmpl +++ /dev/null @@ -1,109 +0,0 @@ -# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] - -Blazing fast, structured, leveled logging in Go. - -## Installation - -`go get -u go.uber.org/zap` - -Note that zap only supports the two most recent minor versions of Go. - -## Quick Start - -In contexts where performance is nice, but not critical, use the -`SugaredLogger`. It's 4-10x faster than other structured logging -packages and includes both structured and `printf`-style APIs. - -```go -logger, _ := zap.NewProduction() -defer logger.Sync() // flushes buffer, if any -sugar := logger.Sugar() -sugar.Infow("failed to fetch URL", - // Structured context as loosely typed key-value pairs. - "url", url, - "attempt", 3, - "backoff", time.Second, -) -sugar.Infof("Failed to fetch URL: %s", url) -``` - -When performance and type safety are critical, use the `Logger`. It's even -faster than the `SugaredLogger` and allocates far less, but it only supports -structured logging. - -```go -logger, _ := zap.NewProduction() -defer logger.Sync() -logger.Info("failed to fetch URL", - // Structured context as strongly typed Field values. - zap.String("url", url), - zap.Int("attempt", 3), - zap.Duration("backoff", time.Second), -) -``` - -See the [documentation][doc] and [FAQ](FAQ.md) for more details. - -## Performance - -For applications that log in the hot path, reflection-based serialization and -string formatting are prohibitively expensive — they're CPU-intensive -and make many small allocations. Put differently, using `encoding/json` and -`fmt.Fprintf` to log tons of `interface{}`s makes your application slow. - -Zap takes a different approach. It includes a reflection-free, zero-allocation -JSON encoder, and the base `Logger` strives to avoid serialization overhead -and allocations wherever possible. By building the high-level `SugaredLogger` -on that foundation, zap lets users *choose* when they need to count every -allocation and when they'd prefer a more familiar, loosely typed API. - -As measured by its own [benchmarking suite][], not only is zap more performant -than comparable structured logging packages — it's also faster than the -standard library. Like all benchmarks, take these with a grain of salt.[1](#footnote-versions) - -Log a message and 10 fields: - -{{.BenchmarkAddingFields}} - -Log a message with a logger that already has 10 fields of context: - -{{.BenchmarkAccumulatedContext}} - -Log a static string, without any context or `printf`-style templating: - -{{.BenchmarkWithoutFields}} - -## Development Status: Stable - -All APIs are finalized, and no breaking changes will be made in the 1.x series -of releases. Users of semver-aware dependency management systems should pin -zap to `^1`. - -## Contributing - -We encourage and support an active, healthy community of contributors — -including you! Details are in the [contribution guide](CONTRIBUTING.md) and -the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on -issues and pull requests, but you can also report any negative conduct to -oss-conduct@uber.com. That email list is a private, safe space; even the zap -maintainers don't have access, so don't hesitate to hold us to a high -standard. - -
- -Released under the [MIT License](LICENSE.txt). - -1 In particular, keep in mind that we may be -benchmarking against slightly older versions of other packages. Versions are -pinned in zap's [glide.lock][] file. [↩](#anchor-versions) - -[doc-img]: https://godoc.org/go.uber.org/zap?status.svg -[doc]: https://godoc.org/go.uber.org/zap -[ci-img]: https://travis-ci.com/uber-go/zap.svg?branch=master -[ci]: https://travis-ci.com/uber-go/zap -[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg -[cov]: https://codecov.io/gh/uber-go/zap -[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks -[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock - diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md deleted file mode 100644 index 3b99bf0ac8..0000000000 --- a/vendor/go.uber.org/zap/CHANGELOG.md +++ /dev/null @@ -1,460 +0,0 @@ -# Changelog - -## 1.17.0 (25 May 2021) - -Bugfixes: -* [#867][]: Encode `` for nil `error` instead of a panic. -* [#931][], [#936][]: Update minimum version constraints to address - vulnerabilities in dependencies. - -Enhancements: -* [#865][]: Improve alignment of fields of the Logger struct, reducing its - size from 96 to 80 bytes. -* [#881][]: Support `grpclog.LoggerV2` in zapgrpc. -* [#903][]: Support URL-encoded POST requests to the AtomicLevel HTTP handler - with the `application/x-www-form-urlencoded` content type. -* [#912][]: Support multi-field encoding with `zap.Inline`. -* [#913][]: Speed up SugaredLogger for calls with a single string. -* [#928][]: Add support for filtering by field name to `zaptest/observer`. - -Thanks to @ash2k, @FMLS, @jimmystewpot, @Oncilla, @tsoslow, @tylitianrui, @withshubh, and @wziww for their contributions to this release. - -## 1.16.0 (1 Sep 2020) - -Bugfixes: -* [#828][]: Fix missing newline in IncreaseLevel error messages. -* [#835][]: Fix panic in JSON encoder when encoding times or durations - without specifying a time or duration encoder. -* [#843][]: Honor CallerSkip when taking stack traces. -* [#862][]: Fix the default file permissions to use `0666` and rely on the umask instead. -* [#854][]: Encode `` for nil `Stringer` instead of a panic error log. - -Enhancements: -* [#629][]: Added `zapcore.TimeEncoderOfLayout` to easily create time encoders - for custom layouts. -* [#697][]: Added support for a configurable delimiter in the console encoder. -* [#852][]: Optimize console encoder by pooling the underlying JSON encoder. -* [#844][]: Add ability to include the calling function as part of logs. -* [#843][]: Add `StackSkip` for including truncated stacks as a field. -* [#861][]: Add options to customize Fatal behaviour for better testability. - -Thanks to @SteelPhase, @tmshn, @lixingwang, @wyxloading, @moul, @segevfiner, @andy-retailnext and @jcorbin for their contributions to this release. - -## 1.15.0 (23 Apr 2020) - -Bugfixes: -* [#804][]: Fix handling of `Time` values out of `UnixNano` range. -* [#812][]: Fix `IncreaseLevel` being reset after a call to `With`. - -Enhancements: -* [#806][]: Add `WithCaller` option to supersede the `AddCaller` option. This - allows disabling annotation of log entries with caller information if - previously enabled with `AddCaller`. -* [#813][]: Deprecate `NewSampler` constructor in favor of - `NewSamplerWithOptions` which supports a `SamplerHook` option. This option - adds support for monitoring sampling decisions through a hook. - -Thanks to @danielbprice for their contributions to this release. - -## 1.14.1 (14 Mar 2020) - -Bugfixes: -* [#791][]: Fix panic on attempting to build a logger with an invalid Config. -* [#795][]: Vendoring Zap with `go mod vendor` no longer includes Zap's - development-time dependencies. -* [#799][]: Fix issue introduced in 1.14.0 that caused invalid JSON output to - be generated for arrays of `time.Time` objects when using string-based time - formats. - -Thanks to @YashishDua for their contributions to this release. - -## 1.14.0 (20 Feb 2020) - -Enhancements: -* [#771][]: Optimize calls for disabled log levels. -* [#773][]: Add millisecond duration encoder. -* [#775][]: Add option to increase the level of a logger. -* [#786][]: Optimize time formatters using `Time.AppendFormat` where possible. - -Thanks to @caibirdme for their contributions to this release. - -## 1.13.0 (13 Nov 2019) - -Enhancements: -* [#758][]: Add `Intp`, `Stringp`, and other similar `*p` field constructors - to log pointers to primitives with support for `nil` values. - -Thanks to @jbizzle for their contributions to this release. - -## 1.12.0 (29 Oct 2019) - -Enhancements: -* [#751][]: Migrate to Go modules. - -## 1.11.0 (21 Oct 2019) - -Enhancements: -* [#725][]: Add `zapcore.OmitKey` to omit keys in an `EncoderConfig`. -* [#736][]: Add `RFC3339` and `RFC3339Nano` time encoders. - -Thanks to @juicemia, @uhthomas for their contributions to this release. - -## 1.10.0 (29 Apr 2019) - -Bugfixes: -* [#657][]: Fix `MapObjectEncoder.AppendByteString` not adding value as a - string. -* [#706][]: Fix incorrect call depth to determine caller in Go 1.12. - -Enhancements: -* [#610][]: Add `zaptest.WrapOptions` to wrap `zap.Option` for creating test - loggers. -* [#675][]: Don't panic when encoding a String field. -* [#704][]: Disable HTML escaping for JSON objects encoded using the - reflect-based encoder. - -Thanks to @iaroslav-ciupin, @lelenanam, @joa, @NWilson for their contributions -to this release. - -## v1.9.1 (06 Aug 2018) - -Bugfixes: - -* [#614][]: MapObjectEncoder should not ignore empty slices. - -## v1.9.0 (19 Jul 2018) - -Enhancements: -* [#602][]: Reduce number of allocations when logging with reflection. -* [#572][], [#606][]: Expose a registry for third-party logging sinks. - -Thanks to @nfarah86, @AlekSi, @JeanMertz, @philippgille, @etsangsplk, and -@dimroc for their contributions to this release. - -## v1.8.0 (13 Apr 2018) - -Enhancements: -* [#508][]: Make log level configurable when redirecting the standard - library's logger. -* [#518][]: Add a logger that writes to a `*testing.TB`. -* [#577][]: Add a top-level alias for `zapcore.Field` to clean up GoDoc. - -Bugfixes: -* [#574][]: Add a missing import comment to `go.uber.org/zap/buffer`. - -Thanks to @DiSiqueira and @djui for their contributions to this release. - -## v1.7.1 (25 Sep 2017) - -Bugfixes: -* [#504][]: Store strings when using AddByteString with the map encoder. - -## v1.7.0 (21 Sep 2017) - -Enhancements: - -* [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user - to specify the level of the logged messages. - -## v1.6.0 (30 Aug 2017) - -Enhancements: - -* [#491][]: Omit zap stack frames from stacktraces. -* [#490][]: Add a `ContextMap` method to observer logs for simpler - field validation in tests. - -## v1.5.0 (22 Jul 2017) - -Enhancements: - -* [#460][] and [#470][]: Support errors produced by `go.uber.org/multierr`. -* [#465][]: Support user-supplied encoders for logger names. - -Bugfixes: - -* [#477][]: Fix a bug that incorrectly truncated deep stacktraces. - -Thanks to @richard-tunein and @pavius for their contributions to this release. - -## v1.4.1 (08 Jun 2017) - -This release fixes two bugs. - -Bugfixes: - -* [#435][]: Support a variety of case conventions when unmarshaling levels. -* [#444][]: Fix a panic in the observer. - -## v1.4.0 (12 May 2017) - -This release adds a few small features and is fully backward-compatible. - -Enhancements: - -* [#424][]: Add a `LineEnding` field to `EncoderConfig`, allowing users to - override the Unix-style default. -* [#425][]: Preserve time zones when logging times. -* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a - variety of operations a bit simpler. - -## v1.3.0 (25 Apr 2017) - -This release adds an enhancement to zap's testing helpers as well as the -ability to marshal an AtomicLevel. It is fully backward-compatible. - -Enhancements: - -* [#415][]: Add a substring-filtering helper to zap's observer. This is - particularly useful when testing the `SugaredLogger`. -* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`. - -## v1.2.0 (13 Apr 2017) - -This release adds a gRPC compatibility wrapper. It is fully backward-compatible. - -Enhancements: - -* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements - `grpclog.Logger`. - -## v1.1.0 (31 Mar 2017) - -This release fixes two bugs and adds some enhancements to zap's testing helpers. -It is fully backward-compatible. - -Bugfixes: - -* [#385][]: Fix caller path trimming on Windows. -* [#396][]: Fix a panic when attempting to use non-existent directories with - zap's configuration struct. - -Enhancements: - -* [#386][]: Add filtering helpers to zaptest's observing logger. - -Thanks to @moitias for contributing to this release. - -## v1.0.0 (14 Mar 2017) - -This is zap's first stable release. All exported APIs are now final, and no -further breaking changes will be made in the 1.x release series. Anyone using a -semver-aware dependency manager should now pin to `^1`. - -Breaking changes: - -* [#366][]: Add byte-oriented APIs to encoders to log UTF-8 encoded text without - casting from `[]byte` to `string`. -* [#364][]: To support buffering outputs, add `Sync` methods to `zapcore.Core`, - `zap.Logger`, and `zap.SugaredLogger`. -* [#371][]: Rename the `testutils` package to `zaptest`, which is less likely to - clash with other testing helpers. - -Bugfixes: - -* [#362][]: Make the ISO8601 time formatters fixed-width, which is friendlier - for tab-separated console output. -* [#369][]: Remove the automatic locks in `zapcore.NewCore`, which allows zap to - work with concurrency-safe `WriteSyncer` implementations. -* [#347][]: Stop reporting errors when trying to `fsync` standard out on Linux - systems. -* [#373][]: Report the correct caller from zap's standard library - interoperability wrappers. - -Enhancements: - -* [#348][]: Add a registry allowing third-party encodings to work with zap's - built-in `Config`. -* [#327][]: Make the representation of logger callers configurable (like times, - levels, and durations). -* [#376][]: Allow third-party encoders to use their own buffer pools, which - removes the last performance advantage that zap's encoders have over plugins. -* [#346][]: Add `CombineWriteSyncers`, a convenience function to tee multiple - `WriteSyncer`s and lock the result. -* [#365][]: Make zap's stacktraces compatible with mid-stack inlining (coming in - Go 1.9). -* [#372][]: Export zap's observing logger as `zaptest/observer`. This makes it - easier for particularly punctilious users to unit test their application's - logging. - -Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their -contributions to this release. - -## v1.0.0-rc.3 (7 Mar 2017) - -This is the third release candidate for zap's stable release. There are no -breaking changes. - -Bugfixes: - -* [#339][]: Byte slices passed to `zap.Any` are now correctly treated as binary blobs - rather than `[]uint8`. - -Enhancements: - -* [#307][]: Users can opt into colored output for log levels. -* [#353][]: In addition to hijacking the output of the standard library's - package-global logging functions, users can now construct a zap-backed - `log.Logger` instance. -* [#311][]: Frames from common runtime functions and some of zap's internal - machinery are now omitted from stacktraces. - -Thanks to @ansel1 and @suyash for their contributions to this release. - -## v1.0.0-rc.2 (21 Feb 2017) - -This is the second release candidate for zap's stable release. It includes two -breaking changes. - -Breaking changes: - -* [#316][]: Zap's global loggers are now fully concurrency-safe - (previously, users had to ensure that `ReplaceGlobals` was called before the - loggers were in use). However, they must now be accessed via the `L()` and - `S()` functions. Users can update their projects with - - ``` - gofmt -r "zap.L -> zap.L()" -w . - gofmt -r "zap.S -> zap.S()" -w . - ``` -* [#309][] and [#317][]: RC1 was mistakenly shipped with invalid - JSON and YAML struct tags on all config structs. This release fixes the tags - and adds static analysis to prevent similar bugs in the future. - -Bugfixes: - -* [#321][]: Redirecting the standard library's `log` output now - correctly reports the logger's caller. - -Enhancements: - -* [#325][] and [#333][]: Zap now transparently supports non-standard, rich - errors like those produced by `github.com/pkg/errors`. -* [#326][]: Though `New(nil)` continues to return a no-op logger, `NewNop()` is - now preferred. Users can update their projects with `gofmt -r 'zap.New(nil) -> - zap.NewNop()' -w .`. -* [#300][]: Incorrectly importing zap as `github.com/uber-go/zap` now returns a - more informative error. - -Thanks to @skipor and @chapsuk for their contributions to this release. - -## v1.0.0-rc.1 (14 Feb 2017) - -This is the first release candidate for zap's stable release. There are multiple -breaking changes and improvements from the pre-release version. Most notably: - -* **Zap's import path is now "go.uber.org/zap"** — all users will - need to update their code. -* User-facing types and functions remain in the `zap` package. Code relevant - largely to extension authors is now in the `zapcore` package. -* The `zapcore.Core` type makes it easy for third-party packages to use zap's - internals but provide a different user-facing API. -* `Logger` is now a concrete type instead of an interface. -* A less verbose (though slower) logging API is included by default. -* Package-global loggers `L` and `S` are included. -* A human-friendly console encoder is included. -* A declarative config struct allows common logger configurations to be managed - as configuration instead of code. -* Sampling is more accurate, and doesn't depend on the standard library's shared - timer heap. - -## v0.1.0-beta.1 (6 Feb 2017) - -This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and -upgrade at their leisure. Since this is the first tagged release, there are no -backward compatibility concerns and all functionality is new. - -Early zap adopters should pin to the 0.1.x minor version until they're ready to -upgrade to the upcoming stable release. - -[#316]: https://github.com/uber-go/zap/pull/316 -[#309]: https://github.com/uber-go/zap/pull/309 -[#317]: https://github.com/uber-go/zap/pull/317 -[#321]: https://github.com/uber-go/zap/pull/321 -[#325]: https://github.com/uber-go/zap/pull/325 -[#333]: https://github.com/uber-go/zap/pull/333 -[#326]: https://github.com/uber-go/zap/pull/326 -[#300]: https://github.com/uber-go/zap/pull/300 -[#339]: https://github.com/uber-go/zap/pull/339 -[#307]: https://github.com/uber-go/zap/pull/307 -[#353]: https://github.com/uber-go/zap/pull/353 -[#311]: https://github.com/uber-go/zap/pull/311 -[#366]: https://github.com/uber-go/zap/pull/366 -[#364]: https://github.com/uber-go/zap/pull/364 -[#371]: https://github.com/uber-go/zap/pull/371 -[#362]: https://github.com/uber-go/zap/pull/362 -[#369]: https://github.com/uber-go/zap/pull/369 -[#347]: https://github.com/uber-go/zap/pull/347 -[#373]: https://github.com/uber-go/zap/pull/373 -[#348]: https://github.com/uber-go/zap/pull/348 -[#327]: https://github.com/uber-go/zap/pull/327 -[#376]: https://github.com/uber-go/zap/pull/376 -[#346]: https://github.com/uber-go/zap/pull/346 -[#365]: https://github.com/uber-go/zap/pull/365 -[#372]: https://github.com/uber-go/zap/pull/372 -[#385]: https://github.com/uber-go/zap/pull/385 -[#396]: https://github.com/uber-go/zap/pull/396 -[#386]: https://github.com/uber-go/zap/pull/386 -[#402]: https://github.com/uber-go/zap/pull/402 -[#415]: https://github.com/uber-go/zap/pull/415 -[#416]: https://github.com/uber-go/zap/pull/416 -[#424]: https://github.com/uber-go/zap/pull/424 -[#425]: https://github.com/uber-go/zap/pull/425 -[#431]: https://github.com/uber-go/zap/pull/431 -[#435]: https://github.com/uber-go/zap/pull/435 -[#444]: https://github.com/uber-go/zap/pull/444 -[#477]: https://github.com/uber-go/zap/pull/477 -[#465]: https://github.com/uber-go/zap/pull/465 -[#460]: https://github.com/uber-go/zap/pull/460 -[#470]: https://github.com/uber-go/zap/pull/470 -[#487]: https://github.com/uber-go/zap/pull/487 -[#490]: https://github.com/uber-go/zap/pull/490 -[#491]: https://github.com/uber-go/zap/pull/491 -[#504]: https://github.com/uber-go/zap/pull/504 -[#508]: https://github.com/uber-go/zap/pull/508 -[#518]: https://github.com/uber-go/zap/pull/518 -[#577]: https://github.com/uber-go/zap/pull/577 -[#574]: https://github.com/uber-go/zap/pull/574 -[#602]: https://github.com/uber-go/zap/pull/602 -[#572]: https://github.com/uber-go/zap/pull/572 -[#606]: https://github.com/uber-go/zap/pull/606 -[#614]: https://github.com/uber-go/zap/pull/614 -[#657]: https://github.com/uber-go/zap/pull/657 -[#706]: https://github.com/uber-go/zap/pull/706 -[#610]: https://github.com/uber-go/zap/pull/610 -[#675]: https://github.com/uber-go/zap/pull/675 -[#704]: https://github.com/uber-go/zap/pull/704 -[#725]: https://github.com/uber-go/zap/pull/725 -[#736]: https://github.com/uber-go/zap/pull/736 -[#751]: https://github.com/uber-go/zap/pull/751 -[#758]: https://github.com/uber-go/zap/pull/758 -[#771]: https://github.com/uber-go/zap/pull/771 -[#773]: https://github.com/uber-go/zap/pull/773 -[#775]: https://github.com/uber-go/zap/pull/775 -[#786]: https://github.com/uber-go/zap/pull/786 -[#791]: https://github.com/uber-go/zap/pull/791 -[#795]: https://github.com/uber-go/zap/pull/795 -[#799]: https://github.com/uber-go/zap/pull/799 -[#804]: https://github.com/uber-go/zap/pull/804 -[#812]: https://github.com/uber-go/zap/pull/812 -[#806]: https://github.com/uber-go/zap/pull/806 -[#813]: https://github.com/uber-go/zap/pull/813 -[#629]: https://github.com/uber-go/zap/pull/629 -[#697]: https://github.com/uber-go/zap/pull/697 -[#828]: https://github.com/uber-go/zap/pull/828 -[#835]: https://github.com/uber-go/zap/pull/835 -[#843]: https://github.com/uber-go/zap/pull/843 -[#844]: https://github.com/uber-go/zap/pull/844 -[#852]: https://github.com/uber-go/zap/pull/852 -[#854]: https://github.com/uber-go/zap/pull/854 -[#861]: https://github.com/uber-go/zap/pull/861 -[#862]: https://github.com/uber-go/zap/pull/862 -[#865]: https://github.com/uber-go/zap/pull/865 -[#867]: https://github.com/uber-go/zap/pull/867 -[#881]: https://github.com/uber-go/zap/pull/881 -[#903]: https://github.com/uber-go/zap/pull/903 -[#912]: https://github.com/uber-go/zap/pull/912 -[#913]: https://github.com/uber-go/zap/pull/913 -[#928]: https://github.com/uber-go/zap/pull/928 -[#931]: https://github.com/uber-go/zap/pull/931 -[#936]: https://github.com/uber-go/zap/pull/936 diff --git a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md deleted file mode 100644 index e327d9aa5c..0000000000 --- a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,75 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, -body size, disability, ethnicity, gender identity and expression, level of -experience, nationality, personal appearance, race, religion, or sexual -identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an -appointed representative at an online or offline event. Representation of a -project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at oss-conduct@uber.com. The project -team will review and investigate all complaints, and will respond in a way -that it deems appropriate to the circumstances. The project team is obligated -to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 1.4, available at -[http://contributor-covenant.org/version/1/4][version]. - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/go.uber.org/zap/CONTRIBUTING.md b/vendor/go.uber.org/zap/CONTRIBUTING.md deleted file mode 100644 index 5cd9656871..0000000000 --- a/vendor/go.uber.org/zap/CONTRIBUTING.md +++ /dev/null @@ -1,75 +0,0 @@ -# Contributing - -We'd love your help making zap the very best structured logging library in Go! - -If you'd like to add new exported APIs, please [open an issue][open-issue] -describing your proposal — discussing API changes ahead of time makes -pull request review much smoother. In your issue, pull request, and any other -communications, please remember to treat your fellow contributors with -respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously. - -Note that you'll need to sign [Uber's Contributor License Agreement][cla] -before we can accept any of your contributions. If necessary, a bot will remind -you to accept the CLA when you open your pull request. - -## Setup - -[Fork][fork], then clone the repository: - -``` -mkdir -p $GOPATH/src/go.uber.org -cd $GOPATH/src/go.uber.org -git clone git@github.com:your_github_username/zap.git -cd zap -git remote add upstream https://github.com/uber-go/zap.git -git fetch upstream -``` - -Make sure that the tests and the linters pass: - -``` -make test -make lint -``` - -If you're not using the minor version of Go specified in the Makefile's -`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is -fine, but it means that you'll only discover lint failures after you open your -pull request. - -## Making Changes - -Start by creating a new branch for your changes: - -``` -cd $GOPATH/src/go.uber.org/zap -git checkout master -git fetch upstream -git rebase upstream/master -git checkout -b cool_new_feature -``` - -Make your changes, then ensure that `make lint` and `make test` still pass. If -you're satisfied with your changes, push them to your fork. - -``` -git push origin cool_new_feature -``` - -Then use the GitHub UI to open a pull request. - -At this point, you're waiting on us to review your changes. We *try* to respond -to issues and pull requests within a few business days, and we may suggest some -improvements or alternatives. Once your changes are approved, one of the -project maintainers will merge them. - -We're much more likely to approve your changes if you: - -* Add tests for new functionality. -* Write a [good commit message][commit-message]. -* Maintain backward compatibility. - -[fork]: https://github.com/uber-go/zap/fork -[open-issue]: https://github.com/uber-go/zap/issues/new -[cla]: https://cla-assistant.io/uber-go/zap -[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/vendor/go.uber.org/zap/FAQ.md b/vendor/go.uber.org/zap/FAQ.md deleted file mode 100644 index b183b20bc1..0000000000 --- a/vendor/go.uber.org/zap/FAQ.md +++ /dev/null @@ -1,164 +0,0 @@ -# Frequently Asked Questions - -## Design - -### Why spend so much effort on logger performance? - -Of course, most applications won't notice the impact of a slow logger: they -already take tens or hundreds of milliseconds for each operation, so an extra -millisecond doesn't matter. - -On the other hand, why *not* make structured logging fast? The `SugaredLogger` -isn't any harder to use than other logging packages, and the `Logger` makes -structured logging possible in performance-sensitive contexts. Across a fleet -of Go microservices, making each application even slightly more efficient adds -up quickly. - -### Why aren't `Logger` and `SugaredLogger` interfaces? - -Unlike the familiar `io.Writer` and `http.Handler`, `Logger` and -`SugaredLogger` interfaces would include *many* methods. As [Rob Pike points -out][go-proverbs], "The bigger the interface, the weaker the abstraction." -Interfaces are also rigid — *any* change requires releasing a new major -version, since it breaks all third-party implementations. - -Making the `Logger` and `SugaredLogger` concrete types doesn't sacrifice much -abstraction, and it lets us add methods without introducing breaking changes. -Your applications should define and depend upon an interface that includes -just the methods you use. - -### Why are some of my logs missing? - -Logs are dropped intentionally by zap when sampling is enabled. The production -configuration (as returned by `NewProductionConfig()` enables sampling which will -cause repeated logs within a second to be sampled. See more details on why sampling -is enabled in [Why sample application logs](https://github.com/uber-go/zap/blob/master/FAQ.md#why-sample-application-logs). - -### Why sample application logs? - -Applications often experience runs of errors, either because of a bug or -because of a misbehaving user. Logging errors is usually a good idea, but it -can easily make this bad situation worse: not only is your application coping -with a flood of errors, it's also spending extra CPU cycles and I/O logging -those errors. Since writes are typically serialized, logging limits throughput -when you need it most. - -Sampling fixes this problem by dropping repetitive log entries. Under normal -conditions, your application writes out every entry. When similar entries are -logged hundreds or thousands of times each second, though, zap begins dropping -duplicates to preserve throughput. - -### Why do the structured logging APIs take a message in addition to fields? - -Subjectively, we find it helpful to accompany structured context with a brief -description. This isn't critical during development, but it makes debugging -and operating unfamiliar systems much easier. - -More concretely, zap's sampling algorithm uses the message to identify -duplicate entries. In our experience, this is a practical middle ground -between random sampling (which often drops the exact entry that you need while -debugging) and hashing the complete entry (which is prohibitively expensive). - -### Why include package-global loggers? - -Since so many other logging packages include a global logger, many -applications aren't designed to accept loggers as explicit parameters. -Changing function signatures is often a breaking change, so zap includes -global loggers to simplify migration. - -Avoid them where possible. - -### Why include dedicated Panic and Fatal log levels? - -In general, application code should handle errors gracefully instead of using -`panic` or `os.Exit`. However, every rule has exceptions, and it's common to -crash when an error is truly unrecoverable. To avoid losing any information -— especially the reason for the crash — the logger must flush any -buffered entries before the process exits. - -Zap makes this easy by offering `Panic` and `Fatal` logging methods that -automatically flush before exiting. Of course, this doesn't guarantee that -logs will never be lost, but it eliminates a common error. - -See the discussion in uber-go/zap#207 for more details. - -### What's `DPanic`? - -`DPanic` stands for "panic in development." In development, it logs at -`PanicLevel`; otherwise, it logs at `ErrorLevel`. `DPanic` makes it easier to -catch errors that are theoretically possible, but shouldn't actually happen, -*without* crashing in production. - -If you've ever written code like this, you need `DPanic`: - -```go -if err != nil { - panic(fmt.Sprintf("shouldn't ever get here: %v", err)) -} -``` - -## Installation - -### What does the error `expects import "go.uber.org/zap"` mean? - -Either zap was installed incorrectly or you're referencing the wrong package -name in your code. - -Zap's source code happens to be hosted on GitHub, but the [import -path][import-path] is `go.uber.org/zap`. This gives us, the project -maintainers, the freedom to move the source code if necessary. However, it -means that you need to take a little care when installing and using the -package. - -If you follow two simple rules, everything should work: install zap with `go -get -u go.uber.org/zap`, and always import it in your code with `import -"go.uber.org/zap"`. Your code shouldn't contain *any* references to -`github.com/uber-go/zap`. - -## Usage - -### Does zap support log rotation? - -Zap doesn't natively support rotating log files, since we prefer to leave this -to an external program like `logrotate`. - -However, it's easy to integrate a log rotation package like -[`gopkg.in/natefinch/lumberjack.v2`][lumberjack] as a `zapcore.WriteSyncer`. - -```go -// lumberjack.Logger is already safe for concurrent use, so we don't need to -// lock it. -w := zapcore.AddSync(&lumberjack.Logger{ - Filename: "/var/log/myapp/foo.log", - MaxSize: 500, // megabytes - MaxBackups: 3, - MaxAge: 28, // days -}) -core := zapcore.NewCore( - zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), - w, - zap.InfoLevel, -) -logger := zap.New(core) -``` - -## Extensions - -We'd love to support every logging need within zap itself, but we're only -familiar with a handful of log ingestion systems, flag-parsing packages, and -the like. Rather than merging code that we can't effectively debug and -support, we'd rather grow an ecosystem of zap extensions. - -We're aware of the following extensions, but haven't used them ourselves: - -| Package | Integration | -| --- | --- | -| `github.com/tchap/zapext` | Sentry, syslog | -| `github.com/fgrosse/zaptest` | Ginkgo | -| `github.com/blendle/zapdriver` | Stackdriver | -| `github.com/moul/zapgorm` | Gorm | -| `github.com/moul/zapfilter` | Advanced filtering rules | - -[go-proverbs]: https://go-proverbs.github.io/ -[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths -[lumberjack]: https://godoc.org/gopkg.in/natefinch/lumberjack.v2 diff --git a/vendor/go.uber.org/zap/LICENSE.txt b/vendor/go.uber.org/zap/LICENSE.txt deleted file mode 100644 index 6652bed45f..0000000000 --- a/vendor/go.uber.org/zap/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2016-2017 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile deleted file mode 100644 index 9b1bc3b0e1..0000000000 --- a/vendor/go.uber.org/zap/Makefile +++ /dev/null @@ -1,73 +0,0 @@ -export GOBIN ?= $(shell pwd)/bin - -GOLINT = $(GOBIN)/golint -STATICCHECK = $(GOBIN)/staticcheck -BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem - -# Directories containing independent Go modules. -# -# We track coverage only for the main module. -MODULE_DIRS = . ./benchmarks ./zapgrpc/internal/test - -# Many Go tools take file globs or directories as arguments instead of packages. -GO_FILES := $(shell \ - find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ - -o -name '*.go' -print | cut -b3-) - -.PHONY: all -all: lint test - -.PHONY: lint -lint: $(GOLINT) $(STATICCHECK) - @rm -rf lint.log - @echo "Checking formatting..." - @gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log - @echo "Checking vet..." - @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go vet ./... 2>&1) &&) true | tee -a lint.log - @echo "Checking lint..." - @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(GOLINT) ./... 2>&1) &&) true | tee -a lint.log - @echo "Checking staticcheck..." - @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(STATICCHECK) ./... 2>&1) &&) true | tee -a lint.log - @echo "Checking for unresolved FIXMEs..." - @git grep -i fixme | grep -v -e Makefile | tee -a lint.log - @echo "Checking for license headers..." - @./checklicense.sh | tee -a lint.log - @[ ! -s lint.log ] - @echo "Checking 'go mod tidy'..." - @make tidy - @if ! git diff --quiet; then \ - echo "'go mod tidy' resulted in changes or working tree is dirty:"; \ - git --no-pager diff; \ - fi - -$(GOLINT): - cd tools && go install golang.org/x/lint/golint - -$(STATICCHECK): - cd tools && go install honnef.co/go/tools/cmd/staticcheck - -.PHONY: test -test: - @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go test -race ./...) &&) true - -.PHONY: cover -cover: - go test -race -coverprofile=cover.out -coverpkg=./... ./... - go tool cover -html=cover.out -o cover.html - -.PHONY: bench -BENCH ?= . -bench: - @$(foreach dir,$(MODULE_DIRS), ( \ - cd $(dir) && \ - go list ./... | xargs -n1 go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) \ - ) &&) true - -.PHONY: updatereadme -updatereadme: - rm -f README.md - cat .readme.tmpl | go run internal/readme/readme.go > README.md - -.PHONY: tidy -tidy: - @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go mod tidy) &&) true diff --git a/vendor/go.uber.org/zap/README.md b/vendor/go.uber.org/zap/README.md deleted file mode 100644 index 1e64d6cffc..0000000000 --- a/vendor/go.uber.org/zap/README.md +++ /dev/null @@ -1,134 +0,0 @@ -# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] - -Blazing fast, structured, leveled logging in Go. - -## Installation - -`go get -u go.uber.org/zap` - -Note that zap only supports the two most recent minor versions of Go. - -## Quick Start - -In contexts where performance is nice, but not critical, use the -`SugaredLogger`. It's 4-10x faster than other structured logging -packages and includes both structured and `printf`-style APIs. - -```go -logger, _ := zap.NewProduction() -defer logger.Sync() // flushes buffer, if any -sugar := logger.Sugar() -sugar.Infow("failed to fetch URL", - // Structured context as loosely typed key-value pairs. - "url", url, - "attempt", 3, - "backoff", time.Second, -) -sugar.Infof("Failed to fetch URL: %s", url) -``` - -When performance and type safety are critical, use the `Logger`. It's even -faster than the `SugaredLogger` and allocates far less, but it only supports -structured logging. - -```go -logger, _ := zap.NewProduction() -defer logger.Sync() -logger.Info("failed to fetch URL", - // Structured context as strongly typed Field values. - zap.String("url", url), - zap.Int("attempt", 3), - zap.Duration("backoff", time.Second), -) -``` - -See the [documentation][doc] and [FAQ](FAQ.md) for more details. - -## Performance - -For applications that log in the hot path, reflection-based serialization and -string formatting are prohibitively expensive — they're CPU-intensive -and make many small allocations. Put differently, using `encoding/json` and -`fmt.Fprintf` to log tons of `interface{}`s makes your application slow. - -Zap takes a different approach. It includes a reflection-free, zero-allocation -JSON encoder, and the base `Logger` strives to avoid serialization overhead -and allocations wherever possible. By building the high-level `SugaredLogger` -on that foundation, zap lets users *choose* when they need to count every -allocation and when they'd prefer a more familiar, loosely typed API. - -As measured by its own [benchmarking suite][], not only is zap more performant -than comparable structured logging packages — it's also faster than the -standard library. Like all benchmarks, take these with a grain of salt.[1](#footnote-versions) - -Log a message and 10 fields: - -| Package | Time | Time % to zap | Objects Allocated | -| :------ | :--: | :-----------: | :---------------: | -| :zap: zap | 862 ns/op | +0% | 5 allocs/op -| :zap: zap (sugared) | 1250 ns/op | +45% | 11 allocs/op -| zerolog | 4021 ns/op | +366% | 76 allocs/op -| go-kit | 4542 ns/op | +427% | 105 allocs/op -| apex/log | 26785 ns/op | +3007% | 115 allocs/op -| logrus | 29501 ns/op | +3322% | 125 allocs/op -| log15 | 29906 ns/op | +3369% | 122 allocs/op - -Log a message with a logger that already has 10 fields of context: - -| Package | Time | Time % to zap | Objects Allocated | -| :------ | :--: | :-----------: | :---------------: | -| :zap: zap | 126 ns/op | +0% | 0 allocs/op -| :zap: zap (sugared) | 187 ns/op | +48% | 2 allocs/op -| zerolog | 88 ns/op | -30% | 0 allocs/op -| go-kit | 5087 ns/op | +3937% | 103 allocs/op -| log15 | 18548 ns/op | +14621% | 73 allocs/op -| apex/log | 26012 ns/op | +20544% | 104 allocs/op -| logrus | 27236 ns/op | +21516% | 113 allocs/op - -Log a static string, without any context or `printf`-style templating: - -| Package | Time | Time % to zap | Objects Allocated | -| :------ | :--: | :-----------: | :---------------: | -| :zap: zap | 118 ns/op | +0% | 0 allocs/op -| :zap: zap (sugared) | 191 ns/op | +62% | 2 allocs/op -| zerolog | 93 ns/op | -21% | 0 allocs/op -| go-kit | 280 ns/op | +137% | 11 allocs/op -| standard library | 499 ns/op | +323% | 2 allocs/op -| apex/log | 1990 ns/op | +1586% | 10 allocs/op -| logrus | 3129 ns/op | +2552% | 24 allocs/op -| log15 | 3887 ns/op | +3194% | 23 allocs/op - -## Development Status: Stable - -All APIs are finalized, and no breaking changes will be made in the 1.x series -of releases. Users of semver-aware dependency management systems should pin -zap to `^1`. - -## Contributing - -We encourage and support an active, healthy community of contributors — -including you! Details are in the [contribution guide](CONTRIBUTING.md) and -the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on -issues and pull requests, but you can also report any negative conduct to -oss-conduct@uber.com. That email list is a private, safe space; even the zap -maintainers don't have access, so don't hesitate to hold us to a high -standard. - -
- -Released under the [MIT License](LICENSE.txt). - -1 In particular, keep in mind that we may be -benchmarking against slightly older versions of other packages. Versions are -pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions) - -[doc-img]: https://pkg.go.dev/badge/go.uber.org/zap -[doc]: https://pkg.go.dev/go.uber.org/zap -[ci-img]: https://github.com/uber-go/zap/actions/workflows/go.yml/badge.svg -[ci]: https://github.com/uber-go/zap/actions/workflows/go.yml -[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg -[cov]: https://codecov.io/gh/uber-go/zap -[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks -[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod - diff --git a/vendor/go.uber.org/zap/array.go b/vendor/go.uber.org/zap/array.go deleted file mode 100644 index 5be3704a3e..0000000000 --- a/vendor/go.uber.org/zap/array.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "time" - - "go.uber.org/zap/zapcore" -) - -// Array constructs a field with the given key and ArrayMarshaler. It provides -// a flexible, but still type-safe and efficient, way to add array-like types -// to the logging context. The struct's MarshalLogArray method is called lazily. -func Array(key string, val zapcore.ArrayMarshaler) Field { - return Field{Key: key, Type: zapcore.ArrayMarshalerType, Interface: val} -} - -// Bools constructs a field that carries a slice of bools. -func Bools(key string, bs []bool) Field { - return Array(key, bools(bs)) -} - -// ByteStrings constructs a field that carries a slice of []byte, each of which -// must be UTF-8 encoded text. -func ByteStrings(key string, bss [][]byte) Field { - return Array(key, byteStringsArray(bss)) -} - -// Complex128s constructs a field that carries a slice of complex numbers. -func Complex128s(key string, nums []complex128) Field { - return Array(key, complex128s(nums)) -} - -// Complex64s constructs a field that carries a slice of complex numbers. -func Complex64s(key string, nums []complex64) Field { - return Array(key, complex64s(nums)) -} - -// Durations constructs a field that carries a slice of time.Durations. -func Durations(key string, ds []time.Duration) Field { - return Array(key, durations(ds)) -} - -// Float64s constructs a field that carries a slice of floats. -func Float64s(key string, nums []float64) Field { - return Array(key, float64s(nums)) -} - -// Float32s constructs a field that carries a slice of floats. -func Float32s(key string, nums []float32) Field { - return Array(key, float32s(nums)) -} - -// Ints constructs a field that carries a slice of integers. -func Ints(key string, nums []int) Field { - return Array(key, ints(nums)) -} - -// Int64s constructs a field that carries a slice of integers. -func Int64s(key string, nums []int64) Field { - return Array(key, int64s(nums)) -} - -// Int32s constructs a field that carries a slice of integers. -func Int32s(key string, nums []int32) Field { - return Array(key, int32s(nums)) -} - -// Int16s constructs a field that carries a slice of integers. -func Int16s(key string, nums []int16) Field { - return Array(key, int16s(nums)) -} - -// Int8s constructs a field that carries a slice of integers. -func Int8s(key string, nums []int8) Field { - return Array(key, int8s(nums)) -} - -// Strings constructs a field that carries a slice of strings. -func Strings(key string, ss []string) Field { - return Array(key, stringArray(ss)) -} - -// Times constructs a field that carries a slice of time.Times. -func Times(key string, ts []time.Time) Field { - return Array(key, times(ts)) -} - -// Uints constructs a field that carries a slice of unsigned integers. -func Uints(key string, nums []uint) Field { - return Array(key, uints(nums)) -} - -// Uint64s constructs a field that carries a slice of unsigned integers. -func Uint64s(key string, nums []uint64) Field { - return Array(key, uint64s(nums)) -} - -// Uint32s constructs a field that carries a slice of unsigned integers. -func Uint32s(key string, nums []uint32) Field { - return Array(key, uint32s(nums)) -} - -// Uint16s constructs a field that carries a slice of unsigned integers. -func Uint16s(key string, nums []uint16) Field { - return Array(key, uint16s(nums)) -} - -// Uint8s constructs a field that carries a slice of unsigned integers. -func Uint8s(key string, nums []uint8) Field { - return Array(key, uint8s(nums)) -} - -// Uintptrs constructs a field that carries a slice of pointer addresses. -func Uintptrs(key string, us []uintptr) Field { - return Array(key, uintptrs(us)) -} - -// Errors constructs a field that carries a slice of errors. -func Errors(key string, errs []error) Field { - return Array(key, errArray(errs)) -} - -type bools []bool - -func (bs bools) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range bs { - arr.AppendBool(bs[i]) - } - return nil -} - -type byteStringsArray [][]byte - -func (bss byteStringsArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range bss { - arr.AppendByteString(bss[i]) - } - return nil -} - -type complex128s []complex128 - -func (nums complex128s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendComplex128(nums[i]) - } - return nil -} - -type complex64s []complex64 - -func (nums complex64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendComplex64(nums[i]) - } - return nil -} - -type durations []time.Duration - -func (ds durations) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range ds { - arr.AppendDuration(ds[i]) - } - return nil -} - -type float64s []float64 - -func (nums float64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendFloat64(nums[i]) - } - return nil -} - -type float32s []float32 - -func (nums float32s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendFloat32(nums[i]) - } - return nil -} - -type ints []int - -func (nums ints) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendInt(nums[i]) - } - return nil -} - -type int64s []int64 - -func (nums int64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendInt64(nums[i]) - } - return nil -} - -type int32s []int32 - -func (nums int32s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendInt32(nums[i]) - } - return nil -} - -type int16s []int16 - -func (nums int16s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendInt16(nums[i]) - } - return nil -} - -type int8s []int8 - -func (nums int8s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendInt8(nums[i]) - } - return nil -} - -type stringArray []string - -func (ss stringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range ss { - arr.AppendString(ss[i]) - } - return nil -} - -type times []time.Time - -func (ts times) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range ts { - arr.AppendTime(ts[i]) - } - return nil -} - -type uints []uint - -func (nums uints) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendUint(nums[i]) - } - return nil -} - -type uint64s []uint64 - -func (nums uint64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendUint64(nums[i]) - } - return nil -} - -type uint32s []uint32 - -func (nums uint32s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendUint32(nums[i]) - } - return nil -} - -type uint16s []uint16 - -func (nums uint16s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendUint16(nums[i]) - } - return nil -} - -type uint8s []uint8 - -func (nums uint8s) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendUint8(nums[i]) - } - return nil -} - -type uintptrs []uintptr - -func (nums uintptrs) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range nums { - arr.AppendUintptr(nums[i]) - } - return nil -} diff --git a/vendor/go.uber.org/zap/buffer/buffer.go b/vendor/go.uber.org/zap/buffer/buffer.go deleted file mode 100644 index 3f4b86e081..0000000000 --- a/vendor/go.uber.org/zap/buffer/buffer.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package buffer provides a thin wrapper around a byte slice. Unlike the -// standard library's bytes.Buffer, it supports a portion of the strconv -// package's zero-allocation formatters. -package buffer // import "go.uber.org/zap/buffer" - -import ( - "strconv" - "time" -) - -const _size = 1024 // by default, create 1 KiB buffers - -// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so -// the only way to construct one is via a Pool. -type Buffer struct { - bs []byte - pool Pool -} - -// AppendByte writes a single byte to the Buffer. -func (b *Buffer) AppendByte(v byte) { - b.bs = append(b.bs, v) -} - -// AppendString writes a string to the Buffer. -func (b *Buffer) AppendString(s string) { - b.bs = append(b.bs, s...) -} - -// AppendInt appends an integer to the underlying buffer (assuming base 10). -func (b *Buffer) AppendInt(i int64) { - b.bs = strconv.AppendInt(b.bs, i, 10) -} - -// AppendTime appends the time formatted using the specified layout. -func (b *Buffer) AppendTime(t time.Time, layout string) { - b.bs = t.AppendFormat(b.bs, layout) -} - -// AppendUint appends an unsigned integer to the underlying buffer (assuming -// base 10). -func (b *Buffer) AppendUint(i uint64) { - b.bs = strconv.AppendUint(b.bs, i, 10) -} - -// AppendBool appends a bool to the underlying buffer. -func (b *Buffer) AppendBool(v bool) { - b.bs = strconv.AppendBool(b.bs, v) -} - -// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN -// or +/- Inf. -func (b *Buffer) AppendFloat(f float64, bitSize int) { - b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize) -} - -// Len returns the length of the underlying byte slice. -func (b *Buffer) Len() int { - return len(b.bs) -} - -// Cap returns the capacity of the underlying byte slice. -func (b *Buffer) Cap() int { - return cap(b.bs) -} - -// Bytes returns a mutable reference to the underlying byte slice. -func (b *Buffer) Bytes() []byte { - return b.bs -} - -// String returns a string copy of the underlying byte slice. -func (b *Buffer) String() string { - return string(b.bs) -} - -// Reset resets the underlying byte slice. Subsequent writes re-use the slice's -// backing array. -func (b *Buffer) Reset() { - b.bs = b.bs[:0] -} - -// Write implements io.Writer. -func (b *Buffer) Write(bs []byte) (int, error) { - b.bs = append(b.bs, bs...) - return len(bs), nil -} - -// TrimNewline trims any final "\n" byte from the end of the buffer. -func (b *Buffer) TrimNewline() { - if i := len(b.bs) - 1; i >= 0 { - if b.bs[i] == '\n' { - b.bs = b.bs[:i] - } - } -} - -// Free returns the Buffer to its Pool. -// -// Callers must not retain references to the Buffer after calling Free. -func (b *Buffer) Free() { - b.pool.put(b) -} diff --git a/vendor/go.uber.org/zap/buffer/pool.go b/vendor/go.uber.org/zap/buffer/pool.go deleted file mode 100644 index 8fb3e202cf..0000000000 --- a/vendor/go.uber.org/zap/buffer/pool.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package buffer - -import "sync" - -// A Pool is a type-safe wrapper around a sync.Pool. -type Pool struct { - p *sync.Pool -} - -// NewPool constructs a new Pool. -func NewPool() Pool { - return Pool{p: &sync.Pool{ - New: func() interface{} { - return &Buffer{bs: make([]byte, 0, _size)} - }, - }} -} - -// Get retrieves a Buffer from the pool, creating one if necessary. -func (p Pool) Get() *Buffer { - buf := p.p.Get().(*Buffer) - buf.Reset() - buf.pool = p - return buf -} - -func (p Pool) put(buf *Buffer) { - p.p.Put(buf) -} diff --git a/vendor/go.uber.org/zap/checklicense.sh b/vendor/go.uber.org/zap/checklicense.sh deleted file mode 100644 index 345ac8b89a..0000000000 --- a/vendor/go.uber.org/zap/checklicense.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -e - -ERROR_COUNT=0 -while read -r file -do - case "$(head -1 "${file}")" in - *"Copyright (c) "*" Uber Technologies, Inc.") - # everything's cool - ;; - *) - echo "$file is missing license header." - (( ERROR_COUNT++ )) - ;; - esac -done < <(git ls-files "*\.go") - -exit $ERROR_COUNT diff --git a/vendor/go.uber.org/zap/config.go b/vendor/go.uber.org/zap/config.go deleted file mode 100644 index 55637fb0b4..0000000000 --- a/vendor/go.uber.org/zap/config.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "fmt" - "sort" - "time" - - "go.uber.org/zap/zapcore" -) - -// SamplingConfig sets a sampling strategy for the logger. Sampling caps the -// global CPU and I/O load that logging puts on your process while attempting -// to preserve a representative subset of your logs. -// -// If specified, the Sampler will invoke the Hook after each decision. -// -// Values configured here are per-second. See zapcore.NewSamplerWithOptions for -// details. -type SamplingConfig struct { - Initial int `json:"initial" yaml:"initial"` - Thereafter int `json:"thereafter" yaml:"thereafter"` - Hook func(zapcore.Entry, zapcore.SamplingDecision) `json:"-" yaml:"-"` -} - -// Config offers a declarative way to construct a logger. It doesn't do -// anything that can't be done with New, Options, and the various -// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to -// toggle common options. -// -// Note that Config intentionally supports only the most common options. More -// unusual logging setups (logging to network connections or message queues, -// splitting output between multiple files, etc.) are possible, but require -// direct use of the zapcore package. For sample code, see the package-level -// BasicConfiguration and AdvancedConfiguration examples. -// -// For an example showing runtime log level changes, see the documentation for -// AtomicLevel. -type Config struct { - // Level is the minimum enabled logging level. Note that this is a dynamic - // level, so calling Config.Level.SetLevel will atomically change the log - // level of all loggers descended from this config. - Level AtomicLevel `json:"level" yaml:"level"` - // Development puts the logger in development mode, which changes the - // behavior of DPanicLevel and takes stacktraces more liberally. - Development bool `json:"development" yaml:"development"` - // DisableCaller stops annotating logs with the calling function's file - // name and line number. By default, all logs are annotated. - DisableCaller bool `json:"disableCaller" yaml:"disableCaller"` - // DisableStacktrace completely disables automatic stacktrace capturing. By - // default, stacktraces are captured for WarnLevel and above logs in - // development and ErrorLevel and above in production. - DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"` - // Sampling sets a sampling policy. A nil SamplingConfig disables sampling. - Sampling *SamplingConfig `json:"sampling" yaml:"sampling"` - // Encoding sets the logger's encoding. Valid values are "json" and - // "console", as well as any third-party encodings registered via - // RegisterEncoder. - Encoding string `json:"encoding" yaml:"encoding"` - // EncoderConfig sets options for the chosen encoder. See - // zapcore.EncoderConfig for details. - EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"` - // OutputPaths is a list of URLs or file paths to write logging output to. - // See Open for details. - OutputPaths []string `json:"outputPaths" yaml:"outputPaths"` - // ErrorOutputPaths is a list of URLs to write internal logger errors to. - // The default is standard error. - // - // Note that this setting only affects internal errors; for sample code that - // sends error-level logs to a different location from info- and debug-level - // logs, see the package-level AdvancedConfiguration example. - ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"` - // InitialFields is a collection of fields to add to the root logger. - InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"` -} - -// NewProductionEncoderConfig returns an opinionated EncoderConfig for -// production environments. -func NewProductionEncoderConfig() zapcore.EncoderConfig { - return zapcore.EncoderConfig{ - TimeKey: "ts", - LevelKey: "level", - NameKey: "logger", - CallerKey: "caller", - FunctionKey: zapcore.OmitKey, - MessageKey: "msg", - StacktraceKey: "stacktrace", - LineEnding: zapcore.DefaultLineEnding, - EncodeLevel: zapcore.LowercaseLevelEncoder, - EncodeTime: zapcore.EpochTimeEncoder, - EncodeDuration: zapcore.SecondsDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, - } -} - -// NewProductionConfig is a reasonable production logging configuration. -// Logging is enabled at InfoLevel and above. -// -// It uses a JSON encoder, writes to standard error, and enables sampling. -// Stacktraces are automatically included on logs of ErrorLevel and above. -func NewProductionConfig() Config { - return Config{ - Level: NewAtomicLevelAt(InfoLevel), - Development: false, - Sampling: &SamplingConfig{ - Initial: 100, - Thereafter: 100, - }, - Encoding: "json", - EncoderConfig: NewProductionEncoderConfig(), - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - } -} - -// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for -// development environments. -func NewDevelopmentEncoderConfig() zapcore.EncoderConfig { - return zapcore.EncoderConfig{ - // Keys can be anything except the empty string. - TimeKey: "T", - LevelKey: "L", - NameKey: "N", - CallerKey: "C", - FunctionKey: zapcore.OmitKey, - MessageKey: "M", - StacktraceKey: "S", - LineEnding: zapcore.DefaultLineEnding, - EncodeLevel: zapcore.CapitalLevelEncoder, - EncodeTime: zapcore.ISO8601TimeEncoder, - EncodeDuration: zapcore.StringDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, - } -} - -// NewDevelopmentConfig is a reasonable development logging configuration. -// Logging is enabled at DebugLevel and above. -// -// It enables development mode (which makes DPanicLevel logs panic), uses a -// console encoder, writes to standard error, and disables sampling. -// Stacktraces are automatically included on logs of WarnLevel and above. -func NewDevelopmentConfig() Config { - return Config{ - Level: NewAtomicLevelAt(DebugLevel), - Development: true, - Encoding: "console", - EncoderConfig: NewDevelopmentEncoderConfig(), - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - } -} - -// Build constructs a logger from the Config and Options. -func (cfg Config) Build(opts ...Option) (*Logger, error) { - enc, err := cfg.buildEncoder() - if err != nil { - return nil, err - } - - sink, errSink, err := cfg.openSinks() - if err != nil { - return nil, err - } - - if cfg.Level == (AtomicLevel{}) { - return nil, fmt.Errorf("missing Level") - } - - log := New( - zapcore.NewCore(enc, sink, cfg.Level), - cfg.buildOptions(errSink)..., - ) - if len(opts) > 0 { - log = log.WithOptions(opts...) - } - return log, nil -} - -func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option { - opts := []Option{ErrorOutput(errSink)} - - if cfg.Development { - opts = append(opts, Development()) - } - - if !cfg.DisableCaller { - opts = append(opts, AddCaller()) - } - - stackLevel := ErrorLevel - if cfg.Development { - stackLevel = WarnLevel - } - if !cfg.DisableStacktrace { - opts = append(opts, AddStacktrace(stackLevel)) - } - - if scfg := cfg.Sampling; scfg != nil { - opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core { - var samplerOpts []zapcore.SamplerOption - if scfg.Hook != nil { - samplerOpts = append(samplerOpts, zapcore.SamplerHook(scfg.Hook)) - } - return zapcore.NewSamplerWithOptions( - core, - time.Second, - cfg.Sampling.Initial, - cfg.Sampling.Thereafter, - samplerOpts..., - ) - })) - } - - if len(cfg.InitialFields) > 0 { - fs := make([]Field, 0, len(cfg.InitialFields)) - keys := make([]string, 0, len(cfg.InitialFields)) - for k := range cfg.InitialFields { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - fs = append(fs, Any(k, cfg.InitialFields[k])) - } - opts = append(opts, Fields(fs...)) - } - - return opts -} - -func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) { - sink, closeOut, err := Open(cfg.OutputPaths...) - if err != nil { - return nil, nil, err - } - errSink, _, err := Open(cfg.ErrorOutputPaths...) - if err != nil { - closeOut() - return nil, nil, err - } - return sink, errSink, nil -} - -func (cfg Config) buildEncoder() (zapcore.Encoder, error) { - return newEncoder(cfg.Encoding, cfg.EncoderConfig) -} diff --git a/vendor/go.uber.org/zap/doc.go b/vendor/go.uber.org/zap/doc.go deleted file mode 100644 index 8638dd1b96..0000000000 --- a/vendor/go.uber.org/zap/doc.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package zap provides fast, structured, leveled logging. -// -// For applications that log in the hot path, reflection-based serialization -// and string formatting are prohibitively expensive - they're CPU-intensive -// and make many small allocations. Put differently, using json.Marshal and -// fmt.Fprintf to log tons of interface{} makes your application slow. -// -// Zap takes a different approach. It includes a reflection-free, -// zero-allocation JSON encoder, and the base Logger strives to avoid -// serialization overhead and allocations wherever possible. By building the -// high-level SugaredLogger on that foundation, zap lets users choose when -// they need to count every allocation and when they'd prefer a more familiar, -// loosely typed API. -// -// Choosing a Logger -// -// In contexts where performance is nice, but not critical, use the -// SugaredLogger. It's 4-10x faster than other structured logging packages and -// supports both structured and printf-style logging. Like log15 and go-kit, -// the SugaredLogger's structured logging APIs are loosely typed and accept a -// variadic number of key-value pairs. (For more advanced use cases, they also -// accept strongly typed fields - see the SugaredLogger.With documentation for -// details.) -// sugar := zap.NewExample().Sugar() -// defer sugar.Sync() -// sugar.Infow("failed to fetch URL", -// "url", "http://example.com", -// "attempt", 3, -// "backoff", time.Second, -// ) -// sugar.Infof("failed to fetch URL: %s", "http://example.com") -// -// By default, loggers are unbuffered. However, since zap's low-level APIs -// allow buffering, calling Sync before letting your process exit is a good -// habit. -// -// In the rare contexts where every microsecond and every allocation matter, -// use the Logger. It's even faster than the SugaredLogger and allocates far -// less, but it only supports strongly-typed, structured logging. -// logger := zap.NewExample() -// defer logger.Sync() -// logger.Info("failed to fetch URL", -// zap.String("url", "http://example.com"), -// zap.Int("attempt", 3), -// zap.Duration("backoff", time.Second), -// ) -// -// Choosing between the Logger and SugaredLogger doesn't need to be an -// application-wide decision: converting between the two is simple and -// inexpensive. -// logger := zap.NewExample() -// defer logger.Sync() -// sugar := logger.Sugar() -// plain := sugar.Desugar() -// -// Configuring Zap -// -// The simplest way to build a Logger is to use zap's opinionated presets: -// NewExample, NewProduction, and NewDevelopment. These presets build a logger -// with a single function call: -// logger, err := zap.NewProduction() -// if err != nil { -// log.Fatalf("can't initialize zap logger: %v", err) -// } -// defer logger.Sync() -// -// Presets are fine for small projects, but larger projects and organizations -// naturally require a bit more customization. For most users, zap's Config -// struct strikes the right balance between flexibility and convenience. See -// the package-level BasicConfiguration example for sample code. -// -// More unusual configurations (splitting output between files, sending logs -// to a message queue, etc.) are possible, but require direct use of -// go.uber.org/zap/zapcore. See the package-level AdvancedConfiguration -// example for sample code. -// -// Extending Zap -// -// The zap package itself is a relatively thin wrapper around the interfaces -// in go.uber.org/zap/zapcore. Extending zap to support a new encoding (e.g., -// BSON), a new log sink (e.g., Kafka), or something more exotic (perhaps an -// exception aggregation service, like Sentry or Rollbar) typically requires -// implementing the zapcore.Encoder, zapcore.WriteSyncer, or zapcore.Core -// interfaces. See the zapcore documentation for details. -// -// Similarly, package authors can use the high-performance Encoder and Core -// implementations in the zapcore package to build their own loggers. -// -// Frequently Asked Questions -// -// An FAQ covering everything from installation errors to design decisions is -// available at https://github.com/uber-go/zap/blob/master/FAQ.md. -package zap // import "go.uber.org/zap" diff --git a/vendor/go.uber.org/zap/encoder.go b/vendor/go.uber.org/zap/encoder.go deleted file mode 100644 index 08ed833543..0000000000 --- a/vendor/go.uber.org/zap/encoder.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "errors" - "fmt" - "sync" - - "go.uber.org/zap/zapcore" -) - -var ( - errNoEncoderNameSpecified = errors.New("no encoder name specified") - - _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){ - "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { - return zapcore.NewConsoleEncoder(encoderConfig), nil - }, - "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { - return zapcore.NewJSONEncoder(encoderConfig), nil - }, - } - _encoderMutex sync.RWMutex -) - -// RegisterEncoder registers an encoder constructor, which the Config struct -// can then reference. By default, the "json" and "console" encoders are -// registered. -// -// Attempting to register an encoder whose name is already taken returns an -// error. -func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error { - _encoderMutex.Lock() - defer _encoderMutex.Unlock() - if name == "" { - return errNoEncoderNameSpecified - } - if _, ok := _encoderNameToConstructor[name]; ok { - return fmt.Errorf("encoder already registered for name %q", name) - } - _encoderNameToConstructor[name] = constructor - return nil -} - -func newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { - if encoderConfig.TimeKey != "" && encoderConfig.EncodeTime == nil { - return nil, fmt.Errorf("missing EncodeTime in EncoderConfig") - } - - _encoderMutex.RLock() - defer _encoderMutex.RUnlock() - if name == "" { - return nil, errNoEncoderNameSpecified - } - constructor, ok := _encoderNameToConstructor[name] - if !ok { - return nil, fmt.Errorf("no encoder registered for name %q", name) - } - return constructor(encoderConfig) -} diff --git a/vendor/go.uber.org/zap/error.go b/vendor/go.uber.org/zap/error.go deleted file mode 100644 index 65982a51e5..0000000000 --- a/vendor/go.uber.org/zap/error.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "sync" - - "go.uber.org/zap/zapcore" -) - -var _errArrayElemPool = sync.Pool{New: func() interface{} { - return &errArrayElem{} -}} - -// Error is shorthand for the common idiom NamedError("error", err). -func Error(err error) Field { - return NamedError("error", err) -} - -// NamedError constructs a field that lazily stores err.Error() under the -// provided key. Errors which also implement fmt.Formatter (like those produced -// by github.com/pkg/errors) will also have their verbose representation stored -// under key+"Verbose". If passed a nil error, the field is a no-op. -// -// For the common case in which the key is simply "error", the Error function -// is shorter and less repetitive. -func NamedError(key string, err error) Field { - if err == nil { - return Skip() - } - return Field{Key: key, Type: zapcore.ErrorType, Interface: err} -} - -type errArray []error - -func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range errs { - if errs[i] == nil { - continue - } - // To represent each error as an object with an "error" attribute and - // potentially an "errorVerbose" attribute, we need to wrap it in a - // type that implements LogObjectMarshaler. To prevent this from - // allocating, pool the wrapper type. - elem := _errArrayElemPool.Get().(*errArrayElem) - elem.error = errs[i] - arr.AppendObject(elem) - elem.error = nil - _errArrayElemPool.Put(elem) - } - return nil -} - -type errArrayElem struct { - error -} - -func (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error { - // Re-use the error field's logic, which supports non-standard error types. - Error(e.error).AddTo(enc) - return nil -} diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go deleted file mode 100644 index bbb745db5b..0000000000 --- a/vendor/go.uber.org/zap/field.go +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "fmt" - "math" - "time" - - "go.uber.org/zap/zapcore" -) - -// Field is an alias for Field. Aliasing this type dramatically -// improves the navigability of this package's API documentation. -type Field = zapcore.Field - -var ( - _minTimeInt64 = time.Unix(0, math.MinInt64) - _maxTimeInt64 = time.Unix(0, math.MaxInt64) -) - -// Skip constructs a no-op field, which is often useful when handling invalid -// inputs in other Field constructors. -func Skip() Field { - return Field{Type: zapcore.SkipType} -} - -// nilField returns a field which will marshal explicitly as nil. See motivation -// in https://github.com/uber-go/zap/issues/753 . If we ever make breaking -// changes and add zapcore.NilType and zapcore.ObjectEncoder.AddNil, the -// implementation here should be changed to reflect that. -func nilField(key string) Field { return Reflect(key, nil) } - -// Binary constructs a field that carries an opaque binary blob. -// -// Binary data is serialized in an encoding-appropriate format. For example, -// zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text, -// use ByteString. -func Binary(key string, val []byte) Field { - return Field{Key: key, Type: zapcore.BinaryType, Interface: val} -} - -// Bool constructs a field that carries a bool. -func Bool(key string, val bool) Field { - var ival int64 - if val { - ival = 1 - } - return Field{Key: key, Type: zapcore.BoolType, Integer: ival} -} - -// Boolp constructs a field that carries a *bool. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Boolp(key string, val *bool) Field { - if val == nil { - return nilField(key) - } - return Bool(key, *val) -} - -// ByteString constructs a field that carries UTF-8 encoded text as a []byte. -// To log opaque binary blobs (which aren't necessarily valid UTF-8), use -// Binary. -func ByteString(key string, val []byte) Field { - return Field{Key: key, Type: zapcore.ByteStringType, Interface: val} -} - -// Complex128 constructs a field that carries a complex number. Unlike most -// numeric fields, this costs an allocation (to convert the complex128 to -// interface{}). -func Complex128(key string, val complex128) Field { - return Field{Key: key, Type: zapcore.Complex128Type, Interface: val} -} - -// Complex128p constructs a field that carries a *complex128. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Complex128p(key string, val *complex128) Field { - if val == nil { - return nilField(key) - } - return Complex128(key, *val) -} - -// Complex64 constructs a field that carries a complex number. Unlike most -// numeric fields, this costs an allocation (to convert the complex64 to -// interface{}). -func Complex64(key string, val complex64) Field { - return Field{Key: key, Type: zapcore.Complex64Type, Interface: val} -} - -// Complex64p constructs a field that carries a *complex64. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Complex64p(key string, val *complex64) Field { - if val == nil { - return nilField(key) - } - return Complex64(key, *val) -} - -// Float64 constructs a field that carries a float64. The way the -// floating-point value is represented is encoder-dependent, so marshaling is -// necessarily lazy. -func Float64(key string, val float64) Field { - return Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))} -} - -// Float64p constructs a field that carries a *float64. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Float64p(key string, val *float64) Field { - if val == nil { - return nilField(key) - } - return Float64(key, *val) -} - -// Float32 constructs a field that carries a float32. The way the -// floating-point value is represented is encoder-dependent, so marshaling is -// necessarily lazy. -func Float32(key string, val float32) Field { - return Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))} -} - -// Float32p constructs a field that carries a *float32. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Float32p(key string, val *float32) Field { - if val == nil { - return nilField(key) - } - return Float32(key, *val) -} - -// Int constructs a field with the given key and value. -func Int(key string, val int) Field { - return Int64(key, int64(val)) -} - -// Intp constructs a field that carries a *int. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Intp(key string, val *int) Field { - if val == nil { - return nilField(key) - } - return Int(key, *val) -} - -// Int64 constructs a field with the given key and value. -func Int64(key string, val int64) Field { - return Field{Key: key, Type: zapcore.Int64Type, Integer: val} -} - -// Int64p constructs a field that carries a *int64. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Int64p(key string, val *int64) Field { - if val == nil { - return nilField(key) - } - return Int64(key, *val) -} - -// Int32 constructs a field with the given key and value. -func Int32(key string, val int32) Field { - return Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)} -} - -// Int32p constructs a field that carries a *int32. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Int32p(key string, val *int32) Field { - if val == nil { - return nilField(key) - } - return Int32(key, *val) -} - -// Int16 constructs a field with the given key and value. -func Int16(key string, val int16) Field { - return Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)} -} - -// Int16p constructs a field that carries a *int16. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Int16p(key string, val *int16) Field { - if val == nil { - return nilField(key) - } - return Int16(key, *val) -} - -// Int8 constructs a field with the given key and value. -func Int8(key string, val int8) Field { - return Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)} -} - -// Int8p constructs a field that carries a *int8. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Int8p(key string, val *int8) Field { - if val == nil { - return nilField(key) - } - return Int8(key, *val) -} - -// String constructs a field with the given key and value. -func String(key string, val string) Field { - return Field{Key: key, Type: zapcore.StringType, String: val} -} - -// Stringp constructs a field that carries a *string. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Stringp(key string, val *string) Field { - if val == nil { - return nilField(key) - } - return String(key, *val) -} - -// Uint constructs a field with the given key and value. -func Uint(key string, val uint) Field { - return Uint64(key, uint64(val)) -} - -// Uintp constructs a field that carries a *uint. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Uintp(key string, val *uint) Field { - if val == nil { - return nilField(key) - } - return Uint(key, *val) -} - -// Uint64 constructs a field with the given key and value. -func Uint64(key string, val uint64) Field { - return Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)} -} - -// Uint64p constructs a field that carries a *uint64. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Uint64p(key string, val *uint64) Field { - if val == nil { - return nilField(key) - } - return Uint64(key, *val) -} - -// Uint32 constructs a field with the given key and value. -func Uint32(key string, val uint32) Field { - return Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)} -} - -// Uint32p constructs a field that carries a *uint32. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Uint32p(key string, val *uint32) Field { - if val == nil { - return nilField(key) - } - return Uint32(key, *val) -} - -// Uint16 constructs a field with the given key and value. -func Uint16(key string, val uint16) Field { - return Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)} -} - -// Uint16p constructs a field that carries a *uint16. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Uint16p(key string, val *uint16) Field { - if val == nil { - return nilField(key) - } - return Uint16(key, *val) -} - -// Uint8 constructs a field with the given key and value. -func Uint8(key string, val uint8) Field { - return Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)} -} - -// Uint8p constructs a field that carries a *uint8. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Uint8p(key string, val *uint8) Field { - if val == nil { - return nilField(key) - } - return Uint8(key, *val) -} - -// Uintptr constructs a field with the given key and value. -func Uintptr(key string, val uintptr) Field { - return Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)} -} - -// Uintptrp constructs a field that carries a *uintptr. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Uintptrp(key string, val *uintptr) Field { - if val == nil { - return nilField(key) - } - return Uintptr(key, *val) -} - -// Reflect constructs a field with the given key and an arbitrary object. It uses -// an encoding-appropriate, reflection-based function to lazily serialize nearly -// any object into the logging context, but it's relatively slow and -// allocation-heavy. Outside tests, Any is always a better choice. -// -// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect -// includes the error message in the final log output. -func Reflect(key string, val interface{}) Field { - return Field{Key: key, Type: zapcore.ReflectType, Interface: val} -} - -// Namespace creates a named, isolated scope within the logger's context. All -// subsequent fields will be added to the new namespace. -// -// This helps prevent key collisions when injecting loggers into sub-components -// or third-party libraries. -func Namespace(key string) Field { - return Field{Key: key, Type: zapcore.NamespaceType} -} - -// Stringer constructs a field with the given key and the output of the value's -// String method. The Stringer's String method is called lazily. -func Stringer(key string, val fmt.Stringer) Field { - return Field{Key: key, Type: zapcore.StringerType, Interface: val} -} - -// Time constructs a Field with the given key and value. The encoder -// controls how the time is serialized. -func Time(key string, val time.Time) Field { - if val.Before(_minTimeInt64) || val.After(_maxTimeInt64) { - return Field{Key: key, Type: zapcore.TimeFullType, Interface: val} - } - return Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()} -} - -// Timep constructs a field that carries a *time.Time. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Timep(key string, val *time.Time) Field { - if val == nil { - return nilField(key) - } - return Time(key, *val) -} - -// Stack constructs a field that stores a stacktrace of the current goroutine -// under provided key. Keep in mind that taking a stacktrace is eager and -// expensive (relatively speaking); this function both makes an allocation and -// takes about two microseconds. -func Stack(key string) Field { - return StackSkip(key, 1) // skip Stack -} - -// StackSkip constructs a field similarly to Stack, but also skips the given -// number of frames from the top of the stacktrace. -func StackSkip(key string, skip int) Field { - // Returning the stacktrace as a string costs an allocation, but saves us - // from expanding the zapcore.Field union struct to include a byte slice. Since - // taking a stacktrace is already so expensive (~10us), the extra allocation - // is okay. - return String(key, takeStacktrace(skip+1)) // skip StackSkip -} - -// Duration constructs a field with the given key and value. The encoder -// controls how the duration is serialized. -func Duration(key string, val time.Duration) Field { - return Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)} -} - -// Durationp constructs a field that carries a *time.Duration. The returned Field will safely -// and explicitly represent `nil` when appropriate. -func Durationp(key string, val *time.Duration) Field { - if val == nil { - return nilField(key) - } - return Duration(key, *val) -} - -// Object constructs a field with the given key and ObjectMarshaler. It -// provides a flexible, but still type-safe and efficient, way to add map- or -// struct-like user-defined types to the logging context. The struct's -// MarshalLogObject method is called lazily. -func Object(key string, val zapcore.ObjectMarshaler) Field { - return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val} -} - -// Inline constructs a Field that is similar to Object, but it -// will add the elements of the provided ObjectMarshaler to the -// current namespace. -func Inline(val zapcore.ObjectMarshaler) Field { - return zapcore.Field{ - Type: zapcore.InlineMarshalerType, - Interface: val, - } -} - -// Any takes a key and an arbitrary value and chooses the best way to represent -// them as a field, falling back to a reflection-based approach only if -// necessary. -// -// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between -// them. To minimize surprises, []byte values are treated as binary blobs, byte -// values are treated as uint8, and runes are always treated as integers. -func Any(key string, value interface{}) Field { - switch val := value.(type) { - case zapcore.ObjectMarshaler: - return Object(key, val) - case zapcore.ArrayMarshaler: - return Array(key, val) - case bool: - return Bool(key, val) - case *bool: - return Boolp(key, val) - case []bool: - return Bools(key, val) - case complex128: - return Complex128(key, val) - case *complex128: - return Complex128p(key, val) - case []complex128: - return Complex128s(key, val) - case complex64: - return Complex64(key, val) - case *complex64: - return Complex64p(key, val) - case []complex64: - return Complex64s(key, val) - case float64: - return Float64(key, val) - case *float64: - return Float64p(key, val) - case []float64: - return Float64s(key, val) - case float32: - return Float32(key, val) - case *float32: - return Float32p(key, val) - case []float32: - return Float32s(key, val) - case int: - return Int(key, val) - case *int: - return Intp(key, val) - case []int: - return Ints(key, val) - case int64: - return Int64(key, val) - case *int64: - return Int64p(key, val) - case []int64: - return Int64s(key, val) - case int32: - return Int32(key, val) - case *int32: - return Int32p(key, val) - case []int32: - return Int32s(key, val) - case int16: - return Int16(key, val) - case *int16: - return Int16p(key, val) - case []int16: - return Int16s(key, val) - case int8: - return Int8(key, val) - case *int8: - return Int8p(key, val) - case []int8: - return Int8s(key, val) - case string: - return String(key, val) - case *string: - return Stringp(key, val) - case []string: - return Strings(key, val) - case uint: - return Uint(key, val) - case *uint: - return Uintp(key, val) - case []uint: - return Uints(key, val) - case uint64: - return Uint64(key, val) - case *uint64: - return Uint64p(key, val) - case []uint64: - return Uint64s(key, val) - case uint32: - return Uint32(key, val) - case *uint32: - return Uint32p(key, val) - case []uint32: - return Uint32s(key, val) - case uint16: - return Uint16(key, val) - case *uint16: - return Uint16p(key, val) - case []uint16: - return Uint16s(key, val) - case uint8: - return Uint8(key, val) - case *uint8: - return Uint8p(key, val) - case []byte: - return Binary(key, val) - case uintptr: - return Uintptr(key, val) - case *uintptr: - return Uintptrp(key, val) - case []uintptr: - return Uintptrs(key, val) - case time.Time: - return Time(key, val) - case *time.Time: - return Timep(key, val) - case []time.Time: - return Times(key, val) - case time.Duration: - return Duration(key, val) - case *time.Duration: - return Durationp(key, val) - case []time.Duration: - return Durations(key, val) - case error: - return NamedError(key, val) - case []error: - return Errors(key, val) - case fmt.Stringer: - return Stringer(key, val) - default: - return Reflect(key, val) - } -} diff --git a/vendor/go.uber.org/zap/flag.go b/vendor/go.uber.org/zap/flag.go deleted file mode 100644 index 1312875072..0000000000 --- a/vendor/go.uber.org/zap/flag.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "flag" - - "go.uber.org/zap/zapcore" -) - -// LevelFlag uses the standard library's flag.Var to declare a global flag -// with the specified name, default, and usage guidance. The returned value is -// a pointer to the value of the flag. -// -// If you don't want to use the flag package's global state, you can use any -// non-nil *Level as a flag.Value with your own *flag.FlagSet. -func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level { - lvl := defaultLevel - flag.Var(&lvl, name, usage) - return &lvl -} diff --git a/vendor/go.uber.org/zap/glide.yaml b/vendor/go.uber.org/zap/glide.yaml deleted file mode 100644 index 8e1d05e9ab..0000000000 --- a/vendor/go.uber.org/zap/glide.yaml +++ /dev/null @@ -1,34 +0,0 @@ -package: go.uber.org/zap -license: MIT -import: -- package: go.uber.org/atomic - version: ^1 -- package: go.uber.org/multierr - version: ^1 -testImport: -- package: github.com/satori/go.uuid -- package: github.com/sirupsen/logrus -- package: github.com/apex/log - subpackages: - - handlers/json -- package: github.com/go-kit/kit - subpackages: - - log -- package: github.com/stretchr/testify - subpackages: - - assert - - require -- package: gopkg.in/inconshreveable/log15.v2 -- package: github.com/mattn/goveralls -- package: github.com/pborman/uuid -- package: github.com/pkg/errors -- package: github.com/rs/zerolog -- package: golang.org/x/tools - subpackages: - - cover -- package: golang.org/x/lint - subpackages: - - golint -- package: github.com/axw/gocov - subpackages: - - gocov diff --git a/vendor/go.uber.org/zap/global.go b/vendor/go.uber.org/zap/global.go deleted file mode 100644 index c1ac0507cd..0000000000 --- a/vendor/go.uber.org/zap/global.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "bytes" - "fmt" - "log" - "os" - "sync" - - "go.uber.org/zap/zapcore" -) - -const ( - _loggerWriterDepth = 2 - _programmerErrorTemplate = "You've found a bug in zap! Please file a bug at " + - "https://github.com/uber-go/zap/issues/new and reference this error: %v" -) - -var ( - _globalMu sync.RWMutex - _globalL = NewNop() - _globalS = _globalL.Sugar() -) - -// L returns the global Logger, which can be reconfigured with ReplaceGlobals. -// It's safe for concurrent use. -func L() *Logger { - _globalMu.RLock() - l := _globalL - _globalMu.RUnlock() - return l -} - -// S returns the global SugaredLogger, which can be reconfigured with -// ReplaceGlobals. It's safe for concurrent use. -func S() *SugaredLogger { - _globalMu.RLock() - s := _globalS - _globalMu.RUnlock() - return s -} - -// ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a -// function to restore the original values. It's safe for concurrent use. -func ReplaceGlobals(logger *Logger) func() { - _globalMu.Lock() - prev := _globalL - _globalL = logger - _globalS = logger.Sugar() - _globalMu.Unlock() - return func() { ReplaceGlobals(prev) } -} - -// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at -// InfoLevel. To redirect the standard library's package-global logging -// functions, use RedirectStdLog instead. -func NewStdLog(l *Logger) *log.Logger { - logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) - f := logger.Info - return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */) -} - -// NewStdLogAt returns *log.Logger which writes to supplied zap logger at -// required level. -func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) { - logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) - logFunc, err := levelToFunc(logger, level) - if err != nil { - return nil, err - } - return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil -} - -// RedirectStdLog redirects output from the standard library's package-global -// logger to the supplied logger at InfoLevel. Since zap already handles caller -// annotations, timestamps, etc., it automatically disables the standard -// library's annotations and prefixing. -// -// It returns a function to restore the original prefix and flags and reset the -// standard library's output to os.Stderr. -func RedirectStdLog(l *Logger) func() { - f, err := redirectStdLogAt(l, InfoLevel) - if err != nil { - // Can't get here, since passing InfoLevel to redirectStdLogAt always - // works. - panic(fmt.Sprintf(_programmerErrorTemplate, err)) - } - return f -} - -// RedirectStdLogAt redirects output from the standard library's package-global -// logger to the supplied logger at the specified level. Since zap already -// handles caller annotations, timestamps, etc., it automatically disables the -// standard library's annotations and prefixing. -// -// It returns a function to restore the original prefix and flags and reset the -// standard library's output to os.Stderr. -func RedirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) { - return redirectStdLogAt(l, level) -} - -func redirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) { - flags := log.Flags() - prefix := log.Prefix() - log.SetFlags(0) - log.SetPrefix("") - logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) - logFunc, err := levelToFunc(logger, level) - if err != nil { - return nil, err - } - log.SetOutput(&loggerWriter{logFunc}) - return func() { - log.SetFlags(flags) - log.SetPrefix(prefix) - log.SetOutput(os.Stderr) - }, nil -} - -func levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) { - switch lvl { - case DebugLevel: - return logger.Debug, nil - case InfoLevel: - return logger.Info, nil - case WarnLevel: - return logger.Warn, nil - case ErrorLevel: - return logger.Error, nil - case DPanicLevel: - return logger.DPanic, nil - case PanicLevel: - return logger.Panic, nil - case FatalLevel: - return logger.Fatal, nil - } - return nil, fmt.Errorf("unrecognized level: %q", lvl) -} - -type loggerWriter struct { - logFunc func(msg string, fields ...Field) -} - -func (l *loggerWriter) Write(p []byte) (int, error) { - p = bytes.TrimSpace(p) - l.logFunc(string(p)) - return len(p), nil -} diff --git a/vendor/go.uber.org/zap/global_go112.go b/vendor/go.uber.org/zap/global_go112.go deleted file mode 100644 index 6b5dbda807..0000000000 --- a/vendor/go.uber.org/zap/global_go112.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2019 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// See #682 for more information. -// +build go1.12 - -package zap - -const _stdLogDefaultDepth = 1 diff --git a/vendor/go.uber.org/zap/global_prego112.go b/vendor/go.uber.org/zap/global_prego112.go deleted file mode 100644 index d3ab9af933..0000000000 --- a/vendor/go.uber.org/zap/global_prego112.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2019 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// See #682 for more information. -// +build !go1.12 - -package zap - -const _stdLogDefaultDepth = 2 diff --git a/vendor/go.uber.org/zap/go.mod b/vendor/go.uber.org/zap/go.mod deleted file mode 100644 index 6578a35454..0000000000 --- a/vendor/go.uber.org/zap/go.mod +++ /dev/null @@ -1,12 +0,0 @@ -module go.uber.org/zap - -go 1.13 - -require ( - github.com/pkg/errors v0.8.1 - github.com/stretchr/testify v1.7.0 - go.uber.org/atomic v1.7.0 - go.uber.org/multierr v1.6.0 - gopkg.in/yaml.v2 v2.2.8 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect -) diff --git a/vendor/go.uber.org/zap/go.sum b/vendor/go.uber.org/zap/go.sum deleted file mode 100644 index 911a87ae1c..0000000000 --- a/vendor/go.uber.org/zap/go.sum +++ /dev/null @@ -1,22 +0,0 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go deleted file mode 100644 index 1297c33b32..0000000000 --- a/vendor/go.uber.org/zap/http_handler.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - - "go.uber.org/zap/zapcore" -) - -// ServeHTTP is a simple JSON endpoint that can report on or change the current -// logging level. -// -// GET -// -// The GET request returns a JSON description of the current logging level like: -// {"level":"info"} -// -// PUT -// -// The PUT request changes the logging level. It is perfectly safe to change the -// logging level while a program is running. Two content types are supported: -// -// Content-Type: application/x-www-form-urlencoded -// -// With this content type, the level can be provided through the request body or -// a query parameter. The log level is URL encoded like: -// -// level=debug -// -// The request body takes precedence over the query parameter, if both are -// specified. -// -// This content type is the default for a curl PUT request. Following are two -// example curl requests that both set the logging level to debug. -// -// curl -X PUT localhost:8080/log/level?level=debug -// curl -X PUT localhost:8080/log/level -d level=debug -// -// For any other content type, the payload is expected to be JSON encoded and -// look like: -// -// {"level":"info"} -// -// An example curl request could look like this: -// -// curl -X PUT localhost:8080/log/level -H "Content-Type: application/json" -d '{"level":"debug"}' -// -func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { - type errorResponse struct { - Error string `json:"error"` - } - type payload struct { - Level zapcore.Level `json:"level"` - } - - enc := json.NewEncoder(w) - - switch r.Method { - case http.MethodGet: - enc.Encode(payload{Level: lvl.Level()}) - case http.MethodPut: - requestedLvl, err := decodePutRequest(r.Header.Get("Content-Type"), r) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - enc.Encode(errorResponse{Error: err.Error()}) - return - } - lvl.SetLevel(requestedLvl) - enc.Encode(payload{Level: lvl.Level()}) - default: - w.WriteHeader(http.StatusMethodNotAllowed) - enc.Encode(errorResponse{ - Error: "Only GET and PUT are supported.", - }) - } -} - -// Decodes incoming PUT requests and returns the requested logging level. -func decodePutRequest(contentType string, r *http.Request) (zapcore.Level, error) { - if contentType == "application/x-www-form-urlencoded" { - return decodePutURL(r) - } - return decodePutJSON(r.Body) -} - -func decodePutURL(r *http.Request) (zapcore.Level, error) { - lvl := r.FormValue("level") - if lvl == "" { - return 0, fmt.Errorf("must specify logging level") - } - var l zapcore.Level - if err := l.UnmarshalText([]byte(lvl)); err != nil { - return 0, err - } - return l, nil -} - -func decodePutJSON(body io.Reader) (zapcore.Level, error) { - var pld struct { - Level *zapcore.Level `json:"level"` - } - if err := json.NewDecoder(body).Decode(&pld); err != nil { - return 0, fmt.Errorf("malformed request body: %v", err) - } - if pld.Level == nil { - return 0, fmt.Errorf("must specify logging level") - } - return *pld.Level, nil - -} diff --git a/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go b/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go deleted file mode 100644 index dad583aaa5..0000000000 --- a/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package bufferpool houses zap's shared internal buffer pool. Third-party -// packages can recreate the same functionality with buffers.NewPool. -package bufferpool - -import "go.uber.org/zap/buffer" - -var ( - _pool = buffer.NewPool() - // Get retrieves a buffer from the pool, creating one if necessary. - Get = _pool.Get -) diff --git a/vendor/go.uber.org/zap/internal/color/color.go b/vendor/go.uber.org/zap/internal/color/color.go deleted file mode 100644 index c4d5d02abc..0000000000 --- a/vendor/go.uber.org/zap/internal/color/color.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package color adds coloring functionality for TTY output. -package color - -import "fmt" - -// Foreground colors. -const ( - Black Color = iota + 30 - Red - Green - Yellow - Blue - Magenta - Cyan - White -) - -// Color represents a text color. -type Color uint8 - -// Add adds the coloring to the given string. -func (c Color) Add(s string) string { - return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s) -} diff --git a/vendor/go.uber.org/zap/internal/exit/exit.go b/vendor/go.uber.org/zap/internal/exit/exit.go deleted file mode 100644 index dfc5b05feb..0000000000 --- a/vendor/go.uber.org/zap/internal/exit/exit.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package exit provides stubs so that unit tests can exercise code that calls -// os.Exit(1). -package exit - -import "os" - -var real = func() { os.Exit(1) } - -// Exit normally terminates the process by calling os.Exit(1). If the package -// is stubbed, it instead records a call in the testing spy. -func Exit() { - real() -} - -// A StubbedExit is a testing fake for os.Exit. -type StubbedExit struct { - Exited bool - prev func() -} - -// Stub substitutes a fake for the call to os.Exit(1). -func Stub() *StubbedExit { - s := &StubbedExit{prev: real} - real = s.exit - return s -} - -// WithStub runs the supplied function with Exit stubbed. It returns the stub -// used, so that users can test whether the process would have crashed. -func WithStub(f func()) *StubbedExit { - s := Stub() - defer s.Unstub() - f() - return s -} - -// Unstub restores the previous exit function. -func (se *StubbedExit) Unstub() { - real = se.prev -} - -func (se *StubbedExit) exit() { - se.Exited = true -} diff --git a/vendor/go.uber.org/zap/level.go b/vendor/go.uber.org/zap/level.go deleted file mode 100644 index 3567a9a1e6..0000000000 --- a/vendor/go.uber.org/zap/level.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "go.uber.org/atomic" - "go.uber.org/zap/zapcore" -) - -const ( - // DebugLevel logs are typically voluminous, and are usually disabled in - // production. - DebugLevel = zapcore.DebugLevel - // InfoLevel is the default logging priority. - InfoLevel = zapcore.InfoLevel - // WarnLevel logs are more important than Info, but don't need individual - // human review. - WarnLevel = zapcore.WarnLevel - // ErrorLevel logs are high-priority. If an application is running smoothly, - // it shouldn't generate any error-level logs. - ErrorLevel = zapcore.ErrorLevel - // DPanicLevel logs are particularly important errors. In development the - // logger panics after writing the message. - DPanicLevel = zapcore.DPanicLevel - // PanicLevel logs a message, then panics. - PanicLevel = zapcore.PanicLevel - // FatalLevel logs a message, then calls os.Exit(1). - FatalLevel = zapcore.FatalLevel -) - -// LevelEnablerFunc is a convenient way to implement zapcore.LevelEnabler with -// an anonymous function. -// -// It's particularly useful when splitting log output between different -// outputs (e.g., standard error and standard out). For sample code, see the -// package-level AdvancedConfiguration example. -type LevelEnablerFunc func(zapcore.Level) bool - -// Enabled calls the wrapped function. -func (f LevelEnablerFunc) Enabled(lvl zapcore.Level) bool { return f(lvl) } - -// An AtomicLevel is an atomically changeable, dynamic logging level. It lets -// you safely change the log level of a tree of loggers (the root logger and -// any children created by adding context) at runtime. -// -// The AtomicLevel itself is an http.Handler that serves a JSON endpoint to -// alter its level. -// -// AtomicLevels must be created with the NewAtomicLevel constructor to allocate -// their internal atomic pointer. -type AtomicLevel struct { - l *atomic.Int32 -} - -// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging -// enabled. -func NewAtomicLevel() AtomicLevel { - return AtomicLevel{ - l: atomic.NewInt32(int32(InfoLevel)), - } -} - -// NewAtomicLevelAt is a convenience function that creates an AtomicLevel -// and then calls SetLevel with the given level. -func NewAtomicLevelAt(l zapcore.Level) AtomicLevel { - a := NewAtomicLevel() - a.SetLevel(l) - return a -} - -// Enabled implements the zapcore.LevelEnabler interface, which allows the -// AtomicLevel to be used in place of traditional static levels. -func (lvl AtomicLevel) Enabled(l zapcore.Level) bool { - return lvl.Level().Enabled(l) -} - -// Level returns the minimum enabled log level. -func (lvl AtomicLevel) Level() zapcore.Level { - return zapcore.Level(int8(lvl.l.Load())) -} - -// SetLevel alters the logging level. -func (lvl AtomicLevel) SetLevel(l zapcore.Level) { - lvl.l.Store(int32(l)) -} - -// String returns the string representation of the underlying Level. -func (lvl AtomicLevel) String() string { - return lvl.Level().String() -} - -// UnmarshalText unmarshals the text to an AtomicLevel. It uses the same text -// representations as the static zapcore.Levels ("debug", "info", "warn", -// "error", "dpanic", "panic", and "fatal"). -func (lvl *AtomicLevel) UnmarshalText(text []byte) error { - if lvl.l == nil { - lvl.l = &atomic.Int32{} - } - - var l zapcore.Level - if err := l.UnmarshalText(text); err != nil { - return err - } - - lvl.SetLevel(l) - return nil -} - -// MarshalText marshals the AtomicLevel to a byte slice. It uses the same -// text representation as the static zapcore.Levels ("debug", "info", "warn", -// "error", "dpanic", "panic", and "fatal"). -func (lvl AtomicLevel) MarshalText() (text []byte, err error) { - return lvl.Level().MarshalText() -} diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go deleted file mode 100644 index 553f258e74..0000000000 --- a/vendor/go.uber.org/zap/logger.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "fmt" - "io/ioutil" - "os" - "runtime" - "strings" - "time" - - "go.uber.org/zap/zapcore" -) - -// A Logger provides fast, leveled, structured logging. All methods are safe -// for concurrent use. -// -// The Logger is designed for contexts in which every microsecond and every -// allocation matters, so its API intentionally favors performance and type -// safety over brevity. For most applications, the SugaredLogger strikes a -// better balance between performance and ergonomics. -type Logger struct { - core zapcore.Core - - development bool - addCaller bool - onFatal zapcore.CheckWriteAction // default is WriteThenFatal - - name string - errorOutput zapcore.WriteSyncer - - addStack zapcore.LevelEnabler - - callerSkip int -} - -// New constructs a new Logger from the provided zapcore.Core and Options. If -// the passed zapcore.Core is nil, it falls back to using a no-op -// implementation. -// -// This is the most flexible way to construct a Logger, but also the most -// verbose. For typical use cases, the highly-opinionated presets -// (NewProduction, NewDevelopment, and NewExample) or the Config struct are -// more convenient. -// -// For sample code, see the package-level AdvancedConfiguration example. -func New(core zapcore.Core, options ...Option) *Logger { - if core == nil { - return NewNop() - } - log := &Logger{ - core: core, - errorOutput: zapcore.Lock(os.Stderr), - addStack: zapcore.FatalLevel + 1, - } - return log.WithOptions(options...) -} - -// NewNop returns a no-op Logger. It never writes out logs or internal errors, -// and it never runs user-defined hooks. -// -// Using WithOptions to replace the Core or error output of a no-op Logger can -// re-enable logging. -func NewNop() *Logger { - return &Logger{ - core: zapcore.NewNopCore(), - errorOutput: zapcore.AddSync(ioutil.Discard), - addStack: zapcore.FatalLevel + 1, - } -} - -// NewProduction builds a sensible production Logger that writes InfoLevel and -// above logs to standard error as JSON. -// -// It's a shortcut for NewProductionConfig().Build(...Option). -func NewProduction(options ...Option) (*Logger, error) { - return NewProductionConfig().Build(options...) -} - -// NewDevelopment builds a development Logger that writes DebugLevel and above -// logs to standard error in a human-friendly format. -// -// It's a shortcut for NewDevelopmentConfig().Build(...Option). -func NewDevelopment(options ...Option) (*Logger, error) { - return NewDevelopmentConfig().Build(options...) -} - -// NewExample builds a Logger that's designed for use in zap's testable -// examples. It writes DebugLevel and above logs to standard out as JSON, but -// omits the timestamp and calling function to keep example output -// short and deterministic. -func NewExample(options ...Option) *Logger { - encoderCfg := zapcore.EncoderConfig{ - MessageKey: "msg", - LevelKey: "level", - NameKey: "logger", - EncodeLevel: zapcore.LowercaseLevelEncoder, - EncodeTime: zapcore.ISO8601TimeEncoder, - EncodeDuration: zapcore.StringDurationEncoder, - } - core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel) - return New(core).WithOptions(options...) -} - -// Sugar wraps the Logger to provide a more ergonomic, but slightly slower, -// API. Sugaring a Logger is quite inexpensive, so it's reasonable for a -// single application to use both Loggers and SugaredLoggers, converting -// between them on the boundaries of performance-sensitive code. -func (log *Logger) Sugar() *SugaredLogger { - core := log.clone() - core.callerSkip += 2 - return &SugaredLogger{core} -} - -// Named adds a new path segment to the logger's name. Segments are joined by -// periods. By default, Loggers are unnamed. -func (log *Logger) Named(s string) *Logger { - if s == "" { - return log - } - l := log.clone() - if log.name == "" { - l.name = s - } else { - l.name = strings.Join([]string{l.name, s}, ".") - } - return l -} - -// WithOptions clones the current Logger, applies the supplied Options, and -// returns the resulting Logger. It's safe to use concurrently. -func (log *Logger) WithOptions(opts ...Option) *Logger { - c := log.clone() - for _, opt := range opts { - opt.apply(c) - } - return c -} - -// With creates a child logger and adds structured context to it. Fields added -// to the child don't affect the parent, and vice versa. -func (log *Logger) With(fields ...Field) *Logger { - if len(fields) == 0 { - return log - } - l := log.clone() - l.core = l.core.With(fields) - return l -} - -// Check returns a CheckedEntry if logging a message at the specified level -// is enabled. It's a completely optional optimization; in high-performance -// applications, Check can help avoid allocating a slice to hold fields. -func (log *Logger) Check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { - return log.check(lvl, msg) -} - -// Debug logs a message at DebugLevel. The message includes any fields passed -// at the log site, as well as any fields accumulated on the logger. -func (log *Logger) Debug(msg string, fields ...Field) { - if ce := log.check(DebugLevel, msg); ce != nil { - ce.Write(fields...) - } -} - -// Info logs a message at InfoLevel. The message includes any fields passed -// at the log site, as well as any fields accumulated on the logger. -func (log *Logger) Info(msg string, fields ...Field) { - if ce := log.check(InfoLevel, msg); ce != nil { - ce.Write(fields...) - } -} - -// Warn logs a message at WarnLevel. The message includes any fields passed -// at the log site, as well as any fields accumulated on the logger. -func (log *Logger) Warn(msg string, fields ...Field) { - if ce := log.check(WarnLevel, msg); ce != nil { - ce.Write(fields...) - } -} - -// Error logs a message at ErrorLevel. The message includes any fields passed -// at the log site, as well as any fields accumulated on the logger. -func (log *Logger) Error(msg string, fields ...Field) { - if ce := log.check(ErrorLevel, msg); ce != nil { - ce.Write(fields...) - } -} - -// DPanic logs a message at DPanicLevel. The message includes any fields -// passed at the log site, as well as any fields accumulated on the logger. -// -// If the logger is in development mode, it then panics (DPanic means -// "development panic"). This is useful for catching errors that are -// recoverable, but shouldn't ever happen. -func (log *Logger) DPanic(msg string, fields ...Field) { - if ce := log.check(DPanicLevel, msg); ce != nil { - ce.Write(fields...) - } -} - -// Panic logs a message at PanicLevel. The message includes any fields passed -// at the log site, as well as any fields accumulated on the logger. -// -// The logger then panics, even if logging at PanicLevel is disabled. -func (log *Logger) Panic(msg string, fields ...Field) { - if ce := log.check(PanicLevel, msg); ce != nil { - ce.Write(fields...) - } -} - -// Fatal logs a message at FatalLevel. The message includes any fields passed -// at the log site, as well as any fields accumulated on the logger. -// -// The logger then calls os.Exit(1), even if logging at FatalLevel is -// disabled. -func (log *Logger) Fatal(msg string, fields ...Field) { - if ce := log.check(FatalLevel, msg); ce != nil { - ce.Write(fields...) - } -} - -// Sync calls the underlying Core's Sync method, flushing any buffered log -// entries. Applications should take care to call Sync before exiting. -func (log *Logger) Sync() error { - return log.core.Sync() -} - -// Core returns the Logger's underlying zapcore.Core. -func (log *Logger) Core() zapcore.Core { - return log.core -} - -func (log *Logger) clone() *Logger { - copy := *log - return © -} - -func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { - // check must always be called directly by a method in the Logger interface - // (e.g., Check, Info, Fatal). - const callerSkipOffset = 2 - - // Check the level first to reduce the cost of disabled log calls. - // Since Panic and higher may exit, we skip the optimization for those levels. - if lvl < zapcore.DPanicLevel && !log.core.Enabled(lvl) { - return nil - } - - // Create basic checked entry thru the core; this will be non-nil if the - // log message will actually be written somewhere. - ent := zapcore.Entry{ - LoggerName: log.name, - Time: time.Now(), - Level: lvl, - Message: msg, - } - ce := log.core.Check(ent, nil) - willWrite := ce != nil - - // Set up any required terminal behavior. - switch ent.Level { - case zapcore.PanicLevel: - ce = ce.Should(ent, zapcore.WriteThenPanic) - case zapcore.FatalLevel: - onFatal := log.onFatal - // Noop is the default value for CheckWriteAction, and it leads to - // continued execution after a Fatal which is unexpected. - if onFatal == zapcore.WriteThenNoop { - onFatal = zapcore.WriteThenFatal - } - ce = ce.Should(ent, onFatal) - case zapcore.DPanicLevel: - if log.development { - ce = ce.Should(ent, zapcore.WriteThenPanic) - } - } - - // Only do further annotation if we're going to write this message; checked - // entries that exist only for terminal behavior don't benefit from - // annotation. - if !willWrite { - return ce - } - - // Thread the error output through to the CheckedEntry. - ce.ErrorOutput = log.errorOutput - if log.addCaller { - frame, defined := getCallerFrame(log.callerSkip + callerSkipOffset) - if !defined { - fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", time.Now().UTC()) - log.errorOutput.Sync() - } - - ce.Entry.Caller = zapcore.EntryCaller{ - Defined: defined, - PC: frame.PC, - File: frame.File, - Line: frame.Line, - Function: frame.Function, - } - } - if log.addStack.Enabled(ce.Entry.Level) { - ce.Entry.Stack = StackSkip("", log.callerSkip+callerSkipOffset).String - } - - return ce -} - -// getCallerFrame gets caller frame. The argument skip is the number of stack -// frames to ascend, with 0 identifying the caller of getCallerFrame. The -// boolean ok is false if it was not possible to recover the information. -// -// Note: This implementation is similar to runtime.Caller, but it returns the whole frame. -func getCallerFrame(skip int) (frame runtime.Frame, ok bool) { - const skipOffset = 2 // skip getCallerFrame and Callers - - pc := make([]uintptr, 1) - numFrames := runtime.Callers(skip+skipOffset, pc) - if numFrames < 1 { - return - } - - frame, _ = runtime.CallersFrames(pc).Next() - return frame, frame.PC != 0 -} diff --git a/vendor/go.uber.org/zap/options.go b/vendor/go.uber.org/zap/options.go deleted file mode 100644 index 0135c20923..0000000000 --- a/vendor/go.uber.org/zap/options.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "fmt" - - "go.uber.org/zap/zapcore" -) - -// An Option configures a Logger. -type Option interface { - apply(*Logger) -} - -// optionFunc wraps a func so it satisfies the Option interface. -type optionFunc func(*Logger) - -func (f optionFunc) apply(log *Logger) { - f(log) -} - -// WrapCore wraps or replaces the Logger's underlying zapcore.Core. -func WrapCore(f func(zapcore.Core) zapcore.Core) Option { - return optionFunc(func(log *Logger) { - log.core = f(log.core) - }) -} - -// Hooks registers functions which will be called each time the Logger writes -// out an Entry. Repeated use of Hooks is additive. -// -// Hooks are useful for simple side effects, like capturing metrics for the -// number of emitted logs. More complex side effects, including anything that -// requires access to the Entry's structured fields, should be implemented as -// a zapcore.Core instead. See zapcore.RegisterHooks for details. -func Hooks(hooks ...func(zapcore.Entry) error) Option { - return optionFunc(func(log *Logger) { - log.core = zapcore.RegisterHooks(log.core, hooks...) - }) -} - -// Fields adds fields to the Logger. -func Fields(fs ...Field) Option { - return optionFunc(func(log *Logger) { - log.core = log.core.With(fs) - }) -} - -// ErrorOutput sets the destination for errors generated by the Logger. Note -// that this option only affects internal errors; for sample code that sends -// error-level logs to a different location from info- and debug-level logs, -// see the package-level AdvancedConfiguration example. -// -// The supplied WriteSyncer must be safe for concurrent use. The Open and -// zapcore.Lock functions are the simplest ways to protect files with a mutex. -func ErrorOutput(w zapcore.WriteSyncer) Option { - return optionFunc(func(log *Logger) { - log.errorOutput = w - }) -} - -// Development puts the logger in development mode, which makes DPanic-level -// logs panic instead of simply logging an error. -func Development() Option { - return optionFunc(func(log *Logger) { - log.development = true - }) -} - -// AddCaller configures the Logger to annotate each message with the filename, -// line number, and function name of zap's caller. See also WithCaller. -func AddCaller() Option { - return WithCaller(true) -} - -// WithCaller configures the Logger to annotate each message with the filename, -// line number, and function name of zap's caller, or not, depending on the -// value of enabled. This is a generalized form of AddCaller. -func WithCaller(enabled bool) Option { - return optionFunc(func(log *Logger) { - log.addCaller = enabled - }) -} - -// AddCallerSkip increases the number of callers skipped by caller annotation -// (as enabled by the AddCaller option). When building wrappers around the -// Logger and SugaredLogger, supplying this Option prevents zap from always -// reporting the wrapper code as the caller. -func AddCallerSkip(skip int) Option { - return optionFunc(func(log *Logger) { - log.callerSkip += skip - }) -} - -// AddStacktrace configures the Logger to record a stack trace for all messages at -// or above a given level. -func AddStacktrace(lvl zapcore.LevelEnabler) Option { - return optionFunc(func(log *Logger) { - log.addStack = lvl - }) -} - -// IncreaseLevel increase the level of the logger. It has no effect if -// the passed in level tries to decrease the level of the logger. -func IncreaseLevel(lvl zapcore.LevelEnabler) Option { - return optionFunc(func(log *Logger) { - core, err := zapcore.NewIncreaseLevelCore(log.core, lvl) - if err != nil { - fmt.Fprintf(log.errorOutput, "failed to IncreaseLevel: %v\n", err) - } else { - log.core = core - } - }) -} - -// OnFatal sets the action to take on fatal logs. -func OnFatal(action zapcore.CheckWriteAction) Option { - return optionFunc(func(log *Logger) { - log.onFatal = action - }) -} diff --git a/vendor/go.uber.org/zap/sink.go b/vendor/go.uber.org/zap/sink.go deleted file mode 100644 index df46fa87a7..0000000000 --- a/vendor/go.uber.org/zap/sink.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "errors" - "fmt" - "io" - "net/url" - "os" - "strings" - "sync" - - "go.uber.org/zap/zapcore" -) - -const schemeFile = "file" - -var ( - _sinkMutex sync.RWMutex - _sinkFactories map[string]func(*url.URL) (Sink, error) // keyed by scheme -) - -func init() { - resetSinkRegistry() -} - -func resetSinkRegistry() { - _sinkMutex.Lock() - defer _sinkMutex.Unlock() - - _sinkFactories = map[string]func(*url.URL) (Sink, error){ - schemeFile: newFileSink, - } -} - -// Sink defines the interface to write to and close logger destinations. -type Sink interface { - zapcore.WriteSyncer - io.Closer -} - -type nopCloserSink struct{ zapcore.WriteSyncer } - -func (nopCloserSink) Close() error { return nil } - -type errSinkNotFound struct { - scheme string -} - -func (e *errSinkNotFound) Error() string { - return fmt.Sprintf("no sink found for scheme %q", e.scheme) -} - -// RegisterSink registers a user-supplied factory for all sinks with a -// particular scheme. -// -// All schemes must be ASCII, valid under section 3.1 of RFC 3986 -// (https://tools.ietf.org/html/rfc3986#section-3.1), and must not already -// have a factory registered. Zap automatically registers a factory for the -// "file" scheme. -func RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error { - _sinkMutex.Lock() - defer _sinkMutex.Unlock() - - if scheme == "" { - return errors.New("can't register a sink factory for empty string") - } - normalized, err := normalizeScheme(scheme) - if err != nil { - return fmt.Errorf("%q is not a valid scheme: %v", scheme, err) - } - if _, ok := _sinkFactories[normalized]; ok { - return fmt.Errorf("sink factory already registered for scheme %q", normalized) - } - _sinkFactories[normalized] = factory - return nil -} - -func newSink(rawURL string) (Sink, error) { - u, err := url.Parse(rawURL) - if err != nil { - return nil, fmt.Errorf("can't parse %q as a URL: %v", rawURL, err) - } - if u.Scheme == "" { - u.Scheme = schemeFile - } - - _sinkMutex.RLock() - factory, ok := _sinkFactories[u.Scheme] - _sinkMutex.RUnlock() - if !ok { - return nil, &errSinkNotFound{u.Scheme} - } - return factory(u) -} - -func newFileSink(u *url.URL) (Sink, error) { - if u.User != nil { - return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u) - } - if u.Fragment != "" { - return nil, fmt.Errorf("fragments not allowed with file URLs: got %v", u) - } - if u.RawQuery != "" { - return nil, fmt.Errorf("query parameters not allowed with file URLs: got %v", u) - } - // Error messages are better if we check hostname and port separately. - if u.Port() != "" { - return nil, fmt.Errorf("ports not allowed with file URLs: got %v", u) - } - if hn := u.Hostname(); hn != "" && hn != "localhost" { - return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u) - } - switch u.Path { - case "stdout": - return nopCloserSink{os.Stdout}, nil - case "stderr": - return nopCloserSink{os.Stderr}, nil - } - return os.OpenFile(u.Path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) -} - -func normalizeScheme(s string) (string, error) { - // https://tools.ietf.org/html/rfc3986#section-3.1 - s = strings.ToLower(s) - if first := s[0]; 'a' > first || 'z' < first { - return "", errors.New("must start with a letter") - } - for i := 1; i < len(s); i++ { // iterate over bytes, not runes - c := s[i] - switch { - case 'a' <= c && c <= 'z': - continue - case '0' <= c && c <= '9': - continue - case c == '.' || c == '+' || c == '-': - continue - } - return "", fmt.Errorf("may not contain %q", c) - } - return s, nil -} diff --git a/vendor/go.uber.org/zap/stacktrace.go b/vendor/go.uber.org/zap/stacktrace.go deleted file mode 100644 index 0cf8c1ddff..0000000000 --- a/vendor/go.uber.org/zap/stacktrace.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "runtime" - "sync" - - "go.uber.org/zap/internal/bufferpool" -) - -var ( - _stacktracePool = sync.Pool{ - New: func() interface{} { - return newProgramCounters(64) - }, - } -) - -func takeStacktrace(skip int) string { - buffer := bufferpool.Get() - defer buffer.Free() - programCounters := _stacktracePool.Get().(*programCounters) - defer _stacktracePool.Put(programCounters) - - var numFrames int - for { - // Skip the call to runtime.Callers and takeStacktrace so that the - // program counters start at the caller of takeStacktrace. - numFrames = runtime.Callers(skip+2, programCounters.pcs) - if numFrames < len(programCounters.pcs) { - break - } - // Don't put the too-short counter slice back into the pool; this lets - // the pool adjust if we consistently take deep stacktraces. - programCounters = newProgramCounters(len(programCounters.pcs) * 2) - } - - i := 0 - frames := runtime.CallersFrames(programCounters.pcs[:numFrames]) - - // Note: On the last iteration, frames.Next() returns false, with a valid - // frame, but we ignore this frame. The last frame is a a runtime frame which - // adds noise, since it's only either runtime.main or runtime.goexit. - for frame, more := frames.Next(); more; frame, more = frames.Next() { - if i != 0 { - buffer.AppendByte('\n') - } - i++ - buffer.AppendString(frame.Function) - buffer.AppendByte('\n') - buffer.AppendByte('\t') - buffer.AppendString(frame.File) - buffer.AppendByte(':') - buffer.AppendInt(int64(frame.Line)) - } - - return buffer.String() -} - -type programCounters struct { - pcs []uintptr -} - -func newProgramCounters(size int) *programCounters { - return &programCounters{make([]uintptr, size)} -} diff --git a/vendor/go.uber.org/zap/sugar.go b/vendor/go.uber.org/zap/sugar.go deleted file mode 100644 index 4084dada79..0000000000 --- a/vendor/go.uber.org/zap/sugar.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "fmt" - - "go.uber.org/zap/zapcore" - - "go.uber.org/multierr" -) - -const ( - _oddNumberErrMsg = "Ignored key without a value." - _nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys." -) - -// A SugaredLogger wraps the base Logger functionality in a slower, but less -// verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar -// method. -// -// Unlike the Logger, the SugaredLogger doesn't insist on structured logging. -// For each log level, it exposes three methods: one for loosely-typed -// structured logging, one for println-style formatting, and one for -// printf-style formatting. For example, SugaredLoggers can produce InfoLevel -// output with Infow ("info with" structured context), Info, or Infof. -type SugaredLogger struct { - base *Logger -} - -// Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring -// is quite inexpensive, so it's reasonable for a single application to use -// both Loggers and SugaredLoggers, converting between them on the boundaries -// of performance-sensitive code. -func (s *SugaredLogger) Desugar() *Logger { - base := s.base.clone() - base.callerSkip -= 2 - return base -} - -// Named adds a sub-scope to the logger's name. See Logger.Named for details. -func (s *SugaredLogger) Named(name string) *SugaredLogger { - return &SugaredLogger{base: s.base.Named(name)} -} - -// With adds a variadic number of fields to the logging context. It accepts a -// mix of strongly-typed Field objects and loosely-typed key-value pairs. When -// processing pairs, the first element of the pair is used as the field key -// and the second as the field value. -// -// For example, -// sugaredLogger.With( -// "hello", "world", -// "failure", errors.New("oh no"), -// Stack(), -// "count", 42, -// "user", User{Name: "alice"}, -// ) -// is the equivalent of -// unsugared.With( -// String("hello", "world"), -// String("failure", "oh no"), -// Stack(), -// Int("count", 42), -// Object("user", User{Name: "alice"}), -// ) -// -// Note that the keys in key-value pairs should be strings. In development, -// passing a non-string key panics. In production, the logger is more -// forgiving: a separate error is logged, but the key-value pair is skipped -// and execution continues. Passing an orphaned key triggers similar behavior: -// panics in development and errors in production. -func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger { - return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)} -} - -// Debug uses fmt.Sprint to construct and log a message. -func (s *SugaredLogger) Debug(args ...interface{}) { - s.log(DebugLevel, "", args, nil) -} - -// Info uses fmt.Sprint to construct and log a message. -func (s *SugaredLogger) Info(args ...interface{}) { - s.log(InfoLevel, "", args, nil) -} - -// Warn uses fmt.Sprint to construct and log a message. -func (s *SugaredLogger) Warn(args ...interface{}) { - s.log(WarnLevel, "", args, nil) -} - -// Error uses fmt.Sprint to construct and log a message. -func (s *SugaredLogger) Error(args ...interface{}) { - s.log(ErrorLevel, "", args, nil) -} - -// DPanic uses fmt.Sprint to construct and log a message. In development, the -// logger then panics. (See DPanicLevel for details.) -func (s *SugaredLogger) DPanic(args ...interface{}) { - s.log(DPanicLevel, "", args, nil) -} - -// Panic uses fmt.Sprint to construct and log a message, then panics. -func (s *SugaredLogger) Panic(args ...interface{}) { - s.log(PanicLevel, "", args, nil) -} - -// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit. -func (s *SugaredLogger) Fatal(args ...interface{}) { - s.log(FatalLevel, "", args, nil) -} - -// Debugf uses fmt.Sprintf to log a templated message. -func (s *SugaredLogger) Debugf(template string, args ...interface{}) { - s.log(DebugLevel, template, args, nil) -} - -// Infof uses fmt.Sprintf to log a templated message. -func (s *SugaredLogger) Infof(template string, args ...interface{}) { - s.log(InfoLevel, template, args, nil) -} - -// Warnf uses fmt.Sprintf to log a templated message. -func (s *SugaredLogger) Warnf(template string, args ...interface{}) { - s.log(WarnLevel, template, args, nil) -} - -// Errorf uses fmt.Sprintf to log a templated message. -func (s *SugaredLogger) Errorf(template string, args ...interface{}) { - s.log(ErrorLevel, template, args, nil) -} - -// DPanicf uses fmt.Sprintf to log a templated message. In development, the -// logger then panics. (See DPanicLevel for details.) -func (s *SugaredLogger) DPanicf(template string, args ...interface{}) { - s.log(DPanicLevel, template, args, nil) -} - -// Panicf uses fmt.Sprintf to log a templated message, then panics. -func (s *SugaredLogger) Panicf(template string, args ...interface{}) { - s.log(PanicLevel, template, args, nil) -} - -// Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit. -func (s *SugaredLogger) Fatalf(template string, args ...interface{}) { - s.log(FatalLevel, template, args, nil) -} - -// Debugw logs a message with some additional context. The variadic key-value -// pairs are treated as they are in With. -// -// When debug-level logging is disabled, this is much faster than -// s.With(keysAndValues).Debug(msg) -func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) { - s.log(DebugLevel, msg, nil, keysAndValues) -} - -// Infow logs a message with some additional context. The variadic key-value -// pairs are treated as they are in With. -func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) { - s.log(InfoLevel, msg, nil, keysAndValues) -} - -// Warnw logs a message with some additional context. The variadic key-value -// pairs are treated as they are in With. -func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) { - s.log(WarnLevel, msg, nil, keysAndValues) -} - -// Errorw logs a message with some additional context. The variadic key-value -// pairs are treated as they are in With. -func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) { - s.log(ErrorLevel, msg, nil, keysAndValues) -} - -// DPanicw logs a message with some additional context. In development, the -// logger then panics. (See DPanicLevel for details.) The variadic key-value -// pairs are treated as they are in With. -func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) { - s.log(DPanicLevel, msg, nil, keysAndValues) -} - -// Panicw logs a message with some additional context, then panics. The -// variadic key-value pairs are treated as they are in With. -func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) { - s.log(PanicLevel, msg, nil, keysAndValues) -} - -// Fatalw logs a message with some additional context, then calls os.Exit. The -// variadic key-value pairs are treated as they are in With. -func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) { - s.log(FatalLevel, msg, nil, keysAndValues) -} - -// Sync flushes any buffered log entries. -func (s *SugaredLogger) Sync() error { - return s.base.Sync() -} - -func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) { - // If logging at this level is completely disabled, skip the overhead of - // string formatting. - if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) { - return - } - - msg := getMessage(template, fmtArgs) - if ce := s.base.Check(lvl, msg); ce != nil { - ce.Write(s.sweetenFields(context)...) - } -} - -// getMessage format with Sprint, Sprintf, or neither. -func getMessage(template string, fmtArgs []interface{}) string { - if len(fmtArgs) == 0 { - return template - } - - if template != "" { - return fmt.Sprintf(template, fmtArgs...) - } - - if len(fmtArgs) == 1 { - if str, ok := fmtArgs[0].(string); ok { - return str - } - } - return fmt.Sprint(fmtArgs...) -} - -func (s *SugaredLogger) sweetenFields(args []interface{}) []Field { - if len(args) == 0 { - return nil - } - - // Allocate enough space for the worst case; if users pass only structured - // fields, we shouldn't penalize them with extra allocations. - fields := make([]Field, 0, len(args)) - var invalid invalidPairs - - for i := 0; i < len(args); { - // This is a strongly-typed field. Consume it and move on. - if f, ok := args[i].(Field); ok { - fields = append(fields, f) - i++ - continue - } - - // Make sure this element isn't a dangling key. - if i == len(args)-1 { - s.base.DPanic(_oddNumberErrMsg, Any("ignored", args[i])) - break - } - - // Consume this value and the next, treating them as a key-value pair. If the - // key isn't a string, add this pair to the slice of invalid pairs. - key, val := args[i], args[i+1] - if keyStr, ok := key.(string); !ok { - // Subsequent errors are likely, so allocate once up front. - if cap(invalid) == 0 { - invalid = make(invalidPairs, 0, len(args)/2) - } - invalid = append(invalid, invalidPair{i, key, val}) - } else { - fields = append(fields, Any(keyStr, val)) - } - i += 2 - } - - // If we encountered any invalid key-value pairs, log an error. - if len(invalid) > 0 { - s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid)) - } - return fields -} - -type invalidPair struct { - position int - key, value interface{} -} - -func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error { - enc.AddInt64("position", int64(p.position)) - Any("key", p.key).AddTo(enc) - Any("value", p.value).AddTo(enc) - return nil -} - -type invalidPairs []invalidPair - -func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error { - var err error - for i := range ps { - err = multierr.Append(err, enc.AppendObject(ps[i])) - } - return err -} diff --git a/vendor/go.uber.org/zap/time.go b/vendor/go.uber.org/zap/time.go deleted file mode 100644 index c5a1f16225..0000000000 --- a/vendor/go.uber.org/zap/time.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import "time" - -func timeToMillis(t time.Time) int64 { - return t.UnixNano() / int64(time.Millisecond) -} diff --git a/vendor/go.uber.org/zap/writer.go b/vendor/go.uber.org/zap/writer.go deleted file mode 100644 index 86a709ab0b..0000000000 --- a/vendor/go.uber.org/zap/writer.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zap - -import ( - "fmt" - "io" - "io/ioutil" - - "go.uber.org/zap/zapcore" - - "go.uber.org/multierr" -) - -// Open is a high-level wrapper that takes a variadic number of URLs, opens or -// creates each of the specified resources, and combines them into a locked -// WriteSyncer. It also returns any error encountered and a function to close -// any opened files. -// -// Passing no URLs returns a no-op WriteSyncer. Zap handles URLs without a -// scheme and URLs with the "file" scheme. Third-party code may register -// factories for other schemes using RegisterSink. -// -// URLs with the "file" scheme must use absolute paths on the local -// filesystem. No user, password, port, fragments, or query parameters are -// allowed, and the hostname must be empty or "localhost". -// -// Since it's common to write logs to the local filesystem, URLs without a -// scheme (e.g., "/var/log/foo.log") are treated as local file paths. Without -// a scheme, the special paths "stdout" and "stderr" are interpreted as -// os.Stdout and os.Stderr. When specified without a scheme, relative file -// paths also work. -func Open(paths ...string) (zapcore.WriteSyncer, func(), error) { - writers, close, err := open(paths) - if err != nil { - return nil, nil, err - } - - writer := CombineWriteSyncers(writers...) - return writer, close, nil -} - -func open(paths []string) ([]zapcore.WriteSyncer, func(), error) { - writers := make([]zapcore.WriteSyncer, 0, len(paths)) - closers := make([]io.Closer, 0, len(paths)) - close := func() { - for _, c := range closers { - c.Close() - } - } - - var openErr error - for _, path := range paths { - sink, err := newSink(path) - if err != nil { - openErr = multierr.Append(openErr, fmt.Errorf("couldn't open sink %q: %v", path, err)) - continue - } - writers = append(writers, sink) - closers = append(closers, sink) - } - if openErr != nil { - close() - return writers, nil, openErr - } - - return writers, close, nil -} - -// CombineWriteSyncers is a utility that combines multiple WriteSyncers into a -// single, locked WriteSyncer. If no inputs are supplied, it returns a no-op -// WriteSyncer. -// -// It's provided purely as a convenience; the result is no different from -// using zapcore.NewMultiWriteSyncer and zapcore.Lock individually. -func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer { - if len(writers) == 0 { - return zapcore.AddSync(ioutil.Discard) - } - return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...)) -} diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go deleted file mode 100644 index 2307af404c..0000000000 --- a/vendor/go.uber.org/zap/zapcore/console_encoder.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import ( - "fmt" - "sync" - - "go.uber.org/zap/buffer" - "go.uber.org/zap/internal/bufferpool" -) - -var _sliceEncoderPool = sync.Pool{ - New: func() interface{} { - return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)} - }, -} - -func getSliceEncoder() *sliceArrayEncoder { - return _sliceEncoderPool.Get().(*sliceArrayEncoder) -} - -func putSliceEncoder(e *sliceArrayEncoder) { - e.elems = e.elems[:0] - _sliceEncoderPool.Put(e) -} - -type consoleEncoder struct { - *jsonEncoder -} - -// NewConsoleEncoder creates an encoder whose output is designed for human - -// rather than machine - consumption. It serializes the core log entry data -// (message, level, timestamp, etc.) in a plain-text format and leaves the -// structured context as JSON. -// -// Note that although the console encoder doesn't use the keys specified in the -// encoder configuration, it will omit any element whose key is set to the empty -// string. -func NewConsoleEncoder(cfg EncoderConfig) Encoder { - if cfg.ConsoleSeparator == "" { - // Use a default delimiter of '\t' for backwards compatibility - cfg.ConsoleSeparator = "\t" - } - return consoleEncoder{newJSONEncoder(cfg, true)} -} - -func (c consoleEncoder) Clone() Encoder { - return consoleEncoder{c.jsonEncoder.Clone().(*jsonEncoder)} -} - -func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) { - line := bufferpool.Get() - - // We don't want the entry's metadata to be quoted and escaped (if it's - // encoded as strings), which means that we can't use the JSON encoder. The - // simplest option is to use the memory encoder and fmt.Fprint. - // - // If this ever becomes a performance bottleneck, we can implement - // ArrayEncoder for our plain-text format. - arr := getSliceEncoder() - if c.TimeKey != "" && c.EncodeTime != nil { - c.EncodeTime(ent.Time, arr) - } - if c.LevelKey != "" && c.EncodeLevel != nil { - c.EncodeLevel(ent.Level, arr) - } - if ent.LoggerName != "" && c.NameKey != "" { - nameEncoder := c.EncodeName - - if nameEncoder == nil { - // Fall back to FullNameEncoder for backward compatibility. - nameEncoder = FullNameEncoder - } - - nameEncoder(ent.LoggerName, arr) - } - if ent.Caller.Defined { - if c.CallerKey != "" && c.EncodeCaller != nil { - c.EncodeCaller(ent.Caller, arr) - } - if c.FunctionKey != "" { - arr.AppendString(ent.Caller.Function) - } - } - for i := range arr.elems { - if i > 0 { - line.AppendString(c.ConsoleSeparator) - } - fmt.Fprint(line, arr.elems[i]) - } - putSliceEncoder(arr) - - // Add the message itself. - if c.MessageKey != "" { - c.addSeparatorIfNecessary(line) - line.AppendString(ent.Message) - } - - // Add any structured context. - c.writeContext(line, fields) - - // If there's no stacktrace key, honor that; this allows users to force - // single-line output. - if ent.Stack != "" && c.StacktraceKey != "" { - line.AppendByte('\n') - line.AppendString(ent.Stack) - } - - if c.LineEnding != "" { - line.AppendString(c.LineEnding) - } else { - line.AppendString(DefaultLineEnding) - } - return line, nil -} - -func (c consoleEncoder) writeContext(line *buffer.Buffer, extra []Field) { - context := c.jsonEncoder.Clone().(*jsonEncoder) - defer func() { - // putJSONEncoder assumes the buffer is still used, but we write out the buffer so - // we can free it. - context.buf.Free() - putJSONEncoder(context) - }() - - addFields(context, extra) - context.closeOpenNamespaces() - if context.buf.Len() == 0 { - return - } - - c.addSeparatorIfNecessary(line) - line.AppendByte('{') - line.Write(context.buf.Bytes()) - line.AppendByte('}') -} - -func (c consoleEncoder) addSeparatorIfNecessary(line *buffer.Buffer) { - if line.Len() > 0 { - line.AppendString(c.ConsoleSeparator) - } -} diff --git a/vendor/go.uber.org/zap/zapcore/core.go b/vendor/go.uber.org/zap/zapcore/core.go deleted file mode 100644 index a1ef8b034b..0000000000 --- a/vendor/go.uber.org/zap/zapcore/core.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -// Core is a minimal, fast logger interface. It's designed for library authors -// to wrap in a more user-friendly API. -type Core interface { - LevelEnabler - - // With adds structured context to the Core. - With([]Field) Core - // Check determines whether the supplied Entry should be logged (using the - // embedded LevelEnabler and possibly some extra logic). If the entry - // should be logged, the Core adds itself to the CheckedEntry and returns - // the result. - // - // Callers must use Check before calling Write. - Check(Entry, *CheckedEntry) *CheckedEntry - // Write serializes the Entry and any Fields supplied at the log site and - // writes them to their destination. - // - // If called, Write should always log the Entry and Fields; it should not - // replicate the logic of Check. - Write(Entry, []Field) error - // Sync flushes buffered logs (if any). - Sync() error -} - -type nopCore struct{} - -// NewNopCore returns a no-op Core. -func NewNopCore() Core { return nopCore{} } -func (nopCore) Enabled(Level) bool { return false } -func (n nopCore) With([]Field) Core { return n } -func (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce } -func (nopCore) Write(Entry, []Field) error { return nil } -func (nopCore) Sync() error { return nil } - -// NewCore creates a Core that writes logs to a WriteSyncer. -func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core { - return &ioCore{ - LevelEnabler: enab, - enc: enc, - out: ws, - } -} - -type ioCore struct { - LevelEnabler - enc Encoder - out WriteSyncer -} - -func (c *ioCore) With(fields []Field) Core { - clone := c.clone() - addFields(clone.enc, fields) - return clone -} - -func (c *ioCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { - if c.Enabled(ent.Level) { - return ce.AddCore(ent, c) - } - return ce -} - -func (c *ioCore) Write(ent Entry, fields []Field) error { - buf, err := c.enc.EncodeEntry(ent, fields) - if err != nil { - return err - } - _, err = c.out.Write(buf.Bytes()) - buf.Free() - if err != nil { - return err - } - if ent.Level > ErrorLevel { - // Since we may be crashing the program, sync the output. Ignore Sync - // errors, pending a clean solution to issue #370. - c.Sync() - } - return nil -} - -func (c *ioCore) Sync() error { - return c.out.Sync() -} - -func (c *ioCore) clone() *ioCore { - return &ioCore{ - LevelEnabler: c.LevelEnabler, - enc: c.enc.Clone(), - out: c.out, - } -} diff --git a/vendor/go.uber.org/zap/zapcore/doc.go b/vendor/go.uber.org/zap/zapcore/doc.go deleted file mode 100644 index 31000e91f7..0000000000 --- a/vendor/go.uber.org/zap/zapcore/doc.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Package zapcore defines and implements the low-level interfaces upon which -// zap is built. By providing alternate implementations of these interfaces, -// external packages can extend zap's capabilities. -package zapcore // import "go.uber.org/zap/zapcore" diff --git a/vendor/go.uber.org/zap/zapcore/encoder.go b/vendor/go.uber.org/zap/zapcore/encoder.go deleted file mode 100644 index 6601ca166c..0000000000 --- a/vendor/go.uber.org/zap/zapcore/encoder.go +++ /dev/null @@ -1,443 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import ( - "encoding/json" - "time" - - "go.uber.org/zap/buffer" -) - -// DefaultLineEnding defines the default line ending when writing logs. -// Alternate line endings specified in EncoderConfig can override this -// behavior. -const DefaultLineEnding = "\n" - -// OmitKey defines the key to use when callers want to remove a key from log output. -const OmitKey = "" - -// A LevelEncoder serializes a Level to a primitive type. -type LevelEncoder func(Level, PrimitiveArrayEncoder) - -// LowercaseLevelEncoder serializes a Level to a lowercase string. For example, -// InfoLevel is serialized to "info". -func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) { - enc.AppendString(l.String()) -} - -// LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring. -// For example, InfoLevel is serialized to "info" and colored blue. -func LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) { - s, ok := _levelToLowercaseColorString[l] - if !ok { - s = _unknownLevelColor.Add(l.String()) - } - enc.AppendString(s) -} - -// CapitalLevelEncoder serializes a Level to an all-caps string. For example, -// InfoLevel is serialized to "INFO". -func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) { - enc.AppendString(l.CapitalString()) -} - -// CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color. -// For example, InfoLevel is serialized to "INFO" and colored blue. -func CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) { - s, ok := _levelToCapitalColorString[l] - if !ok { - s = _unknownLevelColor.Add(l.CapitalString()) - } - enc.AppendString(s) -} - -// UnmarshalText unmarshals text to a LevelEncoder. "capital" is unmarshaled to -// CapitalLevelEncoder, "coloredCapital" is unmarshaled to CapitalColorLevelEncoder, -// "colored" is unmarshaled to LowercaseColorLevelEncoder, and anything else -// is unmarshaled to LowercaseLevelEncoder. -func (e *LevelEncoder) UnmarshalText(text []byte) error { - switch string(text) { - case "capital": - *e = CapitalLevelEncoder - case "capitalColor": - *e = CapitalColorLevelEncoder - case "color": - *e = LowercaseColorLevelEncoder - default: - *e = LowercaseLevelEncoder - } - return nil -} - -// A TimeEncoder serializes a time.Time to a primitive type. -type TimeEncoder func(time.Time, PrimitiveArrayEncoder) - -// EpochTimeEncoder serializes a time.Time to a floating-point number of seconds -// since the Unix epoch. -func EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { - nanos := t.UnixNano() - sec := float64(nanos) / float64(time.Second) - enc.AppendFloat64(sec) -} - -// EpochMillisTimeEncoder serializes a time.Time to a floating-point number of -// milliseconds since the Unix epoch. -func EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { - nanos := t.UnixNano() - millis := float64(nanos) / float64(time.Millisecond) - enc.AppendFloat64(millis) -} - -// EpochNanosTimeEncoder serializes a time.Time to an integer number of -// nanoseconds since the Unix epoch. -func EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { - enc.AppendInt64(t.UnixNano()) -} - -func encodeTimeLayout(t time.Time, layout string, enc PrimitiveArrayEncoder) { - type appendTimeEncoder interface { - AppendTimeLayout(time.Time, string) - } - - if enc, ok := enc.(appendTimeEncoder); ok { - enc.AppendTimeLayout(t, layout) - return - } - - enc.AppendString(t.Format(layout)) -} - -// ISO8601TimeEncoder serializes a time.Time to an ISO8601-formatted string -// with millisecond precision. -// -// If enc supports AppendTimeLayout(t time.Time,layout string), it's used -// instead of appending a pre-formatted string value. -func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { - encodeTimeLayout(t, "2006-01-02T15:04:05.000Z0700", enc) -} - -// RFC3339TimeEncoder serializes a time.Time to an RFC3339-formatted string. -// -// If enc supports AppendTimeLayout(t time.Time,layout string), it's used -// instead of appending a pre-formatted string value. -func RFC3339TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { - encodeTimeLayout(t, time.RFC3339, enc) -} - -// RFC3339NanoTimeEncoder serializes a time.Time to an RFC3339-formatted string -// with nanosecond precision. -// -// If enc supports AppendTimeLayout(t time.Time,layout string), it's used -// instead of appending a pre-formatted string value. -func RFC3339NanoTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { - encodeTimeLayout(t, time.RFC3339Nano, enc) -} - -// TimeEncoderOfLayout returns TimeEncoder which serializes a time.Time using -// given layout. -func TimeEncoderOfLayout(layout string) TimeEncoder { - return func(t time.Time, enc PrimitiveArrayEncoder) { - encodeTimeLayout(t, layout, enc) - } -} - -// UnmarshalText unmarshals text to a TimeEncoder. -// "rfc3339nano" and "RFC3339Nano" are unmarshaled to RFC3339NanoTimeEncoder. -// "rfc3339" and "RFC3339" are unmarshaled to RFC3339TimeEncoder. -// "iso8601" and "ISO8601" are unmarshaled to ISO8601TimeEncoder. -// "millis" is unmarshaled to EpochMillisTimeEncoder. -// "nanos" is unmarshaled to EpochNanosEncoder. -// Anything else is unmarshaled to EpochTimeEncoder. -func (e *TimeEncoder) UnmarshalText(text []byte) error { - switch string(text) { - case "rfc3339nano", "RFC3339Nano": - *e = RFC3339NanoTimeEncoder - case "rfc3339", "RFC3339": - *e = RFC3339TimeEncoder - case "iso8601", "ISO8601": - *e = ISO8601TimeEncoder - case "millis": - *e = EpochMillisTimeEncoder - case "nanos": - *e = EpochNanosTimeEncoder - default: - *e = EpochTimeEncoder - } - return nil -} - -// UnmarshalYAML unmarshals YAML to a TimeEncoder. -// If value is an object with a "layout" field, it will be unmarshaled to TimeEncoder with given layout. -// timeEncoder: -// layout: 06/01/02 03:04pm -// If value is string, it uses UnmarshalText. -// timeEncoder: iso8601 -func (e *TimeEncoder) UnmarshalYAML(unmarshal func(interface{}) error) error { - var o struct { - Layout string `json:"layout" yaml:"layout"` - } - if err := unmarshal(&o); err == nil { - *e = TimeEncoderOfLayout(o.Layout) - return nil - } - - var s string - if err := unmarshal(&s); err != nil { - return err - } - return e.UnmarshalText([]byte(s)) -} - -// UnmarshalJSON unmarshals JSON to a TimeEncoder as same way UnmarshalYAML does. -func (e *TimeEncoder) UnmarshalJSON(data []byte) error { - return e.UnmarshalYAML(func(v interface{}) error { - return json.Unmarshal(data, v) - }) -} - -// A DurationEncoder serializes a time.Duration to a primitive type. -type DurationEncoder func(time.Duration, PrimitiveArrayEncoder) - -// SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed. -func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { - enc.AppendFloat64(float64(d) / float64(time.Second)) -} - -// NanosDurationEncoder serializes a time.Duration to an integer number of -// nanoseconds elapsed. -func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { - enc.AppendInt64(int64(d)) -} - -// MillisDurationEncoder serializes a time.Duration to an integer number of -// milliseconds elapsed. -func MillisDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { - enc.AppendInt64(d.Nanoseconds() / 1e6) -} - -// StringDurationEncoder serializes a time.Duration using its built-in String -// method. -func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { - enc.AppendString(d.String()) -} - -// UnmarshalText unmarshals text to a DurationEncoder. "string" is unmarshaled -// to StringDurationEncoder, and anything else is unmarshaled to -// NanosDurationEncoder. -func (e *DurationEncoder) UnmarshalText(text []byte) error { - switch string(text) { - case "string": - *e = StringDurationEncoder - case "nanos": - *e = NanosDurationEncoder - case "ms": - *e = MillisDurationEncoder - default: - *e = SecondsDurationEncoder - } - return nil -} - -// A CallerEncoder serializes an EntryCaller to a primitive type. -type CallerEncoder func(EntryCaller, PrimitiveArrayEncoder) - -// FullCallerEncoder serializes a caller in /full/path/to/package/file:line -// format. -func FullCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) { - // TODO: consider using a byte-oriented API to save an allocation. - enc.AppendString(caller.String()) -} - -// ShortCallerEncoder serializes a caller in package/file:line format, trimming -// all but the final directory from the full path. -func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) { - // TODO: consider using a byte-oriented API to save an allocation. - enc.AppendString(caller.TrimmedPath()) -} - -// UnmarshalText unmarshals text to a CallerEncoder. "full" is unmarshaled to -// FullCallerEncoder and anything else is unmarshaled to ShortCallerEncoder. -func (e *CallerEncoder) UnmarshalText(text []byte) error { - switch string(text) { - case "full": - *e = FullCallerEncoder - default: - *e = ShortCallerEncoder - } - return nil -} - -// A NameEncoder serializes a period-separated logger name to a primitive -// type. -type NameEncoder func(string, PrimitiveArrayEncoder) - -// FullNameEncoder serializes the logger name as-is. -func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { - enc.AppendString(loggerName) -} - -// UnmarshalText unmarshals text to a NameEncoder. Currently, everything is -// unmarshaled to FullNameEncoder. -func (e *NameEncoder) UnmarshalText(text []byte) error { - switch string(text) { - case "full": - *e = FullNameEncoder - default: - *e = FullNameEncoder - } - return nil -} - -// An EncoderConfig allows users to configure the concrete encoders supplied by -// zapcore. -type EncoderConfig struct { - // Set the keys used for each log entry. If any key is empty, that portion - // of the entry is omitted. - MessageKey string `json:"messageKey" yaml:"messageKey"` - LevelKey string `json:"levelKey" yaml:"levelKey"` - TimeKey string `json:"timeKey" yaml:"timeKey"` - NameKey string `json:"nameKey" yaml:"nameKey"` - CallerKey string `json:"callerKey" yaml:"callerKey"` - FunctionKey string `json:"functionKey" yaml:"functionKey"` - StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"` - LineEnding string `json:"lineEnding" yaml:"lineEnding"` - // Configure the primitive representations of common complex types. For - // example, some users may want all time.Times serialized as floating-point - // seconds since epoch, while others may prefer ISO8601 strings. - EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"` - EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"` - EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"` - EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"` - // Unlike the other primitive type encoders, EncodeName is optional. The - // zero value falls back to FullNameEncoder. - EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"` - // Configures the field separator used by the console encoder. Defaults - // to tab. - ConsoleSeparator string `json:"consoleSeparator" yaml:"consoleSeparator"` -} - -// ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a -// map- or struct-like object to the logging context. Like maps, ObjectEncoders -// aren't safe for concurrent use (though typical use shouldn't require locks). -type ObjectEncoder interface { - // Logging-specific marshalers. - AddArray(key string, marshaler ArrayMarshaler) error - AddObject(key string, marshaler ObjectMarshaler) error - - // Built-in types. - AddBinary(key string, value []byte) // for arbitrary bytes - AddByteString(key string, value []byte) // for UTF-8 encoded bytes - AddBool(key string, value bool) - AddComplex128(key string, value complex128) - AddComplex64(key string, value complex64) - AddDuration(key string, value time.Duration) - AddFloat64(key string, value float64) - AddFloat32(key string, value float32) - AddInt(key string, value int) - AddInt64(key string, value int64) - AddInt32(key string, value int32) - AddInt16(key string, value int16) - AddInt8(key string, value int8) - AddString(key, value string) - AddTime(key string, value time.Time) - AddUint(key string, value uint) - AddUint64(key string, value uint64) - AddUint32(key string, value uint32) - AddUint16(key string, value uint16) - AddUint8(key string, value uint8) - AddUintptr(key string, value uintptr) - - // AddReflected uses reflection to serialize arbitrary objects, so it can be - // slow and allocation-heavy. - AddReflected(key string, value interface{}) error - // OpenNamespace opens an isolated namespace where all subsequent fields will - // be added. Applications can use namespaces to prevent key collisions when - // injecting loggers into sub-components or third-party libraries. - OpenNamespace(key string) -} - -// ArrayEncoder is a strongly-typed, encoding-agnostic interface for adding -// array-like objects to the logging context. Of note, it supports mixed-type -// arrays even though they aren't typical in Go. Like slices, ArrayEncoders -// aren't safe for concurrent use (though typical use shouldn't require locks). -type ArrayEncoder interface { - // Built-in types. - PrimitiveArrayEncoder - - // Time-related types. - AppendDuration(time.Duration) - AppendTime(time.Time) - - // Logging-specific marshalers. - AppendArray(ArrayMarshaler) error - AppendObject(ObjectMarshaler) error - - // AppendReflected uses reflection to serialize arbitrary objects, so it's - // slow and allocation-heavy. - AppendReflected(value interface{}) error -} - -// PrimitiveArrayEncoder is the subset of the ArrayEncoder interface that deals -// only in Go's built-in types. It's included only so that Duration- and -// TimeEncoders cannot trigger infinite recursion. -type PrimitiveArrayEncoder interface { - // Built-in types. - AppendBool(bool) - AppendByteString([]byte) // for UTF-8 encoded bytes - AppendComplex128(complex128) - AppendComplex64(complex64) - AppendFloat64(float64) - AppendFloat32(float32) - AppendInt(int) - AppendInt64(int64) - AppendInt32(int32) - AppendInt16(int16) - AppendInt8(int8) - AppendString(string) - AppendUint(uint) - AppendUint64(uint64) - AppendUint32(uint32) - AppendUint16(uint16) - AppendUint8(uint8) - AppendUintptr(uintptr) -} - -// Encoder is a format-agnostic interface for all log entry marshalers. Since -// log encoders don't need to support the same wide range of use cases as -// general-purpose marshalers, it's possible to make them faster and -// lower-allocation. -// -// Implementations of the ObjectEncoder interface's methods can, of course, -// freely modify the receiver. However, the Clone and EncodeEntry methods will -// be called concurrently and shouldn't modify the receiver. -type Encoder interface { - ObjectEncoder - - // Clone copies the encoder, ensuring that adding fields to the copy doesn't - // affect the original. - Clone() Encoder - - // EncodeEntry encodes an entry and fields, along with any accumulated - // context, into a byte buffer and returns it. Any fields that are empty, - // including fields on the `Entry` type, should be omitted. - EncodeEntry(Entry, []Field) (*buffer.Buffer, error) -} diff --git a/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go deleted file mode 100644 index 4aa8b4f90b..0000000000 --- a/vendor/go.uber.org/zap/zapcore/entry.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import ( - "fmt" - "runtime" - "strings" - "sync" - "time" - - "go.uber.org/zap/internal/bufferpool" - "go.uber.org/zap/internal/exit" - - "go.uber.org/multierr" -) - -var ( - _cePool = sync.Pool{New: func() interface{} { - // Pre-allocate some space for cores. - return &CheckedEntry{ - cores: make([]Core, 4), - } - }} -) - -func getCheckedEntry() *CheckedEntry { - ce := _cePool.Get().(*CheckedEntry) - ce.reset() - return ce -} - -func putCheckedEntry(ce *CheckedEntry) { - if ce == nil { - return - } - _cePool.Put(ce) -} - -// NewEntryCaller makes an EntryCaller from the return signature of -// runtime.Caller. -func NewEntryCaller(pc uintptr, file string, line int, ok bool) EntryCaller { - if !ok { - return EntryCaller{} - } - return EntryCaller{ - PC: pc, - File: file, - Line: line, - Defined: true, - } -} - -// EntryCaller represents the caller of a logging function. -type EntryCaller struct { - Defined bool - PC uintptr - File string - Line int - Function string -} - -// String returns the full path and line number of the caller. -func (ec EntryCaller) String() string { - return ec.FullPath() -} - -// FullPath returns a /full/path/to/package/file:line description of the -// caller. -func (ec EntryCaller) FullPath() string { - if !ec.Defined { - return "undefined" - } - buf := bufferpool.Get() - buf.AppendString(ec.File) - buf.AppendByte(':') - buf.AppendInt(int64(ec.Line)) - caller := buf.String() - buf.Free() - return caller -} - -// TrimmedPath returns a package/file:line description of the caller, -// preserving only the leaf directory name and file name. -func (ec EntryCaller) TrimmedPath() string { - if !ec.Defined { - return "undefined" - } - // nb. To make sure we trim the path correctly on Windows too, we - // counter-intuitively need to use '/' and *not* os.PathSeparator here, - // because the path given originates from Go stdlib, specifically - // runtime.Caller() which (as of Mar/17) returns forward slashes even on - // Windows. - // - // See https://github.com/golang/go/issues/3335 - // and https://github.com/golang/go/issues/18151 - // - // for discussion on the issue on Go side. - // - // Find the last separator. - // - idx := strings.LastIndexByte(ec.File, '/') - if idx == -1 { - return ec.FullPath() - } - // Find the penultimate separator. - idx = strings.LastIndexByte(ec.File[:idx], '/') - if idx == -1 { - return ec.FullPath() - } - buf := bufferpool.Get() - // Keep everything after the penultimate separator. - buf.AppendString(ec.File[idx+1:]) - buf.AppendByte(':') - buf.AppendInt(int64(ec.Line)) - caller := buf.String() - buf.Free() - return caller -} - -// An Entry represents a complete log message. The entry's structured context -// is already serialized, but the log level, time, message, and call site -// information are available for inspection and modification. Any fields left -// empty will be omitted when encoding. -// -// Entries are pooled, so any functions that accept them MUST be careful not to -// retain references to them. -type Entry struct { - Level Level - Time time.Time - LoggerName string - Message string - Caller EntryCaller - Stack string -} - -// CheckWriteAction indicates what action to take after a log entry is -// processed. Actions are ordered in increasing severity. -type CheckWriteAction uint8 - -const ( - // WriteThenNoop indicates that nothing special needs to be done. It's the - // default behavior. - WriteThenNoop CheckWriteAction = iota - // WriteThenGoexit runs runtime.Goexit after Write. - WriteThenGoexit - // WriteThenPanic causes a panic after Write. - WriteThenPanic - // WriteThenFatal causes a fatal os.Exit after Write. - WriteThenFatal -) - -// CheckedEntry is an Entry together with a collection of Cores that have -// already agreed to log it. -// -// CheckedEntry references should be created by calling AddCore or Should on a -// nil *CheckedEntry. References are returned to a pool after Write, and MUST -// NOT be retained after calling their Write method. -type CheckedEntry struct { - Entry - ErrorOutput WriteSyncer - dirty bool // best-effort detection of pool misuse - should CheckWriteAction - cores []Core -} - -func (ce *CheckedEntry) reset() { - ce.Entry = Entry{} - ce.ErrorOutput = nil - ce.dirty = false - ce.should = WriteThenNoop - for i := range ce.cores { - // don't keep references to cores - ce.cores[i] = nil - } - ce.cores = ce.cores[:0] -} - -// Write writes the entry to the stored Cores, returns any errors, and returns -// the CheckedEntry reference to a pool for immediate re-use. Finally, it -// executes any required CheckWriteAction. -func (ce *CheckedEntry) Write(fields ...Field) { - if ce == nil { - return - } - - if ce.dirty { - if ce.ErrorOutput != nil { - // Make a best effort to detect unsafe re-use of this CheckedEntry. - // If the entry is dirty, log an internal error; because the - // CheckedEntry is being used after it was returned to the pool, - // the message may be an amalgamation from multiple call sites. - fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", time.Now(), ce.Entry) - ce.ErrorOutput.Sync() - } - return - } - ce.dirty = true - - var err error - for i := range ce.cores { - err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields)) - } - if ce.ErrorOutput != nil { - if err != nil { - fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", time.Now(), err) - ce.ErrorOutput.Sync() - } - } - - should, msg := ce.should, ce.Message - putCheckedEntry(ce) - - switch should { - case WriteThenPanic: - panic(msg) - case WriteThenFatal: - exit.Exit() - case WriteThenGoexit: - runtime.Goexit() - } -} - -// AddCore adds a Core that has agreed to log this CheckedEntry. It's intended to be -// used by Core.Check implementations, and is safe to call on nil CheckedEntry -// references. -func (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry { - if ce == nil { - ce = getCheckedEntry() - ce.Entry = ent - } - ce.cores = append(ce.cores, core) - return ce -} - -// Should sets this CheckedEntry's CheckWriteAction, which controls whether a -// Core will panic or fatal after writing this log entry. Like AddCore, it's -// safe to call on nil CheckedEntry references. -func (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry { - if ce == nil { - ce = getCheckedEntry() - ce.Entry = ent - } - ce.should = should - return ce -} diff --git a/vendor/go.uber.org/zap/zapcore/error.go b/vendor/go.uber.org/zap/zapcore/error.go deleted file mode 100644 index f2a07d7864..0000000000 --- a/vendor/go.uber.org/zap/zapcore/error.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import ( - "fmt" - "reflect" - "sync" -) - -// Encodes the given error into fields of an object. A field with the given -// name is added for the error message. -// -// If the error implements fmt.Formatter, a field with the name ${key}Verbose -// is also added with the full verbose error message. -// -// Finally, if the error implements errorGroup (from go.uber.org/multierr) or -// causer (from github.com/pkg/errors), a ${key}Causes field is added with an -// array of objects containing the errors this error was comprised of. -// -// { -// "error": err.Error(), -// "errorVerbose": fmt.Sprintf("%+v", err), -// "errorCauses": [ -// ... -// ], -// } -func encodeError(key string, err error, enc ObjectEncoder) (retErr error) { - // Try to capture panics (from nil references or otherwise) when calling - // the Error() method - defer func() { - if rerr := recover(); rerr != nil { - // If it's a nil pointer, just say "". The likeliest causes are a - // error that fails to guard against nil or a nil pointer for a - // value receiver, and in either case, "" is a nice result. - if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { - enc.AddString(key, "") - return - } - - retErr = fmt.Errorf("PANIC=%v", rerr) - } - }() - - basic := err.Error() - enc.AddString(key, basic) - - switch e := err.(type) { - case errorGroup: - return enc.AddArray(key+"Causes", errArray(e.Errors())) - case fmt.Formatter: - verbose := fmt.Sprintf("%+v", e) - if verbose != basic { - // This is a rich error type, like those produced by - // github.com/pkg/errors. - enc.AddString(key+"Verbose", verbose) - } - } - return nil -} - -type errorGroup interface { - // Provides read-only access to the underlying list of errors, preferably - // without causing any allocs. - Errors() []error -} - -// Note that errArry and errArrayElem are very similar to the version -// implemented in the top-level error.go file. We can't re-use this because -// that would require exporting errArray as part of the zapcore API. - -// Encodes a list of errors using the standard error encoding logic. -type errArray []error - -func (errs errArray) MarshalLogArray(arr ArrayEncoder) error { - for i := range errs { - if errs[i] == nil { - continue - } - - el := newErrArrayElem(errs[i]) - arr.AppendObject(el) - el.Free() - } - return nil -} - -var _errArrayElemPool = sync.Pool{New: func() interface{} { - return &errArrayElem{} -}} - -// Encodes any error into a {"error": ...} re-using the same errors logic. -// -// May be passed in place of an array to build a single-element array. -type errArrayElem struct{ err error } - -func newErrArrayElem(err error) *errArrayElem { - e := _errArrayElemPool.Get().(*errArrayElem) - e.err = err - return e -} - -func (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error { - return arr.AppendObject(e) -} - -func (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error { - return encodeError("error", e.err, enc) -} - -func (e *errArrayElem) Free() { - e.err = nil - _errArrayElemPool.Put(e) -} diff --git a/vendor/go.uber.org/zap/zapcore/field.go b/vendor/go.uber.org/zap/zapcore/field.go deleted file mode 100644 index 95bdb0a126..0000000000 --- a/vendor/go.uber.org/zap/zapcore/field.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import ( - "bytes" - "fmt" - "math" - "reflect" - "time" -) - -// A FieldType indicates which member of the Field union struct should be used -// and how it should be serialized. -type FieldType uint8 - -const ( - // UnknownType is the default field type. Attempting to add it to an encoder will panic. - UnknownType FieldType = iota - // ArrayMarshalerType indicates that the field carries an ArrayMarshaler. - ArrayMarshalerType - // ObjectMarshalerType indicates that the field carries an ObjectMarshaler. - ObjectMarshalerType - // BinaryType indicates that the field carries an opaque binary blob. - BinaryType - // BoolType indicates that the field carries a bool. - BoolType - // ByteStringType indicates that the field carries UTF-8 encoded bytes. - ByteStringType - // Complex128Type indicates that the field carries a complex128. - Complex128Type - // Complex64Type indicates that the field carries a complex128. - Complex64Type - // DurationType indicates that the field carries a time.Duration. - DurationType - // Float64Type indicates that the field carries a float64. - Float64Type - // Float32Type indicates that the field carries a float32. - Float32Type - // Int64Type indicates that the field carries an int64. - Int64Type - // Int32Type indicates that the field carries an int32. - Int32Type - // Int16Type indicates that the field carries an int16. - Int16Type - // Int8Type indicates that the field carries an int8. - Int8Type - // StringType indicates that the field carries a string. - StringType - // TimeType indicates that the field carries a time.Time that is - // representable by a UnixNano() stored as an int64. - TimeType - // TimeFullType indicates that the field carries a time.Time stored as-is. - TimeFullType - // Uint64Type indicates that the field carries a uint64. - Uint64Type - // Uint32Type indicates that the field carries a uint32. - Uint32Type - // Uint16Type indicates that the field carries a uint16. - Uint16Type - // Uint8Type indicates that the field carries a uint8. - Uint8Type - // UintptrType indicates that the field carries a uintptr. - UintptrType - // ReflectType indicates that the field carries an interface{}, which should - // be serialized using reflection. - ReflectType - // NamespaceType signals the beginning of an isolated namespace. All - // subsequent fields should be added to the new namespace. - NamespaceType - // StringerType indicates that the field carries a fmt.Stringer. - StringerType - // ErrorType indicates that the field carries an error. - ErrorType - // SkipType indicates that the field is a no-op. - SkipType - - // InlineMarshalerType indicates that the field carries an ObjectMarshaler - // that should be inlined. - InlineMarshalerType -) - -// A Field is a marshaling operation used to add a key-value pair to a logger's -// context. Most fields are lazily marshaled, so it's inexpensive to add fields -// to disabled debug-level log statements. -type Field struct { - Key string - Type FieldType - Integer int64 - String string - Interface interface{} -} - -// AddTo exports a field through the ObjectEncoder interface. It's primarily -// useful to library authors, and shouldn't be necessary in most applications. -func (f Field) AddTo(enc ObjectEncoder) { - var err error - - switch f.Type { - case ArrayMarshalerType: - err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler)) - case ObjectMarshalerType: - err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler)) - case InlineMarshalerType: - err = f.Interface.(ObjectMarshaler).MarshalLogObject(enc) - case BinaryType: - enc.AddBinary(f.Key, f.Interface.([]byte)) - case BoolType: - enc.AddBool(f.Key, f.Integer == 1) - case ByteStringType: - enc.AddByteString(f.Key, f.Interface.([]byte)) - case Complex128Type: - enc.AddComplex128(f.Key, f.Interface.(complex128)) - case Complex64Type: - enc.AddComplex64(f.Key, f.Interface.(complex64)) - case DurationType: - enc.AddDuration(f.Key, time.Duration(f.Integer)) - case Float64Type: - enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer))) - case Float32Type: - enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer))) - case Int64Type: - enc.AddInt64(f.Key, f.Integer) - case Int32Type: - enc.AddInt32(f.Key, int32(f.Integer)) - case Int16Type: - enc.AddInt16(f.Key, int16(f.Integer)) - case Int8Type: - enc.AddInt8(f.Key, int8(f.Integer)) - case StringType: - enc.AddString(f.Key, f.String) - case TimeType: - if f.Interface != nil { - enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location))) - } else { - // Fall back to UTC if location is nil. - enc.AddTime(f.Key, time.Unix(0, f.Integer)) - } - case TimeFullType: - enc.AddTime(f.Key, f.Interface.(time.Time)) - case Uint64Type: - enc.AddUint64(f.Key, uint64(f.Integer)) - case Uint32Type: - enc.AddUint32(f.Key, uint32(f.Integer)) - case Uint16Type: - enc.AddUint16(f.Key, uint16(f.Integer)) - case Uint8Type: - enc.AddUint8(f.Key, uint8(f.Integer)) - case UintptrType: - enc.AddUintptr(f.Key, uintptr(f.Integer)) - case ReflectType: - err = enc.AddReflected(f.Key, f.Interface) - case NamespaceType: - enc.OpenNamespace(f.Key) - case StringerType: - err = encodeStringer(f.Key, f.Interface, enc) - case ErrorType: - err = encodeError(f.Key, f.Interface.(error), enc) - case SkipType: - break - default: - panic(fmt.Sprintf("unknown field type: %v", f)) - } - - if err != nil { - enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error()) - } -} - -// Equals returns whether two fields are equal. For non-primitive types such as -// errors, marshalers, or reflect types, it uses reflect.DeepEqual. -func (f Field) Equals(other Field) bool { - if f.Type != other.Type { - return false - } - if f.Key != other.Key { - return false - } - - switch f.Type { - case BinaryType, ByteStringType: - return bytes.Equal(f.Interface.([]byte), other.Interface.([]byte)) - case ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType: - return reflect.DeepEqual(f.Interface, other.Interface) - default: - return f == other - } -} - -func addFields(enc ObjectEncoder, fields []Field) { - for i := range fields { - fields[i].AddTo(enc) - } -} - -func encodeStringer(key string, stringer interface{}, enc ObjectEncoder) (retErr error) { - // Try to capture panics (from nil references or otherwise) when calling - // the String() method, similar to https://golang.org/src/fmt/print.go#L540 - defer func() { - if err := recover(); err != nil { - // If it's a nil pointer, just say "". The likeliest causes are a - // Stringer that fails to guard against nil or a nil pointer for a - // value receiver, and in either case, "" is a nice result. - if v := reflect.ValueOf(stringer); v.Kind() == reflect.Ptr && v.IsNil() { - enc.AddString(key, "") - return - } - - retErr = fmt.Errorf("PANIC=%v", err) - } - }() - - enc.AddString(key, stringer.(fmt.Stringer).String()) - return nil -} diff --git a/vendor/go.uber.org/zap/zapcore/hook.go b/vendor/go.uber.org/zap/zapcore/hook.go deleted file mode 100644 index 5db4afb302..0000000000 --- a/vendor/go.uber.org/zap/zapcore/hook.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import "go.uber.org/multierr" - -type hooked struct { - Core - funcs []func(Entry) error -} - -// RegisterHooks wraps a Core and runs a collection of user-defined callback -// hooks each time a message is logged. Execution of the callbacks is blocking. -// -// This offers users an easy way to register simple callbacks (e.g., metrics -// collection) without implementing the full Core interface. -func RegisterHooks(core Core, hooks ...func(Entry) error) Core { - funcs := append([]func(Entry) error{}, hooks...) - return &hooked{ - Core: core, - funcs: funcs, - } -} - -func (h *hooked) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { - // Let the wrapped Core decide whether to log this message or not. This - // also gives the downstream a chance to register itself directly with the - // CheckedEntry. - if downstream := h.Core.Check(ent, ce); downstream != nil { - return downstream.AddCore(ent, h) - } - return ce -} - -func (h *hooked) With(fields []Field) Core { - return &hooked{ - Core: h.Core.With(fields), - funcs: h.funcs, - } -} - -func (h *hooked) Write(ent Entry, _ []Field) error { - // Since our downstream had a chance to register itself directly with the - // CheckedMessage, we don't need to call it here. - var err error - for i := range h.funcs { - err = multierr.Append(err, h.funcs[i](ent)) - } - return err -} diff --git a/vendor/go.uber.org/zap/zapcore/increase_level.go b/vendor/go.uber.org/zap/zapcore/increase_level.go deleted file mode 100644 index 5a1749261a..0000000000 --- a/vendor/go.uber.org/zap/zapcore/increase_level.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2020 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import "fmt" - -type levelFilterCore struct { - core Core - level LevelEnabler -} - -// NewIncreaseLevelCore creates a core that can be used to increase the level of -// an existing Core. It cannot be used to decrease the logging level, as it acts -// as a filter before calling the underlying core. If level decreases the log level, -// an error is returned. -func NewIncreaseLevelCore(core Core, level LevelEnabler) (Core, error) { - for l := _maxLevel; l >= _minLevel; l-- { - if !core.Enabled(l) && level.Enabled(l) { - return nil, fmt.Errorf("invalid increase level, as level %q is allowed by increased level, but not by existing core", l) - } - } - - return &levelFilterCore{core, level}, nil -} - -func (c *levelFilterCore) Enabled(lvl Level) bool { - return c.level.Enabled(lvl) -} - -func (c *levelFilterCore) With(fields []Field) Core { - return &levelFilterCore{c.core.With(fields), c.level} -} - -func (c *levelFilterCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { - if !c.Enabled(ent.Level) { - return ce - } - - return c.core.Check(ent, ce) -} - -func (c *levelFilterCore) Write(ent Entry, fields []Field) error { - return c.core.Write(ent, fields) -} - -func (c *levelFilterCore) Sync() error { - return c.core.Sync() -} diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder.go b/vendor/go.uber.org/zap/zapcore/json_encoder.go deleted file mode 100644 index 5cf7d917e9..0000000000 --- a/vendor/go.uber.org/zap/zapcore/json_encoder.go +++ /dev/null @@ -1,534 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import ( - "encoding/base64" - "encoding/json" - "math" - "sync" - "time" - "unicode/utf8" - - "go.uber.org/zap/buffer" - "go.uber.org/zap/internal/bufferpool" -) - -// For JSON-escaping; see jsonEncoder.safeAddString below. -const _hex = "0123456789abcdef" - -var _jsonPool = sync.Pool{New: func() interface{} { - return &jsonEncoder{} -}} - -func getJSONEncoder() *jsonEncoder { - return _jsonPool.Get().(*jsonEncoder) -} - -func putJSONEncoder(enc *jsonEncoder) { - if enc.reflectBuf != nil { - enc.reflectBuf.Free() - } - enc.EncoderConfig = nil - enc.buf = nil - enc.spaced = false - enc.openNamespaces = 0 - enc.reflectBuf = nil - enc.reflectEnc = nil - _jsonPool.Put(enc) -} - -type jsonEncoder struct { - *EncoderConfig - buf *buffer.Buffer - spaced bool // include spaces after colons and commas - openNamespaces int - - // for encoding generic values by reflection - reflectBuf *buffer.Buffer - reflectEnc *json.Encoder -} - -// NewJSONEncoder creates a fast, low-allocation JSON encoder. The encoder -// appropriately escapes all field keys and values. -// -// Note that the encoder doesn't deduplicate keys, so it's possible to produce -// a message like -// {"foo":"bar","foo":"baz"} -// This is permitted by the JSON specification, but not encouraged. Many -// libraries will ignore duplicate key-value pairs (typically keeping the last -// pair) when unmarshaling, but users should attempt to avoid adding duplicate -// keys. -func NewJSONEncoder(cfg EncoderConfig) Encoder { - return newJSONEncoder(cfg, false) -} - -func newJSONEncoder(cfg EncoderConfig, spaced bool) *jsonEncoder { - return &jsonEncoder{ - EncoderConfig: &cfg, - buf: bufferpool.Get(), - spaced: spaced, - } -} - -func (enc *jsonEncoder) AddArray(key string, arr ArrayMarshaler) error { - enc.addKey(key) - return enc.AppendArray(arr) -} - -func (enc *jsonEncoder) AddObject(key string, obj ObjectMarshaler) error { - enc.addKey(key) - return enc.AppendObject(obj) -} - -func (enc *jsonEncoder) AddBinary(key string, val []byte) { - enc.AddString(key, base64.StdEncoding.EncodeToString(val)) -} - -func (enc *jsonEncoder) AddByteString(key string, val []byte) { - enc.addKey(key) - enc.AppendByteString(val) -} - -func (enc *jsonEncoder) AddBool(key string, val bool) { - enc.addKey(key) - enc.AppendBool(val) -} - -func (enc *jsonEncoder) AddComplex128(key string, val complex128) { - enc.addKey(key) - enc.AppendComplex128(val) -} - -func (enc *jsonEncoder) AddDuration(key string, val time.Duration) { - enc.addKey(key) - enc.AppendDuration(val) -} - -func (enc *jsonEncoder) AddFloat64(key string, val float64) { - enc.addKey(key) - enc.AppendFloat64(val) -} - -func (enc *jsonEncoder) AddInt64(key string, val int64) { - enc.addKey(key) - enc.AppendInt64(val) -} - -func (enc *jsonEncoder) resetReflectBuf() { - if enc.reflectBuf == nil { - enc.reflectBuf = bufferpool.Get() - enc.reflectEnc = json.NewEncoder(enc.reflectBuf) - - // For consistency with our custom JSON encoder. - enc.reflectEnc.SetEscapeHTML(false) - } else { - enc.reflectBuf.Reset() - } -} - -var nullLiteralBytes = []byte("null") - -// Only invoke the standard JSON encoder if there is actually something to -// encode; otherwise write JSON null literal directly. -func (enc *jsonEncoder) encodeReflected(obj interface{}) ([]byte, error) { - if obj == nil { - return nullLiteralBytes, nil - } - enc.resetReflectBuf() - if err := enc.reflectEnc.Encode(obj); err != nil { - return nil, err - } - enc.reflectBuf.TrimNewline() - return enc.reflectBuf.Bytes(), nil -} - -func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error { - valueBytes, err := enc.encodeReflected(obj) - if err != nil { - return err - } - enc.addKey(key) - _, err = enc.buf.Write(valueBytes) - return err -} - -func (enc *jsonEncoder) OpenNamespace(key string) { - enc.addKey(key) - enc.buf.AppendByte('{') - enc.openNamespaces++ -} - -func (enc *jsonEncoder) AddString(key, val string) { - enc.addKey(key) - enc.AppendString(val) -} - -func (enc *jsonEncoder) AddTime(key string, val time.Time) { - enc.addKey(key) - enc.AppendTime(val) -} - -func (enc *jsonEncoder) AddUint64(key string, val uint64) { - enc.addKey(key) - enc.AppendUint64(val) -} - -func (enc *jsonEncoder) AppendArray(arr ArrayMarshaler) error { - enc.addElementSeparator() - enc.buf.AppendByte('[') - err := arr.MarshalLogArray(enc) - enc.buf.AppendByte(']') - return err -} - -func (enc *jsonEncoder) AppendObject(obj ObjectMarshaler) error { - enc.addElementSeparator() - enc.buf.AppendByte('{') - err := obj.MarshalLogObject(enc) - enc.buf.AppendByte('}') - return err -} - -func (enc *jsonEncoder) AppendBool(val bool) { - enc.addElementSeparator() - enc.buf.AppendBool(val) -} - -func (enc *jsonEncoder) AppendByteString(val []byte) { - enc.addElementSeparator() - enc.buf.AppendByte('"') - enc.safeAddByteString(val) - enc.buf.AppendByte('"') -} - -func (enc *jsonEncoder) AppendComplex128(val complex128) { - enc.addElementSeparator() - // Cast to a platform-independent, fixed-size type. - r, i := float64(real(val)), float64(imag(val)) - enc.buf.AppendByte('"') - // Because we're always in a quoted string, we can use strconv without - // special-casing NaN and +/-Inf. - enc.buf.AppendFloat(r, 64) - enc.buf.AppendByte('+') - enc.buf.AppendFloat(i, 64) - enc.buf.AppendByte('i') - enc.buf.AppendByte('"') -} - -func (enc *jsonEncoder) AppendDuration(val time.Duration) { - cur := enc.buf.Len() - if e := enc.EncodeDuration; e != nil { - e(val, enc) - } - if cur == enc.buf.Len() { - // User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep - // JSON valid. - enc.AppendInt64(int64(val)) - } -} - -func (enc *jsonEncoder) AppendInt64(val int64) { - enc.addElementSeparator() - enc.buf.AppendInt(val) -} - -func (enc *jsonEncoder) AppendReflected(val interface{}) error { - valueBytes, err := enc.encodeReflected(val) - if err != nil { - return err - } - enc.addElementSeparator() - _, err = enc.buf.Write(valueBytes) - return err -} - -func (enc *jsonEncoder) AppendString(val string) { - enc.addElementSeparator() - enc.buf.AppendByte('"') - enc.safeAddString(val) - enc.buf.AppendByte('"') -} - -func (enc *jsonEncoder) AppendTimeLayout(time time.Time, layout string) { - enc.addElementSeparator() - enc.buf.AppendByte('"') - enc.buf.AppendTime(time, layout) - enc.buf.AppendByte('"') -} - -func (enc *jsonEncoder) AppendTime(val time.Time) { - cur := enc.buf.Len() - if e := enc.EncodeTime; e != nil { - e(val, enc) - } - if cur == enc.buf.Len() { - // User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep - // output JSON valid. - enc.AppendInt64(val.UnixNano()) - } -} - -func (enc *jsonEncoder) AppendUint64(val uint64) { - enc.addElementSeparator() - enc.buf.AppendUint(val) -} - -func (enc *jsonEncoder) AddComplex64(k string, v complex64) { enc.AddComplex128(k, complex128(v)) } -func (enc *jsonEncoder) AddFloat32(k string, v float32) { enc.AddFloat64(k, float64(v)) } -func (enc *jsonEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) } -func (enc *jsonEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) } -func (enc *jsonEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) } -func (enc *jsonEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) } -func (enc *jsonEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) } -func (enc *jsonEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) } -func (enc *jsonEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) } -func (enc *jsonEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) } -func (enc *jsonEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) } -func (enc *jsonEncoder) AppendComplex64(v complex64) { enc.AppendComplex128(complex128(v)) } -func (enc *jsonEncoder) AppendFloat64(v float64) { enc.appendFloat(v, 64) } -func (enc *jsonEncoder) AppendFloat32(v float32) { enc.appendFloat(float64(v), 32) } -func (enc *jsonEncoder) AppendInt(v int) { enc.AppendInt64(int64(v)) } -func (enc *jsonEncoder) AppendInt32(v int32) { enc.AppendInt64(int64(v)) } -func (enc *jsonEncoder) AppendInt16(v int16) { enc.AppendInt64(int64(v)) } -func (enc *jsonEncoder) AppendInt8(v int8) { enc.AppendInt64(int64(v)) } -func (enc *jsonEncoder) AppendUint(v uint) { enc.AppendUint64(uint64(v)) } -func (enc *jsonEncoder) AppendUint32(v uint32) { enc.AppendUint64(uint64(v)) } -func (enc *jsonEncoder) AppendUint16(v uint16) { enc.AppendUint64(uint64(v)) } -func (enc *jsonEncoder) AppendUint8(v uint8) { enc.AppendUint64(uint64(v)) } -func (enc *jsonEncoder) AppendUintptr(v uintptr) { enc.AppendUint64(uint64(v)) } - -func (enc *jsonEncoder) Clone() Encoder { - clone := enc.clone() - clone.buf.Write(enc.buf.Bytes()) - return clone -} - -func (enc *jsonEncoder) clone() *jsonEncoder { - clone := getJSONEncoder() - clone.EncoderConfig = enc.EncoderConfig - clone.spaced = enc.spaced - clone.openNamespaces = enc.openNamespaces - clone.buf = bufferpool.Get() - return clone -} - -func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) { - final := enc.clone() - final.buf.AppendByte('{') - - if final.LevelKey != "" { - final.addKey(final.LevelKey) - cur := final.buf.Len() - final.EncodeLevel(ent.Level, final) - if cur == final.buf.Len() { - // User-supplied EncodeLevel was a no-op. Fall back to strings to keep - // output JSON valid. - final.AppendString(ent.Level.String()) - } - } - if final.TimeKey != "" { - final.AddTime(final.TimeKey, ent.Time) - } - if ent.LoggerName != "" && final.NameKey != "" { - final.addKey(final.NameKey) - cur := final.buf.Len() - nameEncoder := final.EncodeName - - // if no name encoder provided, fall back to FullNameEncoder for backwards - // compatibility - if nameEncoder == nil { - nameEncoder = FullNameEncoder - } - - nameEncoder(ent.LoggerName, final) - if cur == final.buf.Len() { - // User-supplied EncodeName was a no-op. Fall back to strings to - // keep output JSON valid. - final.AppendString(ent.LoggerName) - } - } - if ent.Caller.Defined { - if final.CallerKey != "" { - final.addKey(final.CallerKey) - cur := final.buf.Len() - final.EncodeCaller(ent.Caller, final) - if cur == final.buf.Len() { - // User-supplied EncodeCaller was a no-op. Fall back to strings to - // keep output JSON valid. - final.AppendString(ent.Caller.String()) - } - } - if final.FunctionKey != "" { - final.addKey(final.FunctionKey) - final.AppendString(ent.Caller.Function) - } - } - if final.MessageKey != "" { - final.addKey(enc.MessageKey) - final.AppendString(ent.Message) - } - if enc.buf.Len() > 0 { - final.addElementSeparator() - final.buf.Write(enc.buf.Bytes()) - } - addFields(final, fields) - final.closeOpenNamespaces() - if ent.Stack != "" && final.StacktraceKey != "" { - final.AddString(final.StacktraceKey, ent.Stack) - } - final.buf.AppendByte('}') - if final.LineEnding != "" { - final.buf.AppendString(final.LineEnding) - } else { - final.buf.AppendString(DefaultLineEnding) - } - - ret := final.buf - putJSONEncoder(final) - return ret, nil -} - -func (enc *jsonEncoder) truncate() { - enc.buf.Reset() -} - -func (enc *jsonEncoder) closeOpenNamespaces() { - for i := 0; i < enc.openNamespaces; i++ { - enc.buf.AppendByte('}') - } -} - -func (enc *jsonEncoder) addKey(key string) { - enc.addElementSeparator() - enc.buf.AppendByte('"') - enc.safeAddString(key) - enc.buf.AppendByte('"') - enc.buf.AppendByte(':') - if enc.spaced { - enc.buf.AppendByte(' ') - } -} - -func (enc *jsonEncoder) addElementSeparator() { - last := enc.buf.Len() - 1 - if last < 0 { - return - } - switch enc.buf.Bytes()[last] { - case '{', '[', ':', ',', ' ': - return - default: - enc.buf.AppendByte(',') - if enc.spaced { - enc.buf.AppendByte(' ') - } - } -} - -func (enc *jsonEncoder) appendFloat(val float64, bitSize int) { - enc.addElementSeparator() - switch { - case math.IsNaN(val): - enc.buf.AppendString(`"NaN"`) - case math.IsInf(val, 1): - enc.buf.AppendString(`"+Inf"`) - case math.IsInf(val, -1): - enc.buf.AppendString(`"-Inf"`) - default: - enc.buf.AppendFloat(val, bitSize) - } -} - -// safeAddString JSON-escapes a string and appends it to the internal buffer. -// Unlike the standard library's encoder, it doesn't attempt to protect the -// user from browser vulnerabilities or JSONP-related problems. -func (enc *jsonEncoder) safeAddString(s string) { - for i := 0; i < len(s); { - if enc.tryAddRuneSelf(s[i]) { - i++ - continue - } - r, size := utf8.DecodeRuneInString(s[i:]) - if enc.tryAddRuneError(r, size) { - i++ - continue - } - enc.buf.AppendString(s[i : i+size]) - i += size - } -} - -// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte. -func (enc *jsonEncoder) safeAddByteString(s []byte) { - for i := 0; i < len(s); { - if enc.tryAddRuneSelf(s[i]) { - i++ - continue - } - r, size := utf8.DecodeRune(s[i:]) - if enc.tryAddRuneError(r, size) { - i++ - continue - } - enc.buf.Write(s[i : i+size]) - i += size - } -} - -// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte. -func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool { - if b >= utf8.RuneSelf { - return false - } - if 0x20 <= b && b != '\\' && b != '"' { - enc.buf.AppendByte(b) - return true - } - switch b { - case '\\', '"': - enc.buf.AppendByte('\\') - enc.buf.AppendByte(b) - case '\n': - enc.buf.AppendByte('\\') - enc.buf.AppendByte('n') - case '\r': - enc.buf.AppendByte('\\') - enc.buf.AppendByte('r') - case '\t': - enc.buf.AppendByte('\\') - enc.buf.AppendByte('t') - default: - // Encode bytes < 0x20, except for the escape sequences above. - enc.buf.AppendString(`\u00`) - enc.buf.AppendByte(_hex[b>>4]) - enc.buf.AppendByte(_hex[b&0xF]) - } - return true -} - -func (enc *jsonEncoder) tryAddRuneError(r rune, size int) bool { - if r == utf8.RuneError && size == 1 { - enc.buf.AppendString(`\ufffd`) - return true - } - return false -} diff --git a/vendor/go.uber.org/zap/zapcore/level.go b/vendor/go.uber.org/zap/zapcore/level.go deleted file mode 100644 index e575c9f432..0000000000 --- a/vendor/go.uber.org/zap/zapcore/level.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import ( - "bytes" - "errors" - "fmt" -) - -var errUnmarshalNilLevel = errors.New("can't unmarshal a nil *Level") - -// A Level is a logging priority. Higher levels are more important. -type Level int8 - -const ( - // DebugLevel logs are typically voluminous, and are usually disabled in - // production. - DebugLevel Level = iota - 1 - // InfoLevel is the default logging priority. - InfoLevel - // WarnLevel logs are more important than Info, but don't need individual - // human review. - WarnLevel - // ErrorLevel logs are high-priority. If an application is running smoothly, - // it shouldn't generate any error-level logs. - ErrorLevel - // DPanicLevel logs are particularly important errors. In development the - // logger panics after writing the message. - DPanicLevel - // PanicLevel logs a message, then panics. - PanicLevel - // FatalLevel logs a message, then calls os.Exit(1). - FatalLevel - - _minLevel = DebugLevel - _maxLevel = FatalLevel -) - -// String returns a lower-case ASCII representation of the log level. -func (l Level) String() string { - switch l { - case DebugLevel: - return "debug" - case InfoLevel: - return "info" - case WarnLevel: - return "warn" - case ErrorLevel: - return "error" - case DPanicLevel: - return "dpanic" - case PanicLevel: - return "panic" - case FatalLevel: - return "fatal" - default: - return fmt.Sprintf("Level(%d)", l) - } -} - -// CapitalString returns an all-caps ASCII representation of the log level. -func (l Level) CapitalString() string { - // Printing levels in all-caps is common enough that we should export this - // functionality. - switch l { - case DebugLevel: - return "DEBUG" - case InfoLevel: - return "INFO" - case WarnLevel: - return "WARN" - case ErrorLevel: - return "ERROR" - case DPanicLevel: - return "DPANIC" - case PanicLevel: - return "PANIC" - case FatalLevel: - return "FATAL" - default: - return fmt.Sprintf("LEVEL(%d)", l) - } -} - -// MarshalText marshals the Level to text. Note that the text representation -// drops the -Level suffix (see example). -func (l Level) MarshalText() ([]byte, error) { - return []byte(l.String()), nil -} - -// UnmarshalText unmarshals text to a level. Like MarshalText, UnmarshalText -// expects the text representation of a Level to drop the -Level suffix (see -// example). -// -// In particular, this makes it easy to configure logging levels using YAML, -// TOML, or JSON files. -func (l *Level) UnmarshalText(text []byte) error { - if l == nil { - return errUnmarshalNilLevel - } - if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) { - return fmt.Errorf("unrecognized level: %q", text) - } - return nil -} - -func (l *Level) unmarshalText(text []byte) bool { - switch string(text) { - case "debug", "DEBUG": - *l = DebugLevel - case "info", "INFO", "": // make the zero value useful - *l = InfoLevel - case "warn", "WARN": - *l = WarnLevel - case "error", "ERROR": - *l = ErrorLevel - case "dpanic", "DPANIC": - *l = DPanicLevel - case "panic", "PANIC": - *l = PanicLevel - case "fatal", "FATAL": - *l = FatalLevel - default: - return false - } - return true -} - -// Set sets the level for the flag.Value interface. -func (l *Level) Set(s string) error { - return l.UnmarshalText([]byte(s)) -} - -// Get gets the level for the flag.Getter interface. -func (l *Level) Get() interface{} { - return *l -} - -// Enabled returns true if the given level is at or above this level. -func (l Level) Enabled(lvl Level) bool { - return lvl >= l -} - -// LevelEnabler decides whether a given logging level is enabled when logging a -// message. -// -// Enablers are intended to be used to implement deterministic filters; -// concerns like sampling are better implemented as a Core. -// -// Each concrete Level value implements a static LevelEnabler which returns -// true for itself and all higher logging levels. For example WarnLevel.Enabled() -// will return true for WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, and -// FatalLevel, but return false for InfoLevel and DebugLevel. -type LevelEnabler interface { - Enabled(Level) bool -} diff --git a/vendor/go.uber.org/zap/zapcore/level_strings.go b/vendor/go.uber.org/zap/zapcore/level_strings.go deleted file mode 100644 index 7af8dadcb3..0000000000 --- a/vendor/go.uber.org/zap/zapcore/level_strings.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import "go.uber.org/zap/internal/color" - -var ( - _levelToColor = map[Level]color.Color{ - DebugLevel: color.Magenta, - InfoLevel: color.Blue, - WarnLevel: color.Yellow, - ErrorLevel: color.Red, - DPanicLevel: color.Red, - PanicLevel: color.Red, - FatalLevel: color.Red, - } - _unknownLevelColor = color.Red - - _levelToLowercaseColorString = make(map[Level]string, len(_levelToColor)) - _levelToCapitalColorString = make(map[Level]string, len(_levelToColor)) -) - -func init() { - for level, color := range _levelToColor { - _levelToLowercaseColorString[level] = color.Add(level.String()) - _levelToCapitalColorString[level] = color.Add(level.CapitalString()) - } -} diff --git a/vendor/go.uber.org/zap/zapcore/marshaler.go b/vendor/go.uber.org/zap/zapcore/marshaler.go deleted file mode 100644 index c3c55ba0d9..0000000000 --- a/vendor/go.uber.org/zap/zapcore/marshaler.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -// ObjectMarshaler allows user-defined types to efficiently add themselves to the -// logging context, and to selectively omit information which shouldn't be -// included in logs (e.g., passwords). -// -// Note: ObjectMarshaler is only used when zap.Object is used or when -// passed directly to zap.Any. It is not used when reflection-based -// encoding is used. -type ObjectMarshaler interface { - MarshalLogObject(ObjectEncoder) error -} - -// ObjectMarshalerFunc is a type adapter that turns a function into an -// ObjectMarshaler. -type ObjectMarshalerFunc func(ObjectEncoder) error - -// MarshalLogObject calls the underlying function. -func (f ObjectMarshalerFunc) MarshalLogObject(enc ObjectEncoder) error { - return f(enc) -} - -// ArrayMarshaler allows user-defined types to efficiently add themselves to the -// logging context, and to selectively omit information which shouldn't be -// included in logs (e.g., passwords). -// -// Note: ArrayMarshaler is only used when zap.Array is used or when -// passed directly to zap.Any. It is not used when reflection-based -// encoding is used. -type ArrayMarshaler interface { - MarshalLogArray(ArrayEncoder) error -} - -// ArrayMarshalerFunc is a type adapter that turns a function into an -// ArrayMarshaler. -type ArrayMarshalerFunc func(ArrayEncoder) error - -// MarshalLogArray calls the underlying function. -func (f ArrayMarshalerFunc) MarshalLogArray(enc ArrayEncoder) error { - return f(enc) -} diff --git a/vendor/go.uber.org/zap/zapcore/memory_encoder.go b/vendor/go.uber.org/zap/zapcore/memory_encoder.go deleted file mode 100644 index dfead0829d..0000000000 --- a/vendor/go.uber.org/zap/zapcore/memory_encoder.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import "time" - -// MapObjectEncoder is an ObjectEncoder backed by a simple -// map[string]interface{}. It's not fast enough for production use, but it's -// helpful in tests. -type MapObjectEncoder struct { - // Fields contains the entire encoded log context. - Fields map[string]interface{} - // cur is a pointer to the namespace we're currently writing to. - cur map[string]interface{} -} - -// NewMapObjectEncoder creates a new map-backed ObjectEncoder. -func NewMapObjectEncoder() *MapObjectEncoder { - m := make(map[string]interface{}) - return &MapObjectEncoder{ - Fields: m, - cur: m, - } -} - -// AddArray implements ObjectEncoder. -func (m *MapObjectEncoder) AddArray(key string, v ArrayMarshaler) error { - arr := &sliceArrayEncoder{elems: make([]interface{}, 0)} - err := v.MarshalLogArray(arr) - m.cur[key] = arr.elems - return err -} - -// AddObject implements ObjectEncoder. -func (m *MapObjectEncoder) AddObject(k string, v ObjectMarshaler) error { - newMap := NewMapObjectEncoder() - m.cur[k] = newMap.Fields - return v.MarshalLogObject(newMap) -} - -// AddBinary implements ObjectEncoder. -func (m *MapObjectEncoder) AddBinary(k string, v []byte) { m.cur[k] = v } - -// AddByteString implements ObjectEncoder. -func (m *MapObjectEncoder) AddByteString(k string, v []byte) { m.cur[k] = string(v) } - -// AddBool implements ObjectEncoder. -func (m *MapObjectEncoder) AddBool(k string, v bool) { m.cur[k] = v } - -// AddDuration implements ObjectEncoder. -func (m MapObjectEncoder) AddDuration(k string, v time.Duration) { m.cur[k] = v } - -// AddComplex128 implements ObjectEncoder. -func (m *MapObjectEncoder) AddComplex128(k string, v complex128) { m.cur[k] = v } - -// AddComplex64 implements ObjectEncoder. -func (m *MapObjectEncoder) AddComplex64(k string, v complex64) { m.cur[k] = v } - -// AddFloat64 implements ObjectEncoder. -func (m *MapObjectEncoder) AddFloat64(k string, v float64) { m.cur[k] = v } - -// AddFloat32 implements ObjectEncoder. -func (m *MapObjectEncoder) AddFloat32(k string, v float32) { m.cur[k] = v } - -// AddInt implements ObjectEncoder. -func (m *MapObjectEncoder) AddInt(k string, v int) { m.cur[k] = v } - -// AddInt64 implements ObjectEncoder. -func (m *MapObjectEncoder) AddInt64(k string, v int64) { m.cur[k] = v } - -// AddInt32 implements ObjectEncoder. -func (m *MapObjectEncoder) AddInt32(k string, v int32) { m.cur[k] = v } - -// AddInt16 implements ObjectEncoder. -func (m *MapObjectEncoder) AddInt16(k string, v int16) { m.cur[k] = v } - -// AddInt8 implements ObjectEncoder. -func (m *MapObjectEncoder) AddInt8(k string, v int8) { m.cur[k] = v } - -// AddString implements ObjectEncoder. -func (m *MapObjectEncoder) AddString(k string, v string) { m.cur[k] = v } - -// AddTime implements ObjectEncoder. -func (m MapObjectEncoder) AddTime(k string, v time.Time) { m.cur[k] = v } - -// AddUint implements ObjectEncoder. -func (m *MapObjectEncoder) AddUint(k string, v uint) { m.cur[k] = v } - -// AddUint64 implements ObjectEncoder. -func (m *MapObjectEncoder) AddUint64(k string, v uint64) { m.cur[k] = v } - -// AddUint32 implements ObjectEncoder. -func (m *MapObjectEncoder) AddUint32(k string, v uint32) { m.cur[k] = v } - -// AddUint16 implements ObjectEncoder. -func (m *MapObjectEncoder) AddUint16(k string, v uint16) { m.cur[k] = v } - -// AddUint8 implements ObjectEncoder. -func (m *MapObjectEncoder) AddUint8(k string, v uint8) { m.cur[k] = v } - -// AddUintptr implements ObjectEncoder. -func (m *MapObjectEncoder) AddUintptr(k string, v uintptr) { m.cur[k] = v } - -// AddReflected implements ObjectEncoder. -func (m *MapObjectEncoder) AddReflected(k string, v interface{}) error { - m.cur[k] = v - return nil -} - -// OpenNamespace implements ObjectEncoder. -func (m *MapObjectEncoder) OpenNamespace(k string) { - ns := make(map[string]interface{}) - m.cur[k] = ns - m.cur = ns -} - -// sliceArrayEncoder is an ArrayEncoder backed by a simple []interface{}. Like -// the MapObjectEncoder, it's not designed for production use. -type sliceArrayEncoder struct { - elems []interface{} -} - -func (s *sliceArrayEncoder) AppendArray(v ArrayMarshaler) error { - enc := &sliceArrayEncoder{} - err := v.MarshalLogArray(enc) - s.elems = append(s.elems, enc.elems) - return err -} - -func (s *sliceArrayEncoder) AppendObject(v ObjectMarshaler) error { - m := NewMapObjectEncoder() - err := v.MarshalLogObject(m) - s.elems = append(s.elems, m.Fields) - return err -} - -func (s *sliceArrayEncoder) AppendReflected(v interface{}) error { - s.elems = append(s.elems, v) - return nil -} - -func (s *sliceArrayEncoder) AppendBool(v bool) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendByteString(v []byte) { s.elems = append(s.elems, string(v)) } -func (s *sliceArrayEncoder) AppendComplex128(v complex128) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendComplex64(v complex64) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendDuration(v time.Duration) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendFloat64(v float64) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendFloat32(v float32) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendInt(v int) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendInt64(v int64) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendInt32(v int32) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendInt16(v int16) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendInt8(v int8) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendString(v string) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendTime(v time.Time) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendUint(v uint) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendUint64(v uint64) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendUint32(v uint32) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendUint16(v uint16) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendUint8(v uint8) { s.elems = append(s.elems, v) } -func (s *sliceArrayEncoder) AppendUintptr(v uintptr) { s.elems = append(s.elems, v) } diff --git a/vendor/go.uber.org/zap/zapcore/sampler.go b/vendor/go.uber.org/zap/zapcore/sampler.go deleted file mode 100644 index 25f10ca1d7..0000000000 --- a/vendor/go.uber.org/zap/zapcore/sampler.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import ( - "time" - - "go.uber.org/atomic" -) - -const ( - _numLevels = _maxLevel - _minLevel + 1 - _countersPerLevel = 4096 -) - -type counter struct { - resetAt atomic.Int64 - counter atomic.Uint64 -} - -type counters [_numLevels][_countersPerLevel]counter - -func newCounters() *counters { - return &counters{} -} - -func (cs *counters) get(lvl Level, key string) *counter { - i := lvl - _minLevel - j := fnv32a(key) % _countersPerLevel - return &cs[i][j] -} - -// fnv32a, adapted from "hash/fnv", but without a []byte(string) alloc -func fnv32a(s string) uint32 { - const ( - offset32 = 2166136261 - prime32 = 16777619 - ) - hash := uint32(offset32) - for i := 0; i < len(s); i++ { - hash ^= uint32(s[i]) - hash *= prime32 - } - return hash -} - -func (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 { - tn := t.UnixNano() - resetAfter := c.resetAt.Load() - if resetAfter > tn { - return c.counter.Inc() - } - - c.counter.Store(1) - - newResetAfter := tn + tick.Nanoseconds() - if !c.resetAt.CAS(resetAfter, newResetAfter) { - // We raced with another goroutine trying to reset, and it also reset - // the counter to 1, so we need to reincrement the counter. - return c.counter.Inc() - } - - return 1 -} - -// SamplingDecision is a decision represented as a bit field made by sampler. -// More decisions may be added in the future. -type SamplingDecision uint32 - -const ( - // LogDropped indicates that the Sampler dropped a log entry. - LogDropped SamplingDecision = 1 << iota - // LogSampled indicates that the Sampler sampled a log entry. - LogSampled -) - -// optionFunc wraps a func so it satisfies the SamplerOption interface. -type optionFunc func(*sampler) - -func (f optionFunc) apply(s *sampler) { - f(s) -} - -// SamplerOption configures a Sampler. -type SamplerOption interface { - apply(*sampler) -} - -// nopSamplingHook is the default hook used by sampler. -func nopSamplingHook(Entry, SamplingDecision) {} - -// SamplerHook registers a function which will be called when Sampler makes a -// decision. -// -// This hook may be used to get visibility into the performance of the sampler. -// For example, use it to track metrics of dropped versus sampled logs. -// -// var dropped atomic.Int64 -// zapcore.SamplerHook(func(ent zapcore.Entry, dec zapcore.SamplingDecision) { -// if dec&zapcore.LogDropped > 0 { -// dropped.Inc() -// } -// }) -func SamplerHook(hook func(entry Entry, dec SamplingDecision)) SamplerOption { - return optionFunc(func(s *sampler) { - s.hook = hook - }) -} - -// NewSamplerWithOptions creates a Core that samples incoming entries, which -// caps the CPU and I/O load of logging while attempting to preserve a -// representative subset of your logs. -// -// Zap samples by logging the first N entries with a given level and message -// each tick. If more Entries with the same level and message are seen during -// the same interval, every Mth message is logged and the rest are dropped. -// -// Sampler can be configured to report sampling decisions with the SamplerHook -// option. -// -// Keep in mind that zap's sampling implementation is optimized for speed over -// absolute precision; under load, each tick may be slightly over- or -// under-sampled. -func NewSamplerWithOptions(core Core, tick time.Duration, first, thereafter int, opts ...SamplerOption) Core { - s := &sampler{ - Core: core, - tick: tick, - counts: newCounters(), - first: uint64(first), - thereafter: uint64(thereafter), - hook: nopSamplingHook, - } - for _, opt := range opts { - opt.apply(s) - } - - return s -} - -type sampler struct { - Core - - counts *counters - tick time.Duration - first, thereafter uint64 - hook func(Entry, SamplingDecision) -} - -// NewSampler creates a Core that samples incoming entries, which -// caps the CPU and I/O load of logging while attempting to preserve a -// representative subset of your logs. -// -// Zap samples by logging the first N entries with a given level and message -// each tick. If more Entries with the same level and message are seen during -// the same interval, every Mth message is logged and the rest are dropped. -// -// Keep in mind that zap's sampling implementation is optimized for speed over -// absolute precision; under load, each tick may be slightly over- or -// under-sampled. -// -// Deprecated: use NewSamplerWithOptions. -func NewSampler(core Core, tick time.Duration, first, thereafter int) Core { - return NewSamplerWithOptions(core, tick, first, thereafter) -} - -func (s *sampler) With(fields []Field) Core { - return &sampler{ - Core: s.Core.With(fields), - tick: s.tick, - counts: s.counts, - first: s.first, - thereafter: s.thereafter, - hook: s.hook, - } -} - -func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { - if !s.Enabled(ent.Level) { - return ce - } - - counter := s.counts.get(ent.Level, ent.Message) - n := counter.IncCheckReset(ent.Time, s.tick) - if n > s.first && (n-s.first)%s.thereafter != 0 { - s.hook(ent, LogDropped) - return ce - } - s.hook(ent, LogSampled) - return s.Core.Check(ent, ce) -} diff --git a/vendor/go.uber.org/zap/zapcore/tee.go b/vendor/go.uber.org/zap/zapcore/tee.go deleted file mode 100644 index 07a32eef9a..0000000000 --- a/vendor/go.uber.org/zap/zapcore/tee.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import "go.uber.org/multierr" - -type multiCore []Core - -// NewTee creates a Core that duplicates log entries into two or more -// underlying Cores. -// -// Calling it with a single Core returns the input unchanged, and calling -// it with no input returns a no-op Core. -func NewTee(cores ...Core) Core { - switch len(cores) { - case 0: - return NewNopCore() - case 1: - return cores[0] - default: - return multiCore(cores) - } -} - -func (mc multiCore) With(fields []Field) Core { - clone := make(multiCore, len(mc)) - for i := range mc { - clone[i] = mc[i].With(fields) - } - return clone -} - -func (mc multiCore) Enabled(lvl Level) bool { - for i := range mc { - if mc[i].Enabled(lvl) { - return true - } - } - return false -} - -func (mc multiCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { - for i := range mc { - ce = mc[i].Check(ent, ce) - } - return ce -} - -func (mc multiCore) Write(ent Entry, fields []Field) error { - var err error - for i := range mc { - err = multierr.Append(err, mc[i].Write(ent, fields)) - } - return err -} - -func (mc multiCore) Sync() error { - var err error - for i := range mc { - err = multierr.Append(err, mc[i].Sync()) - } - return err -} diff --git a/vendor/go.uber.org/zap/zapcore/write_syncer.go b/vendor/go.uber.org/zap/zapcore/write_syncer.go deleted file mode 100644 index d4a1af3d07..0000000000 --- a/vendor/go.uber.org/zap/zapcore/write_syncer.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2016 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package zapcore - -import ( - "io" - "sync" - - "go.uber.org/multierr" -) - -// A WriteSyncer is an io.Writer that can also flush any buffered data. Note -// that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer. -type WriteSyncer interface { - io.Writer - Sync() error -} - -// AddSync converts an io.Writer to a WriteSyncer. It attempts to be -// intelligent: if the concrete type of the io.Writer implements WriteSyncer, -// we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync. -func AddSync(w io.Writer) WriteSyncer { - switch w := w.(type) { - case WriteSyncer: - return w - default: - return writerWrapper{w} - } -} - -type lockedWriteSyncer struct { - sync.Mutex - ws WriteSyncer -} - -// Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In -// particular, *os.Files must be locked before use. -func Lock(ws WriteSyncer) WriteSyncer { - if _, ok := ws.(*lockedWriteSyncer); ok { - // no need to layer on another lock - return ws - } - return &lockedWriteSyncer{ws: ws} -} - -func (s *lockedWriteSyncer) Write(bs []byte) (int, error) { - s.Lock() - n, err := s.ws.Write(bs) - s.Unlock() - return n, err -} - -func (s *lockedWriteSyncer) Sync() error { - s.Lock() - err := s.ws.Sync() - s.Unlock() - return err -} - -type writerWrapper struct { - io.Writer -} - -func (w writerWrapper) Sync() error { - return nil -} - -type multiWriteSyncer []WriteSyncer - -// NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes -// and sync calls, much like io.MultiWriter. -func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer { - if len(ws) == 1 { - return ws[0] - } - return multiWriteSyncer(ws) -} - -// See https://golang.org/src/io/multi.go -// When not all underlying syncers write the same number of bytes, -// the smallest number is returned even though Write() is called on -// all of them. -func (ws multiWriteSyncer) Write(p []byte) (int, error) { - var writeErr error - nWritten := 0 - for _, w := range ws { - n, err := w.Write(p) - writeErr = multierr.Append(writeErr, err) - if nWritten == 0 && n != 0 { - nWritten = n - } else if n < nWritten { - nWritten = n - } - } - return nWritten, writeErr -} - -func (ws multiWriteSyncer) Sync() error { - var err error - for _, w := range ws { - err = multierr.Append(err, w.Sync()) - } - return err -} diff --git a/vendor/modules.txt b/vendor/modules.txt index b5617c2ef1..1100dd960a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -398,9 +398,12 @@ github.com/mattermost/gziphandler github.com/mattermost/ldap # github.com/mattermost/logr v1.0.13 ## explicit -github.com/mattermost/logr -github.com/mattermost/logr/format -github.com/mattermost/logr/target +# github.com/mattermost/logr/v2 v2.0.10 +## explicit +github.com/mattermost/logr/v2 +github.com/mattermost/logr/v2/config +github.com/mattermost/logr/v2/formatters +github.com/mattermost/logr/v2/targets # github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 ## explicit github.com/mattermost/rsc/gf256 @@ -681,10 +684,6 @@ github.com/vmihailenco/msgpack/v5/msgpcode github.com/vmihailenco/tagparser/v2 github.com/vmihailenco/tagparser/v2/internal github.com/vmihailenco/tagparser/v2/internal/parser -# github.com/wiggin77/cfg v1.0.2 -github.com/wiggin77/cfg -github.com/wiggin77/cfg/ini -github.com/wiggin77/cfg/timeconv # github.com/wiggin77/merror v1.0.3 ## explicit github.com/wiggin77/merror @@ -730,15 +729,8 @@ go.opentelemetry.io/otel/metric/unit go.uber.org/atomic # go.uber.org/multierr v1.7.0 ## explicit -go.uber.org/multierr # go.uber.org/zap v1.17.0 ## explicit -go.uber.org/zap -go.uber.org/zap/buffer -go.uber.org/zap/internal/bufferpool -go.uber.org/zap/internal/color -go.uber.org/zap/internal/exit -go.uber.org/zap/zapcore # golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e ## explicit golang.org/x/crypto/acme diff --git a/web/context.go b/web/context.go index 3cca15b0e2..f2c6d9882c 100644 --- a/web/context.go +++ b/web/context.go @@ -35,7 +35,7 @@ func (c *Context) LogAuditRec(rec *audit.Record) { // LogAuditRec logs an audit record using specified Level. // If the context is flagged with a permissions error then `level` // is ignored and the audit record is emitted with `LevelPerms`. -func (c *Context) LogAuditRecWithLevel(rec *audit.Record, level mlog.LogLevel) { +func (c *Context) LogAuditRecWithLevel(rec *audit.Record, level mlog.Level) { if rec == nil { return } diff --git a/web/main_test.go b/web/main_test.go index eebef55cc7..1099e210a0 100644 --- a/web/main_test.go +++ b/web/main_test.go @@ -6,7 +6,6 @@ package web import ( "testing" - "github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/testlib" ) @@ -18,8 +17,6 @@ func TestMain(m *testing.M) { EnableResources: true, } - mlog.DisableZap() - mainHelper = testlib.NewMainHelperWithOptions(&options) defer mainHelper.Close() diff --git a/web/oauth_test.go b/web/oauth_test.go index 9627b228e9..ab4266bcc0 100644 --- a/web/oauth_test.go +++ b/web/oauth_test.go @@ -379,8 +379,7 @@ func TestMobileLoginWithOAuth(t *testing.T) { translationFunc := i18n.GetUserTranslations("en") c.AppContext.SetT(translationFunc) - buffer := &bytes.Buffer{} - c.Logger = mlog.NewTestingLogger(t, buffer) + c.Logger = th.TestLogger provider := &MattermostTestProvider{} einterfaces.RegisterOAuthProvider(model.ServiceGitlab, provider) @@ -583,7 +582,8 @@ func TestOAuthComplete_ErrorMessages(t *testing.T) { translationFunc := i18n.GetUserTranslations("en") c.AppContext.SetT(translationFunc) buffer := &bytes.Buffer{} - c.Logger = mlog.NewTestingLogger(t, buffer) + c.Logger = mlog.CreateTestLogger(t, buffer, mlog.StdAll...) + defer c.Logger.Shutdown() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Enable = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true }) provider := &MattermostTestProvider{} @@ -712,8 +712,6 @@ func (th *TestHelper) Logout(client *model.Client4) { } func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string { - utils.DisableDebugLogForTest() - results := make(map[string][]string) for _, roleName := range []string{ @@ -726,24 +724,18 @@ func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string { } { role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } results[roleName] = role.Permissions } - - utils.EnableDebugLogForTest() return results } func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) { - utils.DisableDebugLogForTest() - for roleName, permissions := range data { role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } @@ -755,12 +747,9 @@ func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) { _, err2 := th.App.UpdateRole(role) if err2 != nil { - utils.EnableDebugLogForTest() panic(err2) } } - - utils.EnableDebugLogForTest() } // func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) { @@ -796,17 +785,13 @@ func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) { // } func (th *TestHelper) AddPermissionToRole(permission string, roleName string) { - utils.DisableDebugLogForTest() - role, err1 := th.App.GetRoleByName(context.Background(), roleName) if err1 != nil { - utils.EnableDebugLogForTest() panic(err1) } for _, existingPermission := range role.Permissions { if existingPermission == permission { - utils.EnableDebugLogForTest() return } } @@ -815,9 +800,6 @@ func (th *TestHelper) AddPermissionToRole(permission string, roleName string) { _, err2 := th.App.UpdateRole(role) if err2 != nil { - utils.EnableDebugLogForTest() panic(err2) } - - utils.EnableDebugLogForTest() } diff --git a/web/web_test.go b/web/web_test.go index 214a31b9ff..8041c4cbf2 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -45,6 +45,8 @@ type TestHelper struct { tempWorkspace string IncludeCacheLayer bool + + TestLogger *mlog.Logger } func SetupWithStoreMock(tb testing.TB) *TestHelper { @@ -52,7 +54,7 @@ func SetupWithStoreMock(tb testing.TB) *TestHelper { tb.SkipNow() } - th := setupTestHelper(false) + th := setupTestHelper(tb, false) emptyMockStore := mocks.Store{} emptyMockStore.On("Close").Return(nil) th.App.Srv().Store = &emptyMockStore @@ -65,10 +67,10 @@ func Setup(tb testing.TB) *TestHelper { } store := mainHelper.GetStore() store.DropAllTables() - return setupTestHelper(true) + return setupTestHelper(tb, true) } -func setupTestHelper(includeCacheLayer bool) *TestHelper { +func setupTestHelper(tb testing.TB, includeCacheLayer bool) *TestHelper { memoryStore := config.NewTestMemoryStore() newConfig := memoryStore.Get().Clone() *newConfig.AnnouncementSettings.AdminNoticesEnabled = false @@ -79,7 +81,14 @@ func setupTestHelper(includeCacheLayer bool) *TestHelper { options = append(options, app.ConfigStore(memoryStore)) options = append(options, app.StoreOverride(mainHelper.Store)) - mlog.DisableZap() + testLogger, _ := mlog.NewLogger() + logCfg, _ := config.MloggerConfigFromLoggerConfig(&newConfig.LogSettings, nil, config.GetLogFileLocation) + if errCfg := testLogger.ConfigureTargets(logCfg); errCfg != nil { + panic("failed to configure test logger: " + errCfg.Error()) + } + // lock logger config so server init cannot override it during testing. + testLogger.LockConfiguration() + options = append(options, app.SetLogger(testLogger)) s, err := app.NewServer(options...) if err != nil { @@ -129,6 +138,7 @@ func setupTestHelper(includeCacheLayer bool) *TestHelper { Server: s, Web: web, IncludeCacheLayer: includeCacheLayer, + TestLogger: testLogger, } return th