MM-36764 mlog refactor (#18118)
Refactor mlog - simplify mlog by removing redundant code - remove Zap dependency - update unit test helpers - update logging config - update auditing
Этот коммит содержится в:
3
Makefile
3
Makefile
@@ -453,8 +453,7 @@ run-server: prepackaged-binaries validate-go-version start-docker ## Starts the
|
|||||||
@echo Running mattermost for development
|
@echo Running mattermost for development
|
||||||
|
|
||||||
mkdir -p $(BUILD_WEBAPP_DIR)/dist/files
|
mkdir -p $(BUILD_WEBAPP_DIR)/dist/files
|
||||||
$(GO) run $(GOFLAGS) -ldflags '$(LDFLAGS)' $(PLATFORM_FILES) 2>&1 | \
|
$(GO) run $(GOFLAGS) -ldflags '$(LDFLAGS)' $(PLATFORM_FILES) $(RUN_IN_BACKGROUND)
|
||||||
$(GO) run $(GOFLAGS) -ldflags '$(LDFLAGS)' $(PLATFORM_FILES) logs --logrus $(RUN_IN_BACKGROUND)
|
|
||||||
|
|
||||||
debug-server: start-docker ## Compile and start server using delve.
|
debug-server: start-docker ## Compile and start server using delve.
|
||||||
mkdir -p $(BUILD_WEBAPP_DIR)/dist/files
|
mkdir -p $(BUILD_WEBAPP_DIR)/dist/files
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/store/localcachelayer"
|
"github.com/mattermost/mattermost-server/v6/store/localcachelayer"
|
||||||
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"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/web"
|
||||||
"github.com/mattermost/mattermost-server/v6/wsapi"
|
"github.com/mattermost/mattermost-server/v6/wsapi"
|
||||||
)
|
)
|
||||||
@@ -67,6 +66,8 @@ type TestHelper struct {
|
|||||||
LocalClient *model.Client4
|
LocalClient *model.Client4
|
||||||
|
|
||||||
IncludeCacheLayer bool
|
IncludeCacheLayer bool
|
||||||
|
|
||||||
|
TestLogger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
var mainHelper *testlib.MainHelper
|
var mainHelper *testlib.MainHelper
|
||||||
@@ -120,6 +121,15 @@ func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, ent
|
|||||||
options = append(options, app.StoreOverride(dbStore))
|
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...)
|
s, err := app.NewServer(options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -131,6 +141,7 @@ func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, ent
|
|||||||
ConfigStore: configStore,
|
ConfigStore: configStore,
|
||||||
IncludeCacheLayer: includeCache,
|
IncludeCacheLayer: includeCache,
|
||||||
Context: &request.Context{},
|
Context: &request.Context{},
|
||||||
|
TestLogger: testLogger,
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.SearchEngine != nil && s.SearchEngine.BleveEngine != nil && searchEngine != nil {
|
if s.SearchEngine != nil && s.SearchEngine.BleveEngine != nil && searchEngine != nil {
|
||||||
@@ -336,15 +347,11 @@ func (th *TestHelper) ShutdownApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) TearDown() {
|
func (th *TestHelper) TearDown() {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
if th.IncludeCacheLayer {
|
if th.IncludeCacheLayer {
|
||||||
// Clean all the caches
|
// Clean all the caches
|
||||||
th.App.Srv().InvalidateAllCaches()
|
th.App.Srv().InvalidateAllCaches()
|
||||||
}
|
}
|
||||||
|
|
||||||
th.ShutdownApp()
|
th.ShutdownApp()
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var initBasicOnce sync.Once
|
var initBasicOnce sync.Once
|
||||||
@@ -501,12 +508,10 @@ func (th *TestHelper) CreateBotWithClient(client *model.Client4) *model.Bot {
|
|||||||
Description: "bot",
|
Description: "bot",
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
rbot, _, err := client.CreateBot(bot)
|
rbot, _, err := client.CreateBot(bot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return rbot
|
return rbot
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,12 +532,10 @@ func (th *TestHelper) CreateTeamWithClient(client *model.Client4) *model.Team {
|
|||||||
Type: model.TeamOpen,
|
Type: model.TeamOpen,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
rteam, _, err := client.CreateTeam(team)
|
rteam, _, err := client.CreateTeam(team)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return rteam
|
return rteam
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,7 +551,6 @@ func (th *TestHelper) CreateUserWithClient(client *model.Client4) *model.User {
|
|||||||
Password: "Pa$$word11",
|
Password: "Pa$$word11",
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
ruser, _, err := client.CreateUser(user)
|
ruser, _, err := client.CreateUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -559,7 +561,6 @@ func (th *TestHelper) CreateUserWithClient(client *model.Client4) *model.User {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return ruser
|
return ruser
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,12 +652,10 @@ func (th *TestHelper) CreateChannelWithClientAndTeam(client *model.Client4, chan
|
|||||||
TeamId: teamId,
|
TeamId: teamId,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
rchannel, _, err := client.CreateChannel(channel)
|
rchannel, _, err := client.CreateChannel(channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return rchannel
|
return rchannel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -680,12 +679,10 @@ func (th *TestHelper) CreatePostWithClient(client *model.Client4, channel *model
|
|||||||
Message: "message_" + id,
|
Message: "message_" + id,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
rpost, _, err := client.CreatePost(post)
|
rpost, _, err := client.CreatePost(post)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return rpost
|
return rpost
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,12 +695,10 @@ func (th *TestHelper) CreatePinnedPostWithClient(client *model.Client4, channel
|
|||||||
IsPinned: true,
|
IsPinned: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
rpost, _, err := client.CreatePost(post)
|
rpost, _, err := client.CreatePost(post)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return rpost
|
return rpost
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,12 +708,10 @@ func (th *TestHelper) CreateMessagePostWithClient(client *model.Client4, channel
|
|||||||
Message: message,
|
Message: message,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
rpost, _, err := client.CreatePost(post)
|
rpost, _, err := client.CreatePost(post)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return rpost
|
return rpost
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,13 +731,11 @@ func (th *TestHelper) CreateMessagePostNoClient(channel *model.Channel, message
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
|
func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
var channel *model.Channel
|
var channel *model.Channel
|
||||||
if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil {
|
if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,82 +760,59 @@ func (th *TestHelper) LoginSystemManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) LoginBasicWithClient(client *model.Client4) {
|
func (th *TestHelper) LoginBasicWithClient(client *model.Client4) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
_, _, err := client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
_, _, err := client.Login(th.BasicUser.Email, th.BasicUser.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) LoginBasic2WithClient(client *model.Client4) {
|
func (th *TestHelper) LoginBasic2WithClient(client *model.Client4) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
_, _, err := client.Login(th.BasicUser2.Email, th.BasicUser2.Password)
|
_, _, err := client.Login(th.BasicUser2.Email, th.BasicUser2.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) LoginTeamAdminWithClient(client *model.Client4) {
|
func (th *TestHelper) LoginTeamAdminWithClient(client *model.Client4) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
_, _, err := client.Login(th.TeamAdminUser.Email, th.TeamAdminUser.Password)
|
_, _, err := client.Login(th.TeamAdminUser.Email, th.TeamAdminUser.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) LoginSystemManagerWithClient(client *model.Client4) {
|
func (th *TestHelper) LoginSystemManagerWithClient(client *model.Client4) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
_, _, err := client.Login(th.SystemManagerUser.Email, th.SystemManagerUser.Password)
|
_, _, err := client.Login(th.SystemManagerUser.Email, th.SystemManagerUser.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) LoginSystemAdminWithClient(client *model.Client4) {
|
func (th *TestHelper) LoginSystemAdminWithClient(client *model.Client4) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
_, _, err := client.Login(th.SystemAdminUser.Email, th.SystemAdminUser.Password)
|
_, _, err := client.Login(th.SystemAdminUser.Email, th.SystemAdminUser.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) UpdateActiveUser(user *model.User, active bool) {
|
func (th *TestHelper) UpdateActiveUser(user *model.User, active bool) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
_, err := th.App.UpdateActive(th.Context, user, active)
|
_, err := th.App.UpdateActive(th.Context, user, active)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
_, err := th.App.JoinUserToTeam(th.Context, team, user, "")
|
_, err := th.App.JoinUserToTeam(th.Context, team, user, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
member, err := th.App.AddUserToChannel(user, channel, false)
|
member, err := th.App.AddUserToChannel(user, channel, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
|
|
||||||
return member
|
return member
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,12 +832,10 @@ func (th *TestHelper) CreateGroup() *model.Group {
|
|||||||
RemoteId: "ri_" + id,
|
RemoteId: "ri_" + id,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
group, err := th.App.CreateGroup(group)
|
group, err := th.App.CreateGroup(group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return group
|
return group
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1107,59 +1073,39 @@ func (th *TestHelper) cleanupTestFile(info *model.FileInfo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Channel) {
|
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 {
|
if cm, err := th.App.Srv().Store.Channel().GetMember(context.Background(), channel.Id, user.Id); err == nil {
|
||||||
cm.SchemeAdmin = true
|
cm.SchemeAdmin = true
|
||||||
if _, err = th.App.Srv().Store.Channel().UpdateMember(cm); err != nil {
|
if _, err = th.App.Srv().Store.Channel().UpdateMember(cm); err != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) {
|
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 {
|
if tm, err := th.App.Srv().Store.Team().GetMember(context.Background(), team.Id, user.Id); err == nil {
|
||||||
tm.SchemeAdmin = true
|
tm.SchemeAdmin = true
|
||||||
if _, err = th.App.Srv().Store.Team().UpdateMember(tm); err != nil {
|
if _, err = th.App.Srv().Store.Team().UpdateMember(tm); err != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) {
|
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 {
|
if tm, err := th.App.Srv().Store.Team().GetMember(context.Background(), team.Id, user.Id); err == nil {
|
||||||
tm.SchemeAdmin = false
|
tm.SchemeAdmin = false
|
||||||
if _, err = th.App.Srv().Store.Team().UpdateMember(tm); err != nil {
|
if _, err = th.App.Srv().Store.Team().UpdateMember(tm); err != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string {
|
func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
results := make(map[string][]string)
|
results := make(map[string][]string)
|
||||||
|
|
||||||
for _, roleName := range []string{
|
for _, roleName := range []string{
|
||||||
@@ -1172,24 +1118,18 @@ func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string {
|
|||||||
} {
|
} {
|
||||||
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err1)
|
panic(err1)
|
||||||
}
|
}
|
||||||
|
|
||||||
results[roleName] = role.Permissions
|
results[roleName] = role.Permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) {
|
func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
for roleName, permissions := range data {
|
for roleName, permissions := range data {
|
||||||
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err1)
|
panic(err1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1201,20 +1141,14 @@ func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) {
|
|||||||
|
|
||||||
_, err2 := th.App.UpdateRole(role)
|
_, err2 := th.App.UpdateRole(role)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err2)
|
panic(err2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) {
|
func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err1)
|
panic(err1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1226,7 +1160,6 @@ func (th *TestHelper) RemovePermissionFromRole(permission string, roleName strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
|
if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1234,25 +1167,18 @@ func (th *TestHelper) RemovePermissionFromRole(permission string, roleName strin
|
|||||||
|
|
||||||
_, err2 := th.App.UpdateRole(role)
|
_, err2 := th.App.UpdateRole(role)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err2)
|
panic(err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
|
func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err1)
|
panic(err1)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, existingPermission := range role.Permissions {
|
for _, existingPermission := range role.Permissions {
|
||||||
if existingPermission == permission {
|
if existingPermission == permission {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1261,11 +1187,8 @@ func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
|
|||||||
|
|
||||||
_, err2 := th.App.UpdateRole(role)
|
_, err2 := th.App.UpdateRole(role)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err2)
|
panic(err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) SetupTeamScheme() *model.Scheme {
|
func (th *TestHelper) SetupTeamScheme() *model.Scheme {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
|
"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
|
||||||
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
||||||
"github.com/mattermost/mattermost-server/v6/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateChannel(t *testing.T) {
|
func TestCreateChannel(t *testing.T) {
|
||||||
@@ -3238,7 +3237,6 @@ func TestAutocompleteChannels(t *testing.T) {
|
|||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
// A private channel to make sure private channels are not used
|
// A private channel to make sure private channels are not used
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
ptown, _, _ := th.Client.CreateChannel(&model.Channel{
|
ptown, _, _ := th.Client.CreateChannel(&model.Channel{
|
||||||
DisplayName: "Town",
|
DisplayName: "Town",
|
||||||
Name: "town",
|
Name: "town",
|
||||||
@@ -3251,7 +3249,6 @@ func TestAutocompleteChannels(t *testing.T) {
|
|||||||
Type: model.ChannelTypeOpen,
|
Type: model.ChannelTypeOpen,
|
||||||
TeamId: th.BasicTeam.Id,
|
TeamId: th.BasicTeam.Id,
|
||||||
})
|
})
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
defer func() {
|
defer func() {
|
||||||
th.Client.DeleteChannel(ptown.Id)
|
th.Client.DeleteChannel(ptown.Id)
|
||||||
th.Client.DeleteChannel(tower.Id)
|
th.Client.DeleteChannel(tower.Id)
|
||||||
@@ -3320,7 +3317,6 @@ func TestAutocompleteChannelsForSearch(t *testing.T) {
|
|||||||
defer th.App.PermanentDeleteUser(th.Context, u4)
|
defer th.App.PermanentDeleteUser(th.Context, u4)
|
||||||
|
|
||||||
// A private channel to make sure private channels are not used
|
// A private channel to make sure private channels are not used
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
ptown, _, _ := th.SystemAdminClient.CreateChannel(&model.Channel{
|
ptown, _, _ := th.SystemAdminClient.CreateChannel(&model.Channel{
|
||||||
DisplayName: "Town",
|
DisplayName: "Town",
|
||||||
Name: "town",
|
Name: "town",
|
||||||
@@ -3339,7 +3335,6 @@ func TestAutocompleteChannelsForSearch(t *testing.T) {
|
|||||||
defer func() {
|
defer func() {
|
||||||
th.Client.DeleteChannel(mypriv.Id)
|
th.Client.DeleteChannel(mypriv.Id)
|
||||||
}()
|
}()
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
|
|
||||||
dc1, _, err := th.Client.CreateDirectChannel(th.BasicUser.Id, u1.Id)
|
dc1, _, err := th.Client.CreateDirectChannel(th.BasicUser.Id, u1.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -3450,7 +3445,6 @@ func TestAutocompleteChannelsForSearchGuestUsers(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// A private channel to make sure private channels are not used
|
// A private channel to make sure private channels are not used
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
town, _, _ := th.SystemAdminClient.CreateChannel(&model.Channel{
|
town, _, _ := th.SystemAdminClient.CreateChannel(&model.Channel{
|
||||||
DisplayName: "Town",
|
DisplayName: "Town",
|
||||||
Name: "town",
|
Name: "town",
|
||||||
@@ -3475,8 +3469,6 @@ func TestAutocompleteChannelsForSearchGuestUsers(t *testing.T) {
|
|||||||
_, _, err = th.SystemAdminClient.AddChannelMember(mypriv.Id, guest.Id)
|
_, _, err = th.SystemAdminClient.AddChannelMember(mypriv.Id, guest.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
|
|
||||||
dc1, _, err := th.SystemAdminClient.CreateDirectChannel(th.BasicUser.Id, guest.Id)
|
dc1, _, err := th.SystemAdminClient.CreateDirectChannel(th.BasicUser.Id, guest.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package api4
|
package api4
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -468,7 +467,7 @@ func TestUpdateConfigDiffInAuditRecord(t *testing.T) {
|
|||||||
require.Equal(t, timeoutVal+1, *cfg.ServiceSettings.ReadTimeout)
|
require.Equal(t, timeoutVal+1, *cfg.ServiceSettings.ReadTimeout)
|
||||||
|
|
||||||
// Forcing a flush before attempting to read log's content.
|
// 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, err)
|
||||||
|
|
||||||
require.NoError(t, logFile.Sync())
|
require.NoError(t, logFile.Sync())
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,8 +24,6 @@ func TestMain(m *testing.M) {
|
|||||||
WithReadReplica: replicaFlag,
|
WithReadReplica: replicaFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
mlog.DisableZap()
|
|
||||||
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -327,9 +327,12 @@ func TestGetLogs(t *testing.T) {
|
|||||||
mlog.Info(strconv.Itoa(i))
|
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) {
|
th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) {
|
||||||
logs, _, err := c.GetLogs(0, 10)
|
logs, _, err2 := c.GetLogs(0, 10)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err2)
|
||||||
require.Len(t, logs, 10)
|
require.Len(t, logs, 10)
|
||||||
|
|
||||||
for i := 10; i < 20; i++ {
|
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.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) {
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
_, resp, err := th.Client.GetLogs(0, 10)
|
_, resp, err2 := th.Client.GetLogs(0, 10)
|
||||||
require.Error(t, err)
|
require.Error(t, err2)
|
||||||
CheckForbiddenStatus(t, resp)
|
CheckForbiddenStatus(t, resp)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
10
app/admin.go
10
app/admin.go
@@ -4,7 +4,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -13,11 +12,11 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/v6/config"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/i18n"
|
"github.com/mattermost/mattermost-server/v6/shared/i18n"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mail"
|
"github.com/mattermost/mattermost-server/v6/shared/mail"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"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) {
|
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
|
var lines []string
|
||||||
|
|
||||||
if *s.Config().LogSettings.EnableFile {
|
if *s.Config().LogSettings.EnableFile {
|
||||||
timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), mlog.DefaultFlushTimeout)
|
s.Log.Flush()
|
||||||
defer timeoutCancel()
|
logFile := config.GetLogFileLocation(*s.Config().LogSettings.FileLocation)
|
||||||
mlog.Flush(timeoutCtx)
|
|
||||||
|
|
||||||
logFile := utils.GetLogFileLocation(*s.Config().LogSettings.FileLocation)
|
|
||||||
file, err := os.Open(logFile)
|
file, err := os.Open(logFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
|
return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ type AppIface interface {
|
|||||||
// LogAuditRec logs an audit record using default LvlAuditCLI.
|
// LogAuditRec logs an audit record using default LvlAuditCLI.
|
||||||
LogAuditRec(rec *audit.Record, err error)
|
LogAuditRec(rec *audit.Record, err error)
|
||||||
// LogAuditRecWithLevel logs an audit record using specified Level.
|
// 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 creates a audit record pre-populated with defaults.
|
||||||
MakeAuditRecord(event string, initialStatus string) *audit.Record
|
MakeAuditRecord(event string, initialStatus string) *audit.Record
|
||||||
// MarkChanelAsUnreadFromPost will take a post and set the channel as unread from that one.
|
// MarkChanelAsUnreadFromPost will take a post and set the channel as unread from that one.
|
||||||
|
|||||||
69
app/audit.go
69
app/audit.go
@@ -9,8 +9,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/audit"
|
"github.com/mattermost/mattermost-server/v6/audit"
|
||||||
"github.com/mattermost/mattermost-server/v6/config"
|
"github.com/mattermost/mattermost-server/v6/config"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"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.
|
// 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 {
|
if rec == nil {
|
||||||
return
|
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 {
|
func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) error {
|
||||||
var errs error
|
|
||||||
|
|
||||||
adt.OnQueueFull = s.onAuditTargetQueueFull
|
adt.OnQueueFull = s.onAuditTargetQueueFull
|
||||||
adt.OnError = s.onAuditError
|
adt.OnError = s.onAuditError
|
||||||
|
|
||||||
// Configure target for rotating file output (E0, E10)
|
var logConfigSrc config.LogConfigSrc
|
||||||
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.
|
|
||||||
dsn := *s.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
|
dsn := *s.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
|
||||||
if !bAllowAdvancedLogging || dsn == "" {
|
if bAllowAdvancedLogging && dsn != "" {
|
||||||
return errs
|
var err error
|
||||||
}
|
logConfigSrc, err = config.NewLogConfigSrc(dsn, s.configStore)
|
||||||
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 err != nil {
|
if err != nil {
|
||||||
errs = multierror.Append(err)
|
return fmt.Errorf("invalid config source for audit, %w", err)
|
||||||
continue
|
|
||||||
}
|
|
||||||
if target != nil {
|
|
||||||
adt.AddTarget(target)
|
|
||||||
}
|
}
|
||||||
|
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 {
|
func (s *Server) onAuditTargetQueueFull(qname string, maxQSize int) bool {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/store"
|
"github.com/mattermost/mattermost-server/v6/store"
|
||||||
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
"github.com/mattermost/mattermost-server/v6/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestHelper struct {
|
type TestHelper struct {
|
||||||
@@ -172,12 +171,10 @@ func (th *TestHelper) CreateTeam() *model.Team {
|
|||||||
Type: model.TeamOpen,
|
Type: model.TeamOpen,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err error
|
var err error
|
||||||
if team, err = th.store.Team().Save(team); err != nil {
|
if team, err = th.store.Team().Save(team); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return team
|
return team
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,13 +189,11 @@ func (th *TestHelper) createChannel(team *model.Team, channelType string) *model
|
|||||||
CreatorId: th.BasicUser.Id,
|
CreatorId: th.BasicUser.Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err error
|
var err error
|
||||||
if channel, err = th.store.Channel().Save(channel, *th.configStore.Get().TeamSettings.MaxChannelsPerTeam); err != nil {
|
if channel, err = th.store.Channel().Save(channel, *th.configStore.Get().TeamSettings.MaxChannelsPerTeam); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,8 +25,6 @@ func TestMain(m *testing.M) {
|
|||||||
WithReadReplica: replicaFlag,
|
WithReadReplica: replicaFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
mlog.DisableZap()
|
|
||||||
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var randomJPEG []byte
|
var randomJPEG []byte
|
||||||
@@ -59,8 +58,7 @@ func BenchmarkUploadFile(b *testing.B) {
|
|||||||
prepareTestImages(b)
|
prepareTestImages(b)
|
||||||
th := Setup(b).InitBasic()
|
th := Setup(b).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
// disable logging in the benchmark, as best we can
|
|
||||||
th.App.Log().SetConsoleLevel(mlog.LevelError)
|
|
||||||
teamID := model.NewId()
|
teamID := model.NewId()
|
||||||
channelID := model.NewId()
|
channelID := model.NewId()
|
||||||
userID := model.NewId()
|
userID := model.NewId()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@@ -27,7 +26,6 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
|
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
|
||||||
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
"github.com/mattermost/mattermost-server/v6/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestHelper struct {
|
type TestHelper struct {
|
||||||
@@ -41,7 +39,8 @@ type TestHelper struct {
|
|||||||
BasicPost *model.Post
|
BasicPost *model.Post
|
||||||
|
|
||||||
SystemAdminUser *model.User
|
SystemAdminUser *model.User
|
||||||
LogBuffer *bytes.Buffer
|
LogBuffer *mlog.Buffer
|
||||||
|
TestLogger *mlog.Logger
|
||||||
IncludeCacheLayer bool
|
IncludeCacheLayer bool
|
||||||
|
|
||||||
tempWorkspace string
|
tempWorkspace string
|
||||||
@@ -55,16 +54,16 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
|
|||||||
|
|
||||||
configStore := config.NewTestMemoryStore()
|
configStore := config.NewTestMemoryStore()
|
||||||
|
|
||||||
config := configStore.Get()
|
memoryConfig := configStore.Get()
|
||||||
*config.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins")
|
*memoryConfig.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins")
|
||||||
*config.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp")
|
*memoryConfig.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp")
|
||||||
*config.PluginSettings.AutomaticPrepackagedPlugins = false
|
*memoryConfig.PluginSettings.AutomaticPrepackagedPlugins = false
|
||||||
*config.LogSettings.EnableSentry = false // disable error reporting during tests
|
*memoryConfig.LogSettings.EnableSentry = false // disable error reporting during tests
|
||||||
*config.AnnouncementSettings.AdminNoticesEnabled = false
|
*memoryConfig.AnnouncementSettings.AdminNoticesEnabled = false
|
||||||
*config.AnnouncementSettings.UserNoticesEnabled = false
|
*memoryConfig.AnnouncementSettings.UserNoticesEnabled = false
|
||||||
configStore.Set(config)
|
configStore.Set(memoryConfig)
|
||||||
|
|
||||||
buffer := &bytes.Buffer{}
|
buffer := &mlog.Buffer{}
|
||||||
|
|
||||||
var options []Option
|
var options []Option
|
||||||
options = append(options, ConfigStore(configStore))
|
options = append(options, ConfigStore(configStore))
|
||||||
@@ -80,7 +79,18 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
|
|||||||
} else {
|
} else {
|
||||||
options = append(options, StoreOverride(dbStore))
|
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...)
|
s, err := NewServer(options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -92,6 +102,7 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
|
|||||||
Context: &request.Context{},
|
Context: &request.Context{},
|
||||||
Server: s,
|
Server: s,
|
||||||
LogBuffer: buffer,
|
LogBuffer: buffer,
|
||||||
|
TestLogger: testLogger,
|
||||||
IncludeCacheLayer: includeCacheLayer,
|
IncludeCacheLayer: includeCacheLayer,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,12 +251,10 @@ func (th *TestHelper) CreateTeam() *model.Team {
|
|||||||
Type: model.TeamOpen,
|
Type: model.TeamOpen,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if team, err = th.App.CreateTeam(th.Context, team); err != nil {
|
if team, err = th.App.CreateTeam(th.Context, team); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return team
|
return team
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +277,6 @@ func (th *TestHelper) CreateUserOrGuest(guest bool) *model.User {
|
|||||||
EmailVerified: true,
|
EmailVerified: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if guest {
|
if guest {
|
||||||
if user, err = th.App.CreateGuest(th.Context, user); err != nil {
|
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)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +338,6 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT
|
|||||||
option(channel)
|
option(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var appErr *model.AppError
|
var appErr *model.AppError
|
||||||
if channel, appErr = th.App.CreateChannel(th.Context, channel, true); appErr != nil {
|
if channel, appErr = th.App.CreateChannel(th.Context, channel, true); appErr != nil {
|
||||||
panic(appErr)
|
panic(appErr)
|
||||||
@@ -353,29 +359,24 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
|
func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
var channel *model.Channel
|
var channel *model.Channel
|
||||||
if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil {
|
if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) CreateGroupChannel(user1 *model.User, user2 *model.User) *model.Channel {
|
func (th *TestHelper) CreateGroupChannel(user1 *model.User, user2 *model.User) *model.Channel {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
var channel *model.Channel
|
var channel *model.Channel
|
||||||
if channel, err = th.App.CreateGroupChannel([]string{th.BasicUser.Id, user1.Id, user2.Id}, th.BasicUser.Id); err != nil {
|
if channel, err = th.App.CreateGroupChannel([]string{th.BasicUser.Id, user1.Id, user2.Id}, th.BasicUser.Id); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,12 +390,10 @@ func (th *TestHelper) CreatePost(channel *model.Channel) *model.Post {
|
|||||||
CreateAt: model.GetMillis() - 10000,
|
CreateAt: model.GetMillis() - 10000,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil {
|
if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return post
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,47 +405,32 @@ func (th *TestHelper) CreateMessagePost(channel *model.Channel, message string)
|
|||||||
CreateAt: model.GetMillis() - 10000,
|
CreateAt: model.GetMillis() - 10000,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil {
|
if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return post
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
_, err := th.App.JoinUserToTeam(th.Context, team, user, "")
|
_, err := th.App.JoinUserToTeam(th.Context, team, user, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) RemoveUserFromTeam(user *model.User, team *model.Team) {
|
func (th *TestHelper) RemoveUserFromTeam(user *model.User, team *model.Team) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
err := th.App.RemoveUserFromTeam(th.Context, team.Id, user.Id, "")
|
err := th.App.RemoveUserFromTeam(th.Context, team.Id, user.Id, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
member, err := th.App.AddUserToChannel(user, channel, false)
|
member, err := th.App.AddUserToChannel(user, channel, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
|
|
||||||
return member
|
return member
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,8 +440,6 @@ func (th *TestHelper) CreateRole(roleName string) *model.Role {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) CreateScheme() (*model.Scheme, []*model.Role) {
|
func (th *TestHelper) CreateScheme() (*model.Scheme, []*model.Role) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
scheme, err := th.App.CreateScheme(&model.Scheme{
|
scheme, err := th.App.CreateScheme(&model.Scheme{
|
||||||
DisplayName: "Test Scheme Display Name",
|
DisplayName: "Test Scheme Display Name",
|
||||||
Name: model.NewId(),
|
Name: model.NewId(),
|
||||||
@@ -485,9 +467,6 @@ func (th *TestHelper) CreateScheme() (*model.Scheme, []*model.Role) {
|
|||||||
}
|
}
|
||||||
roles = append(roles, role)
|
roles = append(roles, role)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
|
|
||||||
return scheme, roles
|
return scheme, roles
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,18 +480,14 @@ func (th *TestHelper) CreateGroup() *model.Group {
|
|||||||
RemoteId: model.NewId(),
|
RemoteId: model.NewId(),
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if group, err = th.App.CreateGroup(group); err != nil {
|
if group, err = th.App.CreateGroup(group); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return group
|
return group
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) CreateEmoji() *model.Emoji {
|
func (th *TestHelper) CreateEmoji() *model.Emoji {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
emoji, err := th.App.Srv().Store.Emoji().Save(&model.Emoji{
|
emoji, err := th.App.Srv().Store.Emoji().Save(&model.Emoji{
|
||||||
CreatorId: th.BasicUser.Id,
|
CreatorId: th.BasicUser.Id,
|
||||||
Name: model.NewRandomString(10),
|
Name: model.NewRandomString(10),
|
||||||
@@ -520,15 +495,10 @@ func (th *TestHelper) CreateEmoji() *model.Emoji {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
|
|
||||||
return emoji
|
return emoji
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) AddReactionToPost(post *model.Post, user *model.User, emojiName string) *model.Reaction {
|
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{
|
reaction, err := th.App.SaveReactionForPost(th.Context, &model.Reaction{
|
||||||
UserId: user.Id,
|
UserId: user.Id,
|
||||||
PostId: post.Id,
|
PostId: post.Id,
|
||||||
@@ -537,9 +507,6 @@ func (th *TestHelper) AddReactionToPost(post *model.Post, user *model.User, emoj
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
|
|
||||||
return reaction
|
return reaction
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,11 +635,8 @@ func (th *TestHelper) SetupPluginAPI() *PluginAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) {
|
func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err1)
|
panic(err1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,7 +648,6 @@ func (th *TestHelper) RemovePermissionFromRole(permission string, roleName strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
|
if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,25 +655,18 @@ func (th *TestHelper) RemovePermissionFromRole(permission string, roleName strin
|
|||||||
|
|
||||||
_, err2 := th.App.UpdateRole(role)
|
_, err2 := th.App.UpdateRole(role)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err2)
|
panic(err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
|
func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err1)
|
panic(err1)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, existingPermission := range role.Permissions {
|
for _, existingPermission := range role.Permissions {
|
||||||
if existingPermission == permission {
|
if existingPermission == permission {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -719,11 +675,8 @@ func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
|
|||||||
|
|
||||||
_, err2 := th.App.UpdateRole(role)
|
_, err2 := th.App.UpdateRole(role)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err2)
|
panic(err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is copy of storetest/NewTestId
|
// This function is copy of storetest/NewTestId
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,8 +25,6 @@ func TestMain(m *testing.M) {
|
|||||||
WithReadReplica: replicaFlag,
|
WithReadReplica: replicaFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
mlog.DisableZap()
|
|
||||||
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -11251,7 +11251,7 @@ func (a *OpenTracingAppLayer) LogAuditRec(rec *audit.Record, err error) {
|
|||||||
a.app.LogAuditRec(rec, err)
|
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
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.LogAuditRecWithLevel")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.LogAuditRecWithLevel")
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ type PluginAPI struct {
|
|||||||
id string
|
id string
|
||||||
app *App
|
app *App
|
||||||
ctx *request.Context
|
ctx *request.Context
|
||||||
logger *mlog.SugarLogger
|
logger mlog.Sugar
|
||||||
manifest *model.Manifest
|
manifest *model.Manifest
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ func NewPluginAPI(a *App, c *request.Context, manifest *model.Manifest) *PluginA
|
|||||||
manifest: manifest,
|
manifest: manifest,
|
||||||
ctx: c,
|
ctx: c,
|
||||||
app: a,
|
app: a,
|
||||||
logger: a.Log().With(mlog.String("plugin_id", manifest.Id)).Sugar(),
|
logger: a.Log().Sugar(mlog.String("plugin_id", manifest.Id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -724,11 +724,14 @@ func TestPluginPanicLogs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, err := th.App.CreatePost(th.Context, post, th.BasicChannel, false, true)
|
_, err := th.App.CreatePost(th.Context, post, th.BasicChannel, false, true)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
th.TestLogger.Flush()
|
||||||
|
|
||||||
// We shutdown plugins first so that the read on the log buffer is race-free.
|
// We shutdown plugins first so that the read on the log buffer is race-free.
|
||||||
th.App.Srv().ShutDownPlugins()
|
th.App.Srv().ShutDownPlugins()
|
||||||
tearDown()
|
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")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -970,7 +970,7 @@ func TestCreatePostAsUser(t *testing.T) {
|
|||||||
_, appErr := th.App.CreatePostAsUser(th.Context, post, "", true)
|
_, appErr := th.App.CreatePostAsUser(th.Context, post, "", true)
|
||||||
require.Nil(t, appErr)
|
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) {
|
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)
|
_, appErr = th.App.CreatePostAsUser(th.Context, post, "", true)
|
||||||
require.Nil(t, appErr)
|
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) {
|
t.Run("marks channel as viewed for reply post when CRT is off", func(t *testing.T) {
|
||||||
|
|||||||
176
app/server.go
176
app/server.go
@@ -138,7 +138,6 @@ type Server struct {
|
|||||||
statusCache cache.Cache
|
statusCache cache.Cache
|
||||||
configListenerId string
|
configListenerId string
|
||||||
licenseListenerId string
|
licenseListenerId string
|
||||||
logListenerId string
|
|
||||||
clusterLeaderListenerId string
|
clusterLeaderListenerId string
|
||||||
searchConfigListenerId string
|
searchConfigListenerId string
|
||||||
searchLicenseListenerId string
|
searchLicenseListenerId string
|
||||||
@@ -146,8 +145,6 @@ type Server struct {
|
|||||||
configStore *config.Store
|
configStore *config.Store
|
||||||
postActionCookieSecret []byte
|
postActionCookieSecret []byte
|
||||||
|
|
||||||
advancedLogListenerCleanup func()
|
|
||||||
|
|
||||||
pluginCommands []*PluginCommand
|
pluginCommands []*PluginCommand
|
||||||
pluginCommandsLock sync.RWMutex
|
pluginCommandsLock sync.RWMutex
|
||||||
|
|
||||||
@@ -787,74 +784,79 @@ func (s *Server) DatabaseTypeAndMattermostVersion() (string, string) {
|
|||||||
return *s.Config().SqlSettings.DriverName, mattermostVersion.Value
|
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 {
|
func (s *Server) initLogging() error {
|
||||||
|
var err error
|
||||||
|
// create the app logger if needed
|
||||||
if s.Log == nil {
|
if s.Log == nil {
|
||||||
s.Log = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(&s.Config().LogSettings, utils.GetLogFileLocation))
|
s.Log, err = mlog.NewLogger()
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid advanced logging config, %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.Log.ConfigAdvancedLogging(cfg.Get()); err != nil {
|
// create notification logger if needed
|
||||||
return fmt.Errorf("error configuring advanced logging, %w", err)
|
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))
|
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.
|
||||||
listenerId := cfg.AddListener(func(_, newCfg mlog.LogTargetCfg) {
|
if !errors.Is(err, mlog.ErrConfigurationLock) {
|
||||||
if err := s.Log.ConfigAdvancedLogging(newCfg); err != nil {
|
// revert to default logger if the config is invalid
|
||||||
mlog.Error("Error re-configuring advanced logging", mlog.Err(err))
|
mlog.InitGlobalLogger(nil)
|
||||||
} else {
|
return err
|
||||||
mlog.Info("Re-configured advanced logging")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// In case initLogging is called more than once.
|
|
||||||
if s.advancedLogListenerCleanup != nil {
|
|
||||||
s.advancedLogListenerCleanup()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.advancedLogListenerCleanup = func() {
|
// Redirect default Go logger to app logger.
|
||||||
cfg.RemoveListener(listenerId)
|
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
|
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) {
|
func (s *Server) removeUnlicensedLogTargets(license *model.License) {
|
||||||
if license != nil && *license.Features.AdvancedLogging {
|
if license != nil && *license.Features.AdvancedLogging {
|
||||||
// advanced logging enabled via license; no need to remove any targets
|
// 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)
|
timeoutCtx, cancelCtx := context.WithTimeout(context.Background(), time.Second*10)
|
||||||
defer cancelCtx()
|
defer cancelCtx()
|
||||||
|
|
||||||
mlog.RemoveTargets(timeoutCtx, func(ti mlog.TargetInfo) bool {
|
s.Log.RemoveTargets(timeoutCtx, func(ti mlog.TargetInfo) bool {
|
||||||
return ti.Type != "*target.Writer" && ti.Type != "*target.File"
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mlog.EnableMetrics(s.Metrics.GetLoggerMetricsCollector()); err != nil {
|
s.Log.SetMetricsCollector(s.Metrics.GetLoggerMetricsCollector(), mlog.DefaultMetricsUpdateFreqMillis)
|
||||||
mlog.Error("Failed to enable advanced logging metrics", mlog.Err(err))
|
|
||||||
} else {
|
// logging config needs to be reloaded when metrics collector is added or changed.
|
||||||
mlog.Debug("Advanced logging metrics enabled")
|
if err := s.initLogging(); err != nil {
|
||||||
|
mlog.Error("Error re-configuring logging for metrics")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mlog.Debug("Logging metrics enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimeToWaitForConnectionsToCloseOnServerShutdown = time.Second
|
const TimeToWaitForConnectionsToCloseOnServerShutdown = time.Second
|
||||||
@@ -1014,13 +1024,7 @@ func (s *Server) Shutdown() {
|
|||||||
|
|
||||||
s.WaitForGoroutines()
|
s.WaitForGoroutines()
|
||||||
|
|
||||||
if s.advancedLogListenerCleanup != nil {
|
|
||||||
s.advancedLogListenerCleanup()
|
|
||||||
s.advancedLogListenerCleanup = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
s.RemoveConfigListener(s.configListenerId)
|
s.RemoveConfigListener(s.configListenerId)
|
||||||
s.RemoveConfigListener(s.logListenerId)
|
|
||||||
s.stopSearchEngine()
|
s.stopSearchEngine()
|
||||||
|
|
||||||
s.Audit.Shutdown()
|
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()
|
s.dndTaskMut.Lock()
|
||||||
if s.dndTask != nil {
|
if s.dndTask != nil {
|
||||||
s.dndTask.Cancel()
|
s.dndTask.Cancel()
|
||||||
@@ -1072,10 +1070,15 @@ func (s *Server) Shutdown() {
|
|||||||
|
|
||||||
mlog.Info("Server stopped")
|
mlog.Info("Server stopped")
|
||||||
|
|
||||||
// this should just write the "server stopped" record, the rest are already flushed.
|
// shutdown main and notification loggers which will flush any remaining log records.
|
||||||
timeoutCtx2, timeoutCancel2 := context.WithTimeout(context.Background(), time.Second*5)
|
timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), time.Second*15)
|
||||||
defer timeoutCancel2()
|
defer timeoutCancel()
|
||||||
_ = mlog.ShutdownAdvancedLogging(timeoutCtx2)
|
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 {
|
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 we have debugging of CORS turned on then forward messages to logs
|
||||||
if debug {
|
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)
|
handler = corsWrapper.Handler(handler)
|
||||||
@@ -1222,10 +1225,7 @@ func (s *Server) Start() error {
|
|||||||
s.Busy = NewBusy(s.Cluster)
|
s.Busy = NewBusy(s.Cluster)
|
||||||
|
|
||||||
// Creating a logger for logging errors from http.Server at error level
|
// Creating a logger for logging errors from http.Server at error level
|
||||||
errStdLog, err := s.Log.StdLogAt(mlog.LevelError, mlog.String("source", "httpserver"))
|
errStdLog := s.Log.With(mlog.String("source", "httpserver")).StdLogger(mlog.LvlError)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Server = &http.Server{
|
s.Server = &http.Server{
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
@@ -1270,7 +1270,7 @@ func (s *Server) Start() error {
|
|||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: httpListenAddress,
|
Addr: httpListenAddress,
|
||||||
Handler: m.HTTPHandler(nil),
|
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()
|
go server.ListenAndServe()
|
||||||
} else {
|
} else {
|
||||||
@@ -1284,7 +1284,7 @@ func (s *Server) Start() error {
|
|||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Handler: http.HandlerFunc(handleHTTPRedirect),
|
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)
|
server.Serve(redirectListener)
|
||||||
}()
|
}()
|
||||||
@@ -2026,7 +2026,7 @@ func (a *App) getNotificationsLog() (*model.FileData, string) {
|
|||||||
// Getting notifications.log
|
// Getting notifications.log
|
||||||
if *a.Srv().Config().NotificationLogSettings.EnableFile {
|
if *a.Srv().Config().NotificationLogSettings.EnableFile {
|
||||||
// notifications.log
|
// notifications.log
|
||||||
notificationsLog := utils.GetNotificationsLogFileLocation(*a.Srv().Config().LogSettings.FileLocation)
|
notificationsLog := config.GetNotificationsLogFileLocation(*a.Srv().Config().LogSettings.FileLocation)
|
||||||
|
|
||||||
notificationsLogFileData, notificationsLogFileDataErr := ioutil.ReadFile(notificationsLog)
|
notificationsLogFileData, notificationsLogFileDataErr := ioutil.ReadFile(notificationsLog)
|
||||||
|
|
||||||
@@ -2053,7 +2053,7 @@ func (a *App) getMattermostLog() (*model.FileData, string) {
|
|||||||
// Getting mattermost.log
|
// Getting mattermost.log
|
||||||
if *a.Srv().Config().LogSettings.EnableFile {
|
if *a.Srv().Config().LogSettings.EnableFile {
|
||||||
// mattermost.log
|
// mattermost.log
|
||||||
mattermostLog := utils.GetLogFileLocation(*a.Srv().Config().LogSettings.FileLocation)
|
mattermostLog := config.GetLogFileLocation(*a.Srv().Config().LogSettings.FileLocation)
|
||||||
|
|
||||||
mattermostLogFileData, mattermostLogFileDataErr := ioutil.ReadFile(mattermostLog)
|
mattermostLogFileData, mattermostLogFileDataErr := ioutil.ReadFile(mattermostLog)
|
||||||
|
|
||||||
|
|||||||
@@ -516,29 +516,29 @@ func checkEndpoint(t *testing.T, client *http.Client, url string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPanicLog(t *testing.T) {
|
func TestPanicLog(t *testing.T) {
|
||||||
// Creating a temp file to collect logs
|
// Creating a temp dir for log
|
||||||
tmpfile, err := ioutil.TempFile("", "mlog")
|
tmpDir, err := os.MkdirTemp("", "mlog-test")
|
||||||
if err != nil {
|
require.NoError(t, err, "cannot create tmp dir for log file")
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
require.NoError(t, tmpfile.Close())
|
err2 := os.RemoveAll(tmpDir)
|
||||||
require.NoError(t, os.Remove(tmpfile.Name()))
|
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
|
// Creating logger to log to console and temp file
|
||||||
logger := mlog.NewLogger(&mlog.LoggerConfiguration{
|
logger, _ := mlog.NewLogger()
|
||||||
EnableConsole: true,
|
|
||||||
ConsoleJson: true,
|
logSettings := model.NewLogSettings()
|
||||||
EnableFile: true,
|
logSettings.EnableConsole = model.NewBool(true)
|
||||||
FileLocation: tmpfile.Name(),
|
logSettings.ConsoleJson = model.NewBool(true)
|
||||||
FileLevel: mlog.LevelInfo,
|
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
|
// Creating a server with logger
|
||||||
s, err := NewServer(SetLogger(logger))
|
s, err := NewServer(SetLogger(logger))
|
||||||
@@ -567,16 +567,22 @@ func TestPanicLog(t *testing.T) {
|
|||||||
|
|
||||||
client := &http.Client{Transport: tr}
|
client := &http.Client{Transport: tr}
|
||||||
client.Get("https://localhost:" + strconv.Itoa(s.ListenAddr.Port) + "/panic")
|
client.Get("https://localhost:" + strconv.Itoa(s.ListenAddr.Port) + "/panic")
|
||||||
|
|
||||||
|
err = logger.Flush()
|
||||||
|
assert.NoError(t, err, "flush should succeed")
|
||||||
s.Shutdown()
|
s.Shutdown()
|
||||||
|
|
||||||
// Checking whether panic was logged
|
// Checking whether panic was logged
|
||||||
var panicLogged = false
|
var panicLogged = false
|
||||||
var infoLogged = 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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
scanner := bufio.NewScanner(tmpfile)
|
scanner := bufio.NewScanner(logFile)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
if !infoLogged && strings.Contains(scanner.Text(), "inside panic handler") {
|
if !infoLogged && strings.Contains(scanner.Text(), "inside panic handler") {
|
||||||
infoLogged = true
|
infoLogged = true
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
"github.com/mattermost/mattermost-server/v6/store"
|
"github.com/mattermost/mattermost-server/v6/store"
|
||||||
"github.com/mattermost/mattermost-server/v6/store/localcachelayer"
|
"github.com/mattermost/mattermost-server/v6/store/localcachelayer"
|
||||||
"github.com/mattermost/mattermost-server/v6/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestHelper struct {
|
type TestHelper struct {
|
||||||
@@ -36,6 +35,7 @@ type TestHelper struct {
|
|||||||
|
|
||||||
SystemAdminUser *model.User
|
SystemAdminUser *model.User
|
||||||
LogBuffer *bytes.Buffer
|
LogBuffer *bytes.Buffer
|
||||||
|
TestLogger *mlog.Logger
|
||||||
IncludeCacheLayer bool
|
IncludeCacheLayer bool
|
||||||
|
|
||||||
tempWorkspace string
|
tempWorkspace string
|
||||||
@@ -49,15 +49,15 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
|
|||||||
|
|
||||||
memoryStore := config.NewTestMemoryStore()
|
memoryStore := config.NewTestMemoryStore()
|
||||||
|
|
||||||
config := memoryStore.Get()
|
memoryConfig := memoryStore.Get()
|
||||||
if configSet != nil {
|
if configSet != nil {
|
||||||
configSet(config)
|
configSet(memoryConfig)
|
||||||
}
|
}
|
||||||
*config.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins")
|
*memoryConfig.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins")
|
||||||
*config.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp")
|
*memoryConfig.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp")
|
||||||
*config.PluginSettings.AutomaticPrepackagedPlugins = false
|
*memoryConfig.PluginSettings.AutomaticPrepackagedPlugins = false
|
||||||
*config.LogSettings.EnableSentry = false // disable error reporting during tests
|
*memoryConfig.LogSettings.EnableSentry = false // disable error reporting during tests
|
||||||
memoryStore.Set(config)
|
memoryStore.Set(memoryConfig)
|
||||||
|
|
||||||
buffer := &bytes.Buffer{}
|
buffer := &bytes.Buffer{}
|
||||||
|
|
||||||
@@ -74,7 +74,15 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
|
|||||||
} else {
|
} else {
|
||||||
options = append(options, app.StoreOverride(dbStore))
|
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...)
|
s, err := app.NewServer(options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -86,6 +94,7 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
|
|||||||
Context: &request.Context{},
|
Context: &request.Context{},
|
||||||
Server: s,
|
Server: s,
|
||||||
LogBuffer: buffer,
|
LogBuffer: buffer,
|
||||||
|
TestLogger: testLogger,
|
||||||
IncludeCacheLayer: includeCacheLayer,
|
IncludeCacheLayer: includeCacheLayer,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,12 +197,11 @@ func (th *TestHelper) createTeam() *model.Team {
|
|||||||
Type: model.TeamOpen,
|
Type: model.TeamOpen,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if team, err = th.App.CreateTeam(th.Context, team); err != nil {
|
if team, err = th.App.CreateTeam(th.Context, team); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return team
|
return team
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +224,6 @@ func (th *TestHelper) createUserOrGuest(guest bool) *model.User {
|
|||||||
EmailVerified: true,
|
EmailVerified: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if guest {
|
if guest {
|
||||||
if user, err = th.App.CreateGuest(th.Context, user); err != nil {
|
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)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +268,6 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT
|
|||||||
option(channel)
|
option(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if channel, err = th.App.CreateChannel(th.Context, channel, true); err != nil {
|
if channel, err = th.App.CreateChannel(th.Context, channel, true); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -284,7 +289,6 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,34 +303,28 @@ func (th *TestHelper) createChannelWithAnotherUser(team *model.Team, channelType
|
|||||||
CreatorId: userID,
|
CreatorId: userID,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if channel, err = th.App.CreateChannel(th.Context, channel, true); err != nil {
|
if channel, err = th.App.CreateChannel(th.Context, channel, true); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) createDmChannel(user *model.User) *model.Channel {
|
func (th *TestHelper) createDmChannel(user *model.User) *model.Channel {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
var channel *model.Channel
|
var channel *model.Channel
|
||||||
if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil {
|
if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) createGroupChannel(user1 *model.User, user2 *model.User) *model.Channel {
|
func (th *TestHelper) createGroupChannel(user1 *model.User, user2 *model.User) *model.Channel {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
var channel *model.Channel
|
var channel *model.Channel
|
||||||
if channel, err = th.App.CreateGroupChannel([]string{th.BasicUser.Id, user1.Id, user2.Id}, th.BasicUser.Id); err != nil {
|
if channel, err = th.App.CreateGroupChannel([]string{th.BasicUser.Id, user1.Id, user2.Id}, th.BasicUser.Id); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,36 +338,25 @@ func (th *TestHelper) createPost(channel *model.Channel) *model.Post {
|
|||||||
CreateAt: model.GetMillis() - 10000,
|
CreateAt: model.GetMillis() - 10000,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil {
|
if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return post
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) linkUserToTeam(user *model.User, team *model.Team) {
|
func (th *TestHelper) linkUserToTeam(user *model.User, team *model.Team) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
_, err := th.App.JoinUserToTeam(th.Context, team, user, "")
|
_, err := th.App.JoinUserToTeam(th.Context, team, user, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) addUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
func (th *TestHelper) addUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
member, err := th.App.AddUserToChannel(user, channel, false)
|
member, err := th.App.AddUserToChannel(user, channel, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
|
|
||||||
return member
|
return member
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,11 +388,8 @@ func (th *TestHelper) tearDown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) removePermissionFromRole(permission string, roleName string) {
|
func (th *TestHelper) removePermissionFromRole(permission string, roleName string) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err1)
|
panic(err1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +401,6 @@ func (th *TestHelper) removePermissionFromRole(permission string, roleName strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
|
if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,25 +408,18 @@ func (th *TestHelper) removePermissionFromRole(permission string, roleName strin
|
|||||||
|
|
||||||
_, err2 := th.App.UpdateRole(role)
|
_, err2 := th.App.UpdateRole(role)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err2)
|
panic(err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) addPermissionToRole(permission string, roleName string) {
|
func (th *TestHelper) addPermissionToRole(permission string, roleName string) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err1)
|
panic(err1)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, existingPermission := range role.Permissions {
|
for _, existingPermission := range role.Permissions {
|
||||||
if existingPermission == permission {
|
if existingPermission == permission {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,9 +428,6 @@ func (th *TestHelper) addPermissionToRole(permission string, roleName string) {
|
|||||||
|
|
||||||
_, err2 := th.App.UpdateRole(role)
|
_, err2 := th.App.UpdateRole(role)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
panic(err2)
|
panic(err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package slashcommands
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,8 +17,6 @@ func TestMain(m *testing.M) {
|
|||||||
EnableResources: true,
|
EnableResources: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
mlog.DisableZap()
|
|
||||||
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
|
|
||||||
|
|||||||
150
audit/audit.go
150
audit/audit.go
@@ -5,17 +5,12 @@ package audit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/mattermost/logr"
|
|
||||||
"github.com/mattermost/logr/format"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Audit struct {
|
type Audit struct {
|
||||||
lgr *logr.Logr
|
logger *mlog.Logger
|
||||||
logger logr.Logger
|
|
||||||
|
|
||||||
// OnQueueFull is called on an attempt to add an audit record to a full queue.
|
// 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.
|
// 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) {
|
func (a *Audit) Init(maxQueueSize int) {
|
||||||
a.lgr = &logr.Logr{MaxQueueSize: maxQueueSize}
|
a.logger, _ = mlog.NewLogger(
|
||||||
a.logger = a.lgr.NewLogger()
|
mlog.MaxQueueSize(maxQueueSize),
|
||||||
|
mlog.OnLoggerError(a.onLoggerError),
|
||||||
a.lgr.OnQueueFull = a.onQueueFull
|
mlog.OnQueueFull(a.onQueueFull),
|
||||||
a.lgr.OnTargetQueueFull = a.onTargetQueueFull
|
mlog.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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogRecord emits an audit record with complete info.
|
// LogRecord emits an audit record with complete info.
|
||||||
func (a *Audit) LogRecord(level mlog.LogLevel, rec Record) {
|
func (a *Audit) LogRecord(level mlog.Level, rec Record) {
|
||||||
flds := logr.Fields{}
|
flds := []mlog.Field{
|
||||||
flds[KeyAPIPath] = rec.APIPath
|
mlog.String(KeyAPIPath, rec.APIPath),
|
||||||
flds[KeyEvent] = rec.Event
|
mlog.String(KeyEvent, rec.Event),
|
||||||
flds[KeyStatus] = rec.Status
|
mlog.String(KeyStatus, rec.Status),
|
||||||
flds[KeyUserID] = rec.UserID
|
mlog.String(KeyUserID, rec.UserID),
|
||||||
flds[KeySessionID] = rec.SessionID
|
mlog.String(KeySessionID, rec.SessionID),
|
||||||
flds[KeyClient] = rec.Client
|
mlog.String(KeyClient, rec.Client),
|
||||||
flds[KeyIPAddress] = rec.IPAddress
|
mlog.String(KeyIPAddress, rec.IPAddress),
|
||||||
|
|
||||||
for k, v := range rec.Meta {
|
|
||||||
flds[k] = v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
l := a.logger.WithFields(flds)
|
for k, v := range rec.Meta {
|
||||||
l.Log(logr.Level(level))
|
flds = append(flds, mlog.Any(k, v))
|
||||||
|
}
|
||||||
|
a.logger.Log(level, "", flds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log emits an audit record based on minimum required info.
|
// 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{
|
a.LogRecord(level, Record{
|
||||||
APIPath: path,
|
APIPath: path,
|
||||||
Event: evt,
|
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.
|
// Configure sets zero or more target to output audit logs to.
|
||||||
func (a *Audit) AddTarget(target logr.Target) {
|
func (a *Audit) Configure(cfg mlog.LoggerConfiguration) error {
|
||||||
a.lgr.AddTarget(target)
|
return a.logger.ConfigureTargets(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown cleanly stops the audit engine after making best efforts to flush all targets.
|
// Flush attempts to write all queued audit records to all targets.
|
||||||
func (a *Audit) Shutdown() {
|
func (a *Audit) Flush() error {
|
||||||
err := a.lgr.Shutdown()
|
err := a.logger.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.onLoggerError(err)
|
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 {
|
if a.OnQueueFull != nil {
|
||||||
return a.OnQueueFull("main", maxQueueSize)
|
return a.OnQueueFull("main", maxQueueSize)
|
||||||
}
|
}
|
||||||
@@ -107,7 +90,7 @@ func (a *Audit) onQueueFull(rec *logr.LogRec, maxQueueSize int) bool {
|
|||||||
return true
|
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 {
|
if a.OnQueueFull != nil {
|
||||||
return a.OnQueueFull(fmt.Sprintf("%v", target), maxQueueSize)
|
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) {
|
func (a *Audit) onLoggerError(err error) {
|
||||||
if a.OnError != nil {
|
if a.OnError != nil {
|
||||||
a.OnError(err)
|
a.OnError(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
mlog.Error("Auditing error", mlog.Err(err))
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/api4"
|
"github.com/mattermost/mattermost-server/v6/api4"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,8 +27,6 @@ func TestMain(m *testing.M) {
|
|||||||
EnableResources: true,
|
EnableResources: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
mlog.DisableZap()
|
|
||||||
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
api4.SetMainHelper(mainHelper)
|
api4.SetMainHelper(mainHelper)
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func (e *logSrcEmitter) RemoveListener(id string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// invokeConfigListeners synchronously notifies all listeners about the configuration change.
|
// 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 {
|
e.listeners.Range(func(key, value interface{}) bool {
|
||||||
listener := value.(LogSrcListener)
|
listener := value.(LogSrcListener)
|
||||||
listener(oldCfg, newCfg)
|
listener(oldCfg, newCfg)
|
||||||
|
|||||||
@@ -56,18 +56,18 @@ func TestEmitter(t *testing.T) {
|
|||||||
func TestLogSrcEmitter(t *testing.T) {
|
func TestLogSrcEmitter(t *testing.T) {
|
||||||
var e logSrcEmitter
|
var e logSrcEmitter
|
||||||
|
|
||||||
expectedOldCfg := make(mlog.LogTargetCfg)
|
expectedOldCfg := make(mlog.LoggerConfiguration)
|
||||||
expectedNewCfg := make(mlog.LogTargetCfg)
|
expectedNewCfg := make(mlog.LoggerConfiguration)
|
||||||
|
|
||||||
listener1 := false
|
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, expectedOldCfg, oldCfg)
|
||||||
assert.Equal(t, expectedNewCfg, newCfg)
|
assert.Equal(t, expectedNewCfg, newCfg)
|
||||||
listener1 = true
|
listener1 = true
|
||||||
})
|
})
|
||||||
|
|
||||||
listener2 := false
|
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, expectedOldCfg, oldCfg)
|
||||||
assert.Equal(t, expectedNewCfg, newCfg)
|
assert.Equal(t, expectedNewCfg, newCfg)
|
||||||
listener2 = true
|
listener2 = true
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package config
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -14,23 +13,17 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"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
|
// LogConfigSrc abstracts the Advanced Logging configuration so that implementations can
|
||||||
// fetch from file, database, etc.
|
// fetch from file, database, etc.
|
||||||
type LogConfigSrc interface {
|
type LogConfigSrc interface {
|
||||||
// Get fetches the current, cached configuration.
|
// Get fetches the current, cached configuration.
|
||||||
Get() mlog.LogTargetCfg
|
Get() mlog.LoggerConfiguration
|
||||||
|
|
||||||
// Set updates the dsn specifying the source and reloads
|
// Set updates the dsn specifying the source and reloads
|
||||||
Set(dsn string, configStore *Store) (err error)
|
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 cleans up resources.
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
@@ -38,6 +31,10 @@ type LogConfigSrc interface {
|
|||||||
// NewLogConfigSrc creates an advanced logging configuration source, backed by a
|
// NewLogConfigSrc creates an advanced logging configuration source, backed by a
|
||||||
// file, JSON string, or database.
|
// file, JSON string, or database.
|
||||||
func NewLogConfigSrc(dsn string, configStore *Store) (LogConfigSrc, error) {
|
func NewLogConfigSrc(dsn string, configStore *Store) (LogConfigSrc, error) {
|
||||||
|
if dsn == "" {
|
||||||
|
return nil, errors.New("dsn should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
if configStore == nil {
|
if configStore == nil {
|
||||||
return nil, errors.New("configStore should not be 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 {
|
type jsonSrc struct {
|
||||||
logSrcEmitter
|
logSrcEmitter
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
cfg mlog.LogTargetCfg
|
cfg mlog.LoggerConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
func newJSONSrc(data string) (*jsonSrc, error) {
|
func newJSONSrc(data string) (*jsonSrc, error) {
|
||||||
@@ -72,7 +69,7 @@ func newJSONSrc(data string) (*jsonSrc, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get fetches the current, cached configuration
|
// Get fetches the current, cached configuration
|
||||||
func (src *jsonSrc) Get() mlog.LogTargetCfg {
|
func (src *jsonSrc) Get() mlog.LoggerConfiguration {
|
||||||
src.mutex.RLock()
|
src.mutex.RLock()
|
||||||
defer src.mutex.RUnlock()
|
defer src.mutex.RUnlock()
|
||||||
return src.cfg
|
return src.cfg
|
||||||
@@ -89,7 +86,7 @@ func (src *jsonSrc) Set(data string, _ *Store) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *jsonSrc) set(cfg mlog.LogTargetCfg) {
|
func (src *jsonSrc) set(cfg mlog.LoggerConfiguration) {
|
||||||
src.mutex.Lock()
|
src.mutex.Lock()
|
||||||
defer src.mutex.Unlock()
|
defer src.mutex.Unlock()
|
||||||
|
|
||||||
@@ -106,11 +103,9 @@ func (src *jsonSrc) Close() error {
|
|||||||
// fileSrc
|
// fileSrc
|
||||||
|
|
||||||
type fileSrc struct {
|
type fileSrc struct {
|
||||||
logSrcEmitter
|
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
cfg mlog.LogTargetCfg
|
cfg mlog.LoggerConfiguration
|
||||||
|
path string
|
||||||
path string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFileSrc(path string, configStore *Store) (*fileSrc, error) {
|
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
|
// Get fetches the current, cached configuration
|
||||||
func (src *fileSrc) Get() mlog.LogTargetCfg {
|
func (src *fileSrc) Get() mlog.LoggerConfiguration {
|
||||||
src.mutex.RLock()
|
src.mutex.RLock()
|
||||||
defer src.mutex.RUnlock()
|
defer src.mutex.RUnlock()
|
||||||
return src.cfg
|
return src.cfg
|
||||||
@@ -145,26 +140,14 @@ func (src *fileSrc) Set(path string, configStore *Store) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
src.set(cfg)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (src *fileSrc) set(cfg mlog.LogTargetCfg) {
|
func (src *fileSrc) set(cfg mlog.LoggerConfiguration) {
|
||||||
src.mutex.Lock()
|
src.mutex.Lock()
|
||||||
defer src.mutex.Unlock()
|
defer src.mutex.Unlock()
|
||||||
|
|
||||||
old := src.cfg
|
|
||||||
src.cfg = cfg
|
src.cfg = cfg
|
||||||
src.invokeConfigListeners(old, cfg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close cleans up resources.
|
// Close cleans up resources.
|
||||||
@@ -172,8 +155,8 @@ func (src *fileSrc) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func logTargetCfgFromJSON(data []byte) (mlog.LogTargetCfg, error) {
|
func logTargetCfgFromJSON(data []byte) (mlog.LoggerConfiguration, error) {
|
||||||
cfg := make(mlog.LogTargetCfg)
|
cfg := make(mlog.LoggerConfiguration)
|
||||||
err := json.Unmarshal(data, &cfg)
|
err := json.Unmarshal(data, &cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
216
config/logger.go
Обычный файл
216
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)
|
||||||
|
}
|
||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,8 +22,6 @@ func TestMain(m *testing.M) {
|
|||||||
EnableStore: true,
|
EnableStore: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
mlog.DisableZap()
|
|
||||||
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
package einterfaces
|
package einterfaces
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mattermost/logr"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MetricsInterface interface {
|
type MetricsInterface interface {
|
||||||
@@ -66,7 +66,7 @@ type MetricsInterface interface {
|
|||||||
ObservePluginAPIDuration(pluginID, apiName string, success bool, elapsed float64)
|
ObservePluginAPIDuration(pluginID, apiName string, success bool, elapsed float64)
|
||||||
|
|
||||||
ObserveEnabledUsers(users int64)
|
ObserveEnabledUsers(users int64)
|
||||||
GetLoggerMetricsCollector() logr.MetricsCollector
|
GetLoggerMetricsCollector() mlog.MetricsCollector
|
||||||
|
|
||||||
IncrementRemoteClusterMsgSentCounter(remoteID string)
|
IncrementRemoteClusterMsgSentCounter(remoteID string)
|
||||||
IncrementRemoteClusterMsgReceivedCounter(remoteID string)
|
IncrementRemoteClusterMsgReceivedCounter(remoteID string)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
logr "github.com/mattermost/logr"
|
logr "github.com/mattermost/logr/v2"
|
||||||
mock "github.com/stretchr/testify/mock"
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
model "github.com/mattermost/mattermost-server/v6/model"
|
model "github.com/mattermost/mattermost-server/v6/model"
|
||||||
|
|||||||
13
go.mod
13
go.mod
@@ -50,7 +50,7 @@ require (
|
|||||||
github.com/hashicorp/go-hclog v0.16.1
|
github.com/hashicorp/go-hclog v0.16.1
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.0 // indirect
|
github.com/hashicorp/go-immutable-radix v1.3.0 // indirect
|
||||||
github.com/hashicorp/go-msgpack v1.1.5 // 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-plugin v1.4.2
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||||
github.com/hashicorp/golang-lru v0.5.4 // 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/gosaml2 v0.3.3
|
||||||
github.com/mattermost/gziphandler v0.0.1
|
github.com/mattermost/gziphandler v0.0.1
|
||||||
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d
|
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/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0
|
||||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||||
github.com/mattn/go-runewidth 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/russellhaering/goxmldsig v1.1.0
|
||||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
||||||
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
|
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/smartystreets/assertions v1.0.0 // indirect
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.3
|
||||||
github.com/splitio/go-client/v6 v6.1.0
|
github.com/splitio/go-client/v6 v6.1.0
|
||||||
@@ -118,7 +119,7 @@ require (
|
|||||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.4
|
github.com/vmihailenco/msgpack/v5 v5.3.4
|
||||||
github.com/wiggin77/merror v1.0.3
|
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/willf/bitset v1.1.11 // indirect
|
||||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c
|
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c
|
||||||
github.com/yuin/goldmark v1.3.8
|
github.com/yuin/goldmark v1.3.8
|
||||||
@@ -126,7 +127,7 @@ require (
|
|||||||
go.opentelemetry.io/otel/internal/metric v0.21.0 // indirect
|
go.opentelemetry.io/otel/internal/metric v0.21.0 // indirect
|
||||||
go.uber.org/atomic v1.8.0 // indirect
|
go.uber.org/atomic v1.8.0 // indirect
|
||||||
go.uber.org/multierr v1.7.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/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||||
golang.org/x/image v0.0.0-20210622092929-e6eecd499c2c
|
golang.org/x/image v0.0.0-20210622092929-e6eecd499c2c
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
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/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
gopkg.in/mail.v2 v2.3.1
|
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/olivere/elastic.v6 v6.2.35
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
willnorris.com/go/imageproxy v0.10.0
|
willnorris.com/go/imageproxy v0.10.0
|
||||||
|
|||||||
14
go.sum
14
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/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 h1:6F/fM3csvH6Oy5sUpJuW7YyZSzZZAhJm5VcgKMxA2P8=
|
||||||
github.com/mattermost/logr v1.0.13/go.mod h1:Mt4DPu1NXMe6JxPdwCC0XBoxXmN9eXOIRPoZarU2PXs=
|
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 h1:G9tL6JXRBMzjuD1kkBtcnd42kUiT6QDwxfFYu7adM6o=
|
||||||
github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0/go.mod h1:nV5bfVpT//+B1RPD2JvRnxbkLmJEYXmRaaVl15fsXjs=
|
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=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ package migrations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/app"
|
"github.com/mattermost/mattermost-server/v6/app"
|
||||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||||
"github.com/mattermost/mattermost-server/v6/config"
|
"github.com/mattermost/mattermost-server/v6/config"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"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/store/localcachelayer"
|
||||||
"github.com/mattermost/mattermost-server/v6/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestHelper struct {
|
type TestHelper struct {
|
||||||
@@ -27,6 +28,8 @@ type TestHelper struct {
|
|||||||
SystemAdminUser *model.User
|
SystemAdminUser *model.User
|
||||||
|
|
||||||
tempWorkspace string
|
tempWorkspace string
|
||||||
|
|
||||||
|
TestLogger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupTestHelper(enterprise bool) *TestHelper {
|
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.StoreOverride(mainHelper.Store))
|
||||||
options = append(options, app.SkipPostInitializiation())
|
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...)
|
s, err := app.NewServer(options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -55,9 +67,10 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
th := &TestHelper{
|
th := &TestHelper{
|
||||||
App: app.New(app.ServerConnector(s)),
|
App: app.New(app.ServerConnector(s)),
|
||||||
Context: &request.Context{},
|
Context: &request.Context{},
|
||||||
Server: s,
|
Server: s,
|
||||||
|
TestLogger: testLogger,
|
||||||
}
|
}
|
||||||
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxUsersPerTeam = 50 })
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxUsersPerTeam = 50 })
|
||||||
@@ -89,11 +102,11 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
|||||||
return th
|
return th
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupEnterprise() *TestHelper {
|
func SetupEnterprise(tb testing.TB) *TestHelper {
|
||||||
return setupTestHelper(true)
|
return setupTestHelper(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Setup() *TestHelper {
|
func Setup(tb testing.TB) *TestHelper {
|
||||||
return setupTestHelper(false)
|
return setupTestHelper(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,12 +139,10 @@ func (th *TestHelper) CreateTeam() *model.Team {
|
|||||||
Type: model.TeamOpen,
|
Type: model.TeamOpen,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if team, err = th.App.CreateTeam(th.Context, team); err != nil {
|
if team, err = th.App.CreateTeam(th.Context, team); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return team
|
return team
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,12 +157,10 @@ func (th *TestHelper) CreateUser() *model.User {
|
|||||||
EmailVerified: true,
|
EmailVerified: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if user, err = th.App.CreateUser(th.Context, user); err != nil {
|
if user, err = th.App.CreateUser(th.Context, user); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,23 +179,19 @@ func (th *TestHelper) createChannel(team *model.Team, channelType model.ChannelT
|
|||||||
CreatorId: th.BasicUser.Id,
|
CreatorId: th.BasicUser.Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if channel, err = th.App.CreateChannel(th.Context, channel, true); err != nil {
|
if channel, err = th.App.CreateChannel(th.Context, channel, true); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
|
func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
var channel *model.Channel
|
var channel *model.Channel
|
||||||
if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil {
|
if channel, err = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, user.Id); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,36 +205,25 @@ func (th *TestHelper) CreatePost(channel *model.Channel) *model.Post {
|
|||||||
CreateAt: model.GetMillis() - 10000,
|
CreateAt: model.GetMillis() - 10000,
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil {
|
if post, err = th.App.CreatePost(th.Context, post, channel, false, true); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
return post
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
func (th *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
_, err := th.App.JoinUserToTeam(th.Context, team, user, "")
|
_, err := th.App.JoinUserToTeam(th.Context, team, user, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
func (th *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
||||||
utils.DisableDebugLogForTest()
|
|
||||||
|
|
||||||
member, err := th.App.AddUserToChannel(user, channel, false)
|
member, err := th.App.AddUserToChannel(user, channel, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.EnableDebugLogForTest()
|
|
||||||
|
|
||||||
return member
|
return member
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package migrations
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,8 +17,6 @@ func TestMain(m *testing.M) {
|
|||||||
EnableResources: true,
|
EnableResources: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
mlog.DisableZap()
|
|
||||||
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func TestGetMigrationState(t *testing.T) {
|
|||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
th := Setup()
|
th := Setup(t)
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
migrationKey := model.NewId()
|
migrationKey := model.NewId()
|
||||||
|
|||||||
@@ -1223,6 +1223,12 @@ type LogSettings struct {
|
|||||||
AdvancedLoggingConfig *string `access:"environment_logging,write_restrictable,cloud_restrictable"`
|
AdvancedLoggingConfig *string `access:"environment_logging,write_restrictable,cloud_restrictable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewLogSettings() *LogSettings {
|
||||||
|
settings := &LogSettings{}
|
||||||
|
settings.SetDefaults()
|
||||||
|
return settings
|
||||||
|
}
|
||||||
|
|
||||||
func (s *LogSettings) SetDefaults() {
|
func (s *LogSettings) SetDefaults() {
|
||||||
if s.EnableConsole == nil {
|
if s.EnableConsole == nil {
|
||||||
s.EnableConsole = NewBool(true)
|
s.EnableConsole = NewBool(true)
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ func (h *hclogAdapter) ResetNamed(name string) hclog.Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *hclogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.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 {
|
func (h *hclogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
|
||||||
|
|||||||
@@ -52,12 +52,8 @@ func testPluginHealthCheckSuccess(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
bundle := model.BundleInfoForPath(dir)
|
bundle := model.BundleInfoForPath(dir)
|
||||||
log := mlog.NewLogger(&mlog.LoggerConfiguration{
|
log := mlog.CreateConsoleTestLogger(true, mlog.LvlError)
|
||||||
EnableConsole: true,
|
defer log.Shutdown()
|
||||||
ConsoleJson: true,
|
|
||||||
ConsoleLevel: "error",
|
|
||||||
EnableFile: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -99,12 +95,8 @@ func testPluginHealthCheckPanic(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
bundle := model.BundleInfoForPath(dir)
|
bundle := model.BundleInfoForPath(dir)
|
||||||
log := mlog.NewLogger(&mlog.LoggerConfiguration{
|
log := mlog.CreateConsoleTestLogger(true, mlog.LvlError)
|
||||||
EnableConsole: true,
|
defer log.Shutdown()
|
||||||
ConsoleJson: true,
|
|
||||||
ConsoleLevel: "error",
|
|
||||||
EnableFile: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func newSupervisor(pluginInfo *model.BundleInfo, apiImpl API, driver Driver, par
|
|||||||
wrappedLogger := pluginInfo.WrapLogger(parentLogger)
|
wrappedLogger := pluginInfo.WrapLogger(parentLogger)
|
||||||
|
|
||||||
hclogAdaptedLogger := &hclogAdapter{
|
hclogAdaptedLogger := &hclogAdapter{
|
||||||
wrappedLogger: wrappedLogger.WithCallerSkip(1),
|
wrappedLogger: wrappedLogger,
|
||||||
extrasKey: "wrapped_extras",
|
extrasKey: "wrapped_extras",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
ioutil.WriteFile(filepath.Join(dir, "plugin.json"), []byte(`{"id": "foo", "server": {"executable": "/foo/../../backend.exe"}}`), 0600)
|
||||||
|
|
||||||
bundle := model.BundleInfoForPath(dir)
|
bundle := model.BundleInfoForPath(dir)
|
||||||
log := mlog.NewLogger(&mlog.LoggerConfiguration{
|
log := mlog.CreateConsoleTestLogger(true, mlog.LvlError)
|
||||||
EnableConsole: true,
|
defer log.Shutdown()
|
||||||
ConsoleJson: true,
|
|
||||||
ConsoleLevel: "error",
|
|
||||||
EnableFile: false,
|
|
||||||
})
|
|
||||||
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
||||||
assert.Nil(t, supervisor)
|
assert.Nil(t, supervisor)
|
||||||
assert.Error(t, err)
|
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)
|
ioutil.WriteFile(filepath.Join(dir, "plugin.json"), []byte(`{"id": "foo", "server": {"executable": "thisfileshouldnotexist"}}`), 0600)
|
||||||
|
|
||||||
bundle := model.BundleInfoForPath(dir)
|
bundle := model.BundleInfoForPath(dir)
|
||||||
log := mlog.NewLogger(&mlog.LoggerConfiguration{
|
log := mlog.CreateConsoleTestLogger(true, mlog.LvlError)
|
||||||
EnableConsole: true,
|
defer log.Shutdown()
|
||||||
ConsoleJson: true,
|
|
||||||
ConsoleLevel: "error",
|
|
||||||
EnableFile: false,
|
|
||||||
})
|
|
||||||
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Nil(t, supervisor)
|
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)
|
ioutil.WriteFile(filepath.Join(dir, "plugin.json"), []byte(`{"id": "foo", "server": {"executable": "backend.exe"}}`), 0600)
|
||||||
|
|
||||||
bundle := model.BundleInfoForPath(dir)
|
bundle := model.BundleInfoForPath(dir)
|
||||||
log := mlog.NewLogger(&mlog.LoggerConfiguration{
|
log := mlog.CreateConsoleTestLogger(true, mlog.LvlError)
|
||||||
EnableConsole: true,
|
defer log.Shutdown()
|
||||||
ConsoleJson: true,
|
|
||||||
ConsoleLevel: "error",
|
|
||||||
EnableFile: false,
|
|
||||||
})
|
|
||||||
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
supervisor, err := newSupervisor(bundle, nil, nil, log, nil)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Nil(t, supervisor)
|
require.Nil(t, supervisor)
|
||||||
|
|||||||
@@ -46,12 +46,7 @@ func makeLocalBackend(proxy *ImageProxy) *LocalBackend {
|
|||||||
impl := imageproxy.NewProxy(proxy.HTTPService.MakeTransport(false), nil)
|
impl := imageproxy.NewProxy(proxy.HTTPService.MakeTransport(false), nil)
|
||||||
|
|
||||||
if proxy.Logger != nil {
|
if proxy.Logger != nil {
|
||||||
logger, err := proxy.Logger.StdLogAt(mlog.LevelDebug, mlog.String("image_proxy", "local"))
|
impl.Logger = proxy.Logger.With(mlog.String("image_proxy", "local")).StdLogger(mlog.LvlDebug)
|
||||||
if err != nil {
|
|
||||||
mlog.Warn("Failed to initialize logger for image proxy", mlog.Err(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl.Logger = logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
baseURL, err := url.Parse(*proxy.ConfigService.Config().ServiceSettings.SiteURL)
|
baseURL, err := url.Parse(*proxy.ConfigService.Config().ServiceSettings.SiteURL)
|
||||||
|
|||||||
@@ -5,13 +5,8 @@ package remotecluster
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go.uber.org/zap/zapcore"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
|
"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
|
||||||
@@ -22,14 +17,16 @@ import (
|
|||||||
|
|
||||||
type mockServer struct {
|
type mockServer struct {
|
||||||
remotes []*model.RemoteCluster
|
remotes []*model.RemoteCluster
|
||||||
logger *mockLogger
|
logger *mlog.Logger
|
||||||
user *model.User
|
user *model.User
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMockServer(t *testing.T, remotes []*model.RemoteCluster) *mockServer {
|
func newMockServer(t *testing.T, remotes []*model.RemoteCluster) *mockServer {
|
||||||
|
testLogger := mlog.CreateTestLogger(t, nil, mlog.StdAll...)
|
||||||
|
|
||||||
return &mockServer{
|
return &mockServer{
|
||||||
remotes: remotes,
|
remotes: remotes,
|
||||||
logger: &mockLogger{t: t},
|
logger: testLogger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,88 +61,3 @@ func (ms *mockServer) GetStore() store.Store {
|
|||||||
return storeMock
|
return storeMock
|
||||||
}
|
}
|
||||||
func (ms *mockServer) Shutdown() { ms.logger.Shutdown() }
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type mockLogger struct {
|
|||||||
mlog.LoggerIFace
|
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) {
|
func TestOnReceiveChannelInvite(t *testing.T) {
|
||||||
t.Run("when msg payload is empty, it does nothing", func(t *testing.T) {
|
t.Run("when msg payload is empty, it does nothing", func(t *testing.T) {
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
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()))
|
panic(fmt.Sprintf("Failed to set current working directory to %s: %s", "../..", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
mlog.DisableZap()
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
err := os.Chdir(prevDir)
|
err := os.Chdir(prevDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1228,7 +1228,7 @@ func (ts *TelemetryService) trackChannelModeration() {
|
|||||||
func (ts *TelemetryService) initRudder(endpoint string, rudderKey string) {
|
func (ts *TelemetryService) initRudder(endpoint string, rudderKey string) {
|
||||||
if ts.rudderClient == nil {
|
if ts.rudderClient == nil {
|
||||||
config := rudder.Config{}
|
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
|
config.Endpoint = endpoint
|
||||||
// For testing
|
// For testing
|
||||||
if endpoint != RudderDataplaneURL {
|
if endpoint != RudderDataplaneURL {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"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/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/plugin"
|
"github.com/mattermost/mattermost-server/v6/plugin"
|
||||||
"github.com/mattermost/mattermost-server/v6/plugin/plugintest"
|
"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()) {
|
func initializeMocks(cfg *model.Config) (*mocks.ServerIface, *storeMocks.Store, func(t *testing.T), func()) {
|
||||||
serverIfaceMock := &mocks.ServerIface{}
|
serverIfaceMock := &mocks.ServerIface{}
|
||||||
|
logger, _ := mlog.NewLogger()
|
||||||
|
|
||||||
configService := &FakeConfigService{cfg}
|
configService := &FakeConfigService{cfg}
|
||||||
serverIfaceMock.On("Config").Return(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 },
|
func(m *model.Manifest) plugin.API { return pluginsAPIMock },
|
||||||
nil,
|
nil,
|
||||||
pluginDir, webappPluginDir,
|
pluginDir, webappPluginDir,
|
||||||
mlog.NewLogger(&mlog.LoggerConfiguration{}),
|
logger,
|
||||||
nil)
|
nil)
|
||||||
serverIfaceMock.On("GetPluginsEnvironment").Return(pluginEnv, nil)
|
serverIfaceMock.On("GetPluginsEnvironment").Return(pluginEnv, nil)
|
||||||
|
|
||||||
@@ -270,7 +272,14 @@ func TestRudderTelemetry(t *testing.T) {
|
|||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
defer deferredAssertions(t)
|
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.TelemetryID = telemetryID
|
||||||
telemetryService.rudderClient = nil
|
telemetryService.rudderClient = nil
|
||||||
telemetryService.initRudder(server.URL, RudderKey)
|
telemetryService.initRudder(server.URL, RudderKey)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,8 +25,6 @@ func TestMain(m *testing.M) {
|
|||||||
WithReadReplica: replicaFlag,
|
WithReadReplica: replicaFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
mlog.DisableZap()
|
|
||||||
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -33,12 +33,10 @@ type FileBackendTestSuite struct {
|
|||||||
func TestLocalFileBackendTestSuite(t *testing.T) {
|
func TestLocalFileBackendTestSuite(t *testing.T) {
|
||||||
// Setup a global logger to catch tests logging outside of app context
|
// 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.
|
// 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{
|
logger := mlog.CreateConsoleTestLogger(true, mlog.LvlError)
|
||||||
EnableConsole: true,
|
defer logger.Shutdown()
|
||||||
ConsoleJson: true,
|
|
||||||
ConsoleLevel: "error",
|
mlog.InitGlobalLogger(logger)
|
||||||
EnableFile: false,
|
|
||||||
}))
|
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "")
|
dir, err := ioutil.TempDir("", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -4,26 +4,32 @@
|
|||||||
package mlog
|
package mlog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/mattermost/logr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultLog manually encodes the log to STDERR, providing a basic, default logging implementation
|
// defaultLog manually encodes the log to STDERR, providing a basic, default logging implementation
|
||||||
// before mlog is fully configured.
|
// 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 {
|
log := struct {
|
||||||
Level string `json:"level"`
|
Level string `json:"level"`
|
||||||
Message string `json:"msg"`
|
Message string `json:"msg"`
|
||||||
Fields []Field `json:"fields,omitempty"`
|
Fields map[string]string `json:"fields,omitempty"`
|
||||||
}{
|
}{
|
||||||
level,
|
level.Name,
|
||||||
msg,
|
msg,
|
||||||
fields,
|
mFields,
|
||||||
}
|
}
|
||||||
|
|
||||||
if b, err := json.Marshal(log); err != nil {
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultDebugLog(msg string, fields ...Field) {
|
func defaultCustomMultiLog(lvl []Level, msg string, fields ...Field) {
|
||||||
defaultLog("debug", msg, fields...)
|
for _, level := range lvl {
|
||||||
|
defaultLog(level, msg, fields...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultInfoLog(msg string, fields ...Field) {
|
// shouldQuote returns true if val contains any characters that require quotations.
|
||||||
defaultLog("info", msg, fields...)
|
func shouldQuote(val string) bool {
|
||||||
}
|
for _, c := range val {
|
||||||
|
if !((c >= '0' && c <= '9') ||
|
||||||
func defaultWarnLog(msg string, fields ...Field) {
|
(c >= 'a' && c <= 'z') ||
|
||||||
defaultLog("warn", msg, fields...)
|
(c >= 'A' && c <= 'Z') ||
|
||||||
}
|
c == '-' || c == '.' || c == '_' || c == '/' || c == '@' || c == '^' || c == '+') {
|
||||||
|
return true
|
||||||
func defaultErrorLog(msg string, fields ...Field) {
|
}
|
||||||
defaultLog("error", msg, fields...)
|
}
|
||||||
}
|
return false
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -4,95 +4,119 @@
|
|||||||
package mlog
|
package mlog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"sync"
|
||||||
"log"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"github.com/mattermost/logr"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"go.uber.org/zap/zapcore"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var globalLogger *Logger
|
var (
|
||||||
|
globalLogger *Logger
|
||||||
|
muxGlobalLogger sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
func InitGlobalLogger(logger *Logger) {
|
func InitGlobalLogger(logger *Logger) {
|
||||||
// Clean up previous instance.
|
muxGlobalLogger.Lock()
|
||||||
if globalLogger != nil && globalLogger.logrLogger != nil {
|
defer muxGlobalLogger.Unlock()
|
||||||
globalLogger.logrLogger.Logr().Shutdown()
|
|
||||||
|
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
|
return logger.IsLevelEnabled(level)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// logWriterFunc provides access to mlog via io.Writer, so the standard logger
|
// Log emits the log record for any targets configured for the specified level.
|
||||||
// can be redirected to use mlog and whatever targets are defined.
|
func Log(level Level, msg string, fields ...Field) {
|
||||||
type logWriterFunc func([]byte) (int, error)
|
logger := getGlobalLogger()
|
||||||
|
if logger == nil {
|
||||||
func (lw logWriterFunc) Write(p []byte) (int, error) {
|
defaultLog(level, msg, fields...)
|
||||||
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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logger.Log(level, msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
writer := func(p []byte) (int, error) {
|
// LogM emits the log record for any targets configured for the specified levels.
|
||||||
Log(LvlStdLog, string(p))
|
// Equivalent to calling `Log` once for each level.
|
||||||
return len(p), nil
|
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
|
// Convenience method equivalent to calling `Log` with the `Trace` level.
|
||||||
type LogFunc func(string, ...Field)
|
func Trace(msg string, fields ...Field) {
|
||||||
type LogFuncCustom func(LogLevel, string, ...Field)
|
logger := getGlobalLogger()
|
||||||
type LogFuncCustomMulti func([]LogLevel, string, ...Field)
|
if logger == nil {
|
||||||
type FlushFunc func(context.Context) error
|
defaultLog(LvlTrace, msg, fields...)
|
||||||
type ConfigFunc func(cfg LogTargetCfg) error
|
return
|
||||||
type ShutdownFunc func(context.Context) error
|
}
|
||||||
type AddTargetFunc func(...logr.Target) error
|
logger.Trace(msg, fields...)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DON'T USE THIS Modify the level on the app logger
|
// Convenience method equivalent to calling `Log` with the `Debug` level.
|
||||||
func GloballyEnableDebugLogForTest() {
|
func Debug(msg string, fields ...Field) {
|
||||||
globalLogger.consoleLevel.SetLevel(zapcore.DebugLevel)
|
logger := getGlobalLogger()
|
||||||
|
if logger == nil {
|
||||||
|
defaultLog(LvlDebug, msg, fields...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug(msg, fields...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var IsLevelEnabled IsLevelEnabledFunc = defaultIsLevelEnabled
|
// Convenience method equivalent to calling `Log` with the `Info` level.
|
||||||
var Debug LogFunc = defaultDebugLog
|
func Info(msg string, fields ...Field) {
|
||||||
var Info LogFunc = defaultInfoLog
|
logger := getGlobalLogger()
|
||||||
var Warn LogFunc = defaultWarnLog
|
if logger == nil {
|
||||||
var Error LogFunc = defaultErrorLog
|
defaultLog(LvlInfo, msg, fields...)
|
||||||
var Critical LogFunc = defaultCriticalLog
|
return
|
||||||
var Log LogFuncCustom = defaultCustomLog
|
}
|
||||||
var LogM LogFuncCustomMulti = defaultCustomMultiLog
|
logger.Info(msg, fields...)
|
||||||
var Flush FlushFunc = defaultFlush
|
}
|
||||||
|
|
||||||
var ConfigAdvancedLogging ConfigFunc = defaultAdvancedConfig
|
// Convenience method equivalent to calling `Log` with the `Warn` level.
|
||||||
var ShutdownAdvancedLogging ShutdownFunc = defaultAdvancedShutdown
|
func Warn(msg string, fields ...Field) {
|
||||||
var AddTarget AddTargetFunc = defaultAddTarget
|
logger := getGlobalLogger()
|
||||||
var RemoveTargets RemoveTargetsFunc = defaultRemoveTargets
|
if logger == nil {
|
||||||
var EnableMetrics EnableMetricsFunc = defaultEnableMetrics
|
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...)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
package mlog_test
|
package mlog_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -29,83 +31,86 @@ func TestLoggingBeforeInitialized(t *testing.T) {
|
|||||||
|
|
||||||
func TestLoggingAfterInitialized(t *testing.T) {
|
func TestLoggingAfterInitialized(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Description string
|
description string
|
||||||
LoggerConfiguration *mlog.LoggerConfiguration
|
cfg mlog.TargetCfg
|
||||||
ExpectedLogs []string
|
expectedLogs []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"file logging, json, debug",
|
"file logging, json, debug",
|
||||||
&mlog.LoggerConfiguration{
|
mlog.TargetCfg{
|
||||||
EnableConsole: false,
|
Type: "file",
|
||||||
EnableFile: true,
|
Format: "json",
|
||||||
FileJson: true,
|
FormatOptions: json.RawMessage(`{"enable_caller":true}`),
|
||||||
FileLevel: mlog.LevelDebug,
|
Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError, mlog.LvlWarn, mlog.LvlInfo, mlog.LvlDebug},
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
`{"level":"debug","ts":0,"caller":"mlog/global_test.go:0","msg":"real debug log"}`,
|
`{"timestamp":0,"level":"debug","msg":"real debug log","caller":"mlog/global_test.go:0"}`,
|
||||||
`{"level":"info","ts":0,"caller":"mlog/global_test.go:0","msg":"real info log"}`,
|
`{"timestamp":0,"level":"info","msg":"real info log","caller":"mlog/global_test.go:0"}`,
|
||||||
`{"level":"warn","ts":0,"caller":"mlog/global_test.go:0","msg":"real warning log"}`,
|
`{"timestamp":0,"level":"warn","msg":"real warning log","caller":"mlog/global_test.go:0"}`,
|
||||||
`{"level":"error","ts":0,"caller":"mlog/global_test.go:0","msg":"real error log"}`,
|
`{"timestamp":0,"level":"error","msg":"real error log","caller":"mlog/global_test.go:0"}`,
|
||||||
`{"level":"error","ts":0,"caller":"mlog/global_test.go:0","msg":"real critical log"}`,
|
`{"timestamp":0,"level":"critical","msg":"real critical log","caller":"mlog/global_test.go:0"}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file logging, json, error",
|
"file logging, json, error",
|
||||||
&mlog.LoggerConfiguration{
|
mlog.TargetCfg{
|
||||||
EnableConsole: false,
|
Type: "file",
|
||||||
EnableFile: true,
|
Format: "json",
|
||||||
FileJson: true,
|
FormatOptions: json.RawMessage(`{"enable_caller":true}`),
|
||||||
FileLevel: mlog.LevelError,
|
Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError},
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
`{"level":"error","ts":0,"caller":"mlog/global_test.go:0","msg":"real error log"}`,
|
`{"timestamp":0,"level":"error","msg":"real error log","caller":"mlog/global_test.go:0"}`,
|
||||||
`{"level":"error","ts":0,"caller":"mlog/global_test.go:0","msg":"real critical log"}`,
|
`{"timestamp":0,"level":"critical","msg":"real critical log","caller":"mlog/global_test.go:0"}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file logging, non-json, debug",
|
"file logging, non-json, debug",
|
||||||
&mlog.LoggerConfiguration{
|
mlog.TargetCfg{
|
||||||
EnableConsole: false,
|
Type: "file",
|
||||||
EnableFile: true,
|
Format: "plain",
|
||||||
FileJson: false,
|
FormatOptions: json.RawMessage(`{"delim":" | ", "enable_caller":true}`),
|
||||||
FileLevel: mlog.LevelDebug,
|
Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError, mlog.LvlWarn, mlog.LvlInfo, mlog.LvlDebug},
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
`TIME debug mlog/global_test.go:0 real debug log`,
|
`debug | TIME | real debug log | caller="mlog/global_test.go:0"`,
|
||||||
`TIME info mlog/global_test.go:0 real info log`,
|
`info | TIME | real info log | caller="mlog/global_test.go:0"`,
|
||||||
`TIME warn mlog/global_test.go:0 real warning log`,
|
`warn | TIME | real warning log | caller="mlog/global_test.go:0"`,
|
||||||
`TIME error mlog/global_test.go:0 real error log`,
|
`error | TIME | real error log | caller="mlog/global_test.go:0"`,
|
||||||
`TIME error mlog/global_test.go:0 real critical log`,
|
`critical | TIME | real critical log | caller="mlog/global_test.go:0"`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file logging, non-json, error",
|
"file logging, non-json, error",
|
||||||
&mlog.LoggerConfiguration{
|
mlog.TargetCfg{
|
||||||
EnableConsole: false,
|
Type: "file",
|
||||||
EnableFile: true,
|
Format: "plain",
|
||||||
FileJson: false,
|
FormatOptions: json.RawMessage(`{"delim":" | ", "enable_caller":true}`),
|
||||||
FileLevel: mlog.LevelError,
|
Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError},
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
`TIME error mlog/global_test.go:0 real error log`,
|
`error | TIME | real error log | caller="mlog/global_test.go:0"`,
|
||||||
`TIME error mlog/global_test.go:0 real critical log`,
|
`critical | TIME | real critical log | caller="mlog/global_test.go:0"`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.Description, func(t *testing.T) {
|
t.Run(testCase.description, func(t *testing.T) {
|
||||||
var filePath string
|
var filePath string
|
||||||
if testCase.LoggerConfiguration.EnableFile {
|
if testCase.cfg.Type == "file" {
|
||||||
tempDir, err := ioutil.TempDir(os.TempDir(), "TestLoggingAfterInitialized")
|
tempDir, err := ioutil.TempDir(os.TempDir(), "TestLoggingAfterInitialized")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer os.Remove(tempDir)
|
defer os.Remove(tempDir)
|
||||||
|
|
||||||
filePath = filepath.Join(tempDir, "file.log")
|
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.InitGlobalLogger(logger)
|
||||||
|
|
||||||
mlog.Debug("real debug log")
|
mlog.Debug("real debug log")
|
||||||
@@ -114,32 +119,26 @@ func TestLoggingAfterInitialized(t *testing.T) {
|
|||||||
mlog.Error("real error log")
|
mlog.Error("real error log")
|
||||||
mlog.Critical("real critical log")
|
mlog.Critical("real critical log")
|
||||||
|
|
||||||
if testCase.LoggerConfiguration.EnableFile {
|
logger.Shutdown()
|
||||||
|
|
||||||
|
if testCase.cfg.Type == "file" {
|
||||||
logs, err := ioutil.ReadFile(filePath)
|
logs, err := ioutil.ReadFile(filePath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
actual := strings.TrimSpace(string(logs))
|
actual := strings.TrimSpace(string(logs))
|
||||||
|
|
||||||
if testCase.LoggerConfiguration.FileJson {
|
if testCase.cfg.Format == "json" {
|
||||||
reTs := regexp.MustCompile(`"ts":[0-9\.]+`)
|
reTs := regexp.MustCompile(`"timestamp":"[0-9\.\-\:\sZ]+"`)
|
||||||
reCaller := regexp.MustCompile(`"caller":"([^"]+):[0-9\.]+"`)
|
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"`)
|
actual = reCaller.ReplaceAllString(actual, `"caller":"$1:0"`)
|
||||||
} else {
|
} else {
|
||||||
actualRows := strings.Split(actual, "\n")
|
reTs := regexp.MustCompile(`\[\d\d\d\d-\d\d-\d\d\s[0-9\:\.\s\-Z]+\]`)
|
||||||
for i, actualRow := range actualRows {
|
reCaller := regexp.MustCompile(`caller="([^"]+):[0-9\.]+"`)
|
||||||
actualFields := strings.Split(actualRow, "\t")
|
actual = reTs.ReplaceAllString(actual, "TIME")
|
||||||
if len(actualFields) > 3 {
|
actual = reCaller.ReplaceAllString(actual, `caller="$1:0"`)
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
require.ElementsMatch(t, testCase.ExpectedLogs, strings.Split(actual, "\n"))
|
require.ElementsMatch(t, testCase.expectedLogs, strings.Split(actual, "\n"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -3,49 +3,56 @@
|
|||||||
|
|
||||||
package mlog
|
package mlog
|
||||||
|
|
||||||
// Standard levels
|
import "github.com/mattermost/logr/v2"
|
||||||
|
|
||||||
|
// Standard levels.
|
||||||
var (
|
var (
|
||||||
LvlPanic = LogLevel{ID: 0, Name: "panic", Stacktrace: true}
|
LvlPanic = logr.Panic // ID = 0
|
||||||
LvlFatal = LogLevel{ID: 1, Name: "fatal", Stacktrace: true}
|
LvlFatal = logr.Fatal // ID = 1
|
||||||
LvlError = LogLevel{ID: 2, Name: "error"}
|
LvlError = logr.Error // ID = 2
|
||||||
LvlWarn = LogLevel{ID: 3, Name: "warn"}
|
LvlWarn = logr.Warn // ID = 3
|
||||||
LvlInfo = LogLevel{ID: 4, Name: "info"}
|
LvlInfo = logr.Info // ID = 4
|
||||||
LvlDebug = LogLevel{ID: 5, Name: "debug"}
|
LvlDebug = logr.Debug // ID = 5
|
||||||
LvlTrace = LogLevel{ID: 6, Name: "trace"}
|
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
|
// used by redirected standard logger
|
||||||
LvlStdLog = LogLevel{ID: 10, Name: "stdlog"}
|
LvlStdLog = Level{ID: 10, Name: "stdlog"}
|
||||||
// used only by the logger
|
// 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.
|
// Register custom (discrete) levels here.
|
||||||
// !!!!! ID's must not exceed 32,768 !!!!!!
|
// !!!!! Custom ID's must be between 20 and 32,768 !!!!!!
|
||||||
var (
|
var (
|
||||||
// used by the audit system
|
// used by the audit system
|
||||||
LvlAuditAPI = LogLevel{ID: 100, Name: "audit-api"}
|
LvlAuditAPI = Level{ID: 100, Name: "audit-api"}
|
||||||
LvlAuditContent = LogLevel{ID: 101, Name: "audit-content"}
|
LvlAuditContent = Level{ID: 101, Name: "audit-content"}
|
||||||
LvlAuditPerms = LogLevel{ID: 102, Name: "audit-permissions"}
|
LvlAuditPerms = Level{ID: 102, Name: "audit-permissions"}
|
||||||
LvlAuditCLI = LogLevel{ID: 103, Name: "audit-cli"}
|
LvlAuditCLI = Level{ID: 103, Name: "audit-cli"}
|
||||||
|
|
||||||
// used by the TCP log target
|
// used by the TCP log target
|
||||||
LvlTCPLogTarget = LogLevel{ID: 120, Name: "TcpLogTarget"}
|
LvlTCPLogTarget = Level{ID: 120, Name: "TcpLogTarget"}
|
||||||
|
|
||||||
// used by Remote Cluster Service
|
// used by Remote Cluster Service
|
||||||
LvlRemoteClusterServiceDebug = LogLevel{ID: 130, Name: "RemoteClusterServiceDebug"}
|
LvlRemoteClusterServiceDebug = Level{ID: 130, Name: "RemoteClusterServiceDebug"}
|
||||||
LvlRemoteClusterServiceError = LogLevel{ID: 131, Name: "RemoteClusterServiceError"}
|
LvlRemoteClusterServiceError = Level{ID: 131, Name: "RemoteClusterServiceError"}
|
||||||
LvlRemoteClusterServiceWarn = LogLevel{ID: 132, Name: "RemoteClusterServiceWarn"}
|
LvlRemoteClusterServiceWarn = Level{ID: 132, Name: "RemoteClusterServiceWarn"}
|
||||||
|
|
||||||
// used by Shared Channel Sync Service
|
// used by Shared Channel Sync Service
|
||||||
LvlSharedChannelServiceDebug = LogLevel{ID: 200, Name: "SharedChannelServiceDebug"}
|
LvlSharedChannelServiceDebug = Level{ID: 200, Name: "SharedChannelServiceDebug"}
|
||||||
LvlSharedChannelServiceError = LogLevel{ID: 201, Name: "SharedChannelServiceError"}
|
LvlSharedChannelServiceError = Level{ID: 201, Name: "SharedChannelServiceError"}
|
||||||
LvlSharedChannelServiceWarn = LogLevel{ID: 202, Name: "SharedChannelServiceWarn"}
|
LvlSharedChannelServiceWarn = Level{ID: 202, Name: "SharedChannelServiceWarn"}
|
||||||
LvlSharedChannelServiceMessagesInbound = LogLevel{ID: 203, Name: "SharedChannelServiceMsgInbound"}
|
LvlSharedChannelServiceMessagesInbound = Level{ID: 203, Name: "SharedChannelServiceMsgInbound"}
|
||||||
LvlSharedChannelServiceMessagesOutbound = LogLevel{ID: 204, Name: "SharedChannelServiceMsgOutbound"}
|
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 (
|
var (
|
||||||
MLvlAuditAll = []LogLevel{LvlAuditAPI, LvlAuditContent, LvlAuditPerms, LvlAuditCLI}
|
MLvlAuditAll = []Level{LvlAuditAPI, LvlAuditContent, LvlAuditPerms, LvlAuditCLI}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
407
shared/mlog/mlog.go
Обычный файл
407
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")
|
||||||
55
shared/mlog/options.go
Обычный файл
55
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...)
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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...)
|
|
||||||
}
|
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
141
shared/mlog/tlog.go
Обычный файл
141
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()
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/services/cache"
|
"github.com/mattermost/mattermost-server/v6/services/cache"
|
||||||
cachemocks "github.com/mattermost/mattermost-server/v6/services/cache/mocks"
|
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"
|
||||||
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
|
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
|
||||||
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
||||||
@@ -172,7 +171,6 @@ func getMockStore() *mocks.Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
mlog.DisableZap()
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(nil)
|
mainHelper = testlib.NewMainHelperWithOptions(nil)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package sqlstore_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
|
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
|
||||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||||
)
|
)
|
||||||
@@ -14,7 +13,6 @@ import (
|
|||||||
var mainHelper *testlib.MainHelper
|
var mainHelper *testlib.MainHelper
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
mlog.DisableZap()
|
|
||||||
mainHelper = testlib.NewMainHelperWithOptions(nil)
|
mainHelper = testlib.NewMainHelperWithOptions(nil)
|
||||||
defer mainHelper.Close()
|
defer mainHelper.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
package testlib
|
package testlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AssertLog asserts that a JSON-encoded buffer of logs contains one with the given level and message.
|
// 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)
|
dec := json.NewDecoder(logs)
|
||||||
for {
|
for {
|
||||||
var log struct {
|
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.
|
// 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)
|
dec := json.NewDecoder(logs)
|
||||||
for {
|
for {
|
||||||
var log struct {
|
var log struct {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/services/searchengine"
|
"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"
|
||||||
"github.com/mattermost/mattermost-server/v6/store/searchlayer"
|
"github.com/mattermost/mattermost-server/v6/store/searchlayer"
|
||||||
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
|
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
|
||||||
@@ -53,16 +52,6 @@ func NewMainHelperWithOptions(options *HelperOptions) *MainHelper {
|
|||||||
var mainHelper MainHelper
|
var mainHelper MainHelper
|
||||||
flag.Parse()
|
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()
|
utils.TranslationsPreInit()
|
||||||
|
|
||||||
if options != nil {
|
if options != nil {
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
11
vendor/github.com/mattermost/logr/config.go
сгенерированный
поставляемый
11
vendor/github.com/mattermost/logr/config.go
сгенерированный
поставляемый
@@ -1,11 +0,0 @@
|
|||||||
package logr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/wiggin77/cfg"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ConfigLogger(config *cfg.Config) error {
|
|
||||||
return fmt.Errorf("Not implemented yet")
|
|
||||||
}
|
|
||||||
26
vendor/github.com/mattermost/logr/filter.go
сгенерированный
поставляемый
26
vendor/github.com/mattermost/logr/filter.go
сгенерированный
поставляемый
@@ -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
|
|
||||||
}
|
|
||||||
273
vendor/github.com/mattermost/logr/format/json.go
сгенерированный
поставляемый
273
vendor/github.com/mattermost/logr/format/json.go
сгенерированный
поставляемый
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
75
vendor/github.com/mattermost/logr/format/plain.go
сгенерированный
поставляемый
75
vendor/github.com/mattermost/logr/format/plain.go
сгенерированный
поставляемый
@@ -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
|
|
||||||
}
|
|
||||||
119
vendor/github.com/mattermost/logr/formatter.go
сгенерированный
поставляемый
119
vendor/github.com/mattermost/logr/formatter.go
сгенерированный
поставляемый
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
45
vendor/github.com/mattermost/logr/levelcustom.go
сгенерированный
поставляемый
45
vendor/github.com/mattermost/logr/levelcustom.go
сгенерированный
поставляемый
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
37
vendor/github.com/mattermost/logr/levelstd.go
сгенерированный
поставляемый
37
vendor/github.com/mattermost/logr/levelstd.go
сгенерированный
поставляемый
@@ -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"}
|
|
||||||
)
|
|
||||||
218
vendor/github.com/mattermost/logr/logger.go
сгенерированный
поставляемый
218
vendor/github.com/mattermost/logr/logger.go
сгенерированный
поставляемый
@@ -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...)
|
|
||||||
}
|
|
||||||
664
vendor/github.com/mattermost/logr/logr.go
сгенерированный
поставляемый
664
vendor/github.com/mattermost/logr/logr.go
сгенерированный
поставляемый
@@ -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{}{}
|
|
||||||
}
|
|
||||||
299
vendor/github.com/mattermost/logr/target.go
сгенерированный
поставляемый
299
vendor/github.com/mattermost/logr/target.go
сгенерированный
поставляемый
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
89
vendor/github.com/mattermost/logr/target/syslog.go
сгенерированный
поставляемый
89
vendor/github.com/mattermost/logr/target/syslog.go
сгенерированный
поставляемый
@@ -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
|
|
||||||
}
|
|
||||||
40
vendor/github.com/mattermost/logr/target/writer.go
сгенерированный
поставляемый
40
vendor/github.com/mattermost/logr/target/writer.go
сгенерированный
поставляемый
@@ -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
|
|
||||||
}
|
|
||||||
1
vendor/github.com/mattermost/logr/.gitignore → vendor/github.com/mattermost/logr/v2/.gitignore
сгенерированный
поставляемый
1
vendor/github.com/mattermost/logr/.gitignore → vendor/github.com/mattermost/logr/v2/.gitignore
сгенерированный
поставляемый
@@ -34,3 +34,4 @@ logs
|
|||||||
# test apps
|
# test apps
|
||||||
test/cmd/testapp1/testapp1
|
test/cmd/testapp1/testapp1
|
||||||
test/cmd/simple/simple
|
test/cmd/simple/simple
|
||||||
|
test/cmd/gelf/gelf
|
||||||
0
vendor/github.com/mattermost/logr/.travis.yml → vendor/github.com/mattermost/logr/v2/.travis.yml
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/.travis.yml → vendor/github.com/mattermost/logr/v2/.travis.yml
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/LICENSE → vendor/github.com/mattermost/logr/v2/LICENSE
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/LICENSE → vendor/github.com/mattermost/logr/v2/LICENSE
сгенерированный
поставляемый
49
vendor/github.com/mattermost/logr/README.md → vendor/github.com/mattermost/logr/v2/README.md
сгенерированный
поставляемый
49
vendor/github.com/mattermost/logr/README.md → 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
|
|||||||
<!-- markdownlint-disable MD033 -->
|
<!-- markdownlint-disable MD033 -->
|
||||||
| entity | description |
|
| entity | description |
|
||||||
| ------ | ----------- |
|
| ------ | ----------- |
|
||||||
| Logr | Engine instance typically instantiated once; used to configure logging.<br>```lgr := &Logr{}```|
|
| Logr | Engine instance typically instantiated once; used to configure logging.<br>```lgr,_ := logr.New()```|
|
||||||
| Logger | Provides contextual logging via fields; lightweight, can be created once and accessed globally or create on demand.<br>```logger := lgr.NewLogger()```<br>```logger2 := logger.WithField("user", "Sam")```|
|
| Logger | Provides contextual logging via fields; lightweight, can be created once and accessed globally or create on demand.<br>```logger := lgr.NewLogger()```<br>```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.<br>```filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Fatal}```|
|
| Filter | Determines which logging calls get written versus filtered out. Also determines which logging calls generate a stack trace.<br>```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).<br>```formatter := &format.Plain{Delim: " \| "}```|
|
| 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).<br>```formatter := &format.Plain{Delim: " \| "}```|
|
||||||
|
|
||||||
@@ -26,15 +26,15 @@ It is very much inspired by [Logrus](https://github.com/sirupsen/logrus) but add
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
// Create Logr instance.
|
// Create Logr instance.
|
||||||
lgr := &logr.Logr{}
|
lgr,_ := logr.New()
|
||||||
|
|
||||||
// Create a filter and formatter. Both can be shared by multiple
|
// Create a filter and formatter. Both can be shared by multiple
|
||||||
// targets.
|
// targets.
|
||||||
filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Error}
|
filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Error}
|
||||||
formatter := &format.Plain{Delim: " | "}
|
formatter := &formatters.Plain{Delim: " | "}
|
||||||
|
|
||||||
// WriterTarget outputs to any io.Writer
|
// 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)
|
lgr.AddTarget(t)
|
||||||
|
|
||||||
// One or more Loggers can be created, shared, used concurrently,
|
// 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:
|
Fields are added via Loggers:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
lgr := &Logr{}
|
lgr,_ := logr.New()
|
||||||
// ... add targets ...
|
// ... add targets ...
|
||||||
logger := lgr.NewLogger().WithFields(logr.Fields{
|
logger := lgr.NewLogger().WithFields(logr.Fields{
|
||||||
"user": user,
|
"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}
|
LoginLevel := logr.Level{ID: 100, Name: "login ", Stacktrace: false}
|
||||||
LogoutLevel := logr.Level{ID: 101, Name: "logout", Stacktrace: false}
|
LogoutLevel := logr.Level{ID: 101, Name: "logout", Stacktrace: false}
|
||||||
|
|
||||||
lgr := &logr.Logr{}
|
lgr,_ := logr.New()
|
||||||
|
|
||||||
// create a custom filter with custom levels.
|
// create a custom filter with custom levels.
|
||||||
filter := &logr.CustomFilter{}
|
filter := &logr.CustomFilter{}
|
||||||
filter.Add(LoginLevel, LogoutLevel)
|
filter.Add(LoginLevel, LogoutLevel)
|
||||||
|
|
||||||
formatter := &format.Plain{Delim: " | "}
|
formatter := &formatters.Plain{Delim: " | "}
|
||||||
tgr := target.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
tgr := targets.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
||||||
lgr.AddTarget(tgr)
|
lgr.AddTarget(tgr)
|
||||||
logger := lgr.NewLogger().WithFields(logr.Fields{"user": "Bob", "role": "admin"})
|
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.
|
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
|
```go
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
logr.Basic
|
|
||||||
out io.Writer
|
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 := &Writer{out: out}
|
||||||
w.Basic.Start(w, w, filter, formatter, maxQueue)
|
|
||||||
return w
|
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.
|
// 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
|
func (w *Writer) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||||
// bytes to your sink.
|
return w.out.Write(buf.Bytes())
|
||||||
func (w *Writer) Write(rec *logr.LogRec) error {
|
}
|
||||||
_, stacktrace := w.IsLevelEnabled(rec.Level())
|
|
||||||
|
|
||||||
// take a buffer from the pool to avoid allocations or just allocate a new one.
|
// Called once to cleanup/free resources for target.
|
||||||
buf := rec.Logger().Logr().BorrowBuffer()
|
func (w *Writer) Shutdown() error {
|
||||||
defer rec.Logger().Logr().ReleaseBuffer(buf)
|
return nil
|
||||||
|
|
||||||
buf, err := w.Formatter().Format(rec, stacktrace, buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = w.out.Write(buf.Bytes())
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
209
vendor/github.com/mattermost/logr/v2/config/config.go
сгенерированный
поставляемый
Обычный файл
209
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)
|
||||||
|
}
|
||||||
90
vendor/github.com/mattermost/logr/v2/config/sample-config.json
сгенерированный
поставляемый
Обычный файл
90
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
|
||||||
|
}
|
||||||
|
}
|
||||||
2
vendor/github.com/mattermost/logr/const.go → vendor/github.com/mattermost/logr/v2/const.go
сгенерированный
поставляемый
2
vendor/github.com/mattermost/logr/const.go → 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
|
// MaxLevelID is the maximum value of a level ID. Some level cache implementations will
|
||||||
// allocate a cache of this size. Cannot exceed uint.
|
// 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.
|
// 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
|
// This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called
|
||||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user