summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2018-04-27 12:49:45 -0700
committerGitHub <noreply@github.com>2018-04-27 12:49:45 -0700
commit686c2fbab7607d42183ae685a27ea3d7dce8c3f6 (patch)
tree53ed73cada57bc43f342ac10e2f842cddb095218
parent2acbc77d78456d7ba76ceb687b18985d7d92f814 (diff)
downloadchat-686c2fbab7607d42183ae685a27ea3d7dce8c3f6.tar.gz
chat-686c2fbab7607d42183ae685a27ea3d7dce8c3f6.tar.bz2
chat-686c2fbab7607d42183ae685a27ea3d7dce8c3f6.zip
Structured logging (#8673)
* Implementing structured logging * Changes to en.json to allow refactor to run. * Fixing global logger * Structured logger initalization. * Add caller. * Do some log redirection. * Auto refactor * Cleaning up l4g reference and removing dependancy. * Removing junk. * Copyright headers. * Fixing tests * Revert "Changes to en.json to allow refactor to run." This reverts commit fd8249e99bcad0231e6ea65cd77c32aae9a54026. * Fixing some auto refactor strangeness and typo. * Making keys more human readable.
-rw-r--r--Gopkg.lock40
-rw-r--r--Gopkg.toml11
-rw-r--r--api/api.go4
-rw-r--r--api/api_test.go15
-rw-r--r--api/apitestlib.go15
-rw-r--r--api/channel.go12
-rw-r--r--api/context.go18
-rw-r--r--api/user.go13
-rw-r--r--api/websocket.go6
-rw-r--r--api4/api.go5
-rw-r--r--api4/api_test.go15
-rw-r--r--api4/apitestlib.go32
-rw-r--r--api4/channel.go5
-rw-r--r--api4/context.go18
-rw-r--r--api4/oauth.go6
-rw-r--r--api4/plugin.go4
-rw-r--r--api4/system.go7
-rw-r--r--api4/system_test.go4
-rw-r--r--api4/user.go4
-rw-r--r--api4/webhook.go7
-rw-r--r--api4/websocket.go6
-rw-r--r--app/admin.go8
-rw-r--r--app/analytics.go6
-rw-r--r--app/app.go51
-rw-r--r--app/app_test.go14
-rw-r--r--app/apptestlib.go31
-rw-r--r--app/authorization.go7
-rw-r--r--app/auto_responder.go5
-rw-r--r--app/auto_users.go7
-rw-r--r--app/channel.go44
-rw-r--r--app/cluster_discovery.go16
-rw-r--r--app/command.go6
-rw-r--r--app/command_echo.go5
-rw-r--r--app/command_groupmsg.go4
-rw-r--r--app/command_invite.go7
-rw-r--r--app/command_invite_people.go4
-rw-r--r--app/command_loadtest.go8
-rw-r--r--app/command_msg.go8
-rw-r--r--app/command_mute_test.go5
-rw-r--r--app/command_remove.go4
-rw-r--r--app/config.go8
-rw-r--r--app/diagnostics.go8
-rw-r--r--app/email.go8
-rw-r--r--app/email_batching.go22
-rw-r--r--app/emoji.go11
-rw-r--r--app/file.go40
-rw-r--r--app/import.go19
-rw-r--r--app/ldap.go9
-rw-r--r--app/license.go11
-rw-r--r--app/notification.go32
-rw-r--r--app/oauth.go11
-rw-r--r--app/plugin.go49
-rw-r--r--app/plugin/ldapextras/plugin.go4
-rw-r--r--app/post.go30
-rw-r--r--app/ratelimit.go7
-rw-r--r--app/role.go3
-rw-r--r--app/security_update_check.go15
-rw-r--r--app/server.go34
-rw-r--r--app/session.go15
-rw-r--r--app/slackimport.go105
-rw-r--r--app/status.go9
-rw-r--r--app/team.go10
-rw-r--r--app/user.go44
-rw-r--r--app/web_conn.go29
-rw-r--r--app/web_hub.go24
-rw-r--r--app/webhook.go7
-rw-r--r--app/websocket_router.go6
-rw-r--r--cmd/commands/command.go1
-rw-r--r--cmd/commands/config_flag_test.go1
-rw-r--r--cmd/commands/jobserver.go9
-rw-r--r--cmd/commands/server.go27
-rw-r--r--cmd/commands/user.go3
-rw-r--r--cmd/init.go2
-rw-r--r--config/default.json3
-rw-r--r--einterfaces/brand.go3
-rw-r--r--einterfaces/oauthproviders.go3
-rw-r--r--jobs/jobs.go7
-rw-r--r--jobs/jobs_watcher.go13
-rw-r--r--jobs/schedulers.go26
-rw-r--r--jobs/workers.go6
-rw-r--r--manualtesting/manual_testing.go13
-rw-r--r--manualtesting/test_autolink.go5
-rw-r--r--mlog/global.go42
-rw-r--r--mlog/log.go143
-rw-r--r--model/client.go6
-rw-r--r--model/config.go11
-rw-r--r--store/layered_store.go4
-rw-r--r--store/redis_supplier.go4
-rw-r--r--store/redis_supplier_reactions.go11
-rw-r--r--store/redis_supplier_roles.go17
-rw-r--r--store/sqlstore/channel_member_history_store.go5
-rw-r--r--store/sqlstore/channel_store.go4
-rw-r--r--store/sqlstore/command_webhook_store.go7
-rw-r--r--store/sqlstore/post_store.go14
-rw-r--r--store/sqlstore/preference_store.go4
-rw-r--r--store/sqlstore/session_store.go9
-rw-r--r--store/sqlstore/store_test.go10
-rw-r--r--store/sqlstore/supplier.go66
-rw-r--r--store/sqlstore/supplier_reactions.go7
-rw-r--r--store/sqlstore/tokens_store.go7
-rw-r--r--store/sqlstore/upgrade.go23
-rw-r--r--store/store.go4
-rw-r--r--store/storetest/docker.go11
-rw-r--r--utils/config.go93
-rw-r--r--utils/config_test.go3
-rw-r--r--utils/file_backend_local.go5
-rw-r--r--utils/file_backend_s3.go8
-rw-r--r--utils/file_backend_test.go10
-rw-r--r--utils/html.go15
-rw-r--r--utils/i18n.go6
-rw-r--r--utils/license.go22
-rw-r--r--utils/mail.go15
-rw-r--r--utils/redirect_std_log.go65
-rw-r--r--utils/redirect_std_log_test.go24
-rw-r--r--vendor/github.com/alecthomas/log4go/.gitignore2
-rw-r--r--vendor/github.com/alecthomas/log4go/LICENSE13
-rw-r--r--vendor/github.com/alecthomas/log4go/README14
-rw-r--r--vendor/github.com/alecthomas/log4go/config.go288
-rw-r--r--vendor/github.com/alecthomas/log4go/filelog.go305
-rw-r--r--vendor/github.com/alecthomas/log4go/log4go.go484
-rw-r--r--vendor/github.com/alecthomas/log4go/pattlog.go130
-rw-r--r--vendor/github.com/alecthomas/log4go/socklog.go57
-rw-r--r--vendor/github.com/alecthomas/log4go/termlog.go49
-rw-r--r--vendor/github.com/alecthomas/log4go/wrapper.go278
-rw-r--r--vendor/go.uber.org/atomic/.codecov.yml15
-rw-r--r--vendor/go.uber.org/atomic/.gitignore11
-rw-r--r--vendor/go.uber.org/atomic/.travis.yml23
-rw-r--r--vendor/go.uber.org/atomic/LICENSE.txt19
-rw-r--r--vendor/go.uber.org/atomic/Makefile64
-rw-r--r--vendor/go.uber.org/atomic/README.md36
-rw-r--r--vendor/go.uber.org/atomic/atomic.go309
-rw-r--r--vendor/go.uber.org/atomic/glide.lock17
-rw-r--r--vendor/go.uber.org/atomic/glide.yaml6
-rw-r--r--vendor/go.uber.org/atomic/string.go49
-rw-r--r--vendor/go.uber.org/multierr/.codecov.yml15
-rw-r--r--vendor/go.uber.org/multierr/.gitignore1
-rw-r--r--vendor/go.uber.org/multierr/.travis.yml33
-rw-r--r--vendor/go.uber.org/multierr/CHANGELOG.md28
-rw-r--r--vendor/go.uber.org/multierr/LICENSE.txt19
-rw-r--r--vendor/go.uber.org/multierr/Makefile74
-rw-r--r--vendor/go.uber.org/multierr/README.md23
-rw-r--r--vendor/go.uber.org/multierr/error.go401
-rw-r--r--vendor/go.uber.org/multierr/glide.lock19
-rw-r--r--vendor/go.uber.org/multierr/glide.yaml8
-rw-r--r--vendor/go.uber.org/zap/.codecov.yml17
-rw-r--r--vendor/go.uber.org/zap/.gitignore28
-rw-r--r--vendor/go.uber.org/zap/.readme.tmpl108
-rw-r--r--vendor/go.uber.org/zap/.travis.yml21
-rw-r--r--vendor/go.uber.org/zap/CHANGELOG.md286
-rw-r--r--vendor/go.uber.org/zap/CODE_OF_CONDUCT.md75
-rw-r--r--vendor/go.uber.org/zap/CONTRIBUTING.md81
-rw-r--r--vendor/go.uber.org/zap/FAQ.md154
-rw-r--r--vendor/go.uber.org/zap/LICENSE.txt19
-rw-r--r--vendor/go.uber.org/zap/Makefile76
-rw-r--r--vendor/go.uber.org/zap/README.md136
-rw-r--r--vendor/go.uber.org/zap/array.go320
-rw-r--r--vendor/go.uber.org/zap/buffer/buffer.go106
-rw-r--r--vendor/go.uber.org/zap/buffer/pool.go49
-rwxr-xr-xvendor/go.uber.org/zap/check_license.sh17
-rw-r--r--vendor/go.uber.org/zap/config.go243
-rw-r--r--vendor/go.uber.org/zap/doc.go113
-rw-r--r--vendor/go.uber.org/zap/encoder.go75
-rw-r--r--vendor/go.uber.org/zap/error.go80
-rw-r--r--vendor/go.uber.org/zap/field.go310
-rw-r--r--vendor/go.uber.org/zap/flag.go39
-rw-r--r--vendor/go.uber.org/zap/glide.lock76
-rw-r--r--vendor/go.uber.org/zap/glide.yaml35
-rw-r--r--vendor/go.uber.org/zap/global.go169
-rw-r--r--vendor/go.uber.org/zap/http_handler.go81
-rw-r--r--vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go31
-rw-r--r--vendor/go.uber.org/zap/internal/color/color.go44
-rw-r--r--vendor/go.uber.org/zap/internal/exit/exit.go64
-rw-r--r--vendor/go.uber.org/zap/level.go132
-rw-r--r--vendor/go.uber.org/zap/logger.go305
-rw-r--r--vendor/go.uber.org/zap/options.go109
-rw-r--r--vendor/go.uber.org/zap/stacktrace.go126
-rw-r--r--vendor/go.uber.org/zap/sugar.go304
-rw-r--r--vendor/go.uber.org/zap/time.go27
-rw-r--r--vendor/go.uber.org/zap/writer.go96
-rw-r--r--vendor/go.uber.org/zap/zapcore/console_encoder.go147
-rw-r--r--vendor/go.uber.org/zap/zapcore/core.go113
-rw-r--r--vendor/go.uber.org/zap/zapcore/doc.go24
-rw-r--r--vendor/go.uber.org/zap/zapcore/encoder.go348
-rw-r--r--vendor/go.uber.org/zap/zapcore/entry.go257
-rw-r--r--vendor/go.uber.org/zap/zapcore/error.go120
-rw-r--r--vendor/go.uber.org/zap/zapcore/field.go201
-rw-r--r--vendor/go.uber.org/zap/zapcore/hook.go68
-rw-r--r--vendor/go.uber.org/zap/zapcore/json_encoder.go480
-rw-r--r--vendor/go.uber.org/zap/zapcore/level.go175
-rw-r--r--vendor/go.uber.org/zap/zapcore/level_strings.go46
-rw-r--r--vendor/go.uber.org/zap/zapcore/marshaler.go53
-rw-r--r--vendor/go.uber.org/zap/zapcore/memory_encoder.go179
-rw-r--r--vendor/go.uber.org/zap/zapcore/sampler.go134
-rw-r--r--vendor/go.uber.org/zap/zapcore/tee.go81
-rw-r--r--vendor/go.uber.org/zap/zapcore/write_syncer.go123
-rw-r--r--vendor/gopkg.in/natefinch/lumberjack.v2/.gitignore23
-rw-r--r--vendor/gopkg.in/natefinch/lumberjack.v2/LICENSE21
-rw-r--r--vendor/gopkg.in/natefinch/lumberjack.v2/README.md174
-rw-r--r--vendor/gopkg.in/natefinch/lumberjack.v2/chown.go11
-rw-r--r--vendor/gopkg.in/natefinch/lumberjack.v2/chown_linux.go19
-rw-r--r--vendor/gopkg.in/natefinch/lumberjack.v2/lumberjack.go541
-rw-r--r--web/web.go7
-rw-r--r--web/web_test.go10
-rw-r--r--wsapi/status.go5
-rw-r--r--wsapi/websocket_handler.go10
205 files changed, 9479 insertions, 2487 deletions
diff --git a/Gopkg.lock b/Gopkg.lock
index a8679b889..80eb97aa1 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -9,13 +9,6 @@
[[projects]]
branch = "master"
- name = "github.com/alecthomas/log4go"
- packages = ["."]
- revision = "9c17fbb2767ccbdda78584f28d545c44a4b29c4f"
- source = "https://github.com/mattermost/log4go.git"
-
-[[projects]]
- branch = "master"
name = "github.com/armon/go-metrics"
packages = ["."]
revision = "783273d703149aaeb9897cf58613d5af48861c25"
@@ -512,6 +505,31 @@
revision = "a0b114877d4caeffbd7f87e3757c17fce570fea7"
[[projects]]
+ name = "go.uber.org/atomic"
+ packages = ["."]
+ revision = "8474b86a5a6f79c443ce4b2992817ff32cf208b8"
+ version = "v1.3.1"
+
+[[projects]]
+ name = "go.uber.org/multierr"
+ packages = ["."]
+ revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a"
+ version = "v1.1.0"
+
+[[projects]]
+ name = "go.uber.org/zap"
+ packages = [
+ ".",
+ "buffer",
+ "internal/bufferpool",
+ "internal/color",
+ "internal/exit",
+ "zapcore"
+ ]
+ revision = "eeedf312bc6c57391d84767a4cd413f02a917974"
+ version = "v1.8.0"
+
+[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = [
@@ -608,6 +626,12 @@
version = "2.0.0"
[[projects]]
+ name = "gopkg.in/natefinch/lumberjack.v2"
+ packages = ["."]
+ revision = "a96e63847dc3c67d17befa69c303767e2f84e54f"
+ version = "v2.1"
+
+[[projects]]
name = "gopkg.in/olivere/elastic.v5"
packages = [
".",
@@ -635,6 +659,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "a6a107c250033694b6d11085333da149e3e1171da3c23ce5bc9362148adef141"
+ inputs-digest = "4f92b22eac4e8efa0aa098a6b1f1599ab087160296bd5fb74a9fad5366bbe1bc"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index d0753ad64..7eb44b15f 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -24,13 +24,6 @@
# go-tests = true
# unused-packages = true
-
-# To use our own fork
-[[constraint]]
- name = "github.com/alecthomas/log4go"
- branch = "master"
- source = "https://github.com/mattermost/log4go.git"
-
# To keep us on latest since maintainer stopped releasing versions
[[constraint]]
name = "github.com/go-sql-driver/mysql"
@@ -71,3 +64,7 @@
[prune]
go-tests = true
unused-packages = true
+
+[[constraint]]
+ name = "gopkg.in/natefinch/lumberjack.v2"
+ version = "2.1.0"
diff --git a/api/api.go b/api/api.go
index 70f36db85..2b226bbeb 100644
--- a/api/api.go
+++ b/api/api.go
@@ -6,9 +6,9 @@ package api
import (
"net/http"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
_ "github.com/nicksnyder/go-i18n/i18n"
@@ -114,7 +114,7 @@ func Init(a *app.App, root *mux.Router) *API {
a.InitEmailBatching()
if *a.Config().ServiceSettings.EnableAPIv3 {
- l4g.Info("API version 3 is scheduled for deprecation. Please see https://api.mattermost.com for details.")
+ mlog.Info("API version 3 is scheduled for deprecation. Please see https://api.mattermost.com for details.")
}
return api
diff --git a/api/api_test.go b/api/api_test.go
index d447fc9bd..a4ddf6a37 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -8,20 +8,29 @@ import (
"os"
"testing"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/store/storetest"
"github.com/mattermost/mattermost-server/utils"
)
func TestMain(m *testing.M) {
flag.Parse()
+
+ // Setup a global logger to catch tests logging outside of app context
+ // The global logger will be stomped by apps initalizing 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()
// In the case where a dev just wants to run a single test, it's faster to just use the default
// store.
if filter := flag.Lookup("test.run").Value.String(); filter != "" && filter != "." {
- l4g.Info("-test.run used, not creating temporary containers")
+ mlog.Info("-test.run used, not creating temporary containers")
os.Exit(m.Run())
}
diff --git a/api/apitestlib.go b/api/apitestlib.go
index 699b0eb90..20dbc4073 100644
--- a/api/apitestlib.go
+++ b/api/apitestlib.go
@@ -14,14 +14,13 @@ import (
"github.com/mattermost/mattermost-server/api4"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/store/sqlstore"
"github.com/mattermost/mattermost-server/store/storetest"
"github.com/mattermost/mattermost-server/utils"
"github.com/mattermost/mattermost-server/wsapi"
-
- l4g "github.com/alecthomas/log4go"
)
type TestHelper struct {
@@ -233,8 +232,8 @@ func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
err := me.App.JoinUserToTeam(team, user, "")
if err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -248,8 +247,8 @@ func (me *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team)
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID + " " + model.TEAM_ADMIN_ROLE_ID}
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
utils.EnableDebugLogForTest()
- l4g.Error(tmr.Err.Error())
- l4g.Close()
+ mlog.Error(tmr.Err.Error())
+
time.Sleep(time.Second)
panic(tmr.Err)
}
@@ -262,8 +261,8 @@ func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Tea
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID}
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
utils.EnableDebugLogForTest()
- l4g.Error(tmr.Err.Error())
- l4g.Close()
+ mlog.Error(tmr.Err.Error())
+
time.Sleep(time.Second)
panic(tmr.Err)
}
diff --git a/api/channel.go b/api/channel.go
index 976007725..9c465412c 100644
--- a/api/channel.go
+++ b/api/channel.go
@@ -4,13 +4,13 @@
package api
import (
+ "fmt"
"net/http"
"strconv"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
)
func (api *API) InitChannel() {
@@ -203,7 +203,7 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
} else {
if oldChannelDisplayName != channel.DisplayName {
if err := c.App.PostUpdateChannelDisplayNameMessage(c.Session.UserId, channel, oldChannelDisplayName, channel.DisplayName); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
c.LogAudit("name=" + channel.Name)
@@ -251,7 +251,7 @@ func updateChannelHeader(c *Context, w http.ResponseWriter, r *http.Request) {
return
} else {
if err := c.App.PostUpdateChannelHeaderMessage(c.Session.UserId, channel, oldChannelHeader, channelHeader); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
c.LogAudit("name=" + channel.Name)
w.Write([]byte(channel.ToJson()))
@@ -297,7 +297,7 @@ func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) {
return
} else {
if err := c.App.PostUpdateChannelPurposeMessage(c.Session.UserId, channel, oldChannelPurpose, channelPurpose); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
c.LogAudit("name=" + channel.Name)
w.Write([]byte(channel.ToJson()))
@@ -318,7 +318,7 @@ func getChannels(c *Context, w http.ResponseWriter, r *http.Request) {
if _, err := c.App.GetUser(c.Session.UserId); err != nil {
c.Err = err
c.RemoveSessionCookie(w, r)
- l4g.Error(utils.T("api.channel.get_channels.error"), c.Session.UserId)
+ mlog.Error(fmt.Sprintf("Error in getting users profile for id=%v forcing logout", c.Session.UserId), mlog.String("user_id", c.Session.UserId))
return
}
}
diff --git a/api/context.go b/api/context.go
index 1eb1e3f4f..8ebb5f73b 100644
--- a/api/context.go
+++ b/api/context.go
@@ -11,11 +11,11 @@ import (
"sync/atomic"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
goi18n "github.com/nicksnyder/go-i18n/i18n"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -101,7 +101,7 @@ type handler struct {
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
now := time.Now()
- l4g.Debug("%v", r.URL.Path)
+ mlog.Debug(fmt.Sprintf("%v", r.URL.Path))
c := &Context{}
c.App = h.app
@@ -146,7 +146,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
session, err := c.App.GetSession(token)
if err != nil {
- l4g.Error(utils.T("api.context.invalid_session.error"), err.Error())
+ mlog.Error(fmt.Sprintf("Invalid session err=%v", err.Error()))
c.RemoveSessionCookie(w, r)
if h.requireUser || h.requireSystemAdmin {
c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized)
@@ -268,14 +268,14 @@ func (c *Context) LogError(err *model.AppError) {
if c.Path == "/api/v3/users/websocket" && err.StatusCode == 401 || err.Id == "web.check_browser_compatibility.app_error" {
c.LogDebug(err)
} else if err.Id != "api.post.create_post.town_square_read_only" {
- l4g.Error(utils.TDefault("api.context.log.error"), c.Path, err.Where, err.StatusCode,
- c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError)
+ mlog.Error(fmt.Sprintf("%v:%v code=%v rid=%v uid=%v ip=%v %v [details: %v]", c.Path, err.Where, err.StatusCode,
+ c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError), mlog.String("user_id", c.Session.UserId))
}
}
func (c *Context) LogDebug(err *model.AppError) {
- l4g.Debug(utils.TDefault("api.context.log.error"), c.Path, err.Where, err.StatusCode,
- c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError)
+ mlog.Debug(fmt.Sprintf("%v:%v code=%v rid=%v uid=%v ip=%v %v [details: %v]", c.Path, err.Where, err.StatusCode,
+ c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError), mlog.String("user_id", c.Session.UserId))
}
func (c *Context) UserRequired() {
@@ -387,7 +387,7 @@ func (c *Context) GetTeamURL() string {
if !c.teamURLValid {
c.SetTeamURLFromSession()
if !c.teamURLValid {
- l4g.Debug(utils.T("api.context.invalid_team_url.debug"))
+ mlog.Debug("Team URL accessed when not valid. Team URL should not be used in API functions or those that are team independent")
}
}
return c.teamURL
@@ -424,7 +424,7 @@ func IsApiCall(r *http.Request) bool {
func Handle404(a *app.App, w http.ResponseWriter, r *http.Request) {
err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)
- l4g.Debug("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r))
+ mlog.Debug(fmt.Sprintf("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r)))
if IsApiCall(r) {
w.WriteHeader(err.StatusCode)
diff --git a/api/user.go b/api/user.go
index 35a3687b9..15fd4c7ea 100644
--- a/api/user.go
+++ b/api/user.go
@@ -11,12 +11,11 @@ import (
"strings"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
- "github.com/mattermost/mattermost-server/utils"
)
func (api *API) InitUser() {
@@ -245,7 +244,7 @@ func getMe(c *Context, w http.ResponseWriter, r *http.Request) {
if user, err := c.App.GetUser(c.Session.UserId); err != nil {
c.Err = err
c.RemoveSessionCookie(w, r)
- l4g.Error(utils.T("api.user.get_me.getting.error"), c.Session.UserId)
+ mlog.Error(fmt.Sprintf("Error in getting users profile for id=%v forcing logout", c.Session.UserId), mlog.String("user_id", c.Session.UserId))
return
} else if c.HandleEtag(user.Etag(c.App.Config().PrivacySettings.ShowFullName, c.App.Config().PrivacySettings.ShowEmailAddress), "Get Me", w, r) {
return
@@ -1042,12 +1041,12 @@ func updateMfa(c *Context, w http.ResponseWriter, r *http.Request) {
var user *model.User
var err *model.AppError
if user, err = c.App.GetUser(c.Session.UserId); err != nil {
- l4g.Warn(err.Error())
+ mlog.Warn(err.Error())
return
}
if err := c.App.SendMfaChangeEmail(user.Email, activate, user.Locale, c.App.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
@@ -1171,7 +1170,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
if len(teamId) > 0 {
c.App.Go(func() {
if err := c.App.AddUserToTeamByTeamId(teamId, user); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
} else {
c.App.AddDirectChannels(teamId, user)
}
@@ -1185,7 +1184,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAuditWithUserId(user.Id, "Revoked all sessions for user")
c.App.Go(func() {
if err := c.App.SendSignInChangeEmail(user.Email, strings.Title(model.USER_AUTH_SERVICE_SAML)+" SSO", user.Locale, c.App.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
}
diff --git a/api/websocket.go b/api/websocket.go
index 0da18d896..7f2c9c0db 100644
--- a/api/websocket.go
+++ b/api/websocket.go
@@ -4,12 +4,12 @@
package api
import (
+ "fmt"
"net/http"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/websocket"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
)
func (api *API) InitWebSocket() {
@@ -25,7 +25,7 @@ func connect(c *Context, w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
- l4g.Error(utils.T("api.web_socket.connect.error"), err)
+ mlog.Error(fmt.Sprintf("websocket connect err: %v", err))
c.Err = model.NewAppError("connect", "api.web_socket.connect.upgrade.app_error", nil, "", http.StatusInternalServerError)
return
}
diff --git a/api4/api.go b/api4/api.go
index 88526e4d3..d36c3e3ee 100644
--- a/api4/api.go
+++ b/api4/api.go
@@ -4,11 +4,12 @@
package api4
import (
+ "fmt"
"net/http"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
@@ -243,7 +244,7 @@ func Init(a *app.App, root *mux.Router, full bool) *API {
func Handle404(w http.ResponseWriter, r *http.Request) {
err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)
- l4g.Debug("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r))
+ mlog.Debug(fmt.Sprintf("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r)))
w.WriteHeader(err.StatusCode)
err.DetailedError = "There doesn't appear to be an api call for the url='" + r.URL.Path + "'."
diff --git a/api4/api_test.go b/api4/api_test.go
index fd804b70d..2efd21f22 100644
--- a/api4/api_test.go
+++ b/api4/api_test.go
@@ -8,20 +8,29 @@ import (
"os"
"testing"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/store/storetest"
"github.com/mattermost/mattermost-server/utils"
)
func TestMain(m *testing.M) {
flag.Parse()
+
+ // Setup a global logger to catch tests logging outside of app context
+ // The global logger will be stomped by apps initalizing 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()
// In the case where a dev just wants to run a single test, it's faster to just use the default
// store.
if filter := flag.Lookup("test.run").Value.String(); filter != "" && filter != "." {
- l4g.Info("-test.run used, not creating temporary containers")
+ mlog.Info("-test.run used, not creating temporary containers")
os.Exit(m.Run())
}
diff --git a/api4/apitestlib.go b/api4/apitestlib.go
index 4620c5f4e..48765687a 100644
--- a/api4/apitestlib.go
+++ b/api4/apitestlib.go
@@ -19,8 +19,8 @@ import (
"testing"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/store/sqlstore"
@@ -156,13 +156,13 @@ func (me *TestHelper) TearDown() {
options := map[string]bool{}
options[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME] = true
if result := <-me.App.Srv.Store.User().Search("", "fakeuser", options); result.Err != nil {
- l4g.Error("Error tearing down test users")
+ mlog.Error("Error tearing down test users")
} else {
users := result.Data.([]*model.User)
for _, u := range users {
if err := me.App.PermanentDeleteUser(u); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
}
@@ -171,13 +171,13 @@ func (me *TestHelper) TearDown() {
go func() {
defer wg.Done()
if result := <-me.App.Srv.Store.Team().SearchByName("faketeam"); result.Err != nil {
- l4g.Error("Error tearing down test teams")
+ mlog.Error("Error tearing down test teams")
} else {
teams := result.Data.([]*model.Team)
for _, t := range teams {
if err := me.App.PermanentDeleteTeam(t); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
}
@@ -186,7 +186,7 @@ func (me *TestHelper) TearDown() {
go func() {
defer wg.Done()
if result := <-me.App.Srv.Store.OAuth().GetApps(0, 1000); result.Err != nil {
- l4g.Error("Error tearing down test oauth apps")
+ mlog.Error("Error tearing down test oauth apps")
} else {
apps := result.Data.([]*model.OAuthApp)
@@ -450,8 +450,8 @@ func (me *TestHelper) UpdateActiveUser(user *model.User, active bool) {
_, err := me.App.UpdateActive(user, active)
if err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -464,8 +464,8 @@ func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
err := me.App.JoinUserToTeam(team, user, "")
if err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -478,8 +478,8 @@ func (me *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel)
member, err := me.App.AddUserToChannel(user, channel)
if err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -784,8 +784,8 @@ func (me *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team)
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID + " " + model.TEAM_ADMIN_ROLE_ID}
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
utils.EnableDebugLogForTest()
- l4g.Error(tmr.Err.Error())
- l4g.Close()
+ mlog.Error(tmr.Err.Error())
+
time.Sleep(time.Second)
panic(tmr.Err)
}
@@ -798,8 +798,8 @@ func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Tea
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID}
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
utils.EnableDebugLogForTest()
- l4g.Error(tmr.Err.Error())
- l4g.Close()
+ mlog.Error(tmr.Err.Error())
+
time.Sleep(time.Second)
panic(tmr.Err)
}
diff --git a/api4/channel.go b/api4/channel.go
index 685c188bc..83fa8eb18 100644
--- a/api4/channel.go
+++ b/api4/channel.go
@@ -6,8 +6,7 @@ package api4
import (
"net/http"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -139,7 +138,7 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
} else {
if oldChannelDisplayName != channel.DisplayName {
if err := c.App.PostUpdateChannelDisplayNameMessage(c.Session.UserId, channel, oldChannelDisplayName, channel.DisplayName); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
diff --git a/api4/context.go b/api4/context.go
index 9f3822633..c965e1d80 100644
--- a/api4/context.go
+++ b/api4/context.go
@@ -10,10 +10,10 @@ import (
"strings"
"time"
- l4g "github.com/alecthomas/log4go"
goi18n "github.com/nicksnyder/go-i18n/i18n"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -90,7 +90,7 @@ type handler struct {
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
now := time.Now()
- l4g.Debug("%v - %v", r.Method, r.URL.Path)
+ mlog.Debug(fmt.Sprintf("%v - %v", r.Method, r.URL.Path))
c := &Context{}
c.App = h.app
@@ -124,7 +124,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
session, err := c.App.GetSession(token)
if err != nil {
- l4g.Info(utils.T("api.context.invalid_session.error"), err.Error())
+ mlog.Info(fmt.Sprintf("Invalid session err=%v", err.Error()))
if err.StatusCode == http.StatusInternalServerError {
c.Err = err
} else if h.requireSession {
@@ -220,19 +220,19 @@ func (c *Context) LogError(err *model.AppError) {
err.Id == "web.check_browser_compatibility.app_error" {
c.LogDebug(err)
} else {
- l4g.Error(utils.TDefault("api.context.log.error"), c.Path, err.Where, err.StatusCode,
- c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError)
+ mlog.Error(fmt.Sprintf("%v:%v code=%v rid=%v uid=%v ip=%v %v [details: %v]", c.Path, err.Where, err.StatusCode,
+ c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError), mlog.String("user_id", c.Session.UserId))
}
}
func (c *Context) LogInfo(err *model.AppError) {
- l4g.Info(utils.TDefault("api.context.log.error"), c.Path, err.Where, err.StatusCode,
- c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError)
+ mlog.Info(fmt.Sprintf("%v:%v code=%v rid=%v uid=%v ip=%v %v [details: %v]", c.Path, err.Where, err.StatusCode,
+ c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError), mlog.String("user_id", c.Session.UserId))
}
func (c *Context) LogDebug(err *model.AppError) {
- l4g.Debug(utils.TDefault("api.context.log.error"), c.Path, err.Where, err.StatusCode,
- c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError)
+ mlog.Debug(fmt.Sprintf("%v:%v code=%v rid=%v uid=%v ip=%v %v [details: %v]", c.Path, err.Where, err.StatusCode,
+ c.RequestId, c.Session.UserId, c.IpAddress, err.SystemMessage(utils.TDefault), err.DetailedError), mlog.String("user_id", c.Session.UserId))
}
func (c *Context) IsSystemAdmin() bool {
diff --git a/api4/oauth.go b/api4/oauth.go
index a173159b6..fa120ebbf 100644
--- a/api4/oauth.go
+++ b/api4/oauth.go
@@ -9,8 +9,8 @@ import (
"path/filepath"
"strings"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -463,7 +463,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
if err != nil {
err.Translate(c.T)
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
if action == model.OAUTH_ACTION_MOBILE {
w.Write([]byte(err.ToJson()))
} else {
@@ -475,7 +475,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
user, err := c.App.CompleteOAuth(service, body, teamId, props)
if err != nil {
err.Translate(c.T)
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
if action == model.OAUTH_ACTION_MOBILE {
w.Write([]byte(err.ToJson()))
} else {
diff --git a/api4/plugin.go b/api4/plugin.go
index ee7121ffb..37fbf12cd 100644
--- a/api4/plugin.go
+++ b/api4/plugin.go
@@ -8,7 +8,7 @@ package api4
import (
"net/http"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -17,7 +17,7 @@ const (
)
func (api *API) InitPlugin() {
- l4g.Debug("EXPERIMENTAL: Initializing plugin api")
+ mlog.Debug("EXPERIMENTAL: Initializing plugin api")
api.BaseRoutes.Plugins.Handle("", api.ApiSessionRequired(uploadPlugin)).Methods("POST")
api.BaseRoutes.Plugins.Handle("", api.ApiSessionRequired(getPlugins)).Methods("GET")
diff --git a/api4/system.go b/api4/system.go
index c307a39b7..acb02bc3e 100644
--- a/api4/system.go
+++ b/api4/system.go
@@ -5,11 +5,12 @@ package api4
import (
"bytes"
+ "fmt"
"io"
"net/http"
"runtime"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -61,7 +62,7 @@ func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
rdata := map[string]string{}
rdata["status"] = "unhealthy"
- l4g.Warn(utils.T("api.system.go_routines"), actualGoroutines, *c.App.Config().ServiceSettings.GoroutineHealthThreshold)
+ mlog.Warn(fmt.Sprintf("The number of running goroutines is over the health threshold %v of %v", actualGoroutines, *c.App.Config().ServiceSettings.GoroutineHealthThreshold))
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(model.MapToJson(rdata)))
@@ -229,7 +230,7 @@ func postLog(c *Context, w http.ResponseWriter, r *http.Request) {
err.Where = "client"
c.LogError(err)
} else {
- l4g.Debug(msg)
+ mlog.Debug(fmt.Sprint(msg))
}
m["message"] = msg
diff --git a/api4/system_test.go b/api4/system_test.go
index b12421e62..c0fde6c39 100644
--- a/api4/system_test.go
+++ b/api4/system_test.go
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/stretchr/testify/assert"
)
@@ -392,7 +392,7 @@ func TestGetLogs(t *testing.T) {
Client := th.Client
for i := 0; i < 20; i++ {
- l4g.Info(i)
+ mlog.Info(fmt.Sprint(i))
}
logs, resp := th.SystemAdminClient.GetLogs(0, 10)
diff --git a/api4/user.go b/api4/user.go
index e13bf9448..897c49ad1 100644
--- a/api4/user.go
+++ b/api4/user.go
@@ -9,8 +9,8 @@ import (
"strconv"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
@@ -1177,7 +1177,7 @@ func sendVerificationEmail(c *Context, w http.ResponseWriter, r *http.Request) {
err = c.App.SendEmailVerification(user)
if err != nil {
// Don't want to leak whether the email is valid or not
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
ReturnStatusOK(w)
return
}
diff --git a/api4/webhook.go b/api4/webhook.go
index a0e7b5785..fadc3fbf3 100644
--- a/api4/webhook.go
+++ b/api4/webhook.go
@@ -4,16 +4,15 @@
package api4
import (
+ "fmt"
"io"
"net/http"
"strings"
- l4g "github.com/alecthomas/log4go"
-
"github.com/gorilla/mux"
"github.com/gorilla/schema"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
)
func (api *API) InitWebhook() {
@@ -492,7 +491,7 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
}
if c.App.Config().LogSettings.EnableWebhookDebugging {
- l4g.Debug(utils.T("api.webhook.incoming.debug"), incomingWebhookPayload.ToJson())
+ mlog.Debug(fmt.Sprint("Incoming webhook received. Content=", incomingWebhookPayload.ToJson()))
}
err = c.App.HandleIncomingWebhook(id, incomingWebhookPayload)
diff --git a/api4/websocket.go b/api4/websocket.go
index 7ea19224b..68125621a 100644
--- a/api4/websocket.go
+++ b/api4/websocket.go
@@ -4,12 +4,12 @@
package api4
import (
+ "fmt"
"net/http"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/websocket"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
)
func (api *API) InitWebSocket() {
@@ -25,7 +25,7 @@ func connectWebSocket(c *Context, w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
- l4g.Error(utils.T("api.web_socket.connect.error"), err)
+ mlog.Error(fmt.Sprintf("websocket connect err: %v", err))
c.Err = model.NewAppError("connect", "api.web_socket.connect.upgrade.app_error", nil, "", http.StatusInternalServerError)
return
}
diff --git a/app/admin.go b/app/admin.go
index 60b71505a..892e2d16b 100644
--- a/app/admin.go
+++ b/app/admin.go
@@ -13,7 +13,7 @@ import (
"net/http"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -137,7 +137,7 @@ func (a *App) InvalidateAllCaches() *model.AppError {
}
func (a *App) InvalidateAllCachesSkipSend() {
- l4g.Info(utils.T("api.context.invalidate_all_caches"))
+ mlog.Info("Purging all caches")
a.sessionCache.Purge()
ClearStatusCache()
a.Srv.Store.Channel().ClearCaches()
@@ -209,7 +209,7 @@ func (a *App) SaveConfig(cfg *model.Config, sendConfigChangeClusterMessage bool)
func (a *App) RecycleDatabaseConnection() {
oldStore := a.Srv.Store
- l4g.Warn(utils.T("api.admin.recycle_db_start.warn"))
+ mlog.Warn("Attempting to recycle the database connection.")
a.Srv.Store = a.newStore()
a.Jobs.Store = a.Srv.Store
@@ -218,7 +218,7 @@ func (a *App) RecycleDatabaseConnection() {
oldStore.Close()
}
- l4g.Warn(utils.T("api.admin.recycle_db_end.warn"))
+ mlog.Warn("Finished recycling the database connection.")
}
func (a *App) TestEmail(userId string, cfg *model.Config) *model.AppError {
diff --git a/app/analytics.go b/app/analytics.go
index f4e6fec95..7a32e78c1 100644
--- a/app/analytics.go
+++ b/app/analytics.go
@@ -4,7 +4,9 @@
package app
import (
- l4g "github.com/alecthomas/log4go"
+ "fmt"
+
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
@@ -22,7 +24,7 @@ func (a *App) GetAnalytics(name string, teamId string) (model.AnalyticsRows, *mo
} else {
systemUserCount = r.Data.(int64)
if systemUserCount > int64(*a.Config().AnalyticsSettings.MaxUsersForStatistics) {
- l4g.Debug("More than %v users on the system, intensive queries skipped", *a.Config().AnalyticsSettings.MaxUsersForStatistics)
+ mlog.Debug(fmt.Sprintf("More than %v users on the system, intensive queries skipped", *a.Config().AnalyticsSettings.MaxUsersForStatistics))
skipIntensiveQueries = true
}
}
diff --git a/app/app.go b/app/app.go
index 51ee37cbd..b31f67d6b 100644
--- a/app/app.go
+++ b/app/app.go
@@ -5,6 +5,7 @@ package app
import (
"crypto/ecdsa"
+ "fmt"
"html/template"
"net"
"net/http"
@@ -13,13 +14,13 @@ import (
"sync"
"sync/atomic"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/mattermost/mattermost-server/einterfaces"
ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs"
"github.com/mattermost/mattermost-server/jobs"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/plugin/pluginenv"
"github.com/mattermost/mattermost-server/store"
@@ -35,6 +36,8 @@ type App struct {
Srv *Server
+ Log *mlog.Logger
+
PluginEnv *pluginenv.Environment
PluginConfigListenerId string
@@ -77,6 +80,7 @@ type App struct {
sessionCache *utils.Cache
configListenerId string
licenseListenerId string
+ logListenerId string
disableConfigWatch bool
configWatcher *utils.ConfigWatcher
asymmetricSigningKey *ecdsa.PrivateKey
@@ -127,15 +131,23 @@ func New(options ...Option) (outApp *App, outErr error) {
}
model.AppErrorInit(utils.T)
- // The first time we load config, clear any existing filters to allow the configuration
- // changes to take effect. This is safe only because no one else is logging at this point.
- l4g.Close()
-
if err := app.LoadConfig(app.configFile); err != nil {
- // Re-initialize the default logger as we bail out.
- l4g.Global = l4g.NewDefaultLogger(l4g.DEBUG)
return nil, err
}
+
+ // Initalize logging
+ app.Log = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(&app.Config().LogSettings))
+
+ // Redirect default golang logger to this logger
+ mlog.RedirectStdLog(app.Log)
+
+ // Use this app logger as the global logger (eventually remove all instances of global logging)
+ mlog.InitGlobalLogger(app.Log)
+
+ app.logListenerId = app.AddConfigListener(func(_, after *model.Config) {
+ app.Log.ChangeLevels(utils.MloggerConfigFromLoggerConfig(&after.LogSettings))
+ })
+
app.EnableConfigWatch()
app.LoadTimezones()
@@ -166,7 +178,7 @@ func New(options ...Option) (outApp *App, outErr error) {
})
app.regenerateClientConfig()
- l4g.Info(utils.T("api.server.new_server.init.info"))
+ mlog.Info("Server is initializing...")
app.initEnterprise()
@@ -177,7 +189,7 @@ func New(options ...Option) (outApp *App, outErr error) {
}
if htmlTemplateWatcher, err := utils.NewHTMLTemplateWatcher("templates"); err != nil {
- l4g.Error(utils.T("api.api.init.parsing_templates.error"), err)
+ mlog.Error(fmt.Sprintf("Failed to parse server templates %v", err))
} else {
app.htmlTemplateWatcher = htmlTemplateWatcher
}
@@ -210,7 +222,7 @@ func (a *App) configOrLicenseListener() {
func (a *App) Shutdown() {
appCount--
- l4g.Info(utils.T("api.server.stop_server.stopping.info"))
+ mlog.Info("Stopping Server...")
a.StopServer()
a.HubStop()
@@ -229,7 +241,8 @@ func (a *App) Shutdown() {
a.RemoveConfigListener(a.configListenerId)
a.RemoveLicenseListener(a.licenseListenerId)
- l4g.Info(utils.T("api.server.stop_server.stopped.info"))
+ a.RemoveConfigListener(a.logListenerId)
+ mlog.Info("Server stopped")
a.DisableConfigWatch()
}
@@ -499,7 +512,7 @@ func (a *App) HTTPClient(trustURLs bool) *http.Client {
func (a *App) Handle404(w http.ResponseWriter, r *http.Request) {
err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)
- l4g.Debug("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r))
+ mlog.Debug(fmt.Sprintf("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r)))
utils.RenderWebAppError(w, r, err, a.AsymmetricSigningKey())
}
@@ -511,7 +524,7 @@ func (a *App) DoAdvancedPermissionsMigration() {
return
}
- l4g.Info("Migrating roles to database.")
+ mlog.Info("Migrating roles to database.")
roles := model.MakeDefaultRoles()
roles = utils.SetRolePermissionsFromConfig(roles, a.Config(), a.License() != nil)
@@ -521,8 +534,8 @@ func (a *App) DoAdvancedPermissionsMigration() {
if result := <-a.Srv.Store.Role().Save(role); result.Err != nil {
// If this failed for reasons other than the role already existing, don't mark the migration as done.
if result2 := <-a.Srv.Store.Role().GetByName(role.Name); result2.Err != nil {
- l4g.Critical("Failed to migrate role to database.")
- l4g.Critical(result.Err)
+ mlog.Critical("Failed to migrate role to database.")
+ mlog.Critical(fmt.Sprint(result.Err))
allSucceeded = false
} else {
// If the role already existed, check it is the same and update if not.
@@ -534,8 +547,8 @@ func (a *App) DoAdvancedPermissionsMigration() {
role.Id = fetchedRole.Id
if result := <-a.Srv.Store.Role().Save(role); result.Err != nil {
// Role is not the same, but failed to update.
- l4g.Critical("Failed to migrate role to database.")
- l4g.Critical(result.Err)
+ mlog.Critical("Failed to migrate role to database.")
+ mlog.Critical(fmt.Sprint(result.Err))
allSucceeded = false
}
}
@@ -553,7 +566,7 @@ func (a *App) DoAdvancedPermissionsMigration() {
}
if result := <-a.Srv.Store.System().Save(&system); result.Err != nil {
- l4g.Critical("Failed to mark advanced permissions migration as completed.")
- l4g.Critical(result.Err)
+ mlog.Critical("Failed to mark advanced permissions migration as completed.")
+ mlog.Critical(fmt.Sprint(result.Err))
}
}
diff --git a/app/app_test.go b/app/app_test.go
index a726fc2b5..ccf7faeeb 100644
--- a/app/app_test.go
+++ b/app/app_test.go
@@ -9,10 +9,10 @@ import (
"os"
"testing"
- l4g "github.com/alecthomas/log4go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store/storetest"
"github.com/mattermost/mattermost-server/utils"
@@ -20,12 +20,22 @@ import (
func TestMain(m *testing.M) {
flag.Parse()
+
+ // Setup a global logger to catch tests logging outside of app context
+ // The global logger will be stomped by apps initalizing 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()
// In the case where a dev just wants to run a single test, it's faster to just use the default
// store.
if filter := flag.Lookup("test.run").Value.String(); filter != "" && filter != "." {
- l4g.Info("-test.run used, not creating temporary containers")
+ mlog.Info("-test.run used, not creating temporary containers")
os.Exit(m.Run())
}
diff --git a/app/apptestlib.go b/app/apptestlib.go
index a5c2db91c..626e932e8 100644
--- a/app/apptestlib.go
+++ b/app/apptestlib.go
@@ -11,9 +11,8 @@ import (
"path/filepath"
"time"
- l4g "github.com/alecthomas/log4go"
-
"github.com/mattermost/mattermost-server/einterfaces"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/plugin"
"github.com/mattermost/mattermost-server/plugin/pluginenv"
@@ -169,8 +168,8 @@ func (me *TestHelper) CreateTeam() *model.Team {
utils.DisableDebugLogForTest()
var err *model.AppError
if team, err = me.App.CreateTeam(team); err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -192,8 +191,8 @@ func (me *TestHelper) CreateUser() *model.User {
utils.DisableDebugLogForTest()
var err *model.AppError
if user, err = me.App.CreateUser(user); err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -219,8 +218,8 @@ func (me *TestHelper) createChannel(team *model.Team, channelType string) *model
utils.DisableDebugLogForTest()
var err *model.AppError
if channel, err = me.App.CreateChannel(channel, true); err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -233,8 +232,8 @@ func (me *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
var err *model.AppError
var channel *model.Channel
if channel, err = me.App.CreateDirectChannel(me.BasicUser.Id, user.Id); err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -255,8 +254,8 @@ func (me *TestHelper) CreatePost(channel *model.Channel) *model.Post {
utils.DisableDebugLogForTest()
var err *model.AppError
if post, err = me.App.CreatePost(post, channel, false); err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -269,8 +268,8 @@ func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
err := me.App.JoinUserToTeam(team, user, "")
if err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
@@ -283,8 +282,8 @@ func (me *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel)
member, err := me.App.AddUserToChannel(user, channel)
if err != nil {
- l4g.Error(err.Error())
- l4g.Close()
+ mlog.Error(err.Error())
+
time.Sleep(time.Second)
panic(err)
}
diff --git a/app/authorization.go b/app/authorization.go
index 2187472f7..f281b3e65 100644
--- a/app/authorization.go
+++ b/app/authorization.go
@@ -4,10 +4,11 @@
package app
import (
+ "fmt"
"net/http"
"strings"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -193,8 +194,8 @@ func (a *App) RolesGrantPermission(roleNames []string, permissionId string) bool
if err != nil {
// This should only happen if something is very broken. We can't realistically
// recover the situation, so deny permission and log an error.
- l4g.Error("Failed to get roles from database with role names: " + strings.Join(roleNames, ","))
- l4g.Error(err)
+ mlog.Error("Failed to get roles from database with role names: " + strings.Join(roleNames, ","))
+ mlog.Error(fmt.Sprint(err))
return false
}
diff --git a/app/auto_responder.go b/app/auto_responder.go
index 23402ecd4..aa7f243c4 100644
--- a/app/auto_responder.go
+++ b/app/auto_responder.go
@@ -4,8 +4,7 @@
package app
import (
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -28,7 +27,7 @@ func (a *App) SendAutoResponse(channel *model.Channel, receiver *model.User, roo
}
if _, err := a.CreatePost(autoResponderPost, channel, false); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
}
diff --git a/app/auto_users.go b/app/auto_users.go
index 8ed6767ad..b11f9c572 100644
--- a/app/auto_users.go
+++ b/app/auto_users.go
@@ -4,11 +4,10 @@
package app
import (
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
-
- l4g "github.com/alecthomas/log4go"
)
type AutoUserCreator struct {
@@ -75,7 +74,7 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) {
result, err := cfg.client.CreateUserWithInvite(user, "", "", cfg.team.InviteId)
if err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
return nil, false
}
@@ -83,7 +82,7 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) {
status := &model.Status{UserId: ruser.Id, Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
if result := <-cfg.app.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
- l4g.Error(result.Err.Error())
+ mlog.Error(result.Err.Error())
return nil, false
}
diff --git a/app/channel.go b/app/channel.go
index e7cd747c6..26e3d771c 100644
--- a/app/channel.go
+++ b/app/channel.go
@@ -9,7 +9,7 @@ import (
"strings"
"time"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -60,17 +60,17 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, channelRole s
err = cmResult.Err
}
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(user.Id, townSquare.Id, model.GetMillis()); result.Err != nil {
- l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update ChannelMemberHistory table %v", result.Err))
}
if *a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages {
if requestor == nil {
if err := a.postJoinTeamMessage(user, townSquare); err != nil {
- l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
+ mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
}
} else {
if err := a.postAddToTeamMessage(requestor, user, townSquare, ""); err != nil {
- l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
+ mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
}
}
}
@@ -93,16 +93,16 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, channelRole s
err = cmResult.Err
}
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(user.Id, offTopic.Id, model.GetMillis()); result.Err != nil {
- l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update ChannelMemberHistory table %v", result.Err))
}
if requestor == nil {
if err := a.postJoinChannelMessage(user, offTopic); err != nil {
- l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
+ mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
}
} else {
if err := a.PostAddToChannelMessage(requestor, user, offTopic, ""); err != nil {
- l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
+ mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
}
}
@@ -174,7 +174,7 @@ func (a *App) CreateChannel(channel *model.Channel, addMember bool) (*model.Chan
return nil, cmresult.Err
}
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(channel.CreatorId, sc.Id, model.GetMillis()); result.Err != nil {
- l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update ChannelMemberHistory table %v", result.Err))
}
a.InvalidateCacheForUser(channel.CreatorId)
@@ -227,10 +227,10 @@ func (a *App) createDirectChannel(userId string, otherUserId string) (*model.Cha
channel := result.Data.(*model.Channel)
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId, channel.Id, model.GetMillis()); result.Err != nil {
- l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update ChannelMemberHistory table %v", result.Err))
}
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(otherUserId, channel.Id, model.GetMillis()); result.Err != nil {
- l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update ChannelMemberHistory table %v", result.Err))
}
return channel, nil
@@ -258,7 +258,7 @@ func (a *App) WaitForChannelMembership(channelId string, userId string) {
}
}
- l4g.Error("WaitForChannelMembership giving up channelId=%v userId=%v", channelId, userId)
+ mlog.Error(fmt.Sprintf("WaitForChannelMembership giving up channelId=%v userId=%v", channelId, userId), mlog.String("user_id", userId))
}
}
@@ -329,7 +329,7 @@ func (a *App) createGroupChannel(userIds []string, creatorId string) (*model.Cha
return nil, result.Err
}
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(user.Id, channel.Id, model.GetMillis()); result.Err != nil {
- l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update ChannelMemberHistory table %v", result.Err))
}
}
@@ -413,19 +413,19 @@ func (a *App) PatchChannel(channel *model.Channel, patch *model.ChannelPatch, us
if oldChannelDisplayName != channel.DisplayName {
if err := a.PostUpdateChannelDisplayNameMessage(userId, channel, oldChannelDisplayName, channel.DisplayName); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
if channel.Header != oldChannelHeader {
if err := a.PostUpdateChannelHeaderMessage(userId, channel, oldChannelHeader, channel.Header); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
if channel.Purpose != oldChannelPurpose {
if err := a.PostUpdateChannelPurposeMessage(userId, channel, oldChannelPurpose, channel.Purpose); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
@@ -536,21 +536,21 @@ func (a *App) DeleteChannel(channel *model.Channel, userId string) *model.AppErr
}
if _, err := a.CreatePost(post, channel, false); err != nil {
- l4g.Error(utils.T("api.channel.delete_channel.failed_post.error"), err)
+ mlog.Error(fmt.Sprintf("Failed to post archive message %v", err))
}
}
now := model.GetMillis()
for _, hook := range incomingHooks {
if result := <-a.Srv.Store.Webhook().DeleteIncoming(hook.Id, now); result.Err != nil {
- l4g.Error(utils.T("api.channel.delete_channel.incoming_webhook.error"), hook.Id)
+ mlog.Error(fmt.Sprintf("Encountered error deleting incoming webhook, id=%v", hook.Id))
}
a.InvalidateCacheForWebhook(hook.Id)
}
for _, hook := range outgoingHooks {
if result := <-a.Srv.Store.Webhook().DeleteOutgoing(hook.Id, now); result.Err != nil {
- l4g.Error(utils.T("api.channel.delete_channel.outgoing_webhook.error"), hook.Id)
+ mlog.Error(fmt.Sprintf("Encountered error deleting outgoing webhook, id=%v", hook.Id))
}
}
@@ -594,13 +594,13 @@ func (a *App) addUserToChannel(user *model.User, channel *model.Channel, teamMem
Roles: model.CHANNEL_USER_ROLE_ID,
}
if result := <-a.Srv.Store.Channel().SaveMember(newMember); result.Err != nil {
- l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, result.Err)
+ mlog.Error(fmt.Sprintf("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, result.Err), mlog.String("user_id", user.Id))
return nil, model.NewAppError("AddUserToChannel", "api.channel.add_user.to.channel.failed.app_error", nil, "", http.StatusInternalServerError)
}
a.WaitForChannelMembership(channel.Id, user.Id)
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(user.Id, channel.Id, model.GetMillis()); result.Err != nil {
- l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update ChannelMemberHistory table %v", result.Err))
}
a.InvalidateCacheForUser(user.Id)
@@ -1449,10 +1449,10 @@ func (a *App) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.
channel := result.Data.(*model.Channel)
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId1, channel.Id, model.GetMillis()); result.Err != nil {
- l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update ChannelMemberHistory table %v", result.Err))
}
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId2, channel.Id, model.GetMillis()); result.Err != nil {
- l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update ChannelMemberHistory table %v", result.Err))
}
return channel, nil
diff --git a/app/cluster_discovery.go b/app/cluster_discovery.go
index 2682425f5..f7443680c 100644
--- a/app/cluster_discovery.go
+++ b/app/cluster_discovery.go
@@ -7,7 +7,7 @@ import (
"fmt"
"time"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -36,36 +36,36 @@ func (me *ClusterDiscoveryService) Start() {
<-me.app.Srv.Store.ClusterDiscovery().Cleanup()
if cresult := <-me.app.Srv.Store.ClusterDiscovery().Exists(&me.ClusterDiscovery); cresult.Err != nil {
- l4g.Error(fmt.Sprintf("ClusterDiscoveryService failed to check if row exists for %v with err=%v", me.ClusterDiscovery.ToJson(), cresult.Err))
+ mlog.Error(fmt.Sprintf("ClusterDiscoveryService failed to check if row exists for %v with err=%v", me.ClusterDiscovery.ToJson(), cresult.Err))
} else {
if cresult.Data.(bool) {
if u := <-me.app.Srv.Store.ClusterDiscovery().Delete(&me.ClusterDiscovery); u.Err != nil {
- l4g.Error(fmt.Sprintf("ClusterDiscoveryService failed to start clean for %v with err=%v", me.ClusterDiscovery.ToJson(), u.Err))
+ mlog.Error(fmt.Sprintf("ClusterDiscoveryService failed to start clean for %v with err=%v", me.ClusterDiscovery.ToJson(), u.Err))
}
}
}
if result := <-me.app.Srv.Store.ClusterDiscovery().Save(&me.ClusterDiscovery); result.Err != nil {
- l4g.Error(fmt.Sprintf("ClusterDiscoveryService failed to save for %v with err=%v", me.ClusterDiscovery.ToJson(), result.Err))
+ mlog.Error(fmt.Sprintf("ClusterDiscoveryService failed to save for %v with err=%v", me.ClusterDiscovery.ToJson(), result.Err))
return
}
go func() {
- l4g.Debug(fmt.Sprintf("ClusterDiscoveryService ping writer started for %v", me.ClusterDiscovery.ToJson()))
+ mlog.Debug(fmt.Sprintf("ClusterDiscoveryService ping writer started for %v", me.ClusterDiscovery.ToJson()))
ticker := time.NewTicker(DISCOVERY_SERVICE_WRITE_PING)
defer func() {
ticker.Stop()
if u := <-me.app.Srv.Store.ClusterDiscovery().Delete(&me.ClusterDiscovery); u.Err != nil {
- l4g.Error(fmt.Sprintf("ClusterDiscoveryService failed to cleanup for %v with err=%v", me.ClusterDiscovery.ToJson(), u.Err))
+ mlog.Error(fmt.Sprintf("ClusterDiscoveryService failed to cleanup for %v with err=%v", me.ClusterDiscovery.ToJson(), u.Err))
}
- l4g.Debug(fmt.Sprintf("ClusterDiscoveryService ping writer stopped for %v", me.ClusterDiscovery.ToJson()))
+ mlog.Debug(fmt.Sprintf("ClusterDiscoveryService ping writer stopped for %v", me.ClusterDiscovery.ToJson()))
}()
for {
select {
case <-ticker.C:
if u := <-me.app.Srv.Store.ClusterDiscovery().SetLastPingAt(&me.ClusterDiscovery); u.Err != nil {
- l4g.Error(fmt.Sprintf("ClusterDiscoveryService failed to write ping for %v with err=%v", me.ClusterDiscovery.ToJson(), u.Err))
+ mlog.Error(fmt.Sprintf("ClusterDiscoveryService failed to write ping for %v with err=%v", me.ClusterDiscovery.ToJson(), u.Err))
}
case <-me.stop:
return
diff --git a/app/command.go b/app/command.go
index 039952cf0..796d656a7 100644
--- a/app/command.go
+++ b/app/command.go
@@ -10,7 +10,7 @@ import (
"net/url"
"strings"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
goi18n "github.com/nicksnyder/go-i18n/i18n"
@@ -207,7 +207,7 @@ func (a *App) ExecuteCommand(args *model.CommandArgs) (*model.CommandResponse, *
teamCmds := result.Data.([]*model.Command)
for _, cmd := range teamCmds {
if trigger == cmd.Trigger {
- l4g.Debug(fmt.Sprintf(utils.T("api.command.execute_command.debug"), trigger, args.UserId))
+ mlog.Debug(fmt.Sprintf(utils.T("api.command.execute_command.debug"), trigger, args.UserId))
p := url.Values{}
p.Set("token", cmd.Token)
@@ -308,7 +308,7 @@ func (a *App) HandleCommandResponse(command *model.Command, args *model.CommandA
response.Attachments = a.ProcessSlackAttachments(response.Attachments)
if _, err := a.CreateCommandPost(post, args.TeamId, response); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
return response, nil
diff --git a/app/command_echo.go b/app/command_echo.go
index 9fef8a0a9..f0851964b 100644
--- a/app/command_echo.go
+++ b/app/command_echo.go
@@ -4,11 +4,12 @@
package app
import (
+ "fmt"
"strconv"
"strings"
"time"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
goi18n "github.com/nicksnyder/go-i18n/i18n"
)
@@ -89,7 +90,7 @@ func (me *EchoProvider) DoCommand(a *App, args *model.CommandArgs, message strin
time.Sleep(time.Duration(delay) * time.Second)
if _, err := a.CreatePostMissingChannel(post, true); err != nil {
- l4g.Error(args.T("api.command_echo.create.app_error"), err)
+ mlog.Error(fmt.Sprintf("Unable to create /echo post, err=%v", err))
}
})
diff --git a/app/command_groupmsg.go b/app/command_groupmsg.go
index 32ad431ff..0e783e1a8 100644
--- a/app/command_groupmsg.go
+++ b/app/command_groupmsg.go
@@ -7,7 +7,7 @@ import (
"fmt"
"strings"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
goi18n "github.com/nicksnyder/go-i18n/i18n"
)
@@ -95,7 +95,7 @@ func (me *groupmsgProvider) DoCommand(a *App, args *model.CommandArgs, message s
groupChannel, channelErr := a.CreateGroupChannel(targetUsersSlice, args.UserId)
if channelErr != nil {
- l4g.Error(channelErr.Error())
+ mlog.Error(channelErr.Error())
return &model.CommandResponse{Text: args.T("api.command_groupmsg.group_fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
diff --git a/app/command_invite.go b/app/command_invite.go
index ce443bf3d..9045365ad 100644
--- a/app/command_invite.go
+++ b/app/command_invite.go
@@ -4,9 +4,10 @@
package app
import (
+ "fmt"
"strings"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
goi18n "github.com/nicksnyder/go-i18n/i18n"
)
@@ -41,7 +42,7 @@ func (me *InviteProvider) DoCommand(a *App, args *model.CommandArgs, message str
return &model.CommandResponse{Text: args.T("api.command_invite.missing_message.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
- l4g.Debug(message)
+ mlog.Debug(fmt.Sprint(message))
splitMessage := strings.SplitN(message, " ", 2)
targetUsername := splitMessage[0]
@@ -49,7 +50,7 @@ func (me *InviteProvider) DoCommand(a *App, args *model.CommandArgs, message str
var userProfile *model.User
if result := <-a.Srv.Store.User().GetByUsername(targetUsername); result.Err != nil {
- l4g.Error(result.Err.Error())
+ mlog.Error(result.Err.Error())
return &model.CommandResponse{Text: args.T("api.command_invite.missing_user.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} else {
userProfile = result.Data.(*model.User)
diff --git a/app/command_invite_people.go b/app/command_invite_people.go
index e463bd37c..e5ff5a316 100644
--- a/app/command_invite_people.go
+++ b/app/command_invite_people.go
@@ -6,7 +6,7 @@ package app
import (
"strings"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
goi18n "github.com/nicksnyder/go-i18n/i18n"
)
@@ -63,7 +63,7 @@ func (me *InvitePeopleProvider) DoCommand(a *App, args *model.CommandArgs, messa
}
if err := a.InviteNewUsersToTeam(emailList, args.TeamId, args.UserId); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: args.T("api.command.invite_people.fail")}
}
diff --git a/app/command_loadtest.go b/app/command_loadtest.go
index e43a7113f..a4a8f003a 100644
--- a/app/command_loadtest.go
+++ b/app/command_loadtest.go
@@ -10,7 +10,7 @@ import (
"strconv"
"strings"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
goi18n "github.com/nicksnyder/go-i18n/i18n"
@@ -177,10 +177,10 @@ func (me *LoadTestProvider) SetupCommand(a *App, args *model.CommandArgs, messag
if !err {
return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} else {
- l4g.Info("Testing environment created")
+ mlog.Info("Testing environment created")
for i := 0; i < len(environment.Teams); i++ {
- l4g.Info("Team Created: " + environment.Teams[i].Name)
- l4g.Info("\t User to login: " + environment.Environments[i].Users[0].Email + ", " + USER_PASSWORD)
+ mlog.Info("Team Created: " + environment.Teams[i].Name)
+ mlog.Info("\t User to login: " + environment.Environments[i].Users[0].Email + ", " + USER_PASSWORD)
}
}
} else {
diff --git a/app/command_msg.go b/app/command_msg.go
index cf0e90c74..6877c10d6 100644
--- a/app/command_msg.go
+++ b/app/command_msg.go
@@ -6,7 +6,7 @@ package app
import (
"strings"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
goi18n "github.com/nicksnyder/go-i18n/i18n"
)
@@ -50,7 +50,7 @@ func (me *msgProvider) DoCommand(a *App, args *model.CommandArgs, message string
var userProfile *model.User
if result := <-a.Srv.Store.User().GetByUsername(targetUsername); result.Err != nil {
- l4g.Error(result.Err.Error())
+ mlog.Error(result.Err.Error())
return &model.CommandResponse{Text: args.T("api.command_msg.missing.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} else {
userProfile = result.Data.(*model.User)
@@ -67,13 +67,13 @@ func (me *msgProvider) DoCommand(a *App, args *model.CommandArgs, message string
if channel := <-a.Srv.Store.Channel().GetByName(args.TeamId, channelName, true); channel.Err != nil {
if channel.Err.Id == "store.sql_channel.get_by_name.missing.app_error" {
if directChannel, err := a.CreateDirectChannel(args.UserId, userProfile.Id); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
return &model.CommandResponse{Text: args.T("api.command_msg.dm_fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} else {
targetChannelId = directChannel.Id
}
} else {
- l4g.Error(channel.Err.Error())
+ mlog.Error(channel.Err.Error())
return &model.CommandResponse{Text: args.T("api.command_msg.dm_fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
} else {
diff --git a/app/command_mute_test.go b/app/command_mute_test.go
index 9f82c48cc..18ced3ddf 100644
--- a/app/command_mute_test.go
+++ b/app/command_mute_test.go
@@ -4,11 +4,12 @@
package app
import (
+ "testing"
+ "time"
+
"github.com/mattermost/mattermost-server/model"
"github.com/nicksnyder/go-i18n/i18n"
"github.com/stretchr/testify/assert"
- "testing"
- "time"
)
func TestMuteCommandNoChannel(t *testing.T) {
diff --git a/app/command_remove.go b/app/command_remove.go
index a9c21e2d5..3671a2063 100644
--- a/app/command_remove.go
+++ b/app/command_remove.go
@@ -6,9 +6,9 @@ package app
import (
"strings"
- l4g "github.com/alecthomas/log4go"
goi18n "github.com/nicksnyder/go-i18n/i18n"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -93,7 +93,7 @@ func doCommand(a *App, args *model.CommandArgs, message string) *model.CommandRe
var userProfile *model.User
if result := <-a.Srv.Store.User().GetByUsername(targetUsername); result.Err != nil {
- l4g.Error(result.Err.Error())
+ mlog.Error(result.Err.Error())
return &model.CommandResponse{Text: args.T("api.command_remove.missing.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} else {
userProfile = result.Data.(*model.User)
diff --git a/app/config.go b/app/config.go
index 75d38e24a..b4fbfe725 100644
--- a/app/config.go
+++ b/app/config.go
@@ -17,8 +17,7 @@ import (
"strconv"
"strings"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -60,9 +59,6 @@ func (a *App) LoadConfig(configFile string) *model.AppError {
a.configFile = configPath
- utils.ConfigureLog(&cfg.LogSettings)
- l4g.Info("Using config file at %s", configPath)
-
a.config.Store(cfg)
a.envConfig = envConfig
@@ -101,7 +97,7 @@ func (a *App) EnableConfigWatch() {
a.ReloadConfig()
})
if err != nil {
- l4g.Error(err)
+ mlog.Error(fmt.Sprint(err))
}
a.configWatcher = configWatcher
}
diff --git a/app/diagnostics.go b/app/diagnostics.go
index 6e9adbf3c..cda0814ca 100644
--- a/app/diagnostics.go
+++ b/app/diagnostics.go
@@ -5,11 +5,10 @@ package app
import (
"encoding/json"
- "log"
- "os"
"runtime"
"sync/atomic"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/segmentio/analytics-go"
)
@@ -69,12 +68,12 @@ func (a *App) SendDailyDiagnostics() {
func (a *App) initDiagnostics(endpoint string) {
if client == nil {
client = analytics.New(SEGMENT_KEY)
+ client.Logger = a.Log.StdLog(mlog.String("source", "segment"))
// For testing
if endpoint != "" {
client.Endpoint = endpoint
client.Verbose = true
client.Size = 1
- client.Logger = log.New(os.Stdout, "segment ", log.LstdFlags)
}
client.Identify(&analytics.Identify{
UserId: a.DiagnosticId(),
@@ -299,10 +298,11 @@ func (a *App) trackConfig() {
a.SendDiagnostic(TRACK_CONFIG_LOG, map[string]interface{}{
"enable_console": cfg.LogSettings.EnableConsole,
"console_level": cfg.LogSettings.ConsoleLevel,
+ "console_json": *cfg.LogSettings.ConsoleJson,
"enable_file": cfg.LogSettings.EnableFile,
"file_level": cfg.LogSettings.FileLevel,
+ "file_json": cfg.LogSettings.FileJson,
"enable_webhook_debugging": cfg.LogSettings.EnableWebhookDebugging,
- "isdefault_file_format": isDefault(cfg.LogSettings.FileFormat, ""),
"isdefault_file_location": isDefault(cfg.LogSettings.FileLocation, ""),
})
diff --git a/app/email.go b/app/email.go
index 7a50fd82a..aa05cefdb 100644
--- a/app/email.go
+++ b/app/email.go
@@ -9,9 +9,9 @@ import (
"net/http"
- l4g "github.com/alecthomas/log4go"
"github.com/nicksnyder/go-i18n/i18n"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -282,17 +282,17 @@ func (a *App) SendInviteEmails(team *model.Team, senderName string, invites []st
data := model.MapToJson(props)
if result := <-a.Srv.Store.Token().Save(token); result.Err != nil {
- l4g.Error(utils.T("api.team.invite_members.send.error"), result.Err)
+ mlog.Error(fmt.Sprintf("Failed to send invite email successfully err=%v", result.Err))
continue
}
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token))
if !a.Config().EmailSettings.SendEmailNotifications {
- l4g.Info(utils.T("api.team.invite_members.sending.info"), invite, bodyPage.Props["Link"])
+ mlog.Info(fmt.Sprintf("sending invitation to %v %v", invite, bodyPage.Props["Link"]))
}
if err := a.SendMail(invite, subject, bodyPage.Render()); err != nil {
- l4g.Error(utils.T("api.team.invite_members.send.error"), err)
+ mlog.Error(fmt.Sprintf("Failed to send invite email successfully err=%v", err))
}
}
}
diff --git a/app/email_batching.go b/app/email_batching.go
index 07adda674..e1ea7abb5 100644
--- a/app/email_batching.go
+++ b/app/email_batching.go
@@ -10,12 +10,12 @@ import (
"sync"
"time"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
"net/http"
- l4g "github.com/alecthomas/log4go"
"github.com/nicksnyder/go-i18n/i18n"
)
@@ -41,7 +41,7 @@ func (a *App) AddNotificationEmailToBatch(user *model.User, post *model.Post, te
}
if !a.EmailBatching.Add(user, post, team) {
- l4g.Error(utils.T("api.email_batching.add_notification_email_to_batch.channel_full.app_error"))
+ mlog.Error("Email batching job's receiving channel was full. Please increase the EmailBatchingBufferSize.")
return model.NewAppError("AddNotificationEmailToBatch", "api.email_batching.add_notification_email_to_batch.channel_full.app_error", nil, "", http.StatusInternalServerError)
}
@@ -71,7 +71,7 @@ func NewEmailBatchingJob(a *App, bufferSize int) *EmailBatchingJob {
}
func (job *EmailBatchingJob) Start() {
- l4g.Debug(utils.T("api.email_batching.start.starting"), *job.app.Config().EmailSettings.EmailBatchingInterval)
+ mlog.Debug(fmt.Sprintf("Email batching job starting. Checking for pending emails every %v seconds.", *job.app.Config().EmailSettings.EmailBatchingInterval))
newTask := model.CreateRecurringTask(EMAIL_BATCHING_TASK_NAME, job.CheckPendingEmails, time.Duration(*job.app.Config().EmailSettings.EmailBatchingInterval)*time.Second)
job.taskMutex.Lock()
@@ -107,7 +107,7 @@ func (job *EmailBatchingJob) CheckPendingEmails() {
// without actually sending emails
job.checkPendingNotifications(time.Now(), job.app.sendBatchedEmailNotification)
- l4g.Debug(utils.T("api.email_batching.check_pending_emails.finished_running"), len(job.pendingNotifications))
+ mlog.Debug(fmt.Sprintf("Email batching job ran. %v user(s) still have notifications pending.", len(job.pendingNotifications)))
}
func (job *EmailBatchingJob) handleNewNotifications() {
@@ -141,7 +141,7 @@ func (job *EmailBatchingJob) checkPendingNotifications(now time.Time, handler fu
}
tchan := job.app.Srv.Store.Team().GetByName(notifications[0].teamName)
if result := <-tchan; result.Err != nil {
- l4g.Error("Unable to find Team id for notification", result.Err)
+ mlog.Error(fmt.Sprint("Unable to find Team id for notification", result.Err))
continue
} else if team, ok := result.Data.(*model.Team); ok {
inspectedTeamNames[notification.teamName] = team.Id
@@ -151,12 +151,12 @@ func (job *EmailBatchingJob) checkPendingNotifications(now time.Time, handler fu
// all queued notifications
mchan := job.app.Srv.Store.Channel().GetMembersForUser(inspectedTeamNames[notification.teamName], userId)
if result := <-mchan; result.Err != nil {
- l4g.Error("Unable to find ChannelMembers for user", result.Err)
+ mlog.Error(fmt.Sprint("Unable to find ChannelMembers for user", result.Err))
continue
} else if channelMembers, ok := result.Data.(*model.ChannelMembers); ok {
for _, channelMember := range *channelMembers {
if channelMember.LastViewedAt >= batchStartTime {
- l4g.Debug("Deleted notifications for user %s", userId)
+ mlog.Debug(fmt.Sprintf("Deleted notifications for user %s", userId), mlog.String("user_id", userId))
delete(job.pendingNotifications, userId)
break
}
@@ -198,7 +198,7 @@ func (a *App) sendBatchedEmailNotification(userId string, notifications []*batch
var user *model.User
if result := <-uchan; result.Err != nil {
- l4g.Warn("api.email_batching.send_batched_email_notification.user.app_error")
+ mlog.Warn("api.email_batching.send_batched_email_notification.user.app_error")
return
} else {
user = result.Data.(*model.User)
@@ -212,7 +212,7 @@ func (a *App) sendBatchedEmailNotification(userId string, notifications []*batch
var sender *model.User
schan := a.Srv.Store.User().Get(notification.post.UserId)
if result := <-schan; result.Err != nil {
- l4g.Warn(utils.T("api.email_batching.render_batched_post.sender.app_error"))
+ mlog.Warn("Unable to find sender of post for batched email notification")
continue
} else {
sender = result.Data.(*model.User)
@@ -221,7 +221,7 @@ func (a *App) sendBatchedEmailNotification(userId string, notifications []*batch
var channel *model.Channel
cchan := a.Srv.Store.Channel().Get(notification.post.ChannelId, true)
if result := <-cchan; result.Err != nil {
- l4g.Warn(utils.T("api.email_batching.render_batched_post.channel.app_error"))
+ mlog.Warn("Unable to find channel of post for batched email notification")
continue
} else {
channel = result.Data.(*model.Channel)
@@ -250,7 +250,7 @@ func (a *App) sendBatchedEmailNotification(userId string, notifications []*batch
body.Props["BodyText"] = translateFunc("api.email_batching.send_batched_email_notification.body_text", len(notifications))
if err := a.SendMail(user.Email, subject, body.Render()); err != nil {
- l4g.Warn(utils.T("api.email_batchings.send_batched_email_notification.send.app_error"), user.Email, err)
+ mlog.Warn(fmt.Sprint("api.email_batchings.send_batched_email_notification.send.app_error FIXME: NOT FOUND IN TRANSLATIONS FILE", user.Email, err))
}
}
diff --git a/app/emoji.go b/app/emoji.go
index eebe59ccf..f48501cf9 100644
--- a/app/emoji.go
+++ b/app/emoji.go
@@ -5,6 +5,7 @@ package app
import (
"bytes"
+ "fmt"
"image"
"image/draw"
"image/gif"
@@ -14,13 +15,11 @@ import (
"mime/multipart"
"net/http"
- l4g "github.com/alecthomas/log4go"
-
"image/color/palette"
"github.com/disintegration/imaging"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
)
const (
@@ -242,13 +241,13 @@ func imageToPaletted(img image.Image) *image.Paletted {
func (a *App) deleteEmojiImage(id string) {
if err := a.MoveFile(getEmojiImagePath(id), "emoji/"+id+"/image_deleted"); err != nil {
- l4g.Error("Failed to rename image when deleting emoji %v", id)
+ mlog.Error(fmt.Sprintf("Failed to rename image when deleting emoji %v", id))
}
}
func (a *App) deleteReactionsForEmoji(emojiName string) {
if result := <-a.Srv.Store.Reaction().DeleteAllWithEmojiName(emojiName); result.Err != nil {
- l4g.Warn(utils.T("api.emoji.delete.delete_reactions.app_error"), emojiName)
- l4g.Warn(result.Err)
+ mlog.Warn(fmt.Sprintf("Unable to delete reactions when deleting emoji with emoji name %v", emojiName))
+ mlog.Warn(fmt.Sprint(result.Err))
}
}
diff --git a/app/file.go b/app/file.go
index 06ee61c92..a1addd7ac 100644
--- a/app/file.go
+++ b/app/file.go
@@ -22,11 +22,11 @@ import (
"sync"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/disintegration/imaging"
"github.com/rwcarlsen/goexif/exif"
_ "golang.org/x/image/bmp"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -98,7 +98,7 @@ func (a *App) GetInfoForFilename(post *model.Post, teamId string, filename strin
// Find the path from the Filename of the form /{channelId}/{userId}/{uid}/{nameWithExtension}
split := strings.SplitN(filename, "/", 5)
if len(split) < 5 {
- l4g.Error(utils.T("api.file.migrate_filenames_to_file_infos.unexpected_filename.error"), post.Id, filename)
+ mlog.Error(fmt.Sprintf("Unable to decipher filename when migrating post to use FileInfos, post_id=%v, filename=%v", post.Id, filename), mlog.String("post_id", post.Id))
return nil
}
@@ -108,7 +108,7 @@ func (a *App) GetInfoForFilename(post *model.Post, teamId string, filename strin
name, _ := url.QueryUnescape(split[4])
if split[0] != "" || split[1] != post.ChannelId || split[2] != post.UserId || strings.Contains(split[4], "/") {
- l4g.Warn(utils.T("api.file.migrate_filenames_to_file_infos.mismatched_filename.warn"), post.Id, post.ChannelId, post.UserId, filename)
+ mlog.Warn(fmt.Sprintf("Found an unusual filename when migrating post to use FileInfos, post_id=%v, channel_id=%v, user_id=%v, filename=%v", post.Id, post.ChannelId, post.UserId, filename), mlog.String("post_id", post.Id))
}
pathPrefix := fmt.Sprintf("teams/%s/channels/%s/users/%s/%s/", teamId, channelId, userId, oldId)
@@ -117,13 +117,13 @@ func (a *App) GetInfoForFilename(post *model.Post, teamId string, filename strin
// Open the file and populate the fields of the FileInfo
var info *model.FileInfo
if data, err := a.ReadFile(path); err != nil {
- l4g.Error(utils.T("api.file.migrate_filenames_to_file_infos.file_not_found.error"), post.Id, filename, path, err)
+ mlog.Error(fmt.Sprint("api.file.migrate_filenames_to_file_infos.file_not_found.error FIXME: NOT FOUND IN TRANSLATIONS FILE", post.Id, filename, path, err), mlog.String("post_id", post.Id))
return nil
} else {
var err *model.AppError
info, err = model.GetInfoForBytes(name, data)
if err != nil {
- l4g.Warn(utils.T("api.file.migrate_filenames_to_file_infos.info.app_error"), post.Id, filename, err)
+ mlog.Warn(fmt.Sprintf("Unable to fully decode file info when migrating post to use FileInfos, post_id=%v, filename=%v, err=%v", post.Id, filename, err), mlog.String("post_id", post.Id))
}
}
@@ -151,7 +151,7 @@ func (a *App) FindTeamIdForFilename(post *model.Post, filename string) string {
// This post is in a direct channel so we need to figure out what team the files are stored under.
if result := <-a.Srv.Store.Team().GetTeamsByUserId(post.UserId); result.Err != nil {
- l4g.Error(utils.T("api.file.migrate_filenames_to_file_infos.teams.app_error"), post.Id, result.Err)
+ mlog.Error(fmt.Sprintf("Unable to get teams when migrating post to use FileInfos, post_id=%v, err=%v", post.Id, result.Err), mlog.String("post_id", post.Id))
} else if teams := result.Data.([]*model.Team); len(teams) == 1 {
// The user has only one team so the post must've been sent from it
return teams[0].Id
@@ -173,7 +173,7 @@ var fileMigrationLock sync.Mutex
// Creates and stores FileInfos for a post created before the FileInfos table existed.
func (a *App) MigrateFilenamesToFileInfos(post *model.Post) []*model.FileInfo {
if len(post.Filenames) == 0 {
- l4g.Warn(utils.T("api.file.migrate_filenames_to_file_infos.no_filenames.warn"), post.Id)
+ mlog.Warn(fmt.Sprintf("Unable to migrate post to use FileInfos with an empty Filenames field, post_id=%v", post.Id), mlog.String("post_id", post.Id))
return []*model.FileInfo{}
}
@@ -184,7 +184,7 @@ func (a *App) MigrateFilenamesToFileInfos(post *model.Post) []*model.FileInfo {
var channel *model.Channel
if result := <-cchan; result.Err != nil {
- l4g.Error(utils.T("api.file.migrate_filenames_to_file_infos.channel.app_error"), post.Id, post.ChannelId, result.Err)
+ mlog.Error(fmt.Sprintf("Unable to get channel when migrating post to use FileInfos, post_id=%v, channel_id=%v, err=%v", post.Id, post.ChannelId, result.Err), mlog.String("post_id", post.Id))
return []*model.FileInfo{}
} else {
channel = result.Data.(*model.Channel)
@@ -202,7 +202,7 @@ func (a *App) MigrateFilenamesToFileInfos(post *model.Post) []*model.FileInfo {
// Create FileInfo objects for this post
infos := make([]*model.FileInfo, 0, len(filenames))
if teamId == "" {
- l4g.Error(utils.T("api.file.migrate_filenames_to_file_infos.team_id.error"), post.Id, filenames)
+ mlog.Error(fmt.Sprint("api.file.migrate_filenames_to_file_infos.team_id.error FIXME: NOT FOUND IN TRANSLATIONS FILE", post.Id, filenames), mlog.String("post_id", post.Id))
} else {
for _, filename := range filenames {
info := a.GetInfoForFilename(post, teamId, filename)
@@ -219,26 +219,26 @@ func (a *App) MigrateFilenamesToFileInfos(post *model.Post) []*model.FileInfo {
defer fileMigrationLock.Unlock()
if result := <-a.Srv.Store.Post().Get(post.Id); result.Err != nil {
- l4g.Error(utils.T("api.file.migrate_filenames_to_file_infos.get_post_again.app_error"), post.Id, result.Err)
+ mlog.Error(fmt.Sprint("api.file.migrate_filenames_to_file_infos.get_post_again.app_error FIXME: NOT FOUND IN TRANSLATIONS FILE", post.Id, result.Err), mlog.String("post_id", post.Id))
return []*model.FileInfo{}
} else if newPost := result.Data.(*model.PostList).Posts[post.Id]; len(newPost.Filenames) != len(post.Filenames) {
// Another thread has already created FileInfos for this post, so just return those
if result := <-a.Srv.Store.FileInfo().GetForPost(post.Id, true, false); result.Err != nil {
- l4g.Error(utils.T("api.file.migrate_filenames_to_file_infos.get_post_file_infos_again.app_error"), post.Id, result.Err)
+ mlog.Error(fmt.Sprint("api.file.migrate_filenames_to_file_infos.get_post_file_infos_again.app_error FIXME: NOT FOUND IN TRANSLATIONS FILE", post.Id, result.Err), mlog.String("post_id", post.Id))
return []*model.FileInfo{}
} else {
- l4g.Debug(utils.T("api.file.migrate_filenames_to_file_infos.not_migrating_post.debug"), post.Id)
+ mlog.Debug(fmt.Sprintf("Post already migrated to use FileInfos, post_id=%v", post.Id), mlog.String("post_id", post.Id))
return result.Data.([]*model.FileInfo)
}
}
- l4g.Debug(utils.T("api.file.migrate_filenames_to_file_infos.migrating_post.debug"), post.Id)
+ mlog.Debug(fmt.Sprintf("Migrating post to use FileInfos, post_id=%v", post.Id), mlog.String("post_id", post.Id))
savedInfos := make([]*model.FileInfo, 0, len(infos))
fileIds := make([]string, 0, len(filenames))
for _, info := range infos {
if result := <-a.Srv.Store.FileInfo().Save(info); result.Err != nil {
- l4g.Error(utils.T("api.file.migrate_filenames_to_file_infos.save_file_info.app_error"), post.Id, info.Id, info.Path, result.Err)
+ mlog.Error(fmt.Sprint("api.file.migrate_filenames_to_file_infos.save_file_info.app_error FIXME: NOT FOUND IN TRANSLATIONS FILE", post.Id, info.Id, info.Path, result.Err), mlog.String("post_id", post.Id))
continue
}
@@ -255,7 +255,7 @@ func (a *App) MigrateFilenamesToFileInfos(post *model.Post) []*model.FileInfo {
// Update Posts to clear Filenames and set FileIds
if result := <-a.Srv.Store.Post().Update(newPost, post); result.Err != nil {
- l4g.Error(utils.T("api.file.migrate_filenames_to_file_infos.save_post.app_error"), post.Id, newPost.FileIds, post.Filenames, result.Err)
+ mlog.Error(fmt.Sprint("api.file.migrate_filenames_to_file_infos.save_post.app_error FIXME: NOT FOUND IN TRANSLATIONS FILE", post.Id, newPost.FileIds, post.Filenames, result.Err), mlog.String("post_id", post.Id))
return []*model.FileInfo{}
} else {
return savedInfos
@@ -415,7 +415,7 @@ func prepareImage(fileData []byte) (*image.Image, int, int) {
// Decode image bytes into Image object
img, imgType, err := image.Decode(bytes.NewReader(fileData))
if err != nil {
- l4g.Error(utils.T("api.file.handle_images_forget.decode.error"), err)
+ mlog.Error(fmt.Sprintf("Unable to decode image err=%v", err))
return nil, 0, 0
}
@@ -492,12 +492,12 @@ func (a *App) generateThumbnailImage(img image.Image, thumbnailPath string, widt
buf := new(bytes.Buffer)
if err := jpeg.Encode(buf, thumbnail, &jpeg.Options{Quality: 90}); err != nil {
- l4g.Error(utils.T("api.file.handle_images_forget.encode_jpeg.error"), thumbnailPath, err)
+ mlog.Error(fmt.Sprintf("Unable to encode image as jpeg path=%v err=%v", thumbnailPath, err))
return
}
if err := a.WriteFile(buf.Bytes(), thumbnailPath); err != nil {
- l4g.Error(utils.T("api.file.handle_images_forget.upload_thumb.error"), thumbnailPath, err)
+ mlog.Error(fmt.Sprintf("Unable to upload thumbnail path=%v err=%v", thumbnailPath, err))
return
}
}
@@ -514,12 +514,12 @@ func (a *App) generatePreviewImage(img image.Image, previewPath string, width in
buf := new(bytes.Buffer)
if err := jpeg.Encode(buf, preview, &jpeg.Options{Quality: 90}); err != nil {
- l4g.Error(utils.T("api.file.handle_images_forget.encode_preview.error"), previewPath, err)
+ mlog.Error(fmt.Sprintf("Unable to encode image as preview jpg path=%v err=%v", previewPath, err))
return
}
if err := a.WriteFile(buf.Bytes(), previewPath); err != nil {
- l4g.Error(utils.T("api.file.handle_images_forget.upload_preview.error"), previewPath, err)
+ mlog.Error(fmt.Sprintf("Unable to upload preview path=%v err=%v", previewPath, err))
return
}
}
diff --git a/app/import.go b/app/import.go
index 23a315be7..fc0bfafa6 100644
--- a/app/import.go
+++ b/app/import.go
@@ -7,6 +7,7 @@ import (
"bufio"
"bytes"
"encoding/json"
+ "fmt"
"io"
"net/http"
"os"
@@ -16,11 +17,9 @@ import (
"time"
"unicode/utf8"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
- "github.com/mattermost/mattermost-server/utils"
)
// Import Data Models
@@ -715,10 +714,10 @@ func (a *App) ImportUser(data *UserImportData, dryRun bool) *model.AppError {
if data.ProfileImage != nil {
file, err := os.Open(*data.ProfileImage)
if err != nil {
- l4g.Error(utils.T("api.import.import_user.profile_image.error"), err)
+ mlog.Error(fmt.Sprint("api.import.import_user.profile_image.error FIXME: NOT FOUND IN TRANSLATIONS FILE", err))
}
if err := a.SetProfileImageFromFile(savedUser.Id, file); err != nil {
- l4g.Error(utils.T("api.import.import_user.profile_image.error"), err)
+ mlog.Error(fmt.Sprint("api.import.import_user.profile_image.error FIXME: NOT FOUND IN TRANSLATIONS FILE", err))
}
}
@@ -1654,12 +1653,12 @@ func (a *App) OldImportPost(post *model.Post) {
post.Hashtags, _ = model.ParseHashtags(post.Message)
if result := <-a.Srv.Store.Post().Save(post); result.Err != nil {
- l4g.Debug(utils.T("api.import.import_post.saving.debug"), post.UserId, post.Message)
+ mlog.Debug(fmt.Sprintf("Error saving post. user=%v, message=%v", post.UserId, post.Message))
}
for _, fileId := range post.FileIds {
if result := <-a.Srv.Store.FileInfo().AttachToPost(fileId, post.Id); result.Err != nil {
- l4g.Error(utils.T("api.import.import_post.attach_files.error"), post.Id, post.FileIds, result.Err)
+ mlog.Error(fmt.Sprintf("Error attaching files to post. postId=%v, fileIds=%v, message=%v", post.Id, post.FileIds, result.Err), mlog.String("post_id", post.Id))
}
}
@@ -1675,17 +1674,17 @@ func (a *App) OldImportUser(team *model.Team, user *model.User) *model.User {
user.Roles = model.SYSTEM_USER_ROLE_ID
if result := <-a.Srv.Store.User().Save(user); result.Err != nil {
- l4g.Error(utils.T("api.import.import_user.saving.error"), result.Err)
+ mlog.Error(fmt.Sprintf("Error saving user. err=%v", result.Err))
return nil
} else {
ruser := result.Data.(*model.User)
if cresult := <-a.Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
- l4g.Error(utils.T("api.import.import_user.set_email.error"), cresult.Err)
+ mlog.Error(fmt.Sprintf("Failed to set email verified err=%v", cresult.Err))
}
if err := a.JoinUserToTeam(team, user, ""); err != nil {
- l4g.Error(utils.T("api.import.import_user.join_team.error"), err)
+ mlog.Error(fmt.Sprintf("Failed to join team when importing err=%v", err))
}
return ruser
diff --git a/app/ldap.go b/app/ldap.go
index ff7a5ed21..22c3b746b 100644
--- a/app/ldap.go
+++ b/app/ldap.go
@@ -4,9 +4,10 @@
package app
import (
+ "fmt"
"net/http"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -18,7 +19,7 @@ func (a *App) SyncLdap() {
if ldapI := a.Ldap; ldapI != nil {
ldapI.StartSynchronizeJob(false)
} else {
- l4g.Error("%v", model.NewAppError("SyncLdap", "ent.ldap.disabled.app_error", nil, "", http.StatusNotImplemented).Error())
+ mlog.Error(fmt.Sprintf("%v", model.NewAppError("SyncLdap", "ent.ldap.disabled.app_error", nil, "", http.StatusNotImplemented).Error()))
}
}
})
@@ -68,7 +69,7 @@ func (a *App) SwitchEmailToLdap(email, password, code, ldapId, ldapPassword stri
a.Go(func() {
if err := a.SendSignInChangeEmail(user.Email, "AD/LDAP", user.Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
@@ -114,7 +115,7 @@ func (a *App) SwitchLdapToEmail(ldapPassword, code, email, newPassword string) (
a.Go(func() {
if err := a.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
diff --git a/app/license.go b/app/license.go
index 148b10317..310a61fdb 100644
--- a/app/license.go
+++ b/app/license.go
@@ -9,8 +9,7 @@ import (
"net/http"
"strings"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -30,7 +29,7 @@ func (a *App) LoadLicense() {
if license != nil {
if _, err := a.SaveLicense(licenseBytes); err != nil {
- l4g.Info("Failed to save license key loaded from disk err=%v", err.Error())
+ mlog.Info(fmt.Sprintf("Failed to save license key loaded from disk err=%v", err.Error()))
} else {
licenseId = license.Id
}
@@ -40,9 +39,9 @@ func (a *App) LoadLicense() {
if result := <-a.Srv.Store.License().Get(licenseId); result.Err == nil {
record := result.Data.(*model.LicenseRecord)
a.ValidateAndSetLicenseBytes([]byte(record.Bytes))
- l4g.Info("License key valid unlocking enterprise features.")
+ mlog.Info("License key valid unlocking enterprise features.")
} else {
- l4g.Info(utils.T("mattermost.load_license.find.warn"))
+ mlog.Info("License key from https://mattermost.com required to unlock enterprise features.")
}
}
@@ -140,7 +139,7 @@ func (a *App) ValidateAndSetLicenseBytes(b []byte) {
return
}
- l4g.Warn(utils.T("utils.license.load_license.invalid.warn"))
+ mlog.Warn("No valid enterprise license found")
}
func (a *App) SetClientLicense(m map[string]string) {
diff --git a/app/notification.go b/app/notification.go
index d1f6225b3..7198de764 100644
--- a/app/notification.go
+++ b/app/notification.go
@@ -15,7 +15,7 @@ import (
"time"
"unicode"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -193,14 +193,14 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
// Remove the user as recipient when the user has muted the channel.
if channelMuted, ok := channelMemberNotifyPropsMap[id][model.MARK_UNREAD_NOTIFY_PROP]; ok {
if channelMuted == model.CHANNEL_MARK_UNREAD_MENTION {
- l4g.Debug("Channel muted for user_id %v, channel_mute %v", id, channelMuted)
+ mlog.Debug(fmt.Sprintf("Channel muted for user_id %v, channel_mute %v", id, channelMuted))
userAllowsEmails = false
}
}
//If email verification is required and user email is not verified don't send email.
if a.Config().EmailSettings.RequireEmailVerification && !profileMap[id].EmailVerified {
- l4g.Error("Skipped sending notification email to %v, address not verified. [details: user_id=%v]", profileMap[id].Email, id)
+ mlog.Error(fmt.Sprintf("Skipped sending notification email to %v, address not verified. [details: user_id=%v]", profileMap[id].Email, id))
continue
}
@@ -266,7 +266,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
// MUST be completed before push notifications send
for _, uchan := range updateMentionChans {
if result := <-uchan; result.Err != nil {
- l4g.Warn(utils.T("api.post.update_mention_count_and_forget.update_error"), post.Id, post.ChannelId, result.Err)
+ mlog.Warn(fmt.Sprintf("Failed to update mention count, post_id=%v channel_id=%v err=%v", post.Id, post.ChannelId, result.Err), mlog.String("post_id", post.Id))
}
}
@@ -274,7 +274,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
if *a.Config().EmailSettings.SendPushNotifications {
pushServer := *a.Config().EmailSettings.PushNotificationServer
if license := a.License(); pushServer == model.MHPNS && (license == nil || !*license.Features.MHPNS) {
- l4g.Warn(utils.T("api.post.send_notifications_and_forget.push_notification.mhpnsWarn"))
+ mlog.Warn("api.post.send_notifications_and_forget.push_notification.mhpnsWarn FIXME: NOT FOUND IN TRANSLATIONS FILE")
sendPushNotifications = false
} else {
sendPushNotifications = true
@@ -330,7 +330,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
var infos []*model.FileInfo
if result := <-fchan; result.Err != nil {
- l4g.Warn(utils.T("api.post.send_notifications.files.error"), post.Id, result.Err)
+ mlog.Warn(fmt.Sprint("api.post.send_notifications.files.error FIXME: NOT FOUND IN TRANSLATIONS FILE", post.Id, result.Err), mlog.String("post_id", post.Id))
} else {
infos = result.Data.([]*model.FileInfo)
}
@@ -415,7 +415,7 @@ func (a *App) sendNotificationEmail(post *model.Post, user *model.User, channel
a.Go(func() {
if err := a.SendMail(user.Email, html.UnescapeString(subjectText), bodyText); err != nil {
- l4g.Error(utils.T("api.post.send_notifications_and_forget.send.error"), user.Email, err)
+ mlog.Error(fmt.Sprint("api.post.send_notifications_and_forget.send.error FIXME: NOT FOUND IN TRANSLATIONS FILE", user.Email, err))
}
})
@@ -577,7 +577,7 @@ func (a *App) GetMessageForNotification(post *model.Post, translateFunc i18n.Tra
// extract the filenames from their paths and determine what type of files are attached
var infos []*model.FileInfo
if result := <-a.Srv.Store.FileInfo().GetForPost(post.Id, true, true); result.Err != nil {
- l4g.Warn(utils.T("api.post.get_message_for_notification.get_files.error"), post.Id, result.Err)
+ mlog.Warn(fmt.Sprintf("Encountered error when getting files for notification message, post_id=%v, err=%v", post.Id, result.Err), mlog.String("post_id", post.Id))
} else {
infos = result.Data.([]*model.FileInfo)
}
@@ -617,7 +617,7 @@ func (a *App) sendPushNotification(post *model.Post, user *model.User, channel *
msg := model.PushNotification{}
if badge := <-a.Srv.Store.User().GetUnreadCount(user.Id); badge.Err != nil {
msg.Badge = 1
- l4g.Error(utils.T("store.sql_user.get_unread_count.app_error"), user.Id, badge.Err)
+ mlog.Error(fmt.Sprint("We could not get the unread message count for the user", user.Id, badge.Err), mlog.String("user_id", user.Id))
} else {
msg.Badge = int(badge.Data.(int64))
}
@@ -651,7 +651,7 @@ func (a *App) sendPushNotification(post *model.Post, user *model.User, channel *
tmpMessage := *model.PushNotificationFromJson(strings.NewReader(msg.ToJson()))
tmpMessage.SetDeviceIdAndPlatform(session.DeviceId)
- l4g.Debug("Sending push notification to device %v for user %v with msg of '%v'", tmpMessage.DeviceId, user.Id, msg.Message)
+ mlog.Debug(fmt.Sprintf("Sending push notification to device %v for user %v with msg of '%v'", tmpMessage.DeviceId, user.Id, msg.Message), mlog.String("user_id", user.Id))
a.Go(func(session *model.Session) func() {
return func() {
@@ -729,7 +729,7 @@ func (a *App) ClearPushNotification(userId string, channelId string) {
sessions, err := a.getMobileAppSessions(userId)
if err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
return
}
@@ -739,12 +739,12 @@ func (a *App) ClearPushNotification(userId string, channelId string) {
msg.ContentAvailable = 0
if badge := <-a.Srv.Store.User().GetUnreadCount(userId); badge.Err != nil {
msg.Badge = 0
- l4g.Error(utils.T("store.sql_user.get_unread_count.app_error"), userId, badge.Err)
+ mlog.Error(fmt.Sprint("We could not get the unread message count for the user", userId, badge.Err), mlog.String("user_id", userId))
} else {
msg.Badge = int(badge.Data.(int64))
}
- l4g.Debug(utils.T("api.post.send_notifications_and_forget.clear_push_notification.debug"), msg.DeviceId, msg.ChannelId)
+ mlog.Debug(fmt.Sprintf("Clearing push notification to %v with channel_id %v", msg.DeviceId, msg.ChannelId))
for _, session := range sessions {
tmpMessage := *model.PushNotificationFromJson(strings.NewReader(msg.ToJson()))
@@ -762,7 +762,7 @@ func (a *App) sendToPushProxy(msg model.PushNotification, session *model.Session
request, _ := http.NewRequest("POST", strings.TrimRight(*a.Config().EmailSettings.PushNotificationServer, "/")+model.API_URL_SUFFIX_V1+"/send_push", strings.NewReader(msg.ToJson()))
if resp, err := a.HTTPClient(true).Do(request); err != nil {
- l4g.Error("Device push reported as error for UserId=%v SessionId=%v message=%v", session.UserId, session.Id, err.Error())
+ mlog.Error(fmt.Sprintf("Device push reported as error for UserId=%v SessionId=%v message=%v", session.UserId, session.Id, err.Error()), mlog.String("user_id", session.UserId))
} else {
pushResponse := model.PushResponseFromJson(resp.Body)
if resp.Body != nil {
@@ -770,13 +770,13 @@ func (a *App) sendToPushProxy(msg model.PushNotification, session *model.Session
}
if pushResponse[model.PUSH_STATUS] == model.PUSH_STATUS_REMOVE {
- l4g.Info("Device was reported as removed for UserId=%v SessionId=%v removing push for this session", session.UserId, session.Id)
+ mlog.Info(fmt.Sprintf("Device was reported as removed for UserId=%v SessionId=%v removing push for this session", session.UserId, session.Id), mlog.String("user_id", session.UserId))
a.AttachDeviceId(session.Id, "", session.ExpiresAt)
a.ClearSessionCacheForUser(session.UserId)
}
if pushResponse[model.PUSH_STATUS] == model.PUSH_STATUS_FAIL {
- l4g.Error("Device push reported as error for UserId=%v SessionId=%v message=%v", session.UserId, session.Id, pushResponse[model.PUSH_STATUS_ERROR_MSG])
+ mlog.Error(fmt.Sprintf("Device push reported as error for UserId=%v SessionId=%v message=%v", session.UserId, session.Id, pushResponse[model.PUSH_STATUS_ERROR_MSG]), mlog.String("user_id", session.UserId))
}
}
}
diff --git a/app/oauth.go b/app/oauth.go
index 630fd3e2d..13fbd5a73 100644
--- a/app/oauth.go
+++ b/app/oauth.go
@@ -6,14 +6,15 @@ package app
import (
"bytes"
b64 "encoding/base64"
+ "fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/einterfaces"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -226,7 +227,7 @@ func (a *App) GetOAuthAccessToken(clientId, grantType, redirectUri, code, secret
accessData = &model.AccessData{ClientId: clientId, UserId: user.Id, Token: session.Token, RefreshToken: model.NewId(), RedirectUri: redirectUri, ExpiresAt: session.ExpiresAt, Scope: authData.Scope}
if result := <-a.Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
- l4g.Error(result.Err)
+ mlog.Error(fmt.Sprint(result.Err))
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
}
@@ -295,7 +296,7 @@ func (a *App) newSessionUpdateToken(appName string, accessData *model.AccessData
accessData.RefreshToken = model.NewId()
accessData.ExpiresAt = session.ExpiresAt
if result := <-a.Srv.Store.OAuth().UpdateAccessData(accessData); result.Err != nil {
- l4g.Error(result.Err)
+ mlog.Error(fmt.Sprint(result.Err))
return nil, model.NewAppError("newSessionUpdateToken", "web.get_access_token.internal_saving.app_error", nil, "", http.StatusInternalServerError)
}
accessRsp := &model.AccessResponse{
@@ -528,7 +529,7 @@ func (a *App) CompleteSwitchWithOAuth(service string, userData io.ReadCloser, em
a.Go(func() {
if err := a.SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
@@ -774,7 +775,7 @@ func (a *App) SwitchOAuthToEmail(email, password, requesterId string) (string, *
a.Go(func() {
if err := a.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
diff --git a/app/plugin.go b/app/plugin.go
index 903f4b767..3da9cea40 100644
--- a/app/plugin.go
+++ b/app/plugin.go
@@ -17,9 +17,8 @@ import (
"strings"
"unicode/utf8"
- l4g "github.com/alecthomas/log4go"
-
"github.com/gorilla/mux"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
@@ -48,7 +47,7 @@ func (a *App) initBuiltInPlugins() {
"ldapextras": &ldapextras.Plugin{},
}
for id, p := range plugins {
- l4g.Debug("Initializing built-in plugin: " + id)
+ mlog.Debug("Initializing built-in plugin: " + id)
api := &BuiltInPluginAPI{
id: id,
router: a.Srv.Router.PathPrefix("/plugins/" + id).Subrouter(),
@@ -70,13 +69,13 @@ func (a *App) initBuiltInPlugins() {
// and deactivate all other plugins.
func (a *App) ActivatePlugins() {
if a.PluginEnv == nil {
- l4g.Error("plugin env not initialized")
+ mlog.Error("plugin env not initialized")
return
}
plugins, err := a.PluginEnv.Plugins()
if err != nil {
- l4g.Error("failed to activate plugins: " + err.Error())
+ mlog.Error("failed to activate plugins: " + err.Error())
return
}
@@ -92,13 +91,13 @@ func (a *App) ActivatePlugins() {
if pluginState.Enable && !active {
if err := a.activatePlugin(plugin.Manifest); err != nil {
- l4g.Error("%v plugin enabled in config.json but failing to activate err=%v", plugin.Manifest.Id, err.DetailedError)
+ mlog.Error(fmt.Sprintf("%v plugin enabled in config.json but failing to activate err=%v", plugin.Manifest.Id, err.DetailedError))
continue
}
} else if !pluginState.Enable && active {
if err := a.deactivatePlugin(plugin.Manifest); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
}
@@ -115,7 +114,7 @@ func (a *App) activatePlugin(manifest *model.Manifest) *model.AppError {
a.Publish(message)
}
- l4g.Info("Activated %v plugin", manifest.Id)
+ mlog.Info(fmt.Sprintf("Activated %v plugin", manifest.Id))
return nil
}
@@ -132,7 +131,7 @@ func (a *App) deactivatePlugin(manifest *model.Manifest) *model.AppError {
a.Publish(message)
}
- l4g.Info("Deactivated %v plugin", manifest.Id)
+ mlog.Info(fmt.Sprintf("Deactivated %v plugin", manifest.Id))
return nil
}
@@ -370,15 +369,15 @@ func (a *App) InitPlugins(pluginPath, webappPath string, supervisorOverride plug
return
}
- l4g.Info("Starting up plugins")
+ mlog.Info("Starting up plugins")
if err := os.Mkdir(pluginPath, 0744); err != nil && !os.IsExist(err) {
- l4g.Error("failed to start up plugins: " + err.Error())
+ mlog.Error("failed to start up plugins: " + err.Error())
return
}
if err := os.Mkdir(webappPath, 0744); err != nil && !os.IsExist(err) {
- l4g.Error("failed to start up plugins: " + err.Error())
+ mlog.Error("failed to start up plugins: " + err.Error())
return
}
@@ -400,15 +399,15 @@ func (a *App) InitPlugins(pluginPath, webappPath string, supervisorOverride plug
if supervisorOverride != nil {
options = append(options, pluginenv.SupervisorProvider(supervisorOverride))
} else if err := sandbox.CheckSupport(); err != nil {
- l4g.Warn(err.Error())
- l4g.Warn("plugin sandboxing is not supported. plugins will run with the same access level as the server. See documentation to learn more: https://developers.mattermost.com/extend/plugins/security/")
+ mlog.Warn(err.Error())
+ mlog.Warn("plugin sandboxing is not supported. plugins will run with the same access level as the server. See documentation to learn more: https://developers.mattermost.com/extend/plugins/security/")
options = append(options, pluginenv.SupervisorProvider(rpcplugin.SupervisorProvider))
} else {
options = append(options, pluginenv.SupervisorProvider(sandbox.SupervisorProvider))
}
if env, err := pluginenv.New(options...); err != nil {
- l4g.Error("failed to start up plugins: " + err.Error())
+ mlog.Error("failed to start up plugins: " + err.Error())
return
} else {
a.PluginEnv = env
@@ -416,15 +415,15 @@ func (a *App) InitPlugins(pluginPath, webappPath string, supervisorOverride plug
for id, asset := range prepackagedPlugins {
if tarball, err := asset("plugin.tar.gz"); err != nil {
- l4g.Error("failed to install prepackaged plugin: " + err.Error())
+ mlog.Error("failed to install prepackaged plugin: " + err.Error())
} else if tarball != nil {
a.removePlugin(id, true)
if _, err := a.installPlugin(bytes.NewReader(tarball), true); err != nil {
- l4g.Error("failed to install prepackaged plugin: " + err.Error())
+ mlog.Error("failed to install prepackaged plugin: " + err.Error())
}
if _, ok := a.Config().PluginSettings.PluginStates[id]; !ok && id != "zoom" {
if err := a.EnablePlugin(id); err != nil {
- l4g.Error("failed to enable prepackaged plugin: " + err.Error())
+ mlog.Error("failed to enable prepackaged plugin: " + err.Error())
}
}
}
@@ -441,7 +440,7 @@ func (a *App) InitPlugins(pluginPath, webappPath string, supervisorOverride plug
}
for _, err := range a.PluginEnv.Hooks().OnConfigurationChange() {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
@@ -451,7 +450,7 @@ func (a *App) InitPlugins(pluginPath, webappPath string, supervisorOverride plug
func (a *App) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
if a.PluginEnv == nil || !*a.Config().PluginSettings.Enable {
err := model.NewAppError("ServePluginRequest", "app.plugin.disabled.app_error", nil, "Enable plugins to serve plugin requests", http.StatusNotImplemented)
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
w.WriteHeader(err.StatusCode)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(err.ToJson()))
@@ -507,10 +506,10 @@ func (a *App) ShutDownPlugins() {
return
}
- l4g.Info("Shutting down plugins")
+ mlog.Info("Shutting down plugins")
for _, err := range a.PluginEnv.Shutdown() {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
a.RemoveConfigListener(a.PluginConfigListenerId)
a.PluginConfigListenerId = ""
@@ -533,7 +532,7 @@ func (a *App) SetPluginKey(pluginId string, key string, value []byte) *model.App
result := <-a.Srv.Store.Plugin().SaveOrUpdate(kv)
if result.Err != nil {
- l4g.Error(result.Err.Error())
+ mlog.Error(result.Err.Error())
}
return result.Err
@@ -546,7 +545,7 @@ func (a *App) GetPluginKey(pluginId string, key string) ([]byte, *model.AppError
if result.Err.StatusCode == http.StatusNotFound {
return nil, nil
}
- l4g.Error(result.Err.Error())
+ mlog.Error(result.Err.Error())
return nil, result.Err
}
@@ -559,7 +558,7 @@ func (a *App) DeletePluginKey(pluginId string, key string) *model.AppError {
result := <-a.Srv.Store.Plugin().Delete(pluginId, getKeyHash(key))
if result.Err != nil {
- l4g.Error(result.Err.Error())
+ mlog.Error(result.Err.Error())
}
return result.Err
diff --git a/app/plugin/ldapextras/plugin.go b/app/plugin/ldapextras/plugin.go
index 3af55c2ac..e0645b68e 100644
--- a/app/plugin/ldapextras/plugin.go
+++ b/app/plugin/ldapextras/plugin.go
@@ -8,10 +8,10 @@ import (
"net/http"
"sync/atomic"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
"github.com/mattermost/mattermost-server/app/plugin"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -34,7 +34,7 @@ func (p *Plugin) config() *Configuration {
func (p *Plugin) OnConfigurationChange() {
var configuration Configuration
if err := p.api.LoadPluginConfiguration(&configuration); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
p.configuration.Store(&configuration)
}
diff --git a/app/post.go b/app/post.go
index 4ce009369..41139da63 100644
--- a/app/post.go
+++ b/app/post.go
@@ -14,8 +14,8 @@ import (
"regexp"
"strings"
- l4g "github.com/alecthomas/log4go"
"github.com/dyatlov/go-opengraph/opengraph"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -78,7 +78,7 @@ func (a *App) CreatePostAsUser(post *model.Post) (*model.Post, *model.AppError)
// Update the LastViewAt only if the post does not have from_webhook prop set (eg. Zapier app)
if _, ok := post.Props["from_webhook"]; !ok {
if result := <-a.Srv.Store.Channel().UpdateLastViewedAt([]string{post.ChannelId}, post.UserId); result.Err != nil {
- l4g.Error(utils.T("api.post.create_post.last_viewed.error"), post.ChannelId, post.UserId, result.Err)
+ mlog.Error(fmt.Sprintf("Encountered error updating last viewed, channel_id=%s, user_id=%s, err=%v", post.ChannelId, post.UserId, result.Err))
}
if *a.Config().ServiceSettings.EnableChannelViewedMessages {
@@ -182,7 +182,7 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo
for _, fileId := range post.FileIds {
if result := <-a.Srv.Store.FileInfo().AttachToPost(fileId, post.Id); result.Err != nil {
- l4g.Error(utils.T("api.post.create_post.attach_files.error"), post.Id, post.FileIds, post.UserId, result.Err)
+ mlog.Error(fmt.Sprintf("Encountered error attaching files to post, post_id=%s, user_id=%s, file_ids=%v, err=%v", post.Id, post.FileIds, post.UserId, result.Err), mlog.String("post_id", post.Id))
}
}
@@ -266,7 +266,7 @@ func (a *App) handlePostEvents(post *model.Post, user *model.User, channel *mode
if triggerWebhooks {
a.Go(func() {
if err := a.handleWebhookEvents(post, team, channel, user); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
}
@@ -378,7 +378,7 @@ func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
a.Go(func() {
if rchannel := <-a.Srv.Store.Channel().GetForPost(rpost.Id); rchannel.Err != nil {
- l4g.Error("Couldn't get channel %v for post %v for Elasticsearch indexing.", rpost.ChannelId, rpost.Id)
+ mlog.Error(fmt.Sprintf("Couldn't get channel %v for post %v for Elasticsearch indexing.", rpost.ChannelId, rpost.Id))
} else {
esInterface.IndexPost(rpost, rchannel.Data.(*model.Channel).TeamId)
}
@@ -576,7 +576,7 @@ func (a *App) DeletePost(postId string) (*model.Post, *model.AppError) {
func (a *App) DeleteFlaggedPosts(postId string) {
if result := <-a.Srv.Store.Preference().DeleteCategoryAndName(model.PREFERENCE_CATEGORY_FLAGGED_POST, postId); result.Err != nil {
- l4g.Warn(utils.T("api.post.delete_flagged_post.app_error.warn"), result.Err)
+ mlog.Warn(fmt.Sprintf("Unable to delete flagged post preference when deleting post, err=%v", result.Err))
return
}
}
@@ -587,7 +587,7 @@ func (a *App) DeletePostFiles(post *model.Post) {
}
if result := <-a.Srv.Store.FileInfo().DeleteForPost(post.Id); result.Err != nil {
- l4g.Warn(utils.T("api.post.delete_post_files.app_error.warn"), post.Id, result.Err)
+ mlog.Warn(fmt.Sprintf("Encountered error when deleting files for post, post_id=%v, err=%v", post.Id, result.Err), mlog.String("post_id", post.Id))
}
}
@@ -605,7 +605,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
// Convert channel names to channel IDs
for idx, channelName := range params.InChannels {
if channel, err := a.GetChannelByName(channelName, teamId); err != nil {
- l4g.Error(err)
+ mlog.Error(fmt.Sprint(err))
} else {
params.InChannels[idx] = channel.Id
}
@@ -614,7 +614,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
// Convert usernames to user IDs
for idx, username := range params.FromUsers {
if user, err := a.GetUserByUsername(username); err != nil {
- l4g.Error(err)
+ mlog.Error(fmt.Sprint(err))
} else {
params.FromUsers[idx] = user.Id
}
@@ -632,7 +632,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
// We only allow the user to search in channels they are a member of.
userChannels, err := a.GetChannelsForUser(teamId, userId)
if err != nil {
- l4g.Error(err)
+ mlog.Error(fmt.Sprint(err))
return nil, err
}
@@ -721,13 +721,13 @@ func (a *App) GetOpenGraphMetadata(requestURL string) *opengraph.OpenGraph {
res, err := a.HTTPClient(false).Get(requestURL)
if err != nil {
- l4g.Error("GetOpenGraphMetadata request failed for url=%v with err=%v", requestURL, err.Error())
+ mlog.Error(fmt.Sprintf("GetOpenGraphMetadata request failed for url=%v with err=%v", requestURL, err.Error()))
return og
}
defer consumeAndClose(res)
if err := og.ProcessHTML(res.Body); err != nil {
- l4g.Error("GetOpenGraphMetadata processing failed for url=%v with err=%v", requestURL, err.Error())
+ mlog.Error(fmt.Sprintf("GetOpenGraphMetadata processing failed for url=%v with err=%v", requestURL, err.Error()))
}
makeOpenGraphURLsAbsolute(og, requestURL)
@@ -738,7 +738,7 @@ func (a *App) GetOpenGraphMetadata(requestURL string) *opengraph.OpenGraph {
func makeOpenGraphURLsAbsolute(og *opengraph.OpenGraph, requestURL string) {
parsedRequestURL, err := url.Parse(requestURL)
if err != nil {
- l4g.Warn("makeOpenGraphURLsAbsolute failed to parse url=%v", requestURL)
+ mlog.Warn(fmt.Sprintf("makeOpenGraphURLsAbsolute failed to parse url=%v", requestURL))
return
}
@@ -749,7 +749,7 @@ func makeOpenGraphURLsAbsolute(og *opengraph.OpenGraph, requestURL string) {
parsedResultURL, err := url.Parse(resultURL)
if err != nil {
- l4g.Warn("makeOpenGraphURLsAbsolute failed to parse result url=%v", resultURL)
+ mlog.Warn(fmt.Sprintf("makeOpenGraphURLsAbsolute failed to parse result url=%v", resultURL))
return resultURL
}
@@ -962,7 +962,7 @@ func (a *App) ImageProxyRemover() (f func(string) string) {
func (a *App) MaxPostSize() int {
maxPostSize := model.POST_MESSAGE_MAX_RUNES_V1
if result := <-a.Srv.Store.Post().GetMaxPostSize(); result.Err != nil {
- l4g.Error(result.Err)
+ mlog.Error(fmt.Sprint(result.Err))
} else {
maxPostSize = result.Data.(int)
}
diff --git a/app/ratelimit.go b/app/ratelimit.go
index 13508f36f..77d15caa2 100644
--- a/app/ratelimit.go
+++ b/app/ratelimit.go
@@ -4,12 +4,13 @@
package app
import (
+ "fmt"
"math"
"net/http"
"strconv"
"strings"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
"github.com/pkg/errors"
@@ -74,14 +75,14 @@ func (rl *RateLimiter) GenerateKey(r *http.Request) string {
func (rl *RateLimiter) RateLimitWriter(key string, w http.ResponseWriter) bool {
limited, context, err := rl.throttledRateLimiter.RateLimit(key, 1)
if err != nil {
- l4g.Critical("Internal server error when rate limiting. Rate Limiting broken. Error:" + err.Error())
+ mlog.Critical("Internal server error when rate limiting. Rate Limiting broken. Error:" + err.Error())
return false
}
setRateLimitHeaders(w, context)
if limited {
- l4g.Error("Denied due to throttling settings code=429 key=%v", key)
+ mlog.Error(fmt.Sprintf("Denied due to throttling settings code=429 key=%v", key))
http.Error(w, "limit exceeded", 429)
}
diff --git a/app/role.go b/app/role.go
index c99d8365b..c9278e0bd 100644
--- a/app/role.go
+++ b/app/role.go
@@ -6,8 +6,9 @@ package app
import (
"reflect"
- "github.com/mattermost/mattermost-server/model"
"net/http"
+
+ "github.com/mattermost/mattermost-server/model"
)
func (a *App) GetRole(id string) (*model.Role, *model.AppError) {
diff --git a/app/security_update_check.go b/app/security_update_check.go
index 1f1759fef..15bb81e5a 100644
--- a/app/security_update_check.go
+++ b/app/security_update_check.go
@@ -4,13 +4,14 @@
package app
import (
+ "fmt"
"io/ioutil"
"net/http"
"net/url"
"runtime"
"strconv"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -38,7 +39,7 @@ func (a *App) DoSecurityUpdateCheck() {
currentTime := model.GetMillis()
if (currentTime - lastSecurityTime) > SECURITY_UPDATE_PERIOD {
- l4g.Debug(utils.T("mattermost.security_checks.debug"))
+ mlog.Debug("Checking for security update from Mattermost")
v := url.Values{}
@@ -75,7 +76,7 @@ func (a *App) DoSecurityUpdateCheck() {
res, err := http.Get(SECURITY_URL + "/security?" + v.Encode())
if err != nil {
- l4g.Error(utils.T("mattermost.security_info.error"))
+ mlog.Error("Failed to get security update information from Mattermost.")
return
}
@@ -86,26 +87,26 @@ func (a *App) DoSecurityUpdateCheck() {
if bulletin.AppliesToVersion == model.CurrentVersion {
if props["SecurityBulletin_"+bulletin.Id] == "" {
if results := <-a.Srv.Store.User().GetSystemAdminProfiles(); results.Err != nil {
- l4g.Error(utils.T("mattermost.system_admins.error"))
+ mlog.Error("Failed to get system admins for security update information from Mattermost.")
return
} else {
users := results.Data.(map[string]*model.User)
resBody, err := http.Get(SECURITY_URL + "/bulletins/" + bulletin.Id)
if err != nil {
- l4g.Error(utils.T("mattermost.security_bulletin.error"))
+ mlog.Error("Failed to get security bulletin details")
return
}
body, err := ioutil.ReadAll(resBody.Body)
res.Body.Close()
if err != nil || resBody.StatusCode != 200 {
- l4g.Error(utils.T("mattermost.security_bulletin_read.error"))
+ mlog.Error("Failed to read security bulletin details")
return
}
for _, user := range users {
- l4g.Info(utils.T("mattermost.send_bulletin.info"), bulletin.Id, user.Email)
+ mlog.Info(fmt.Sprintf("Sending security bulletin for %v to %v", bulletin.Id, user.Email))
a.SendMail(user.Email, utils.T("mattermost.bulletin.subject"), string(body))
}
}
diff --git a/app/server.go b/app/server.go
index e89041ebe..7d229201d 100644
--- a/app/server.go
+++ b/app/server.go
@@ -15,12 +15,12 @@ import (
"strings"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"golang.org/x/crypto/acme/autocert"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -50,8 +50,8 @@ type RecoveryLogger struct {
}
func (rl *RecoveryLogger) Println(i ...interface{}) {
- l4g.Error("Please check the std error output for the stack trace")
- l4g.Error(i)
+ mlog.Error("Please check the std error output for the stack trace")
+ mlog.Error(fmt.Sprint(i))
}
type CorsWrapper struct {
@@ -97,12 +97,12 @@ func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) {
}
func (a *App) StartServer() error {
- l4g.Info(utils.T("api.server.start_server.starting.info"))
+ mlog.Info("Starting Server...")
var handler http.Handler = &CorsWrapper{a.Config, a.Srv.Router}
if *a.Config().RateLimitSettings.Enable {
- l4g.Info(utils.T("api.server.start_server.rate.info"))
+ mlog.Info("RateLimiter is enabled")
rateLimiter, err := NewRateLimiter(&a.Config().RateLimitSettings)
if err != nil {
@@ -117,6 +117,7 @@ func (a *App) StartServer() error {
Handler: handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler),
ReadTimeout: time.Duration(*a.Config().ServiceSettings.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(*a.Config().ServiceSettings.WriteTimeout) * time.Second,
+ ErrorLog: a.Log.StdLog(mlog.String("source", "httpserver")),
}
addr := *a.Config().ServiceSettings.ListenAddress
@@ -135,7 +136,7 @@ func (a *App) StartServer() error {
}
a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr)
- l4g.Info(utils.T("api.server.start_server.listening.info"), listener.Addr().String())
+ mlog.Info(fmt.Sprintf("Server is listening on %v", listener.Addr().String()))
// Migration from old let's encrypt library
if *a.Config().ServiceSettings.UseLetsEncrypt {
@@ -151,24 +152,33 @@ func (a *App) StartServer() error {
if *a.Config().ServiceSettings.Forward80To443 {
if host, port, err := net.SplitHostPort(addr); err != nil {
- l4g.Error("Unable to setup forwarding: " + err.Error())
+ mlog.Error("Unable to setup forwarding: " + err.Error())
} else if port != "443" {
return fmt.Errorf(utils.T("api.server.start_server.forward80to443.enabled_but_listening_on_wrong_port"), port)
} else {
httpListenAddress := net.JoinHostPort(host, "http")
if *a.Config().ServiceSettings.UseLetsEncrypt {
- go http.ListenAndServe(httpListenAddress, m.HTTPHandler(nil))
+ server := &http.Server{
+ Addr: httpListenAddress,
+ Handler: m.HTTPHandler(nil),
+ ErrorLog: a.Log.StdLog(mlog.String("source", "le_forwarder_server")),
+ }
+ go server.ListenAndServe()
} else {
go func() {
redirectListener, err := net.Listen("tcp", httpListenAddress)
if err != nil {
- l4g.Error("Unable to setup forwarding: " + err.Error())
+ mlog.Error("Unable to setup forwarding: " + err.Error())
return
}
defer redirectListener.Close()
- http.Serve(redirectListener, http.HandlerFunc(redirectHTTPToHTTPS))
+ server := &http.Server{
+ Handler: handler,
+ ErrorLog: a.Log.StdLog(mlog.String("source", "forwarder_server")),
+ }
+ server.Serve(redirectListener)
}()
}
}
@@ -197,7 +207,7 @@ func (a *App) StartServer() error {
err = a.Srv.Server.Serve(listener)
}
if err != nil && err != http.ErrServerClosed {
- l4g.Critical(utils.T("api.server.start_server.starting.critical"), err)
+ mlog.Critical(fmt.Sprintf("Error starting server, err:%v", err))
time.Sleep(time.Second)
}
close(a.Srv.didFinishListen)
@@ -213,7 +223,7 @@ func (a *App) StopServer() {
didShutdown := false
for a.Srv.didFinishListen != nil && !didShutdown {
if err := a.Srv.Server.Shutdown(ctx); err != nil {
- l4g.Warn(err.Error())
+ mlog.Warn(err.Error())
}
timer := time.NewTimer(time.Millisecond * 50)
select {
diff --git a/app/session.go b/app/session.go
index 03754398d..53170cec0 100644
--- a/app/session.go
+++ b/app/session.go
@@ -4,12 +4,11 @@
package app
import (
+ "fmt"
"net/http"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
-
- l4g "github.com/alecthomas/log4go"
)
func (a *App) CreateSession(session *model.Session) (*model.Session, *model.AppError) {
@@ -171,10 +170,10 @@ func (a *App) RevokeSessionsForDeviceId(userId string, deviceId string, currentS
sessions := result.Data.([]*model.Session)
for _, session := range sessions {
if session.DeviceId == deviceId && session.Id != currentSessionId {
- l4g.Debug(utils.T("api.user.login.revoking.app_error"), session.Id, userId)
+ mlog.Debug(fmt.Sprintf("Revoking sessionId=%v for userId=%v re-login with same device Id", session.Id, userId), mlog.String("user_id", userId))
if err := a.RevokeSession(session); err != nil {
// Soft error so we still remove the other sessions
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
}
@@ -233,7 +232,7 @@ func (a *App) UpdateLastActivityAtIfNeeded(session model.Session) {
}
if result := <-a.Srv.Store.Session().UpdateLastActivityAt(session.Id, now); result.Err != nil {
- l4g.Error(utils.T("api.status.last_activity.error"), session.UserId, session.Id, result.Err)
+ mlog.Error(fmt.Sprintf("Failed to update LastActivityAt for user_id=%v and session_id=%v, err=%v", session.UserId, session.Id, result.Err), mlog.String("user_id", session.UserId))
}
session.LastActivityAt = now
@@ -256,11 +255,11 @@ func (a *App) CreateUserAccessToken(token *model.UserAccessToken) (*model.UserAc
}
if result := <-uchan; result.Err != nil {
- l4g.Error(result.Err.Error())
+ mlog.Error(result.Err.Error())
} else {
user := result.Data.(*model.User)
if err := a.SendUserAccessTokenAddedEmail(user.Email, user.Locale); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
diff --git a/app/slackimport.go b/app/slackimport.go
index d5e7fe2b2..3333af604 100644
--- a/app/slackimport.go
+++ b/app/slackimport.go
@@ -7,6 +7,7 @@ import (
"archive/zip"
"bytes"
"encoding/json"
+ "fmt"
"io"
"mime/multipart"
"path/filepath"
@@ -17,7 +18,7 @@ import (
"net/http"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -81,7 +82,7 @@ func SlackConvertTimeStamp(ts string) int64 {
timeStamp, err := strconv.ParseInt(timeString, 10, 64)
if err != nil {
- l4g.Warn(utils.T("api.slackimport.slack_convert_timestamp.bad.warn"))
+ mlog.Warn("Slack Import: Bad timestamp detected.")
return 1
}
return timeStamp * 1000 // Convert to milliseconds
@@ -105,7 +106,7 @@ func SlackParseChannels(data io.Reader) ([]SlackChannel, error) {
var channels []SlackChannel
if err := decoder.Decode(&channels); err != nil {
- l4g.Warn(utils.T("api.slackimport.slack_parse_channels.error"))
+ mlog.Warn("Slack Import: Error occurred when parsing some Slack channels. Import may work anyway.")
return channels, err
}
return channels, nil
@@ -127,23 +128,23 @@ func SlackParsePosts(data io.Reader) ([]SlackPost, error) {
var posts []SlackPost
if err := decoder.Decode(&posts); err != nil {
- l4g.Warn(utils.T("api.slackimport.slack_parse_posts.error"))
+ mlog.Warn("Slack Import: Error occurred when parsing some Slack posts. Import may work anyway.")
return posts, err
}
return posts, nil
}
-func (a *App) SlackAddUsers(teamId string, slackusers []SlackUser, log *bytes.Buffer) map[string]*model.User {
+func (a *App) SlackAddUsers(teamId string, slackusers []SlackUser, importerLog *bytes.Buffer) map[string]*model.User {
// Log header
- log.WriteString(utils.T("api.slackimport.slack_add_users.created"))
- log.WriteString("===============\r\n\r\n")
+ importerLog.WriteString(utils.T("api.slackimport.slack_add_users.created"))
+ importerLog.WriteString("===============\r\n\r\n")
addedUsers := make(map[string]*model.User)
// Need the team
var team *model.Team
if result := <-a.Srv.Store.Team().Get(teamId); result.Err != nil {
- log.WriteString(utils.T("api.slackimport.slack_import.team_fail"))
+ importerLog.WriteString(utils.T("api.slackimport.slack_import.team_fail"))
return addedUsers
} else {
team = result.Data.(*model.Team)
@@ -155,8 +156,8 @@ func (a *App) SlackAddUsers(teamId string, slackusers []SlackUser, log *bytes.Bu
email := sUser.Profile.Email
if email == "" {
email = sUser.Username + "@example.com"
- log.WriteString(utils.T("api.slackimport.slack_add_users.missing_email_address", map[string]interface{}{"Email": email, "Username": sUser.Username}))
- l4g.Warn(utils.T("api.slackimport.slack_add_users.missing_email_address.warn", map[string]interface{}{"Email": email, "Username": sUser.Username}))
+ importerLog.WriteString(utils.T("api.slackimport.slack_add_users.missing_email_address", map[string]interface{}{"Email": email, "Username": sUser.Username}))
+ mlog.Warn("Slack Import: User {{.Username}} does not have an email address in the Slack export. Used {{.Email}} as a placeholder. The user should update their email address once logged in to the system.")
}
password := model.NewId()
@@ -166,9 +167,9 @@ func (a *App) SlackAddUsers(teamId string, slackusers []SlackUser, log *bytes.Bu
existingUser := result.Data.(*model.User)
addedUsers[sUser.Id] = existingUser
if err := a.JoinUserToTeam(team, addedUsers[sUser.Id], ""); err != nil {
- log.WriteString(utils.T("api.slackimport.slack_add_users.merge_existing_failed", map[string]interface{}{"Email": existingUser.Email, "Username": existingUser.Username}))
+ importerLog.WriteString(utils.T("api.slackimport.slack_add_users.merge_existing_failed", map[string]interface{}{"Email": existingUser.Email, "Username": existingUser.Username}))
} else {
- log.WriteString(utils.T("api.slackimport.slack_add_users.merge_existing", map[string]interface{}{"Email": existingUser.Email, "Username": existingUser.Username}))
+ importerLog.WriteString(utils.T("api.slackimport.slack_add_users.merge_existing", map[string]interface{}{"Email": existingUser.Email, "Username": existingUser.Username}))
}
continue
}
@@ -183,9 +184,9 @@ func (a *App) SlackAddUsers(teamId string, slackusers []SlackUser, log *bytes.Bu
if mUser := a.OldImportUser(team, &newUser); mUser != nil {
addedUsers[sUser.Id] = mUser
- log.WriteString(utils.T("api.slackimport.slack_add_users.email_pwd", map[string]interface{}{"Email": newUser.Email, "Password": password}))
+ importerLog.WriteString(utils.T("api.slackimport.slack_add_users.email_pwd", map[string]interface{}{"Email": newUser.Email, "Password": password}))
} else {
- log.WriteString(utils.T("api.slackimport.slack_add_users.unable_import", map[string]interface{}{"Username": sUser.Username}))
+ importerLog.WriteString(utils.T("api.slackimport.slack_add_users.unable_import", map[string]interface{}{"Username": sUser.Username}))
}
}
@@ -227,10 +228,10 @@ func (a *App) SlackAddPosts(teamId string, channel *model.Channel, posts []Slack
switch {
case sPost.Type == "message" && (sPost.SubType == "" || sPost.SubType == "file_share"):
if sPost.User == "" {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.without_user.debug"))
+ mlog.Debug("Slack Import: Unable to import the message as the user field is missing.")
continue
} else if users[sPost.User] == nil {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
+ mlog.Debug(fmt.Sprintf("Slack Import: Unable to add the message as the Slack user %v does not exist in Mattermost.", sPost.User))
continue
}
newPost := model.Post{
@@ -248,19 +249,19 @@ func (a *App) SlackAddPosts(teamId string, channel *model.Channel, posts []Slack
a.OldImportPost(&newPost)
for _, fileId := range newPost.FileIds {
if result := <-a.Srv.Store.FileInfo().AttachToPost(fileId, newPost.Id); result.Err != nil {
- l4g.Error(utils.T("api.slackimport.slack_add_posts.attach_files.error"), newPost.Id, newPost.FileIds, result.Err)
+ mlog.Error(fmt.Sprintf("Slack Import: An error occurred when attaching files to a message, post_id=%s, file_ids=%v, err=%v.", newPost.Id, newPost.FileIds, result.Err))
}
}
case sPost.Type == "message" && sPost.SubType == "file_comment":
if sPost.Comment == nil {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.msg_no_comment.debug"))
+ mlog.Debug("Slack Import: Unable to import the message as it has no comments.")
continue
} else if sPost.Comment.User == "" {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.msg_no_usr.debug"))
+ mlog.Debug("Slack Import: Unable to import the message as the user field is missing.")
continue
} else if users[sPost.Comment.User] == nil {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
+ mlog.Debug(fmt.Sprintf("Slack Import: Unable to add the message as the Slack user %v does not exist in Mattermost.", sPost.User))
continue
}
newPost := model.Post{
@@ -272,10 +273,10 @@ func (a *App) SlackAddPosts(teamId string, channel *model.Channel, posts []Slack
a.OldImportPost(&newPost)
case sPost.Type == "message" && sPost.SubType == "bot_message":
if botUser == nil {
- l4g.Warn(utils.T("api.slackimport.slack_add_posts.bot_user_no_exists.warn"))
+ mlog.Warn("Slack Import: Unable to import the bot message as the bot user does not exist.")
continue
} else if sPost.BotId == "" {
- l4g.Warn(utils.T("api.slackimport.slack_add_posts.no_bot_id.warn"))
+ mlog.Warn("Slack Import: Unable to import bot message as the BotId field is missing.")
continue
}
@@ -296,10 +297,10 @@ func (a *App) SlackAddPosts(teamId string, channel *model.Channel, posts []Slack
a.OldImportIncomingWebhookPost(post, props)
case sPost.Type == "message" && (sPost.SubType == "channel_join" || sPost.SubType == "channel_leave"):
if sPost.User == "" {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.msg_no_usr.debug"))
+ mlog.Debug("Slack Import: Unable to import the message as the user field is missing.")
continue
} else if users[sPost.User] == nil {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
+ mlog.Debug(fmt.Sprintf("Slack Import: Unable to add the message as the Slack user %v does not exist in Mattermost.", sPost.User))
continue
}
@@ -323,10 +324,10 @@ func (a *App) SlackAddPosts(teamId string, channel *model.Channel, posts []Slack
a.OldImportPost(&newPost)
case sPost.Type == "message" && sPost.SubType == "me_message":
if sPost.User == "" {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.without_user.debug"))
+ mlog.Debug("Slack Import: Unable to import the message as the user field is missing.")
continue
} else if users[sPost.User] == nil {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
+ mlog.Debug(fmt.Sprintf("Slack Import: Unable to add the message as the Slack user %v does not exist in Mattermost.", sPost.User))
continue
}
newPost := model.Post{
@@ -338,10 +339,10 @@ func (a *App) SlackAddPosts(teamId string, channel *model.Channel, posts []Slack
a.OldImportPost(&newPost)
case sPost.Type == "message" && sPost.SubType == "channel_topic":
if sPost.User == "" {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.msg_no_usr.debug"))
+ mlog.Debug("Slack Import: Unable to import the message as the user field is missing.")
continue
} else if users[sPost.User] == nil {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
+ mlog.Debug(fmt.Sprintf("Slack Import: Unable to add the message as the Slack user %v does not exist in Mattermost.", sPost.User))
continue
}
newPost := model.Post{
@@ -354,10 +355,10 @@ func (a *App) SlackAddPosts(teamId string, channel *model.Channel, posts []Slack
a.OldImportPost(&newPost)
case sPost.Type == "message" && sPost.SubType == "channel_purpose":
if sPost.User == "" {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.msg_no_usr.debug"))
+ mlog.Debug("Slack Import: Unable to import the message as the user field is missing.")
continue
} else if users[sPost.User] == nil {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
+ mlog.Debug(fmt.Sprintf("Slack Import: Unable to add the message as the Slack user %v does not exist in Mattermost.", sPost.User))
continue
}
newPost := model.Post{
@@ -370,10 +371,10 @@ func (a *App) SlackAddPosts(teamId string, channel *model.Channel, posts []Slack
a.OldImportPost(&newPost)
case sPost.Type == "message" && sPost.SubType == "channel_name":
if sPost.User == "" {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.msg_no_usr.debug"))
+ mlog.Debug("Slack Import: Unable to import the message as the user field is missing.")
continue
} else if users[sPost.User] == nil {
- l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
+ mlog.Debug(fmt.Sprintf("Slack Import: Unable to add the message as the Slack user %v does not exist in Mattermost.", sPost.User))
continue
}
newPost := model.Post{
@@ -385,7 +386,7 @@ func (a *App) SlackAddPosts(teamId string, channel *model.Channel, posts []Slack
}
a.OldImportPost(&newPost)
default:
- l4g.Warn(utils.T("api.slackimport.slack_add_posts.unsupported.warn"), sPost.Type, sPost.SubType)
+ mlog.Warn(fmt.Sprintf("Slack Import: Unable to import the message as its type is not supported: post_type=%v, post_subtype=%v.", sPost.Type, sPost.SubType))
}
}
}
@@ -395,7 +396,7 @@ func (a *App) SlackUploadFile(sPost SlackPost, uploads map[string]*zip.File, tea
if file, ok := uploads[sPost.File.Id]; ok {
openFile, err := file.Open()
if err != nil {
- l4g.Warn(utils.T("api.slackimport.slack_add_posts.upload_file_open_failed.warn", map[string]interface{}{"FileId": sPost.File.Id, "Error": err.Error()}))
+ mlog.Warn("Slack Import: Unable to open the file {{.FileId}} from the Slack export: {{.Error}}.")
return nil, false
}
defer openFile.Close()
@@ -403,17 +404,17 @@ func (a *App) SlackUploadFile(sPost SlackPost, uploads map[string]*zip.File, tea
timestamp := utils.TimeFromMillis(SlackConvertTimeStamp(sPost.TimeStamp))
uploadedFile, err := a.OldImportFile(timestamp, openFile, teamId, channelId, userId, filepath.Base(file.Name))
if err != nil {
- l4g.Warn(utils.T("api.slackimport.slack_add_posts.upload_file_upload_failed.warn", map[string]interface{}{"FileId": sPost.File.Id, "Error": err.Error()}))
+ mlog.Warn("Slack Import: An error occurred when uploading file {{.FileId}}: {{.Error}}.")
return nil, false
}
return uploadedFile, true
} else {
- l4g.Warn(utils.T("api.slackimport.slack_add_posts.upload_file_not_found.warn", map[string]interface{}{"FileId": sPost.File.Id}))
+ mlog.Warn("Slack Import: Unable to import file {{.FileId}} as the file is missing from the Slack export zip file.")
return nil, false
}
} else {
- l4g.Warn(utils.T("api.slackimport.slack_add_posts.upload_file_not_in_json.warn"))
+ mlog.Warn("Slack Import: Unable to attach the file to the post as the latter has no file section present in Slack export.")
return nil, false
}
}
@@ -421,7 +422,7 @@ func (a *App) SlackUploadFile(sPost SlackPost, uploads map[string]*zip.File, tea
func (a *App) deactivateSlackBotUser(user *model.User) {
_, err := a.UpdateActive(user, false)
if err != nil {
- l4g.Warn(utils.T("api.slackimport.slack_deactivate_bot_user.failed_to_deactivate", err))
+ mlog.Warn("Slack Import: Unable to deactivate the user account used for the bot.")
}
}
@@ -439,32 +440,32 @@ func (a *App) addSlackUsersToChannel(members []string, users map[string]*model.U
func SlackSanitiseChannelProperties(channel model.Channel) model.Channel {
if utf8.RuneCountInString(channel.DisplayName) > model.CHANNEL_DISPLAY_NAME_MAX_RUNES {
- l4g.Warn("api.slackimport.slack_sanitise_channel_properties.display_name_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName})
+ mlog.Warn(fmt.Sprint("api.slackimport.slack_sanitise_channel_properties.display_name_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName}))
channel.DisplayName = truncateRunes(channel.DisplayName, model.CHANNEL_DISPLAY_NAME_MAX_RUNES)
}
if len(channel.Name) > model.CHANNEL_NAME_MAX_LENGTH {
- l4g.Warn("api.slackimport.slack_sanitise_channel_properties.name_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName})
+ mlog.Warn(fmt.Sprint("api.slackimport.slack_sanitise_channel_properties.name_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName}))
channel.Name = channel.Name[0:model.CHANNEL_NAME_MAX_LENGTH]
}
if utf8.RuneCountInString(channel.Purpose) > model.CHANNEL_PURPOSE_MAX_RUNES {
- l4g.Warn("api.slackimport.slack_sanitise_channel_properties.purpose_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName})
+ mlog.Warn(fmt.Sprint("api.slackimport.slack_sanitise_channel_properties.purpose_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName}))
channel.Purpose = truncateRunes(channel.Purpose, model.CHANNEL_PURPOSE_MAX_RUNES)
}
if utf8.RuneCountInString(channel.Header) > model.CHANNEL_HEADER_MAX_RUNES {
- l4g.Warn("api.slackimport.slack_sanitise_channel_properties.header_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName})
+ mlog.Warn(fmt.Sprint("api.slackimport.slack_sanitise_channel_properties.header_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName}))
channel.Header = truncateRunes(channel.Header, model.CHANNEL_HEADER_MAX_RUNES)
}
return channel
}
-func (a *App) SlackAddChannels(teamId string, slackchannels []SlackChannel, posts map[string][]SlackPost, users map[string]*model.User, uploads map[string]*zip.File, botUser *model.User, log *bytes.Buffer) map[string]*model.Channel {
+func (a *App) SlackAddChannels(teamId string, slackchannels []SlackChannel, posts map[string][]SlackPost, users map[string]*model.User, uploads map[string]*zip.File, botUser *model.User, importerLog *bytes.Buffer) map[string]*model.Channel {
// Write Header
- log.WriteString(utils.T("api.slackimport.slack_add_channels.added"))
- log.WriteString("=================\r\n\r\n")
+ importerLog.WriteString(utils.T("api.slackimport.slack_add_channels.added"))
+ importerLog.WriteString("=================\r\n\r\n")
addedChannels := make(map[string]*model.Channel)
for _, sChannel := range slackchannels {
@@ -482,7 +483,7 @@ func (a *App) SlackAddChannels(teamId string, slackchannels []SlackChannel, post
if result := <-a.Srv.Store.Channel().GetByName(teamId, sChannel.Name, true); result.Err == nil {
// The channel already exists as an active channel. Merge with the existing one.
mChannel = result.Data.(*model.Channel)
- log.WriteString(utils.T("api.slackimport.slack_add_channels.merge", map[string]interface{}{"DisplayName": newChannel.DisplayName}))
+ importerLog.WriteString(utils.T("api.slackimport.slack_add_channels.merge", map[string]interface{}{"DisplayName": newChannel.DisplayName}))
} else if result := <-a.Srv.Store.Channel().GetDeletedByName(teamId, sChannel.Name); result.Err == nil {
// The channel already exists but has been deleted. Generate a random string for the handle instead.
newChannel.Name = model.NewId()
@@ -493,14 +494,14 @@ func (a *App) SlackAddChannels(teamId string, slackchannels []SlackChannel, post
// Haven't found an existing channel to merge with. Try importing it as a new one.
mChannel = a.OldImportChannel(&newChannel)
if mChannel == nil {
- l4g.Warn(utils.T("api.slackimport.slack_add_channels.import_failed.warn"), newChannel.DisplayName)
- log.WriteString(utils.T("api.slackimport.slack_add_channels.import_failed", map[string]interface{}{"DisplayName": newChannel.DisplayName}))
+ mlog.Warn(fmt.Sprintf("Slack Import: Unable to import Slack channel: %s.", newChannel.DisplayName))
+ importerLog.WriteString(utils.T("api.slackimport.slack_add_channels.import_failed", map[string]interface{}{"DisplayName": newChannel.DisplayName}))
continue
}
}
- a.addSlackUsersToChannel(sChannel.Members, users, mChannel, log)
- log.WriteString(newChannel.DisplayName + "\r\n")
+ a.addSlackUsersToChannel(sChannel.Members, users, mChannel, importerLog)
+ importerLog.WriteString(newChannel.DisplayName + "\r\n")
addedChannels[sChannel.Id] = mChannel
a.SlackAddPosts(teamId, mChannel, posts[sChannel.Name], users, uploads, botUser)
}
@@ -513,7 +514,7 @@ func SlackConvertUserMentions(users []SlackUser, posts map[string][]SlackPost) m
for _, user := range users {
r, err := regexp.Compile("<@" + user.Id + `(\|` + user.Username + ")?>")
if err != nil {
- l4g.Warn(utils.T("api.slackimport.slack_convert_user_mentions.compile_regexp_failed.warn"), user.Id, user.Username)
+ mlog.Warn(fmt.Sprint("Slack Import: Unable to compile the @mention, matching regular expression for the Slack user {{.Username}} (id={{.UserID}}).", user.Id, user.Username), mlog.String("user_id", user.Id))
continue
}
regexes["@"+user.Username] = r
@@ -541,7 +542,7 @@ func SlackConvertChannelMentions(channels []SlackChannel, posts map[string][]Sla
for _, channel := range channels {
r, err := regexp.Compile("<#" + channel.Id + `(\|` + channel.Name + ")?>")
if err != nil {
- l4g.Warn(utils.T("api.slackimport.slack_convert_channel_mentions.compile_regexp_failed.warn"), channel.Id, channel.Name)
+ mlog.Warn(fmt.Sprint("Slack Import: Unable to compile the !channel, matching regular expression for the Slack channel {{.ChannelName}} (id={{.ChannelID}}).", channel.Id, channel.Name))
continue
}
regexes["~"+channel.Name] = r
diff --git a/app/status.go b/app/status.go
index 64d5379ef..d8e93e2f1 100644
--- a/app/status.go
+++ b/app/status.go
@@ -4,8 +4,9 @@
package app
import (
- l4g "github.com/alecthomas/log4go"
+ "fmt"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -208,7 +209,7 @@ func (a *App) SetStatusOnline(userId string, sessionId string, manual bool) {
}
if result := <-schan; result.Err != nil {
- l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
+ mlog.Error(fmt.Sprintf("Failed to save status for user_id=%v, err=%v", userId, result.Err), mlog.String("user_id", userId))
}
}
@@ -292,7 +293,7 @@ func (a *App) SaveAndBroadcastStatus(status *model.Status) *model.AppError {
a.AddStatusCache(status)
if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
- l4g.Error(utils.T("api.status.save_status.error"), status.UserId, result.Err)
+ mlog.Error(fmt.Sprintf("Failed to save status for user_id=%v, err=%v", status.UserId, result.Err))
}
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
@@ -320,7 +321,7 @@ func (a *App) SetStatusOutOfOffice(userId string) {
a.AddStatusCache(status)
if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
- l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
+ mlog.Error(fmt.Sprintf("Failed to save status for user_id=%v, err=%v", userId, result.Err), mlog.String("user_id", userId))
}
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
diff --git a/app/team.go b/app/team.go
index 47e28f2ed..78d932870 100644
--- a/app/team.go
+++ b/app/team.go
@@ -13,9 +13,9 @@ import (
"net/url"
"strings"
- l4g "github.com/alecthomas/log4go"
"github.com/disintegration/imaging"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -350,7 +350,7 @@ func (a *App) JoinUserToTeam(team *model.Team, user *model.User, userRequestorId
// Soft error if there is an issue joining the default channels
if err := a.JoinDefaultChannels(team.Id, user, channelRole, userRequestorId); err != nil {
- l4g.Error(utils.T("api.user.create_user.joining.error"), user.Id, team.Id, err)
+ mlog.Error(fmt.Sprintf("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", user.Id, team.Id, err), mlog.String("user_id", user.Id))
}
a.ClearSessionCacheForUser(user.Id)
@@ -638,11 +638,11 @@ func (a *App) LeaveTeam(team *model.Team, user *model.User, requestorId string)
if *a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages {
if requestorId == user.Id {
if err := a.postLeaveTeamMessage(user, channel); err != nil {
- l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
+ mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
}
} else {
if err := a.postRemoveFromTeamMessage(user, channel); err != nil {
- l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
+ mlog.Error(fmt.Sprint("Failed to post join/leave message", err))
}
}
}
@@ -908,7 +908,7 @@ func (a *App) GetTeamIdFromQuery(query url.Values) (string, *model.AppError) {
} else if len(inviteId) > 0 {
if result := <-a.Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil {
// soft fail, so we still create user but don't auto-join team
- l4g.Error("%v", result.Err)
+ mlog.Error(fmt.Sprintf("%v", result.Err))
} else {
return result.Data.(*model.Team).Id, nil
}
diff --git a/app/user.go b/app/user.go
index 34076555e..fd8b6b377 100644
--- a/app/user.go
+++ b/app/user.go
@@ -22,10 +22,10 @@ import (
"strconv"
"strings"
- l4g "github.com/alecthomas/log4go"
"github.com/disintegration/imaging"
"github.com/golang/freetype"
"github.com/mattermost/mattermost-server/einterfaces"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -118,7 +118,7 @@ func (a *App) CreateUserWithInviteId(user *model.User, inviteId string) (*model.
a.AddDirectChannels(team.Id, ruser)
if err := a.SendWelcomeEmail(ruser.Id, ruser.Email, ruser.EmailVerified, ruser.Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
return ruser, nil
@@ -131,7 +131,7 @@ func (a *App) CreateUserAsAdmin(user *model.User) (*model.User, *model.AppError)
}
if err := a.SendWelcomeEmail(ruser.Id, ruser.Email, ruser.EmailVerified, ruser.Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
return ruser, nil
@@ -155,7 +155,7 @@ func (a *App) CreateUserFromSignup(user *model.User) (*model.User, *model.AppErr
}
if err := a.SendWelcomeEmail(ruser.Id, ruser.Email, ruser.EmailVerified, ruser.Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
return ruser, nil
@@ -172,7 +172,7 @@ func (a *App) IsUserSignUpAllowed() *model.AppError {
func (a *App) IsFirstUserAccount() bool {
if a.SessionCacheLength() == 0 {
if cr := <-a.Srv.Store.User().GetTotalUsersCount(); cr.Err != nil {
- l4g.Error(cr.Err)
+ mlog.Error(fmt.Sprint(cr.Err))
return false
} else {
count := cr.Data.(int64)
@@ -227,20 +227,20 @@ func (a *App) createUser(user *model.User) (*model.User, *model.AppError) {
}
if result := <-a.Srv.Store.User().Save(user); result.Err != nil {
- l4g.Error(utils.T("api.user.create_user.save.error"), result.Err)
+ mlog.Error(fmt.Sprintf("Couldn't save the user err=%v", result.Err))
return nil, result.Err
} else {
ruser := result.Data.(*model.User)
if user.EmailVerified {
if err := a.VerifyUserEmail(ruser.Id); err != nil {
- l4g.Error(utils.T("api.user.create_user.verified.error"), err)
+ mlog.Error(fmt.Sprintf("Failed to set email verified err=%v", err))
}
}
pref := model.Preference{UserId: ruser.Id, Category: model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, Name: ruser.Id, Value: "0"}
if presult := <-a.Srv.Store.Preference().Save(&model.Preferences{pref}); presult.Err != nil {
- l4g.Error(utils.T("api.user.create_user.tutorial.error"), presult.Err.Message)
+ mlog.Error(fmt.Sprintf("Encountered error saving tutorial preference, err=%v", presult.Err.Message))
}
ruser.Sanitize(map[string]bool{})
@@ -306,7 +306,7 @@ func (a *App) CreateOAuthUser(service string, userData io.Reader, teamId string)
err = a.AddDirectChannels(teamId, user)
if err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
@@ -851,7 +851,7 @@ func (a *App) SetProfileImageFromFile(userId string, file multipart.File) *model
a.InvalidateCacheForUser(userId)
if user, err := a.GetUser(userId); err != nil {
- l4g.Error(utils.T("api.user.get_me.getting.error"), userId)
+ mlog.Error(fmt.Sprintf("Error in getting users profile for id=%v forcing logout", userId), mlog.String("user_id", userId))
} else {
options := a.Config().GetSanitizeOptions()
user.SanitizeProfile(options)
@@ -1056,14 +1056,14 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
if rusers[0].Email != rusers[1].Email {
a.Go(func() {
if err := a.SendEmailChangeEmail(rusers[1].Email, rusers[0].Email, rusers[0].Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
if a.Config().EmailSettings.RequireEmailVerification {
a.Go(func() {
if err := a.SendEmailVerification(rusers[0]); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
}
@@ -1072,7 +1072,7 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
if rusers[0].Username != rusers[1].Username {
a.Go(func() {
if err := a.SendChangeUsernameEmail(rusers[1].Username, rusers[0].Username, rusers[0].Email, rusers[0].Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
}
@@ -1117,12 +1117,12 @@ func (a *App) UpdateMfa(activate bool, userId, token string) *model.AppError {
var err *model.AppError
if user, err = a.GetUser(userId); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
return
}
if err := a.SendMfaChangeEmail(user.Email, activate, user.Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
@@ -1160,7 +1160,7 @@ func (a *App) UpdatePasswordSendEmail(user *model.User, newPassword, method stri
a.Go(func() {
if err := a.SendPasswordChangeEmail(user.Email, method, user.Locale, a.GetSiteURL()); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
@@ -1194,7 +1194,7 @@ func (a *App) ResetPasswordFromToken(userSuppliedTokenString, newPassword string
}
if err := a.DeleteToken(token); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
return nil
@@ -1278,7 +1278,7 @@ func (a *App) UpdateUserRoles(userId string, newRoles string, sendWebSocketEvent
if result := <-schan; result.Err != nil {
// soft error since the user roles were still updated
- l4g.Error(result.Err)
+ mlog.Error(fmt.Sprint(result.Err))
}
a.ClearSessionCacheForUser(user.Id)
@@ -1294,9 +1294,9 @@ func (a *App) UpdateUserRoles(userId string, newRoles string, sendWebSocketEvent
}
func (a *App) PermanentDeleteUser(user *model.User) *model.AppError {
- l4g.Warn(utils.T("api.user.permanent_delete_user.attempting.warn"), user.Email, user.Id)
+ mlog.Warn(fmt.Sprintf("Attempting to permanently delete account %v id=%v", user.Email, user.Id), mlog.String("user_id", user.Id))
if user.IsInRole(model.SYSTEM_ADMIN_ROLE_ID) {
- l4g.Warn(utils.T("api.user.permanent_delete_user.system_admin.warn"), user.Email)
+ mlog.Warn(fmt.Sprintf("You are deleting %v that is a system administrator. You may need to set another account as the system administrator using the command line tools.", user.Email))
}
if _, err := a.UpdateActive(user, false); err != nil {
@@ -1351,7 +1351,7 @@ func (a *App) PermanentDeleteUser(user *model.User) *model.AppError {
return result.Err
}
- l4g.Warn(utils.T("api.user.permanent_delete_user.deleted.warn"), user.Email, user.Id)
+ mlog.Warn(fmt.Sprintf("Permanently deleted account %v id=%v", user.Email, user.Id), mlog.String("user_id", user.Id))
return nil
}
@@ -1395,7 +1395,7 @@ func (a *App) VerifyEmailFromToken(userSuppliedTokenString string) *model.AppErr
return err
}
if err := a.DeleteToken(token); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
diff --git a/app/web_conn.go b/app/web_conn.go
index 9ae5505b2..9d8134f34 100644
--- a/app/web_conn.go
+++ b/app/web_conn.go
@@ -8,10 +8,9 @@ import (
"sync/atomic"
"time"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
- l4g "github.com/alecthomas/log4go"
"github.com/gorilla/websocket"
goi18n "github.com/nicksnyder/go-i18n/i18n"
)
@@ -138,9 +137,9 @@ func (c *WebConn) readPump() {
if err := c.WebSocket.ReadJSON(&req); err != nil {
// browsers will appear as CloseNoStatusReceived
if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseNoStatusReceived) {
- l4g.Debug(fmt.Sprintf("websocket.read: client side closed socket userId=%v", c.UserId))
+ mlog.Debug(fmt.Sprintf("websocket.read: client side closed socket userId=%v", c.UserId))
} else {
- l4g.Debug(fmt.Sprintf("websocket.read: closing websocket for userId=%v error=%v", c.UserId, err.Error()))
+ mlog.Debug(fmt.Sprintf("websocket.read: closing websocket for userId=%v error=%v", c.UserId, err.Error()))
}
return
@@ -177,7 +176,7 @@ func (c *WebConn) writePump() {
if msg.EventType() == model.WEBSOCKET_EVENT_TYPING ||
msg.EventType() == model.WEBSOCKET_EVENT_STATUS_CHANGE ||
msg.EventType() == model.WEBSOCKET_EVENT_CHANNEL_VIEWED {
- l4g.Info(fmt.Sprintf("websocket.slow: dropping message userId=%v type=%v channelId=%v", c.UserId, msg.EventType(), evt.Broadcast.ChannelId))
+ mlog.Info(fmt.Sprintf("websocket.slow: dropping message userId=%v type=%v channelId=%v", c.UserId, msg.EventType(), evt.Broadcast.ChannelId))
skipSend = true
}
}
@@ -196,9 +195,9 @@ func (c *WebConn) writePump() {
if len(c.Send) >= SEND_DEADLOCK_WARN {
if evtOk {
- l4g.Error(fmt.Sprintf("websocket.full: message userId=%v type=%v channelId=%v size=%v", c.UserId, msg.EventType(), evt.Broadcast.ChannelId, len(msg.ToJson())))
+ mlog.Error(fmt.Sprintf("websocket.full: message userId=%v type=%v channelId=%v size=%v", c.UserId, msg.EventType(), evt.Broadcast.ChannelId, len(msg.ToJson())))
} else {
- l4g.Error(fmt.Sprintf("websocket.full: message userId=%v type=%v size=%v", c.UserId, msg.EventType(), len(msg.ToJson())))
+ mlog.Error(fmt.Sprintf("websocket.full: message userId=%v type=%v size=%v", c.UserId, msg.EventType(), len(msg.ToJson())))
}
}
@@ -206,9 +205,9 @@ func (c *WebConn) writePump() {
if err := c.WebSocket.WriteMessage(websocket.TextMessage, msgBytes); err != nil {
// browsers will appear as CloseNoStatusReceived
if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseNoStatusReceived) {
- l4g.Debug(fmt.Sprintf("websocket.send: client side closed socket userId=%v", c.UserId))
+ mlog.Debug(fmt.Sprintf("websocket.send: client side closed socket userId=%v", c.UserId))
} else {
- l4g.Debug(fmt.Sprintf("websocket.send: closing websocket for userId=%v, error=%v", c.UserId, err.Error()))
+ mlog.Debug(fmt.Sprintf("websocket.send: closing websocket for userId=%v, error=%v", c.UserId, err.Error()))
}
return
@@ -226,9 +225,9 @@ func (c *WebConn) writePump() {
if err := c.WebSocket.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
// browsers will appear as CloseNoStatusReceived
if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseNoStatusReceived) {
- l4g.Debug(fmt.Sprintf("websocket.ticker: client side closed socket userId=%v", c.UserId))
+ mlog.Debug(fmt.Sprintf("websocket.ticker: client side closed socket userId=%v", c.UserId))
} else {
- l4g.Debug(fmt.Sprintf("websocket.ticker: closing websocket for userId=%v error=%v", c.UserId, err.Error()))
+ mlog.Debug(fmt.Sprintf("websocket.ticker: closing websocket for userId=%v error=%v", c.UserId, err.Error()))
}
return
@@ -237,7 +236,7 @@ func (c *WebConn) writePump() {
return
case <-authTicker.C:
if c.GetSessionToken() == "" {
- l4g.Debug(fmt.Sprintf("websocket.authTicker: did not authenticate ip=%v", c.WebSocket.RemoteAddr()))
+ mlog.Debug(fmt.Sprintf("websocket.authTicker: did not authenticate ip=%v", c.WebSocket.RemoteAddr()))
return
}
authTicker.Stop()
@@ -261,7 +260,7 @@ func (webCon *WebConn) IsAuthenticated() bool {
session, err := webCon.App.GetSession(webCon.GetSessionToken())
if err != nil {
- l4g.Error(utils.T("api.websocket.invalid_session.error"), err.Error())
+ mlog.Error(fmt.Sprintf("Invalid session err=%v", err.Error()))
webCon.SetSessionToken("")
webCon.SetSession(nil)
webCon.SetSessionExpiresAt(0)
@@ -334,7 +333,7 @@ func (webCon *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool {
if webCon.AllChannelMembers == nil {
if result := <-webCon.App.Srv.Store.Channel().GetAllChannelMembersForUser(webCon.UserId, true); result.Err != nil {
- l4g.Error("webhub.shouldSendEvent: " + result.Err.Error())
+ mlog.Error("webhub.shouldSendEvent: " + result.Err.Error())
return false
} else {
webCon.AllChannelMembers = result.Data.(map[string]string)
@@ -365,7 +364,7 @@ func (webCon *WebConn) IsMemberOfTeam(teamId string) bool {
if currentSession == nil || len(currentSession.Token) == 0 {
session, err := webCon.App.GetSession(webCon.GetSessionToken())
if err != nil {
- l4g.Error(utils.T("api.websocket.invalid_session.error"), err.Error())
+ mlog.Error(fmt.Sprintf("Invalid session err=%v", err.Error()))
return false
} else {
webCon.SetSession(session)
diff --git a/app/web_hub.go b/app/web_hub.go
index 971b03481..18eb97c8e 100644
--- a/app/web_hub.go
+++ b/app/web_hub.go
@@ -13,10 +13,8 @@ import (
"sync/atomic"
"time"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
)
const (
@@ -66,7 +64,7 @@ func (a *App) TotalWebsocketConnections() int {
func (a *App) HubStart() {
// Total number of hubs is twice the number of CPUs.
numberOfHubs := runtime.NumCPU() * 2
- l4g.Info(utils.T("api.web_hub.start.starting.debug"), numberOfHubs)
+ mlog.Info(fmt.Sprintf("Starting %v websocket hubs", numberOfHubs))
a.Hubs = make([]*Hub, numberOfHubs)
a.HubsStopCheckingForDeadlock = make(chan bool, 1)
@@ -89,7 +87,7 @@ func (a *App) HubStart() {
case <-ticker.C:
for _, hub := range a.Hubs {
if len(hub.broadcast) >= DEADLOCK_WARN {
- l4g.Error("Hub processing might be deadlock on hub %v goroutine %v with %v events in the buffer", hub.connectionIndex, hub.goroutineId, len(hub.broadcast))
+ mlog.Error(fmt.Sprintf("Hub processing might be deadlock on hub %v goroutine %v with %v events in the buffer", hub.connectionIndex, hub.goroutineId, len(hub.broadcast)))
buf := make([]byte, 1<<16)
runtime.Stack(buf, true)
output := fmt.Sprintf("%s", buf)
@@ -97,7 +95,7 @@ func (a *App) HubStart() {
for _, part := range splits {
if strings.Contains(part, fmt.Sprintf("%v", hub.goroutineId)) {
- l4g.Error("Trace for possible deadlock goroutine %v", part)
+ mlog.Error(fmt.Sprintf("Trace for possible deadlock goroutine %v", part))
}
}
}
@@ -111,12 +109,12 @@ func (a *App) HubStart() {
}
func (a *App) HubStop() {
- l4g.Info(utils.T("api.web_hub.start.stopping.debug"))
+ mlog.Info("stopping websocket hub connections")
select {
case a.HubsStopCheckingForDeadlock <- true:
default:
- l4g.Warn("We appear to have already sent the stop checking for deadlocks command")
+ mlog.Warn("We appear to have already sent the stop checking for deadlocks command")
}
for _, hub := range a.Hubs {
@@ -367,7 +365,7 @@ func (h *Hub) Start() {
doStart = func() {
h.goroutineId = getGoroutineId()
- l4g.Debug("Hub for index %v is starting with goroutine %v", h.connectionIndex, h.goroutineId)
+ mlog.Debug(fmt.Sprintf("Hub for index %v is starting with goroutine %v", h.connectionIndex, h.goroutineId))
connections := newHubConnectionIndex()
@@ -404,7 +402,7 @@ func (h *Hub) Start() {
select {
case webCon.Send <- msg:
default:
- l4g.Error(fmt.Sprintf("webhub.broadcast: cannot send, closing websocket for userId=%v", webCon.UserId))
+ mlog.Error(fmt.Sprintf("webhub.broadcast: cannot send, closing websocket for userId=%v", webCon.UserId))
close(webCon.Send)
connections.Remove(webCon)
}
@@ -438,12 +436,12 @@ func (h *Hub) Start() {
doRecover = func() {
if !h.ExplicitStop {
if r := recover(); r != nil {
- l4g.Error(fmt.Sprintf("Recovering from Hub panic. Panic was: %v", r))
+ mlog.Error(fmt.Sprintf("Recovering from Hub panic. Panic was: %v", r))
} else {
- l4g.Error("Webhub stopped unexpectedly. Recovering.")
+ mlog.Error("Webhub stopped unexpectedly. Recovering.")
}
- l4g.Error(string(debug.Stack()))
+ mlog.Error(string(debug.Stack()))
go doRecoverableStart()
}
diff --git a/app/webhook.go b/app/webhook.go
index 5c3e963ce..a5ab28952 100644
--- a/app/webhook.go
+++ b/app/webhook.go
@@ -4,13 +4,14 @@
package app
import (
+ "fmt"
"io"
"net/http"
"regexp"
"strings"
"unicode/utf8"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -107,7 +108,7 @@ func (a *App) TriggerWebhook(payload *model.OutgoingWebhookPayload, hook *model.
req.Header.Set("Content-Type", contentType)
req.Header.Set("Accept", "application/json")
if resp, err := a.HTTPClient(false).Do(req); err != nil {
- l4g.Error(utils.T("api.post.handle_webhook_events_and_forget.event_post.error"), err.Error())
+ mlog.Error(fmt.Sprintf("Event POST failed, err=%s", err.Error()))
} else {
defer consumeAndClose(resp)
@@ -134,7 +135,7 @@ func (a *App) TriggerWebhook(payload *model.OutgoingWebhookPayload, hook *model.
}
if _, err := a.CreateWebhookPost(hook.CreatorId, channel, text, webhookResp.Username, webhookResp.IconURL, webhookResp.Props, webhookResp.Type, postRootId); err != nil {
- l4g.Error(utils.T("api.post.handle_webhook_events_and_forget.create_post.error"), err)
+ mlog.Error(fmt.Sprintf("Failed to create response post, err=%v", err))
}
}
}
diff --git a/app/websocket_router.go b/app/websocket_router.go
index 6bc3a6ff7..6c5e142a1 100644
--- a/app/websocket_router.go
+++ b/app/websocket_router.go
@@ -4,10 +4,10 @@
package app
import (
- l4g "github.com/alecthomas/log4go"
-
+ "fmt"
"net/http"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -91,7 +91,7 @@ func (wr *WebSocketRouter) ServeWebSocket(conn *WebConn, r *model.WebSocketReque
}
func ReturnWebSocketError(conn *WebConn, r *model.WebSocketRequest, err *model.AppError) {
- l4g.Error(utils.T("api.web_socket_router.log.error"), r.Seq, conn.UserId, err.SystemMessage(utils.T), err.DetailedError)
+ mlog.Error(fmt.Sprintf("websocket routing error: seq=%v uid=%v %v [details: %v]", r.Seq, conn.UserId, err.SystemMessage(utils.T), err.DetailedError))
err.DetailedError = ""
errorResp := model.NewWebSocketError(r.Seq, err)
diff --git a/cmd/commands/command.go b/cmd/commands/command.go
index 380816644..e7b7e0a0d 100644
--- a/cmd/commands/command.go
+++ b/cmd/commands/command.go
@@ -5,6 +5,7 @@ package commands
import (
"errors"
+
"github.com/mattermost/mattermost-server/app"
"github.com/mattermost/mattermost-server/cmd"
"github.com/mattermost/mattermost-server/model"
diff --git a/cmd/commands/config_flag_test.go b/cmd/commands/config_flag_test.go
index f31c989d8..59178b620 100644
--- a/cmd/commands/config_flag_test.go
+++ b/cmd/commands/config_flag_test.go
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"encoding/json"
+
"github.com/mattermost/mattermost-server/cmd"
"github.com/mattermost/mattermost-server/utils"
)
diff --git a/cmd/commands/jobserver.go b/cmd/commands/jobserver.go
index b96984b41..a7671e190 100644
--- a/cmd/commands/jobserver.go
+++ b/cmd/commands/jobserver.go
@@ -8,8 +8,8 @@ import (
"os/signal"
"syscall"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/cmd"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/spf13/cobra"
)
@@ -36,13 +36,12 @@ func jobserverCmdF(command *cobra.Command, args []string) {
if err != nil {
panic(err.Error())
}
- defer l4g.Close()
defer a.Shutdown()
a.LoadLicense()
// Run jobs
- l4g.Info("Starting Mattermost job server")
+ mlog.Info("Starting Mattermost job server")
if !noJobs {
a.Jobs.StartWorkers()
}
@@ -55,10 +54,10 @@ func jobserverCmdF(command *cobra.Command, args []string) {
<-signalChan
// Cleanup anything that isn't handled by a defer statement
- l4g.Info("Stopping Mattermost job server")
+ mlog.Info("Stopping Mattermost job server")
a.Jobs.StopSchedulers()
a.Jobs.StopWorkers()
- l4g.Info("Stopped Mattermost job server")
+ mlog.Info("Stopped Mattermost job server")
}
diff --git a/cmd/commands/server.go b/cmd/commands/server.go
index 9048696ca..77f195f4b 100644
--- a/cmd/commands/server.go
+++ b/cmd/commands/server.go
@@ -4,18 +4,19 @@
package commands
import (
+ "fmt"
"net"
"os"
"os/signal"
"syscall"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/api"
"github.com/mattermost/mattermost-server/api4"
"github.com/mattermost/mattermost-server/app"
"github.com/mattermost/mattermost-server/cmd"
"github.com/mattermost/mattermost-server/manualtesting"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
"github.com/mattermost/mattermost-server/web"
@@ -61,7 +62,7 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
a, err := app.New(options...)
if err != nil {
- l4g.Critical(err.Error())
+ mlog.Critical(err.Error())
return err
}
defer a.Shutdown()
@@ -69,17 +70,17 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
utils.TestConnection(a.Config())
pwd, _ := os.Getwd()
- l4g.Info(utils.T("mattermost.current_version"), model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash, model.BuildHashEnterprise)
- l4g.Info(utils.T("mattermost.entreprise_enabled"), model.BuildEnterpriseReady)
- l4g.Info(utils.T("mattermost.working_dir"), pwd)
- l4g.Info(utils.T("mattermost.config_file"), utils.FindConfigFile(configFileLocation))
+ mlog.Info(fmt.Sprintf("Current version is %v (%v/%v/%v/%v)", model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash, model.BuildHashEnterprise))
+ mlog.Info(fmt.Sprintf("Enterprise Enabled: %v", model.BuildEnterpriseReady))
+ mlog.Info(fmt.Sprintf("Current working directory is %v", pwd))
+ mlog.Info(fmt.Sprintf("Loaded config file from %v", utils.FindConfigFile(configFileLocation)))
backend, appErr := a.FileBackend()
if appErr == nil {
appErr = backend.TestConnection()
}
if appErr != nil {
- l4g.Error("Problem with file storage settings: " + appErr.Error())
+ mlog.Error("Problem with file storage settings: " + appErr.Error())
}
if model.BuildEnterpriseReady == "true" {
@@ -99,7 +100,7 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
serverErr := a.StartServer()
if serverErr != nil {
- l4g.Critical(serverErr.Error())
+ mlog.Critical(serverErr.Error())
return serverErr
}
@@ -111,7 +112,7 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
license := a.License()
if license == nil && len(a.Config().SqlSettings.DataSourceReplicas) > 1 {
- l4g.Warn(utils.T("store.sql.read_replicas_not_licensed.critical"))
+ mlog.Warn("More than 1 read replica functionality disabled by current license. Please contact your system administrator about upgrading your enterprise license.")
a.UpdateConfig(func(cfg *model.Config) {
cfg.SqlSettings.DataSourceReplicas = cfg.SqlSettings.DataSourceReplicas[:1]
})
@@ -171,7 +172,7 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
if a.Elasticsearch != nil {
a.Go(func() {
if err := a.Elasticsearch.Start(); err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
})
}
@@ -241,7 +242,7 @@ func runSessionCleanupJob(a *app.App) {
func resetStatuses(a *app.App) {
if result := <-a.Srv.Store.Status().ResetAll(); result.Err != nil {
- l4g.Error(utils.T("mattermost.reset_status.error"), result.Err.Error())
+ mlog.Error(fmt.Sprint("mattermost.reset_status.error FIXME: NOT FOUND IN TRANSLATIONS FILE", result.Err.Error()))
}
}
@@ -260,11 +261,11 @@ func notifyReady() {
// notify systemd that the server is ready.
systemdSocket := os.Getenv("NOTIFY_SOCKET")
if systemdSocket != "" {
- l4g.Info("Sending systemd READY notification.")
+ mlog.Info("Sending systemd READY notification.")
err := sendSystemdReadyNotification(systemdSocket)
if err != nil {
- l4g.Error(err.Error())
+ mlog.Error(err.Error())
}
}
}
diff --git a/cmd/commands/user.go b/cmd/commands/user.go
index 96e8698e5..da3fb454b 100644
--- a/cmd/commands/user.go
+++ b/cmd/commands/user.go
@@ -9,7 +9,6 @@ import (
"fmt"
"io/ioutil"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/app"
"github.com/mattermost/mattermost-server/cmd"
"github.com/mattermost/mattermost-server/model"
@@ -644,7 +643,7 @@ func migrateAuthToSamlCmdF(command *cobra.Command, args []string) error {
if err := migrate.MigrateToSaml(fromAuth, matches, autoFlag, dryRunFlag); err != nil {
return errors.New("Error while migrating users: " + err.Error())
}
- l4g.Close()
+
cmd.CommandPrettyPrintln("Successfully migrated accounts.")
}
diff --git a/cmd/init.go b/cmd/init.go
index c85bd5362..e3b4e97e1 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -33,8 +33,6 @@ func InitDBCommandContext(configFileLocation string) (*app.App, error) {
}
model.AppErrorInit(utils.T)
- utils.ConfigureCmdLineLog()
-
a, err := app.New(app.ConfigFile(configFileLocation))
if err != nil {
return nil, err
diff --git a/config/default.json b/config/default.json
index 584380453..8bf06dc8b 100644
--- a/config/default.json
+++ b/config/default.json
@@ -118,9 +118,10 @@
"LogSettings": {
"EnableConsole": true,
"ConsoleLevel": "DEBUG",
+ "ConsoleJson": true,
"EnableFile": true,
"FileLevel": "INFO",
- "FileFormat": "",
+ "FileJson": true,
"FileLocation": "",
"EnableWebhookDebugging": true,
"EnableDiagnostics": true
diff --git a/einterfaces/brand.go b/einterfaces/brand.go
index fc584a91c..11e7d4e42 100644
--- a/einterfaces/brand.go
+++ b/einterfaces/brand.go
@@ -4,8 +4,9 @@
package einterfaces
import (
- "github.com/mattermost/mattermost-server/model"
"mime/multipart"
+
+ "github.com/mattermost/mattermost-server/model"
)
type BrandInterface interface {
diff --git a/einterfaces/oauthproviders.go b/einterfaces/oauthproviders.go
index 7e24d2a70..ed54a204a 100644
--- a/einterfaces/oauthproviders.go
+++ b/einterfaces/oauthproviders.go
@@ -4,8 +4,9 @@
package einterfaces
import (
- "github.com/mattermost/mattermost-server/model"
"io"
+
+ "github.com/mattermost/mattermost-server/model"
)
type OauthProvider interface {
diff --git a/jobs/jobs.go b/jobs/jobs.go
index b367730b5..850491403 100644
--- a/jobs/jobs.go
+++ b/jobs/jobs.go
@@ -5,11 +5,12 @@ package jobs
import (
"context"
+ "fmt"
"time"
"net/http"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -125,10 +126,10 @@ func (srv *JobServer) CancellationWatcher(ctx context.Context, jobId string, can
for {
select {
case <-ctx.Done():
- l4g.Debug("CancellationWatcher for Job: %v Aborting as job has finished.", jobId)
+ mlog.Debug(fmt.Sprintf("CancellationWatcher for Job: %v Aborting as job has finished.", jobId))
return
case <-time.After(CANCEL_WATCHER_POLLING_INTERVAL * time.Millisecond):
- l4g.Debug("CancellationWatcher for Job: %v polling.", jobId)
+ mlog.Debug(fmt.Sprintf("CancellationWatcher for Job: %v polling.", jobId))
if result := <-srv.Store.Job().Get(jobId); result.Err == nil {
jobStatus := result.Data.(*model.Job)
if jobStatus.Status == model.JOB_STATUS_CANCEL_REQUESTED {
diff --git a/jobs/jobs_watcher.go b/jobs/jobs_watcher.go
index 645715db4..07979442d 100644
--- a/jobs/jobs_watcher.go
+++ b/jobs/jobs_watcher.go
@@ -4,10 +4,11 @@
package jobs
import (
+ "fmt"
"math/rand"
"time"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -35,7 +36,7 @@ func (srv *JobServer) MakeWatcher(workers *Workers, pollingInterval int) *Watche
}
func (watcher *Watcher) Start() {
- l4g.Debug("Watcher Started")
+ mlog.Debug("Watcher Started")
// Delay for some random number of milliseconds before starting to ensure that multiple
// instances of the jobserver don't poll at a time too close to each other.
@@ -43,14 +44,14 @@ func (watcher *Watcher) Start() {
<-time.After(time.Duration(rand.Intn(watcher.pollingInterval)) * time.Millisecond)
defer func() {
- l4g.Debug("Watcher Finished")
+ mlog.Debug("Watcher Finished")
watcher.stopped <- true
}()
for {
select {
case <-watcher.stop:
- l4g.Debug("Watcher: Received stop signal")
+ mlog.Debug("Watcher: Received stop signal")
return
case <-time.After(time.Duration(watcher.pollingInterval) * time.Millisecond):
watcher.PollAndNotify()
@@ -59,14 +60,14 @@ func (watcher *Watcher) Start() {
}
func (watcher *Watcher) Stop() {
- l4g.Debug("Watcher Stopping")
+ mlog.Debug("Watcher Stopping")
watcher.stop <- true
<-watcher.stopped
}
func (watcher *Watcher) PollAndNotify() {
if result := <-watcher.srv.Store.Job().GetAllByStatus(model.JOB_STATUS_PENDING); result.Err != nil {
- l4g.Error("Error occurred getting all pending statuses: %v", result.Err.Error())
+ mlog.Error(fmt.Sprintf("Error occurred getting all pending statuses: %v", result.Err.Error()))
} else {
jobs := result.Data.([]*model.Job)
diff --git a/jobs/schedulers.go b/jobs/schedulers.go
index 839cbbae8..2823036df 100644
--- a/jobs/schedulers.go
+++ b/jobs/schedulers.go
@@ -4,11 +4,11 @@
package jobs
import (
+ "fmt"
"sync"
"time"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -25,7 +25,7 @@ type Schedulers struct {
}
func (srv *JobServer) InitSchedulers() *Schedulers {
- l4g.Debug("Initialising schedulers.")
+ mlog.Debug("Initialising schedulers.")
schedulers := &Schedulers{
stop: make(chan bool),
@@ -59,10 +59,10 @@ func (schedulers *Schedulers) Start() *Schedulers {
go func() {
schedulers.startOnce.Do(func() {
- l4g.Info("Starting schedulers.")
+ mlog.Info("Starting schedulers.")
defer func() {
- l4g.Info("Schedulers stopped.")
+ mlog.Info("Schedulers stopped.")
close(schedulers.stopped)
}()
@@ -78,7 +78,7 @@ func (schedulers *Schedulers) Start() *Schedulers {
for {
select {
case <-schedulers.stop:
- l4g.Debug("Schedulers received stop signal.")
+ mlog.Debug("Schedulers received stop signal.")
return
case now = <-time.After(1 * time.Minute):
cfg := schedulers.jobs.Config()
@@ -93,8 +93,8 @@ func (schedulers *Schedulers) Start() *Schedulers {
if scheduler != nil {
if scheduler.Enabled(cfg) {
if _, err := schedulers.scheduleJob(cfg, scheduler); err != nil {
- l4g.Warn("Failed to schedule job with scheduler: %v", scheduler.Name())
- l4g.Error(err)
+ mlog.Warn(fmt.Sprintf("Failed to schedule job with scheduler: %v", scheduler.Name()))
+ mlog.Error(fmt.Sprint(err))
} else {
schedulers.setNextRunTime(cfg, idx, now, true)
}
@@ -119,7 +119,7 @@ func (schedulers *Schedulers) Start() *Schedulers {
}
func (schedulers *Schedulers) Stop() *Schedulers {
- l4g.Info("Stopping schedulers.")
+ mlog.Info("Stopping schedulers.")
close(schedulers.stop)
<-schedulers.stopped
return schedulers
@@ -130,7 +130,7 @@ func (schedulers *Schedulers) setNextRunTime(cfg *model.Config, idx int, now tim
if !pendingJobs {
if pj, err := schedulers.jobs.CheckForPendingJobsByType(scheduler.JobType()); err != nil {
- l4g.Error("Failed to set next job run time: " + err.Error())
+ mlog.Error("Failed to set next job run time: " + err.Error())
schedulers.nextRunTimes[idx] = nil
return
} else {
@@ -140,13 +140,13 @@ func (schedulers *Schedulers) setNextRunTime(cfg *model.Config, idx int, now tim
lastSuccessfulJob, err := schedulers.jobs.GetLastSuccessfulJobByType(scheduler.JobType())
if err != nil {
- l4g.Error("Failed to set next job run time: " + err.Error())
+ mlog.Error("Failed to set next job run time: " + err.Error())
schedulers.nextRunTimes[idx] = nil
return
}
schedulers.nextRunTimes[idx] = scheduler.NextScheduleTime(cfg, now, pendingJobs, lastSuccessfulJob)
- l4g.Debug("Next run time for scheduler %v: %v", scheduler.Name(), schedulers.nextRunTimes[idx])
+ mlog.Debug(fmt.Sprintf("Next run time for scheduler %v: %v", scheduler.Name(), schedulers.nextRunTimes[idx]))
}
func (schedulers *Schedulers) scheduleJob(cfg *model.Config, scheduler model.Scheduler) (*model.Job, *model.AppError) {
@@ -164,6 +164,6 @@ func (schedulers *Schedulers) scheduleJob(cfg *model.Config, scheduler model.Sch
}
func (schedulers *Schedulers) handleConfigChange(oldConfig *model.Config, newConfig *model.Config) {
- l4g.Debug("Schedulers received config change.")
+ mlog.Debug("Schedulers received config change.")
schedulers.configChanged <- newConfig
}
diff --git a/jobs/workers.go b/jobs/workers.go
index ca34855e5..57a255013 100644
--- a/jobs/workers.go
+++ b/jobs/workers.go
@@ -6,7 +6,7 @@ package jobs
import (
"sync"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -54,7 +54,7 @@ func (srv *JobServer) InitWorkers() *Workers {
}
func (workers *Workers) Start() *Workers {
- l4g.Info("Starting workers")
+ mlog.Info("Starting workers")
workers.startOnce.Do(func() {
if workers.DataRetention != nil && (*workers.ConfigService.Config().DataRetentionSettings.EnableMessageDeletion || *workers.ConfigService.Config().DataRetentionSettings.EnableFileDeletion) {
@@ -152,7 +152,7 @@ func (workers *Workers) Stop() *Workers {
workers.LdapSync.Stop()
}
- l4g.Info("Stopped workers")
+ mlog.Info("Stopped workers")
return workers
}
diff --git a/manualtesting/manual_testing.go b/manualtesting/manual_testing.go
index 9743df006..7b78fd312 100644
--- a/manualtesting/manual_testing.go
+++ b/manualtesting/manual_testing.go
@@ -4,6 +4,7 @@
package manualtesting
import (
+ "fmt"
"hash/fnv"
"math/rand"
"net/http"
@@ -11,9 +12,9 @@ import (
"strconv"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/api"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -34,7 +35,7 @@ func Init(api3 *api.API) {
func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
// Let the world know
- l4g.Info(utils.T("manaultesting.manual_test.setup.info"))
+ mlog.Info("Setting up for manual test...")
// URL Parameters
params, err := url.ParseQuery(r.URL.RawQuery)
@@ -51,7 +52,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
hash := hasher.Sum32()
rand.Seed(int64(hash))
} else {
- l4g.Debug(utils.T("manaultesting.manual_test.uid.debug"))
+ mlog.Debug("No uid in URL")
}
// Create a client for tests to use
@@ -63,7 +64,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
var teamID string
var userID string
if ok1 && ok2 {
- l4g.Info(utils.T("manaultesting.manual_test.create.info"))
+ mlog.Info("Creating user and team")
// Create team for testing
team := &model.Team{
DisplayName: teamDisplayName[0],
@@ -155,7 +156,7 @@ func getChannelID(a *app.App, channelname string, teamid string, userid string)
// Grab all the channels
result := <-a.Srv.Store.Channel().GetChannels(teamid, userid)
if result.Err != nil {
- l4g.Debug(utils.T("manaultesting.get_channel_id.unable.debug"))
+ mlog.Debug("Unable to get channels")
return "", false
}
@@ -166,6 +167,6 @@ func getChannelID(a *app.App, channelname string, teamid string, userid string)
return channel.Id, true
}
}
- l4g.Debug(utils.T("manaultesting.get_channel_id.no_found.debug"), channelname, strconv.Itoa(len(data)))
+ mlog.Debug(fmt.Sprintf("Could not find channel: %v, %v possibilities searched", channelname, strconv.Itoa(len(data))))
return "", false
}
diff --git a/manualtesting/test_autolink.go b/manualtesting/test_autolink.go
index 434e50c95..3fe589241 100644
--- a/manualtesting/test_autolink.go
+++ b/manualtesting/test_autolink.go
@@ -6,9 +6,8 @@ package manualtesting
import (
"net/http"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
)
const LINK_POST_TEXT = `
@@ -23,7 +22,7 @@ https://medium.com/@slackhq/11-useful-tips-for-getting-the-most-of-slack-5dfb3d1
`
func testAutoLink(env TestEnvironment) *model.AppError {
- l4g.Info(utils.T("manaultesting.test_autolink.info"))
+ mlog.Info("Manual Auto Link Test")
channelID, err := getChannelID(env.Context.App, model.DEFAULT_CHANNEL, env.CreatedTeamId, env.CreatedUserId)
if !err {
return model.NewAppError("/manualtest", "manaultesting.test_autolink.unable.app_error", nil, "", http.StatusInternalServerError)
diff --git a/mlog/global.go b/mlog/global.go
new file mode 100644
index 000000000..91d5858a4
--- /dev/null
+++ b/mlog/global.go
@@ -0,0 +1,42 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package mlog
+
+import (
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+var globalLogger *Logger
+
+func InitGlobalLogger(logger *Logger) {
+ globalLogger = logger
+ Debug = globalLogger.Debug
+ Info = globalLogger.Info
+ Warn = globalLogger.Warn
+ Error = globalLogger.Error
+ Critical = globalLogger.Critical
+}
+
+func RedirectStdLog(logger *Logger) {
+ zap.RedirectStdLogAt(logger.zap.With(zap.String("source", "stdlog")), zapcore.ErrorLevel)
+}
+
+type LogFunc func(string, ...Field)
+
+// 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
+func GloballyEnableDebugLogForTest() {
+ globalLogger.consoleLevel.SetLevel(zapcore.DebugLevel)
+}
+
+var Debug LogFunc
+var Info LogFunc
+var Warn LogFunc
+var Error LogFunc
+var Critical LogFunc
diff --git a/mlog/log.go b/mlog/log.go
new file mode 100644
index 000000000..801030c6c
--- /dev/null
+++ b/mlog/log.go
@@ -0,0 +1,143 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package mlog
+
+import (
+ "log"
+ "os"
+
+ "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"
+)
+
+// Type and function aliases from zap to limit the libraries scope into MM code
+type Field = zapcore.Field
+
+var Int64 = zap.Int64
+var Int = zap.Int
+var String = zap.String
+
+type LoggerConfiguration struct {
+ EnableConsole bool
+ ConsoleJson bool
+ ConsoleLevel string
+ EnableFile bool
+ FileJson bool
+ FileLevel string
+ FileLocation string
+}
+
+type Logger struct {
+ zap *zap.Logger
+ consoleLevel zap.AtomicLevel
+ fileLevel zap.AtomicLevel
+}
+
+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 NewLogger(config *LoggerConfiguration) *Logger {
+ cores := []zapcore.Core{}
+ logger := &Logger{
+ consoleLevel: zap.NewAtomicLevelAt(getZapLevel(config.ConsoleLevel)),
+ fileLevel: zap.NewAtomicLevelAt(getZapLevel(config.FileLevel)),
+ }
+
+ encoderConfig := zap.NewProductionEncoderConfig()
+ var encoder zapcore.Encoder
+ if config.ConsoleJson {
+ encoder = zapcore.NewJSONEncoder(encoderConfig)
+ } else {
+ encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
+ encoder = zapcore.NewConsoleEncoder(encoderConfig)
+ }
+
+ if config.EnableConsole {
+ writer := zapcore.Lock(os.Stdout)
+ core := zapcore.NewCore(encoder, writer, logger.consoleLevel)
+ cores = append(cores, core)
+ }
+
+ if config.EnableFile {
+ writer := zapcore.AddSync(&lumberjack.Logger{
+ Filename: config.FileLocation,
+ MaxSize: 100,
+ Compress: true,
+ })
+ core := zapcore.NewCore(encoder, writer, logger.fileLevel)
+ cores = append(cores, core)
+ }
+
+ combinedCore := zapcore.NewTee(cores...)
+
+ logger.zap = zap.New(combinedCore,
+ zap.AddCallerSkip(2),
+ 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...)
+ return &newlogger
+}
+
+func (l *Logger) StdLog(fields ...Field) *log.Logger {
+ return zap.NewStdLog(l.With(fields...).zap)
+}
+
+func (l *Logger) Debug(message string, fields ...Field) {
+ l.zap.Debug(message, fields...)
+}
+
+func (l *Logger) Info(message string, fields ...Field) {
+ l.zap.Info(message, fields...)
+}
+
+func (l *Logger) Warn(message string, fields ...Field) {
+ l.zap.Warn(message, fields...)
+}
+
+func (l *Logger) Error(message string, fields ...Field) {
+ l.zap.Error(message, fields...)
+}
+
+func (l *Logger) Critical(message string, fields ...Field) {
+ l.zap.Error(message, fields...)
+}
diff --git a/model/client.go b/model/client.go
index 317374d36..e648ca279 100644
--- a/model/client.go
+++ b/model/client.go
@@ -14,8 +14,6 @@ import (
"strconv"
"strings"
"time"
-
- l4g "github.com/alecthomas/log4go"
)
var UsedApiV3 *int32 = new(int32)
@@ -210,7 +208,7 @@ func getCookie(name string, resp *http.Response) *http.Cookie {
// Must is a convenience function used for testing.
func (c *Client) Must(result *Result, err *AppError) *Result {
if err != nil {
- l4g.Close()
+
time.Sleep(time.Second)
panic(err)
}
@@ -221,7 +219,7 @@ func (c *Client) Must(result *Result, err *AppError) *Result {
// MustGeneric is a convenience function used for testing.
func (c *Client) MustGeneric(result interface{}, err *AppError) interface{} {
if err != nil {
- l4g.Close()
+
time.Sleep(time.Second)
panic(err)
}
diff --git a/model/config.go b/model/config.go
index fb43dea42..5074b7637 100644
--- a/model/config.go
+++ b/model/config.go
@@ -585,9 +585,10 @@ func (s *SqlSettings) SetDefaults() {
type LogSettings struct {
EnableConsole bool
ConsoleLevel string
+ ConsoleJson *bool
EnableFile bool
FileLevel string
- FileFormat string
+ FileJson *bool
FileLocation string
EnableWebhookDebugging bool
EnableDiagnostics *bool
@@ -597,6 +598,14 @@ func (s *LogSettings) SetDefaults() {
if s.EnableDiagnostics == nil {
s.EnableDiagnostics = NewBool(true)
}
+
+ if s.ConsoleJson == nil {
+ s.ConsoleJson = NewBool(true)
+ }
+
+ if s.FileJson == nil {
+ s.FileJson = NewBool(true)
+ }
}
type PasswordSettings struct {
diff --git a/store/layered_store.go b/store/layered_store.go
index 5ef907260..a0a31fb39 100644
--- a/store/layered_store.go
+++ b/store/layered_store.go
@@ -6,8 +6,8 @@ package store
import (
"context"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/einterfaces"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -42,7 +42,7 @@ func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsIn
// Setup the chain
if ENABLE_EXPERIMENTAL_REDIS {
- l4g.Debug("Experimental redis enabled.")
+ mlog.Debug("Experimental redis enabled.")
store.RedisLayer = NewRedisSupplier()
store.RedisLayer.SetChainNext(store.DatabaseLayer)
store.LayerChainHead = store.RedisLayer
diff --git a/store/redis_supplier.go b/store/redis_supplier.go
index 751227be9..ce8cb0f0d 100644
--- a/store/redis_supplier.go
+++ b/store/redis_supplier.go
@@ -9,8 +9,8 @@ import (
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/go-redis/redis"
+ "github.com/mattermost/mattermost-server/mlog"
)
const REDIS_EXPIRY_TIME = 30 * time.Minute
@@ -45,7 +45,7 @@ func NewRedisSupplier() *RedisSupplier {
})
if _, err := supplier.client.Ping().Result(); err != nil {
- l4g.Error("Unable to ping redis server: " + err.Error())
+ mlog.Error("Unable to ping redis server: " + err.Error())
return nil
}
diff --git a/store/redis_supplier_reactions.go b/store/redis_supplier_reactions.go
index cece8113d..ec9a4b4e0 100644
--- a/store/redis_supplier_reactions.go
+++ b/store/redis_supplier_reactions.go
@@ -6,21 +6,20 @@ package store
import (
"context"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
func (s *RedisSupplier) ReactionSave(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
- l4g.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
+ mlog.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
}
return s.Next().ReactionSave(ctx, reaction, hints...)
}
func (s *RedisSupplier) ReactionDelete(ctx context.Context, reaction *model.Reaction, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
if err := s.client.Del("reactions:" + reaction.PostId).Err(); err != nil {
- l4g.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
+ mlog.Error("Redis failed to remove key reactions:" + reaction.PostId + " Error: " + err.Error())
}
return s.Next().ReactionDelete(ctx, reaction, hints...)
}
@@ -34,13 +33,13 @@ func (s *RedisSupplier) ReactionGetForPost(ctx context.Context, postId string, h
return result
}
if err != nil {
- l4g.Error("Redis encountered an error on read: " + err.Error())
+ mlog.Error("Redis encountered an error on read: " + err.Error())
}
result := s.Next().ReactionGetForPost(ctx, postId, hints...)
if err := s.save("reactions:"+postId, result.Data, REDIS_EXPIRY_TIME); err != nil {
- l4g.Error("Redis encountered and error on write: " + err.Error())
+ mlog.Error("Redis encountered and error on write: " + err.Error())
}
return result
diff --git a/store/redis_supplier_roles.go b/store/redis_supplier_roles.go
index 232a8c040..c53614113 100644
--- a/store/redis_supplier_roles.go
+++ b/store/redis_supplier_roles.go
@@ -7,8 +7,7 @@ import (
"context"
"fmt"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -16,7 +15,7 @@ func (s *RedisSupplier) RoleSave(ctx context.Context, role *model.Role, hints ..
key := buildRedisKeyForRoleName(role.Name)
if err := s.client.Del(key).Err(); err != nil {
- l4g.Error("Redis failed to remove key " + key + " Error: " + err.Error())
+ mlog.Error("Redis failed to remove key " + key + " Error: " + err.Error())
}
return s.Next().RoleSave(ctx, role, hints...)
@@ -34,7 +33,7 @@ func (s *RedisSupplier) RoleGetByName(ctx context.Context, name string, hints ..
var role *model.Role
found, err := s.load(key, &role)
if err != nil {
- l4g.Error("Redis encountered an error on read: " + err.Error())
+ mlog.Error("Redis encountered an error on read: " + err.Error())
} else if found {
result := NewSupplierResult()
result.Data = role
@@ -45,7 +44,7 @@ func (s *RedisSupplier) RoleGetByName(ctx context.Context, name string, hints ..
if result.Err == nil {
if err := s.save(key, result.Data, REDIS_EXPIRY_TIME); err != nil {
- l4g.Error("Redis encountered and error on write: " + err.Error())
+ mlog.Error("Redis encountered and error on write: " + err.Error())
}
}
@@ -64,7 +63,7 @@ func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string,
} else {
rolesToQuery = append(rolesToQuery, roleName)
if err != nil {
- l4g.Error("Redis encountered an error on read: " + err.Error())
+ mlog.Error("Redis encountered an error on read: " + err.Error())
}
}
}
@@ -75,7 +74,7 @@ func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string,
rolesFound := result.Data.([]*model.Role)
for _, role := range rolesFound {
if err := s.save(buildRedisKeyForRoleName(role.Name), role, REDIS_EXPIRY_TIME); err != nil {
- l4g.Error("Redis encountered and error on write: " + err.Error())
+ mlog.Error("Redis encountered and error on write: " + err.Error())
}
}
result.Data = append(foundRoles, result.Data.([]*model.Role)...)
@@ -87,10 +86,10 @@ func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string,
func (s *RedisSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
defer func() {
if keys, err := s.client.Keys("roles:*").Result(); err != nil {
- l4g.Error("Redis encountered an error on read: " + err.Error())
+ mlog.Error("Redis encountered an error on read: " + err.Error())
} else {
if err := s.client.Del(keys...).Err(); err != nil {
- l4g.Error("Redis encountered an error on delete: " + err.Error())
+ mlog.Error("Redis encountered an error on delete: " + err.Error())
}
}
}()
diff --git a/store/sqlstore/channel_member_history_store.go b/store/sqlstore/channel_member_history_store.go
index 6fc78b514..70ad16467 100644
--- a/store/sqlstore/channel_member_history_store.go
+++ b/store/sqlstore/channel_member_history_store.go
@@ -4,11 +4,12 @@
package sqlstore
import (
+ "fmt"
"net/http"
"database/sql"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
@@ -60,7 +61,7 @@ func (s SqlChannelMemberHistoryStore) LogLeaveEvent(userId string, channelId str
result.Err = model.NewAppError("SqlChannelMemberHistoryStore.LogLeaveEvent", "store.sql_channel_member_history.log_leave_event.update_error", params, err.Error(), http.StatusInternalServerError)
} else if rows, err := sqlResult.RowsAffected(); err == nil && rows != 1 {
// there was no join event to update - this is best effort, so no need to raise an error
- l4g.Warn("Channel join event for user %v and channel %v not found", userId, channelId)
+ mlog.Warn(fmt.Sprintf("Channel join event for user %v and channel %v not found", userId, channelId), mlog.String("user_id", userId))
}
})
}
diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go
index 21785c461..3bd87961a 100644
--- a/store/sqlstore/channel_store.go
+++ b/store/sqlstore/channel_store.go
@@ -11,9 +11,9 @@ import (
"strconv"
"strings"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/gorp"
"github.com/mattermost/mattermost-server/einterfaces"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -852,7 +852,7 @@ func (s SqlChannelStore) IsUserInChannelUseCache(userId string, channelId string
}
if result := <-s.GetAllChannelMembersForUser(userId, true); result.Err != nil {
- l4g.Error("SqlChannelStore.IsUserInChannelUseCache: " + result.Err.Error())
+ mlog.Error("SqlChannelStore.IsUserInChannelUseCache: " + result.Err.Error())
return false
} else {
ids := result.Data.(map[string]string)
diff --git a/store/sqlstore/command_webhook_store.go b/store/sqlstore/command_webhook_store.go
index 40fa8577c..1ea4f7328 100644
--- a/store/sqlstore/command_webhook_store.go
+++ b/store/sqlstore/command_webhook_store.go
@@ -7,8 +7,7 @@ import (
"database/sql"
"net/http"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
@@ -86,9 +85,9 @@ func (s SqlCommandWebhookStore) TryUse(id string, limit int) store.StoreChannel
}
func (s SqlCommandWebhookStore) Cleanup() {
- l4g.Debug("Cleaning up command webhook store.")
+ mlog.Debug("Cleaning up command webhook store.")
exptime := model.GetMillis() - model.COMMAND_WEBHOOK_LIFETIME
if _, err := s.GetMaster().Exec("DELETE FROM CommandWebhooks WHERE CreateAt < :ExpTime", map[string]interface{}{"ExpTime": exptime}); err != nil {
- l4g.Error("Unable to cleanup command webhook store.")
+ mlog.Error("Unable to cleanup command webhook store.")
}
}
diff --git a/store/sqlstore/post_store.go b/store/sqlstore/post_store.go
index dc7248057..75154791c 100644
--- a/store/sqlstore/post_store.go
+++ b/store/sqlstore/post_store.go
@@ -12,8 +12,8 @@ import (
"strings"
"sync"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/einterfaces"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -947,7 +947,7 @@ func (s *SqlPostStore) Search(teamId string, userId string, params *model.Search
_, err := s.GetSearchReplica().Select(&posts, searchQuery, queryParams)
if err != nil {
- l4g.Warn(utils.T("store.sql_post.search.warn"), err.Error())
+ mlog.Warn(fmt.Sprintf("Query error searching posts: %v", err.Error()))
// Don't return the error to the caller as it is of no use to the user. Instead return an empty set of search results.
} else {
for _, p := range posts {
@@ -1147,7 +1147,7 @@ func (s *SqlPostStore) GetPostsByIds(postIds []string) store.StoreChannel {
_, err := s.GetReplica().Select(&posts, query, params)
if err != nil {
- l4g.Error(err)
+ mlog.Error(fmt.Sprint(err))
result.Err = model.NewAppError("SqlPostStore.GetPostsByIds", "store.sql_post.get_posts_by_ids.app_error", nil, "", http.StatusInternalServerError)
} else {
result.Data = posts
@@ -1247,7 +1247,7 @@ func (s *SqlPostStore) determineMaxPostSize() int {
table_name = 'posts'
AND column_name = 'message'
`); err != nil {
- l4g.Error(utils.T("store.sql_post.query_max_post_size.error") + err.Error())
+ mlog.Error(utils.T("store.sql_post.query_max_post_size.error") + err.Error())
}
} else if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
// The Post.Message column in MySQL has historically been TEXT, with a maximum
@@ -1263,10 +1263,10 @@ func (s *SqlPostStore) determineMaxPostSize() int {
AND column_name = 'Message'
LIMIT 0, 1
`); err != nil {
- l4g.Error(utils.T("store.sql_post.query_max_post_size.error") + err.Error())
+ mlog.Error(utils.T("store.sql_post.query_max_post_size.error") + err.Error())
}
} else {
- l4g.Warn(utils.T("store.sql_post.query_max_post_size.unrecognized_driver"))
+ mlog.Warn("No implementation found to determine the maximum supported post size")
}
// Assume a worst-case representation of four bytes per rune.
@@ -1279,7 +1279,7 @@ func (s *SqlPostStore) determineMaxPostSize() int {
maxPostSize = model.POST_MESSAGE_MAX_RUNES_V1
}
- l4g.Info(utils.T("store.sql_post.query_max_post_size.max_post_size_bytes"), maxPostSize, maxPostSizeBytes)
+ mlog.Info(fmt.Sprintf("Post.Message supports at most %d characters (%d bytes)", maxPostSize, maxPostSizeBytes))
return maxPostSize
}
diff --git a/store/sqlstore/preference_store.go b/store/sqlstore/preference_store.go
index 791b51d2f..bfe5f6613 100644
--- a/store/sqlstore/preference_store.go
+++ b/store/sqlstore/preference_store.go
@@ -6,9 +6,9 @@ package sqlstore
import (
"net/http"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/gorp"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
@@ -38,7 +38,7 @@ func (s SqlPreferenceStore) CreateIndexesIfNotExists() {
}
func (s SqlPreferenceStore) DeleteUnusedFeatures() {
- l4g.Debug("Deleting any unused pre-release features")
+ mlog.Debug("Deleting any unused pre-release features")
sql := `DELETE
FROM Preferences
diff --git a/store/sqlstore/session_store.go b/store/sqlstore/session_store.go
index 221603865..c63abefbb 100644
--- a/store/sqlstore/session_store.go
+++ b/store/sqlstore/session_store.go
@@ -4,10 +4,11 @@
package sqlstore
import (
+ "fmt"
"net/http"
"time"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
@@ -223,7 +224,7 @@ func (me SqlSessionStore) AnalyticsSessionCount() store.StoreChannel {
}
func (me SqlSessionStore) Cleanup(expiryTime int64, batchSize int64) {
- l4g.Debug("Cleaning up session store.")
+ mlog.Debug("Cleaning up session store.")
var query string
if me.DriverName() == model.DATABASE_DRIVER_POSTGRES {
@@ -236,13 +237,13 @@ func (me SqlSessionStore) Cleanup(expiryTime int64, batchSize int64) {
for rowsAffected > 0 {
if sqlResult, err := me.GetMaster().Exec(query, map[string]interface{}{"ExpiresAt": expiryTime, "Limit": batchSize}); err != nil {
- l4g.Error("Unable to cleanup session store. err=%v", err.Error())
+ mlog.Error(fmt.Sprintf("Unable to cleanup session store. err=%v", err.Error()))
return
} else {
var rowErr error
rowsAffected, rowErr = sqlResult.RowsAffected()
if rowErr != nil {
- l4g.Error("Unable to cleanup session store. err=%v", err.Error())
+ mlog.Error(fmt.Sprintf("Unable to cleanup session store. err=%v", err.Error()))
return
}
}
diff --git a/store/sqlstore/store_test.go b/store/sqlstore/store_test.go
index 41d6a9b7e..58065d65d 100644
--- a/store/sqlstore/store_test.go
+++ b/store/sqlstore/store_test.go
@@ -8,6 +8,7 @@ import (
"sync"
"testing"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/store/storetest"
@@ -98,6 +99,15 @@ func tearDownStores() {
}
func TestMain(m *testing.M) {
+ // Setup a global logger to catch tests logging outside of app context
+ // The global logger will be stomped by apps initalizing 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()
status := 0
diff --git a/store/sqlstore/supplier.go b/store/sqlstore/supplier.go
index 8f37db0cd..69b30be59 100644
--- a/store/sqlstore/supplier.go
+++ b/store/sqlstore/supplier.go
@@ -15,11 +15,11 @@ import (
"sync/atomic"
"time"
- l4g "github.com/alecthomas/log4go"
"github.com/go-sql-driver/mysql"
"github.com/lib/pq"
"github.com/mattermost/gorp"
"github.com/mattermost/mattermost-server/einterfaces"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
@@ -144,7 +144,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
err := supplier.GetMaster().CreateTablesIfNotExists()
if err != nil {
- l4g.Critical(utils.T("store.sql.creating_tables.critical"), err)
+ mlog.Critical(fmt.Sprintf("Error creating database tables: %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_TABLE)
}
@@ -189,13 +189,13 @@ func (s *SqlSupplier) Next() store.LayeredStoreSupplier {
func setupConnection(con_type string, dataSource string, settings *model.SqlSettings) *gorp.DbMap {
db, err := dbsql.Open(*settings.DriverName, dataSource)
if err != nil {
- l4g.Critical("Failed to open SQL connection to err:%v", err.Error())
+ mlog.Critical(fmt.Sprintf("Failed to open SQL connection to err:%v", err.Error()))
time.Sleep(time.Second)
os.Exit(EXIT_DB_OPEN)
}
for i := 0; i < DB_PING_ATTEMPTS; i++ {
- l4g.Info("Pinging SQL %v database", con_type)
+ mlog.Info(fmt.Sprintf("Pinging SQL %v database", con_type))
ctx, cancel := context.WithTimeout(context.Background(), DB_PING_TIMEOUT_SECS*time.Second)
defer cancel()
err = db.PingContext(ctx)
@@ -203,11 +203,11 @@ func setupConnection(con_type string, dataSource string, settings *model.SqlSett
break
} else {
if i == DB_PING_ATTEMPTS-1 {
- l4g.Critical("Failed to ping DB, server will exit err=%v", err)
+ mlog.Critical(fmt.Sprintf("Failed to ping DB, server will exit err=%v", err))
time.Sleep(time.Second)
os.Exit(EXIT_PING)
} else {
- l4g.Error("Failed to ping DB retrying in %v seconds err=%v", DB_PING_TIMEOUT_SECS, err)
+ mlog.Error(fmt.Sprintf("Failed to ping DB retrying in %v seconds err=%v", DB_PING_TIMEOUT_SECS, err))
time.Sleep(DB_PING_TIMEOUT_SECS * time.Second)
}
}
@@ -228,7 +228,7 @@ func setupConnection(con_type string, dataSource string, settings *model.SqlSett
} else if *settings.DriverName == model.DATABASE_DRIVER_POSTGRES {
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.PostgresDialect{}, QueryTimeout: connectionTimeout}
} else {
- l4g.Critical(utils.T("store.sql.dialect_driver.critical"))
+ mlog.Critical("Failed to create dialect specific driver")
time.Sleep(time.Second)
os.Exit(EXIT_NO_DRIVER)
}
@@ -338,7 +338,7 @@ func (ss *SqlSupplier) DoesTableExist(tableName string) bool {
)
if err != nil {
- l4g.Critical(utils.T("store.sql.table_exists.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to check if table exists %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_TABLE_EXISTS)
}
@@ -360,7 +360,7 @@ func (ss *SqlSupplier) DoesTableExist(tableName string) bool {
)
if err != nil {
- l4g.Critical(utils.T("store.sql.table_exists.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to check if table exists %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_TABLE_EXISTS_MYSQL)
}
@@ -382,7 +382,7 @@ func (ss *SqlSupplier) DoesTableExist(tableName string) bool {
return count > 0
} else {
- l4g.Critical(utils.T("store.sql.column_exists_missing_driver.critical"))
+ mlog.Critical("Failed to check if column exists because of missing driver")
time.Sleep(time.Second)
os.Exit(EXIT_COLUMN_EXISTS)
return false
@@ -406,7 +406,7 @@ func (ss *SqlSupplier) DoesColumnExist(tableName string, columnName string) bool
return false
}
- l4g.Critical(utils.T("store.sql.column_exists.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to check if column exists %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_DOES_COLUMN_EXISTS_POSTGRES)
}
@@ -429,7 +429,7 @@ func (ss *SqlSupplier) DoesColumnExist(tableName string, columnName string) bool
)
if err != nil {
- l4g.Critical(utils.T("store.sql.column_exists.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to check if column exists %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_DOES_COLUMN_EXISTS_MYSQL)
}
@@ -452,7 +452,7 @@ func (ss *SqlSupplier) DoesColumnExist(tableName string, columnName string) bool
return count > 0
} else {
- l4g.Critical(utils.T("store.sql.column_exists_missing_driver.critical"))
+ mlog.Critical("Failed to check if column exists because of missing driver")
time.Sleep(time.Second)
os.Exit(EXIT_DOES_COLUMN_EXISTS_MISSING)
return false
@@ -468,7 +468,7 @@ func (ss *SqlSupplier) CreateColumnIfNotExists(tableName string, columnName stri
if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType + " DEFAULT '" + defaultValue + "'")
if err != nil {
- l4g.Critical(utils.T("store.sql.create_column.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to create column %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_COLUMN_POSTGRES)
}
@@ -478,7 +478,7 @@ func (ss *SqlSupplier) CreateColumnIfNotExists(tableName string, columnName stri
} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType + " DEFAULT '" + defaultValue + "'")
if err != nil {
- l4g.Critical(utils.T("store.sql.create_column.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to create column %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_COLUMN_MYSQL)
}
@@ -486,7 +486,7 @@ func (ss *SqlSupplier) CreateColumnIfNotExists(tableName string, columnName stri
return true
} else {
- l4g.Critical(utils.T("store.sql.create_column_missing_driver.critical"))
+ mlog.Critical("Failed to create column because of missing driver")
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_COLUMN_MISSING)
return false
@@ -501,7 +501,7 @@ func (ss *SqlSupplier) RemoveColumnIfExists(tableName string, columnName string)
_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
if err != nil {
- l4g.Critical("Failed to drop column %v", err)
+ mlog.Critical(fmt.Sprintf("Failed to drop column %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_REMOVE_COLUMN)
}
@@ -516,7 +516,7 @@ func (ss *SqlSupplier) RemoveTableIfExists(tableName string) bool {
_, err := ss.GetMaster().ExecNoTimeout("DROP TABLE " + tableName)
if err != nil {
- l4g.Critical("Failed to drop table %v", err)
+ mlog.Critical(fmt.Sprintf("Failed to drop table %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_REMOVE_TABLE)
}
@@ -537,7 +537,7 @@ func (ss *SqlSupplier) RenameColumnIfExists(tableName string, oldColumnName stri
}
if err != nil {
- l4g.Critical(utils.T("store.sql.rename_column.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to rename column %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_RENAME_COLUMN)
}
@@ -559,7 +559,7 @@ func (ss *SqlSupplier) GetMaxLengthOfColumnIfExists(tableName string, columnName
}
if err != nil {
- l4g.Critical(utils.T("store.sql.maxlength_column.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to get max length of column %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_MAX_COLUMN)
}
@@ -580,7 +580,7 @@ func (ss *SqlSupplier) AlterColumnTypeIfExists(tableName string, columnName stri
}
if err != nil {
- l4g.Critical(utils.T("store.sql.alter_column_type.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to alter column type %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_ALTER_COLUMN)
}
@@ -621,7 +621,7 @@ func (ss *SqlSupplier) createIndexIfNotExists(indexName string, tableName string
query := ""
if indexType == INDEX_TYPE_FULL_TEXT {
if len(columnNames) != 1 {
- l4g.Critical("Unable to create multi column full text index")
+ mlog.Critical("Unable to create multi column full text index")
os.Exit(EXIT_CREATE_INDEX_POSTGRES)
}
columnName := columnNames[0]
@@ -633,7 +633,7 @@ func (ss *SqlSupplier) createIndexIfNotExists(indexName string, tableName string
_, err := ss.GetMaster().ExecNoTimeout(query)
if err != nil {
- l4g.Critical("Failed to create index %v, %v", errExists, err)
+ mlog.Critical(fmt.Sprintf("Failed to create index %v, %v", errExists, err))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_INDEX_POSTGRES)
}
@@ -641,7 +641,7 @@ func (ss *SqlSupplier) createIndexIfNotExists(indexName string, tableName string
count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
if err != nil {
- l4g.Critical(utils.T("store.sql.check_index.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to check index %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_INDEX_MYSQL)
}
@@ -657,19 +657,19 @@ func (ss *SqlSupplier) createIndexIfNotExists(indexName string, tableName string
_, err = ss.GetMaster().ExecNoTimeout("CREATE " + uniqueStr + fullTextIndex + " INDEX " + indexName + " ON " + tableName + " (" + strings.Join(columnNames, ", ") + ")")
if err != nil {
- l4g.Critical("Failed to create index %v", err)
+ mlog.Critical(fmt.Sprintf("Failed to create index %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_INDEX_FULL_MYSQL)
}
} else if ss.DriverName() == model.DATABASE_DRIVER_SQLITE {
_, err := ss.GetMaster().ExecNoTimeout("CREATE INDEX IF NOT EXISTS " + indexName + " ON " + tableName + " (" + strings.Join(columnNames, ", ") + ")")
if err != nil {
- l4g.Critical("Failed to create index %v", err)
+ mlog.Critical(fmt.Sprintf("Failed to create index %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_INDEX_SQLITE)
}
} else {
- l4g.Critical(utils.T("store.sql.create_index_missing_driver.critical"))
+ mlog.Critical("Failed to create index because of missing driver")
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_INDEX_MISSING)
}
@@ -688,7 +688,7 @@ func (ss *SqlSupplier) RemoveIndexIfExists(indexName string, tableName string) b
_, err = ss.GetMaster().ExecNoTimeout("DROP INDEX " + indexName)
if err != nil {
- l4g.Critical(utils.T("store.sql.remove_index.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to remove index %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_REMOVE_INDEX_POSTGRES)
}
@@ -698,7 +698,7 @@ func (ss *SqlSupplier) RemoveIndexIfExists(indexName string, tableName string) b
count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
if err != nil {
- l4g.Critical(utils.T("store.sql.check_index.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to check index %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_REMOVE_INDEX_MYSQL)
}
@@ -709,19 +709,19 @@ func (ss *SqlSupplier) RemoveIndexIfExists(indexName string, tableName string) b
_, err = ss.GetMaster().ExecNoTimeout("DROP INDEX " + indexName + " ON " + tableName)
if err != nil {
- l4g.Critical(utils.T("store.sql.remove_index.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to remove index %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_REMOVE_INDEX_MYSQL)
}
} else if ss.DriverName() == model.DATABASE_DRIVER_SQLITE {
_, err := ss.GetMaster().ExecNoTimeout("DROP INDEX IF EXISTS " + indexName)
if err != nil {
- l4g.Critical(utils.T("store.sql.remove_index.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to remove index %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_REMOVE_INDEX_SQLITE)
}
} else {
- l4g.Critical(utils.T("store.sql.create_index_missing_driver.critical"))
+ mlog.Critical("Failed to create index because of missing driver")
time.Sleep(time.Second)
os.Exit(EXIT_REMOVE_INDEX_MISSING)
}
@@ -758,7 +758,7 @@ func (ss *SqlSupplier) GetAllConns() []*gorp.DbMap {
}
func (ss *SqlSupplier) Close() {
- l4g.Info(utils.T("store.sql.closing.info"))
+ mlog.Info("Closing SqlStore")
ss.master.Db.Close()
for _, replica := range ss.replicas {
replica.Db.Close()
diff --git a/store/sqlstore/supplier_reactions.go b/store/sqlstore/supplier_reactions.go
index aa3b078ea..5a9c9302a 100644
--- a/store/sqlstore/supplier_reactions.go
+++ b/store/sqlstore/supplier_reactions.go
@@ -5,14 +5,13 @@ package sqlstore
import (
"context"
+ "fmt"
"net/http"
- l4g "github.com/alecthomas/log4go"
-
"github.com/mattermost/gorp"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
- "github.com/mattermost/mattermost-server/utils"
)
func initSqlSupplierReactions(sqlStore SqlStore) {
@@ -136,7 +135,7 @@ func (s *SqlSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiN
for _, reaction := range reactions {
if _, err := s.GetMaster().Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY,
map[string]interface{}{"PostId": reaction.PostId, "UpdateAt": model.GetMillis()}); err != nil {
- l4g.Warn(utils.T("store.sql_reaction.delete_all_with_emoji_name.update_post.warn"), reaction.PostId, err.Error())
+ mlog.Warn(fmt.Sprintf("Unable to update Post.HasReactions while removing reactions post_id=%v, error=%v", reaction.PostId, err.Error()))
}
}
diff --git a/store/sqlstore/tokens_store.go b/store/sqlstore/tokens_store.go
index ccb58cef1..6dde1bfbf 100644
--- a/store/sqlstore/tokens_store.go
+++ b/store/sqlstore/tokens_store.go
@@ -7,8 +7,7 @@ import (
"database/sql"
"net/http"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
@@ -70,9 +69,9 @@ func (s SqlTokenStore) GetByToken(tokenString string) store.StoreChannel {
}
func (s SqlTokenStore) Cleanup() {
- l4g.Debug("Cleaning up token store.")
+ mlog.Debug("Cleaning up token store.")
deltime := model.GetMillis() - model.MAX_TOKEN_EXIPRY_TIME
if _, err := s.GetMaster().Exec("DELETE FROM Tokens WHERE CreateAt < :DelTime", map[string]interface{}{"DelTime": deltime}); err != nil {
- l4g.Error("Unable to cleanup token store.")
+ mlog.Error("Unable to cleanup token store.")
}
}
diff --git a/store/sqlstore/upgrade.go b/store/sqlstore/upgrade.go
index 059d1a866..ec2b1a1c0 100644
--- a/store/sqlstore/upgrade.go
+++ b/store/sqlstore/upgrade.go
@@ -5,14 +5,13 @@ package sqlstore
import (
"encoding/json"
+ "fmt"
"os"
"strings"
"time"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
- "github.com/mattermost/mattermost-server/utils"
)
const (
@@ -82,17 +81,17 @@ func UpgradeDatabase(sqlStore SqlStore) {
// so lets set it to the current version.
if sqlStore.GetCurrentSchemaVersion() == "" {
if result := <-sqlStore.System().SaveOrUpdate(&model.System{Name: "Version", Value: model.CurrentVersion}); result.Err != nil {
- l4g.Critical(result.Err.Error())
+ mlog.Critical(result.Err.Error())
time.Sleep(time.Second)
os.Exit(EXIT_VERSION_SAVE_MISSING)
}
- l4g.Info("The database schema has been set to version %v", model.CurrentVersion)
+ mlog.Info(fmt.Sprintf("The database schema has been set to version %v", model.CurrentVersion))
}
// If we're not on the current version then it's too old to be upgraded
if sqlStore.GetCurrentSchemaVersion() != model.CurrentVersion {
- l4g.Critical(utils.T("store.sql.schema_version.critical"), sqlStore.GetCurrentSchemaVersion(), OLDEST_SUPPORTED_VERSION, model.CurrentVersion, OLDEST_SUPPORTED_VERSION)
+ mlog.Critical(fmt.Sprintf("Database schema version %v is no longer supported. This Mattermost server supports automatic upgrades from schema version %v through schema version %v. Downgrades are not supported. Please manually upgrade to at least version %v before continuing", sqlStore.GetCurrentSchemaVersion(), OLDEST_SUPPORTED_VERSION, model.CurrentVersion, OLDEST_SUPPORTED_VERSION))
time.Sleep(time.Second)
os.Exit(EXIT_TOO_OLD)
}
@@ -100,18 +99,18 @@ func UpgradeDatabase(sqlStore SqlStore) {
func saveSchemaVersion(sqlStore SqlStore, version string) {
if result := <-sqlStore.System().Update(&model.System{Name: "Version", Value: version}); result.Err != nil {
- l4g.Critical(result.Err.Error())
+ mlog.Critical(result.Err.Error())
time.Sleep(time.Second)
os.Exit(EXIT_VERSION_SAVE)
}
- l4g.Warn(utils.T("store.sql.upgraded.warn"), version)
+ mlog.Warn(fmt.Sprintf("The database schema has been upgraded to version %v", version))
}
func shouldPerformUpgrade(sqlStore SqlStore, currentSchemaVersion string, expectedSchemaVersion string) bool {
if sqlStore.GetCurrentSchemaVersion() == currentSchemaVersion {
- l4g.Warn(utils.T("store.sql.schema_out_of_date.warn"), currentSchemaVersion)
- l4g.Warn(utils.T("store.sql.schema_upgrade_attempt.warn"), expectedSchemaVersion)
+ mlog.Warn(fmt.Sprintf("The database schema version of %v appears to be out of date", currentSchemaVersion))
+ mlog.Warn(fmt.Sprintf("Attempting to upgrade the database schema version to %v", expectedSchemaVersion))
return true
}
@@ -135,7 +134,7 @@ func UpgradeDatabaseToVersion32(sqlStore SqlStore) {
}
func themeMigrationFailed(err error) {
- l4g.Critical(utils.T("store.sql_user.migrate_theme.critical"), err)
+ mlog.Critical(fmt.Sprintf("Failed to migrate User.ThemeProps to Preferences table %v", err))
time.Sleep(time.Second)
os.Exit(EXIT_THEME_MIGRATION)
}
@@ -403,7 +402,7 @@ func UpgradeDatabaseToVersion49(sqlStore SqlStore) {
defaultTimezone := model.DefaultUserTimezone()
defaultTimezoneValue, err := json.Marshal(defaultTimezone)
if err != nil {
- l4g.Critical(err)
+ mlog.Critical(fmt.Sprint(err))
}
sqlStore.CreateColumnIfNotExists("Users", "Timezone", "varchar(256)", "varchar(256)", string(defaultTimezoneValue))
sqlStore.RemoveIndexIfExists("idx_channels_displayname", "Channels")
diff --git a/store/store.go b/store/store.go
index e64089068..0b5c9df5f 100644
--- a/store/store.go
+++ b/store/store.go
@@ -6,8 +6,6 @@ package store
import (
"time"
- l4g "github.com/alecthomas/log4go"
-
"github.com/mattermost/mattermost-server/model"
)
@@ -32,7 +30,7 @@ func Do(f func(result *StoreResult)) StoreChannel {
func Must(sc StoreChannel) interface{} {
r := <-sc
if r.Err != nil {
- l4g.Close()
+
time.Sleep(time.Second)
panic(r.Err)
}
diff --git a/store/storetest/docker.go b/store/storetest/docker.go
index cd2a3075a..f3830a6fe 100644
--- a/store/storetest/docker.go
+++ b/store/storetest/docker.go
@@ -12,8 +12,7 @@ import (
"strings"
"time"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -31,7 +30,7 @@ type RunningContainer struct {
}
func (c *RunningContainer) Stop() error {
- l4g.Info("Removing container: %v", c.Id)
+ mlog.Info(fmt.Sprintf("Removing container: %v", c.Id))
return exec.Command("docker", "rm", "-f", c.Id).Run()
}
@@ -47,7 +46,7 @@ func NewMySQLContainer() (*RunningContainer, *model.SqlSettings, error) {
if err != nil {
return nil, nil, err
}
- l4g.Info("Waiting for mysql connectivity")
+ mlog.Info("Waiting for mysql connectivity")
port := container.NetworkSettings.Ports["3306/tcp"][0].HostPort
if err := waitForPort(port); err != nil {
container.Stop()
@@ -66,7 +65,7 @@ func NewPostgreSQLContainer() (*RunningContainer, *model.SqlSettings, error) {
if err != nil {
return nil, nil, err
}
- l4g.Info("Waiting for postgres connectivity")
+ mlog.Info("Waiting for postgres connectivity")
port := container.NetworkSettings.Ports["5432/tcp"][0].HostPort
if err := waitForPort(port); err != nil {
container.Stop()
@@ -111,7 +110,7 @@ func runContainer(args []string) (*RunningContainer, error) {
exec.Command("docker", "rm", "-f", id).Run()
return nil, err
}
- l4g.Info("Running container: %v", id)
+ mlog.Info(fmt.Sprintf("Running container: %v", id))
return &RunningContainer{containers[0]}, nil
}
diff --git a/utils/config.go b/utils/config.go
index 51b7ea003..34cd0ed9f 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -15,7 +15,6 @@ import (
"strconv"
"strings"
- l4g "github.com/alecthomas/log4go"
"github.com/fsnotify/fsnotify"
"github.com/pkg/errors"
"github.com/spf13/viper"
@@ -23,6 +22,7 @@ import (
"net/http"
"github.com/mattermost/mattermost-server/einterfaces"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils/jsonutils"
)
@@ -32,8 +32,6 @@ const (
LOG_FILENAME = "mattermost.log"
)
-var originalDisableDebugLvl l4g.Level = l4g.DEBUG
-
// FindConfigFile attempts to find an existing configuration file. fileName can be an absolute or
// relative path or name such as "/opt/mattermost/config.json" or simply "config.json". An empty
// string is returned if no configuration is found.
@@ -66,71 +64,26 @@ func FindDir(dir string) (string, bool) {
return "./", false
}
-func DisableDebugLogForTest() {
- if l4g.Global["stdout"] != nil {
- originalDisableDebugLvl = l4g.Global["stdout"].Level
- l4g.Global["stdout"].Level = l4g.ERROR
+func MloggerConfigFromLoggerConfig(s *model.LogSettings) *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: GetLogFileLocation(s.FileLocation),
}
}
-func EnableDebugLogForTest() {
- if l4g.Global["stdout"] != nil {
- l4g.Global["stdout"].Level = originalDisableDebugLvl
- }
-}
-
-func ConfigureCmdLineLog() {
- ls := model.LogSettings{}
- ls.EnableConsole = true
- ls.ConsoleLevel = "WARN"
- ConfigureLog(&ls)
+// DON'T USE THIS Modify the level on the app logger
+func DisableDebugLogForTest() {
+ mlog.GloballyDisableDebugLogForTest()
}
-// ConfigureLog enables and configures logging.
-//
-// Note that it is not currently possible to disable filters nor to modify previously enabled
-// filters, given the lack of concurrency guarantees from the underlying l4g library.
-//
-// TODO: this code initializes console and file logging. It will eventually be replaced by JSON logging in logger/logger.go
-// See PLT-3893 for more information
-func ConfigureLog(s *model.LogSettings) {
- if _, alreadySet := l4g.Global["stdout"]; !alreadySet && s.EnableConsole {
- level := l4g.DEBUG
- if s.ConsoleLevel == "INFO" {
- level = l4g.INFO
- } else if s.ConsoleLevel == "WARN" {
- level = l4g.WARNING
- } else if s.ConsoleLevel == "ERROR" {
- level = l4g.ERROR
- }
-
- lw := l4g.NewConsoleLogWriter()
- lw.SetFormat("[%D %T] [%L] %M")
- l4g.AddFilter("stdout", level, lw)
- }
-
- if _, alreadySet := l4g.Global["file"]; !alreadySet && s.EnableFile {
- var fileFormat = s.FileFormat
-
- if fileFormat == "" {
- fileFormat = "[%D %T] [%L] %M"
- }
-
- level := l4g.DEBUG
- if s.FileLevel == "INFO" {
- level = l4g.INFO
- } else if s.FileLevel == "WARN" {
- level = l4g.WARNING
- } else if s.FileLevel == "ERROR" {
- level = l4g.ERROR
- }
-
- flw := l4g.NewFileLogWriter(GetLogFileLocation(s.FileLocation), false)
- flw.SetFormat(fileFormat)
- flw.SetRotate(true)
- flw.SetRotateLines(LOG_ROTATE_SIZE)
- l4g.AddFilter("file", level, flw)
- }
+// DON'T USE THIS Modify the level on the app logger
+func EnableDebugLogForTest() {
+ mlog.GloballyEnableDebugLogForTest()
}
func GetLogFileLocation(fileLocation string) string {
@@ -189,17 +142,17 @@ func NewConfigWatcher(cfgFileName string, f func()) (*ConfigWatcher, error) {
// we only care about the config file
if filepath.Clean(event.Name) == configFile {
if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
- l4g.Info(fmt.Sprintf("Config file watcher detected a change reloading %v", cfgFileName))
+ mlog.Info(fmt.Sprintf("Config file watcher detected a change reloading %v", cfgFileName))
if _, _, configReadErr := ReadConfigFile(cfgFileName, true); configReadErr == nil {
f()
} else {
- l4g.Error(fmt.Sprintf("Failed to read while watching config file at %v with err=%v", cfgFileName, configReadErr.Error()))
+ mlog.Error(fmt.Sprintf("Failed to read while watching config file at %v with err=%v", cfgFileName, configReadErr.Error()))
}
}
}
case err := <-watcher.Errors:
- l4g.Error(fmt.Sprintf("Failed while watching config file at %v with err=%v", cfgFileName, err.Error()))
+ mlog.Error(fmt.Sprintf("Failed while watching config file at %v with err=%v", cfgFileName, err.Error()))
case <-ret.close:
return
}
@@ -278,7 +231,7 @@ func newViper(allowEnvironmentOverrides bool) *viper.Viper {
func structToMap(t reflect.Type) (out map[string]interface{}) {
defer func() {
if r := recover(); r != nil {
- l4g.Error("Panicked in structToMap. This should never happen. %v", r)
+ mlog.Error(fmt.Sprintf("Panicked in structToMap. This should never happen. %v", r))
}
}()
@@ -345,7 +298,7 @@ func flattenStructToMap(in map[string]interface{}) map[string]interface{} {
func fixEnvSettingsCase(in map[string]interface{}) (out map[string]interface{}, err error) {
defer func() {
if r := recover(); r != nil {
- l4g.Error("Panicked in fixEnvSettingsCase. This should never happen. %v", r)
+ mlog.Error(fmt.Sprintf("Panicked in fixEnvSettingsCase. This should never happen. %v", r))
out = in
}
}()
@@ -450,13 +403,13 @@ func LoadConfig(fileName string) (*model.Config, string, map[string]interface{},
if needSave {
if err := SaveConfig(configPath, config); err != nil {
- l4g.Warn(err.Error())
+ mlog.Warn(err.Error())
}
}
if err := ValidateLocales(config); err != nil {
if err := SaveConfig(configPath, config); err != nil {
- l4g.Warn(err.Error())
+ mlog.Warn(err.Error())
}
}
diff --git a/utils/config_test.go b/utils/config_test.go
index 11b110367..ec66a30f0 100644
--- a/utils/config_test.go
+++ b/utils/config_test.go
@@ -19,9 +19,8 @@ import (
func TestConfig(t *testing.T) {
TranslationsPreInit()
- cfg, _, _, err := LoadConfig("config.json")
+ _, _, _, err := LoadConfig("config.json")
require.Nil(t, err)
- InitTranslations(cfg.LocalizationSettings)
}
func TestReadConfig(t *testing.T) {
diff --git a/utils/file_backend_local.go b/utils/file_backend_local.go
index 1367ccc1e..f85ace55a 100644
--- a/utils/file_backend_local.go
+++ b/utils/file_backend_local.go
@@ -9,8 +9,7 @@ import (
"os"
"path/filepath"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -28,7 +27,7 @@ func (b *LocalFileBackend) TestConnection() *model.AppError {
return model.NewAppError("TestFileConnection", "Don't have permissions to write to local path specified or other error.", nil, err.Error(), http.StatusInternalServerError)
}
os.Remove(filepath.Join(b.directory, TEST_FILE_PATH))
- l4g.Info("Able to write files to local storage.")
+ mlog.Info("Able to write files to local storage.")
return nil
}
diff --git a/utils/file_backend_s3.go b/utils/file_backend_s3.go
index 75282897f..2f644f602 100644
--- a/utils/file_backend_s3.go
+++ b/utils/file_backend_s3.go
@@ -11,10 +11,10 @@ import (
"path/filepath"
"strings"
- l4g "github.com/alecthomas/log4go"
s3 "github.com/minio/minio-go"
"github.com/minio/minio-go/pkg/credentials"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -70,14 +70,14 @@ func (b *S3FileBackend) TestConnection() *model.AppError {
}
if !exists {
- l4g.Warn("Bucket specified does not exist. Attempting to create...")
+ mlog.Warn("Bucket specified does not exist. Attempting to create...")
err := s3Clnt.MakeBucket(b.bucket, b.region)
if err != nil {
- l4g.Error("Unable to create bucket.")
+ mlog.Error("Unable to create bucket.")
return model.NewAppError("TestFileConnection", "Unable to create bucket", nil, err.Error(), http.StatusInternalServerError)
}
}
- l4g.Info("Connection to S3 or minio is good. Bucket exists.")
+ mlog.Info("Connection to S3 or minio is good. Bucket exists.")
return nil
}
diff --git a/utils/file_backend_test.go b/utils/file_backend_test.go
index 2b8e2a527..047e9df62 100644
--- a/utils/file_backend_test.go
+++ b/utils/file_backend_test.go
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -23,6 +24,15 @@ type FileBackendTestSuite struct {
}
func TestLocalFileBackendTestSuite(t *testing.T) {
+ // Setup a global logger to catch tests logging outside of app context
+ // The global logger will be stomped by apps initalizing 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,
+ }))
+
dir, err := ioutil.TempDir("", "")
require.NoError(t, err)
defer os.RemoveAll(dir)
diff --git a/utils/html.go b/utils/html.go
index f9a7abe5b..0de33435d 100644
--- a/utils/html.go
+++ b/utils/html.go
@@ -6,14 +6,15 @@ package utils
import (
"bytes"
"errors"
+ "fmt"
"html/template"
"io"
"path/filepath"
"reflect"
"sync/atomic"
- l4g "github.com/alecthomas/log4go"
"github.com/fsnotify/fsnotify"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/nicksnyder/go-i18n/i18n"
)
@@ -25,7 +26,7 @@ type HTMLTemplateWatcher struct {
func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) {
templatesDir, _ := FindDir(directory)
- l4g.Debug("Parsing server templates at %v", templatesDir)
+ mlog.Debug(fmt.Sprintf("Parsing server templates at %v", templatesDir))
ret := &HTMLTemplateWatcher{
stop: make(chan struct{}),
@@ -57,15 +58,15 @@ func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) {
return
case event := <-watcher.Events:
if event.Op&fsnotify.Write == fsnotify.Write {
- l4g.Info("Re-parsing templates because of modified file %v", event.Name)
+ mlog.Info(fmt.Sprintf("Re-parsing templates because of modified file %v", event.Name))
if htmlTemplates, err := template.ParseGlob(filepath.Join(templatesDir, "*.html")); err != nil {
- l4g.Error("Failed to parse templates %v", err)
+ mlog.Error(fmt.Sprintf("Failed to parse templates %v", err))
} else {
ret.templates.Store(htmlTemplates)
}
}
case err := <-watcher.Errors:
- l4g.Error("Failed in directory watcher %s", err)
+ mlog.Error(fmt.Sprintf("Failed in directory watcher %s", err))
}
}
}()
@@ -110,7 +111,7 @@ func (t *HTMLTemplate) RenderToWriter(w io.Writer) error {
}
if err := t.Templates.ExecuteTemplate(w, t.TemplateName, t); err != nil {
- l4g.Error(T("api.api.render.error"), t.TemplateName, err)
+ mlog.Error(fmt.Sprintf("Error rendering template %v err=%v", t.TemplateName, err))
return err
}
@@ -134,7 +135,7 @@ func escapeForHtml(arg interface{}) interface{} {
}
return safeArg
default:
- l4g.Warn("Unable to escape value for HTML template %v of type %v", arg, reflect.ValueOf(arg).Type())
+ mlog.Warn(fmt.Sprintf("Unable to escape value for HTML template %v of type %v", arg, reflect.ValueOf(arg).Type()))
return ""
}
}
diff --git a/utils/i18n.go b/utils/i18n.go
index 72704c241..d7c55e4e6 100644
--- a/utils/i18n.go
+++ b/utils/i18n.go
@@ -10,7 +10,7 @@ import (
"path/filepath"
"strings"
- l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/nicksnyder/go-i18n/i18n"
)
@@ -67,7 +67,7 @@ func InitTranslationsWithDir(dir string) error {
func GetTranslationsBySystemLocale() (i18n.TranslateFunc, error) {
locale := *settings.DefaultServerLocale
if _, ok := locales[locale]; !ok {
- l4g.Error("Failed to load system translations for '%v' attempting to fall back to '%v'", locale, model.DEFAULT_LOCALE)
+ mlog.Error(fmt.Sprintf("Failed to load system translations for '%v' attempting to fall back to '%v'", locale, model.DEFAULT_LOCALE))
locale = model.DEFAULT_LOCALE
}
@@ -80,7 +80,7 @@ func GetTranslationsBySystemLocale() (i18n.TranslateFunc, error) {
return nil, fmt.Errorf("Failed to load system translations")
}
- l4g.Info(translations("utils.i18n.loaded"), locale, locales[locale])
+ mlog.Info(fmt.Sprintf("Loaded system translations for '%v' from '%v'", locale, locales[locale]))
return translations, nil
}
diff --git a/utils/license.go b/utils/license.go
index cf874b62b..aa89026ea 100644
--- a/utils/license.go
+++ b/utils/license.go
@@ -10,14 +10,14 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/pem"
+ "fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -36,12 +36,12 @@ func ValidateLicense(signed []byte) (bool, string) {
_, err := base64.StdEncoding.Decode(decoded, signed)
if err != nil {
- l4g.Error(T("utils.license.validate_license.decode.error"), err.Error())
+ mlog.Error(fmt.Sprintf("Encountered error decoding license, err=%v", err.Error()))
return false, ""
}
if len(decoded) <= 256 {
- l4g.Error(T("utils.license.validate_license.not_long.error"))
+ mlog.Error("Signed license not long enough")
return false, ""
}
@@ -57,7 +57,7 @@ func ValidateLicense(signed []byte) (bool, string) {
public, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
- l4g.Error(T("utils.license.validate_license.signing.error"), err.Error())
+ mlog.Error(fmt.Sprintf("Encountered error signing license, err=%v", err.Error()))
return false, ""
}
@@ -69,7 +69,7 @@ func ValidateLicense(signed []byte) (bool, string) {
err = rsa.VerifyPKCS1v15(rsaPublic, crypto.SHA512, d, signature)
if err != nil {
- l4g.Error(T("utils.license.validate_license.invalid.error"), err.Error())
+ mlog.Error(fmt.Sprintf("Invalid signature, err=%v", err.Error()))
return false, ""
}
@@ -80,15 +80,15 @@ func GetAndValidateLicenseFileFromDisk(location string) (*model.License, []byte)
fileName := GetLicenseFileLocation(location)
if _, err := os.Stat(fileName); err != nil {
- l4g.Debug("We could not find the license key in the database or on disk at %v", fileName)
+ mlog.Debug(fmt.Sprintf("We could not find the license key in the database or on disk at %v", fileName))
return nil, nil
}
- l4g.Info("License key has not been uploaded. Loading license key from disk at %v", fileName)
+ mlog.Info(fmt.Sprintf("License key has not been uploaded. Loading license key from disk at %v", fileName))
licenseBytes := GetLicenseFileFromDisk(fileName)
if success, licenseStr := ValidateLicense(licenseBytes); !success {
- l4g.Error("Found license key at %v but it appears to be invalid.", fileName)
+ mlog.Error(fmt.Sprintf("Found license key at %v but it appears to be invalid.", fileName))
return nil, nil
} else {
return model.LicenseFromJson(strings.NewReader(licenseStr)), licenseBytes
@@ -98,14 +98,14 @@ func GetAndValidateLicenseFileFromDisk(location string) (*model.License, []byte)
func GetLicenseFileFromDisk(fileName string) []byte {
file, err := os.Open(fileName)
if err != nil {
- l4g.Error("Failed to open license key from disk at %v err=%v", fileName, err.Error())
+ mlog.Error(fmt.Sprintf("Failed to open license key from disk at %v err=%v", fileName, err.Error()))
return nil
}
defer file.Close()
licenseBytes, err := ioutil.ReadAll(file)
if err != nil {
- l4g.Error("Failed to read license key from disk at %v err=%v", fileName, err.Error())
+ mlog.Error(fmt.Sprintf("Failed to read license key from disk at %v err=%v", fileName, err.Error()))
return nil
}
diff --git a/utils/mail.go b/utils/mail.go
index ee5a8dd6f..119ca0674 100644
--- a/utils/mail.go
+++ b/utils/mail.go
@@ -6,6 +6,7 @@ package utils
import (
"crypto/tls"
"errors"
+ "fmt"
"io"
"mime"
"net"
@@ -17,8 +18,8 @@ import (
"net/http"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/html2text"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -128,14 +129,14 @@ func ConnectToSMTPServer(config *model.Config) (net.Conn, *model.AppError) {
func NewSMTPClientAdvanced(conn net.Conn, hostname string, connectionInfo *SmtpConnectionInfo) (*smtp.Client, *model.AppError) {
c, err := smtp.NewClient(conn, connectionInfo.SmtpServerName+":"+connectionInfo.SmtpPort)
if err != nil {
- l4g.Error(T("utils.mail.new_client.open.error"), err)
+ mlog.Error(fmt.Sprintf("Failed to open a connection to SMTP server %v", err))
return nil, model.NewAppError("SendMail", "utils.mail.connect_smtp.open_tls.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if hostname != "" {
err := c.Hello(hostname)
if err != nil {
- l4g.Error(T("utils.mail.new_client.helo.error"), err)
+ mlog.Error(fmt.Sprintf("Failed to to set the HELO to SMTP server %v", err))
return nil, model.NewAppError("SendMail", "utils.mail.connect_smtp.helo.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
@@ -180,14 +181,14 @@ func TestConnection(config *model.Config) {
conn, err1 := ConnectToSMTPServer(config)
if err1 != nil {
- l4g.Error(T("utils.mail.test.configured.error"), T(err1.Message), err1.DetailedError)
+ mlog.Error(fmt.Sprintf("SMTP server settings do not appear to be configured properly err=%v details=%v", T(err1.Message), err1.DetailedError))
return
}
defer conn.Close()
c, err2 := NewSMTPClient(conn, config)
if err2 != nil {
- l4g.Error(T("utils.mail.test.configured.error"), T(err2.Message), err2.DetailedError)
+ mlog.Error(fmt.Sprintf("SMTP server settings do not appear to be configured properly err=%v details=%v", T(err2.Message), err2.DetailedError))
return
}
defer c.Quit()
@@ -228,13 +229,13 @@ func SendMailUsingConfigAdvanced(mimeTo, smtpTo string, from mail.Address, subje
}
func SendMail(c *smtp.Client, mimeTo, smtpTo string, from mail.Address, subject, htmlBody string, attachments []*model.FileInfo, mimeHeaders map[string]string, fileBackend FileBackend, date time.Time) *model.AppError {
- l4g.Debug(T("utils.mail.send_mail.sending.debug"), mimeTo, subject)
+ mlog.Debug(fmt.Sprintf("sending mail to %v with subject of '%v'", mimeTo, subject))
htmlMessage := "\r\n<html><body>" + htmlBody + "</body></html>"
txtBody, err := html2text.FromString(htmlBody)
if err != nil {
- l4g.Warn(err)
+ mlog.Warn(fmt.Sprint(err))
txtBody = ""
}
diff --git a/utils/redirect_std_log.go b/utils/redirect_std_log.go
deleted file mode 100644
index 4fbfcf8ec..000000000
--- a/utils/redirect_std_log.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package utils
-
-import (
- "bufio"
- "log"
- "os"
- "strings"
-
- l4g "github.com/alecthomas/log4go"
-)
-
-type RedirectStdLog struct {
- reader *os.File
- writer *os.File
- system string
- ignoreDebug bool
-}
-
-func NewRedirectStdLog(system string, ignoreDebug bool) *log.Logger {
- r, w, _ := os.Pipe()
- logger := &RedirectStdLog{
- reader: r,
- writer: w,
- system: system,
- ignoreDebug: ignoreDebug,
- }
-
- go func(l *RedirectStdLog) {
- scanner := bufio.NewScanner(l.reader)
- for scanner.Scan() {
- line := scanner.Text()
-
- if strings.Index(line, "[DEBUG]") == 0 {
- if !ignoreDebug {
- l4g.Debug("%v%v", system, line[7:])
- }
- } else if strings.Index(line, "[DEBG]") == 0 {
- if !ignoreDebug {
- l4g.Debug("%v%v", system, line[6:])
- }
- } else if strings.Index(line, "[WARN]") == 0 {
- l4g.Info("%v%v", system, line[6:])
- } else if strings.Index(line, "[ERROR]") == 0 {
- l4g.Error("%v%v", system, line[7:])
- } else if strings.Index(line, "[EROR]") == 0 {
- l4g.Error("%v%v", system, line[6:])
- } else if strings.Index(line, "[ERR]") == 0 {
- l4g.Error("%v%v", system, line[5:])
- } else if strings.Index(line, "[INFO]") == 0 {
- l4g.Info("%v%v", system, line[6:])
- } else {
- l4g.Info("%v %v", system, line)
- }
- }
- }(logger)
-
- return log.New(logger.writer, "", 0)
-}
-
-func (l *RedirectStdLog) Write(p []byte) (n int, err error) {
- return l.writer.Write(p)
-}
diff --git a/utils/redirect_std_log_test.go b/utils/redirect_std_log_test.go
deleted file mode 100644
index cbe55c921..000000000
--- a/utils/redirect_std_log_test.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package utils
-
-import (
- "testing"
- "time"
-)
-
-func TestRedirectStdLog(t *testing.T) {
- log := NewRedirectStdLog("test", false)
-
- log.Println("[DEBUG] this is a message")
- log.Println("[DEBG] this is a message")
- log.Println("[WARN] this is a message")
- log.Println("[ERROR] this is a message")
- log.Println("[EROR] this is a message")
- log.Println("[ERR] this is a message")
- log.Println("[INFO] this is a message")
- log.Println("this is a message")
-
- time.Sleep(time.Second * 1)
-}
diff --git a/vendor/github.com/alecthomas/log4go/.gitignore b/vendor/github.com/alecthomas/log4go/.gitignore
deleted file mode 100644
index f6207cd8a..000000000
--- a/vendor/github.com/alecthomas/log4go/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.sw[op]
-.DS_Store
diff --git a/vendor/github.com/alecthomas/log4go/LICENSE b/vendor/github.com/alecthomas/log4go/LICENSE
deleted file mode 100644
index 7093402bf..000000000
--- a/vendor/github.com/alecthomas/log4go/LICENSE
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright (c) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/alecthomas/log4go/README b/vendor/github.com/alecthomas/log4go/README
deleted file mode 100644
index 3361567f7..000000000
--- a/vendor/github.com/alecthomas/log4go/README
+++ /dev/null
@@ -1,14 +0,0 @@
-# This is an unmaintained fork, left only so it doesn't break imports.
-
-Please see http://log4go.googlecode.com/
-
-Installation:
-- Run `goinstall log4go.googlecode.com/hg`
-
-Usage:
-- Add the following import:
-import l4g "log4go.googlecode.com/hg"
-
-Acknowledgements:
-- pomack
- For providing awesome patches to bring log4go up to the latest Go spec
diff --git a/vendor/github.com/alecthomas/log4go/config.go b/vendor/github.com/alecthomas/log4go/config.go
deleted file mode 100644
index 577c3eb2f..000000000
--- a/vendor/github.com/alecthomas/log4go/config.go
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
-
-package log4go
-
-import (
- "encoding/xml"
- "fmt"
- "io/ioutil"
- "os"
- "strconv"
- "strings"
-)
-
-type xmlProperty struct {
- Name string `xml:"name,attr"`
- Value string `xml:",chardata"`
-}
-
-type xmlFilter struct {
- Enabled string `xml:"enabled,attr"`
- Tag string `xml:"tag"`
- Level string `xml:"level"`
- Type string `xml:"type"`
- Property []xmlProperty `xml:"property"`
-}
-
-type xmlLoggerConfig struct {
- Filter []xmlFilter `xml:"filter"`
-}
-
-// Load XML configuration; see examples/example.xml for documentation
-func (log Logger) LoadConfiguration(filename string) {
- log.Close()
-
- // Open the configuration file
- fd, err := os.Open(filename)
- if err != nil {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
- os.Exit(1)
- }
-
- contents, err := ioutil.ReadAll(fd)
- if err != nil {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
- os.Exit(1)
- }
-
- xc := new(xmlLoggerConfig)
- if err := xml.Unmarshal(contents, xc); err != nil {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
- os.Exit(1)
- }
-
- for _, xmlfilt := range xc.Filter {
- var filt LogWriter
- var lvl Level
- bad, good, enabled := false, true, false
-
- // Check required children
- if len(xmlfilt.Enabled) == 0 {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
- bad = true
- } else {
- enabled = xmlfilt.Enabled != "false"
- }
- if len(xmlfilt.Tag) == 0 {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
- bad = true
- }
- if len(xmlfilt.Type) == 0 {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
- bad = true
- }
- if len(xmlfilt.Level) == 0 {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
- bad = true
- }
-
- switch xmlfilt.Level {
- case "FINEST":
- lvl = FINEST
- case "FINE":
- lvl = FINE
- case "DEBUG":
- lvl = DEBUG
- case "TRACE":
- lvl = TRACE
- case "INFO":
- lvl = INFO
- case "WARNING":
- lvl = WARNING
- case "ERROR":
- lvl = ERROR
- case "CRITICAL":
- lvl = CRITICAL
- default:
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
- bad = true
- }
-
- // Just so all of the required attributes are errored at the same time if missing
- if bad {
- os.Exit(1)
- }
-
- switch xmlfilt.Type {
- case "console":
- filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
- case "file":
- filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
- case "xml":
- filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
- case "socket":
- filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
- default:
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
- os.Exit(1)
- }
-
- // Just so all of the required params are errored at the same time if wrong
- if !good {
- os.Exit(1)
- }
-
- // If we're disabled (syntax and correctness checks only), don't add to logger
- if !enabled {
- continue
- }
-
- log[xmlfilt.Tag] = &Filter{lvl, filt}
- }
-}
-
-func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (*ConsoleLogWriter, bool) {
- // Parse properties
- for _, prop := range props {
- switch prop.Name {
- default:
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
- }
- }
-
- // If it's disabled, we're just checking syntax
- if !enabled {
- return nil, true
- }
-
- return NewConsoleLogWriter(), true
-}
-
-// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
-func strToNumSuffix(str string, mult int) int {
- num := 1
- if len(str) > 1 {
- switch str[len(str)-1] {
- case 'G', 'g':
- num *= mult
- fallthrough
- case 'M', 'm':
- num *= mult
- fallthrough
- case 'K', 'k':
- num *= mult
- str = str[0 : len(str)-1]
- }
- }
- parsed, _ := strconv.Atoi(str)
- return parsed * num
-}
-func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
- file := ""
- format := "[%D %T] [%L] (%S) %M"
- maxlines := 0
- maxsize := 0
- daily := false
- rotate := false
-
- // Parse properties
- for _, prop := range props {
- switch prop.Name {
- case "filename":
- file = strings.Trim(prop.Value, " \r\n")
- case "format":
- format = strings.Trim(prop.Value, " \r\n")
- case "maxlines":
- maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
- case "maxsize":
- maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
- case "daily":
- daily = strings.Trim(prop.Value, " \r\n") != "false"
- case "rotate":
- rotate = strings.Trim(prop.Value, " \r\n") != "false"
- default:
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
- }
- }
-
- // Check properties
- if len(file) == 0 {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
- return nil, false
- }
-
- // If it's disabled, we're just checking syntax
- if !enabled {
- return nil, true
- }
-
- flw := NewFileLogWriter(file, rotate)
- flw.SetFormat(format)
- flw.SetRotateLines(maxlines)
- flw.SetRotateSize(maxsize)
- flw.SetRotateDaily(daily)
- return flw, true
-}
-
-func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
- file := ""
- maxrecords := 0
- maxsize := 0
- daily := false
- rotate := false
-
- // Parse properties
- for _, prop := range props {
- switch prop.Name {
- case "filename":
- file = strings.Trim(prop.Value, " \r\n")
- case "maxrecords":
- maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
- case "maxsize":
- maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
- case "daily":
- daily = strings.Trim(prop.Value, " \r\n") != "false"
- case "rotate":
- rotate = strings.Trim(prop.Value, " \r\n") != "false"
- default:
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename)
- }
- }
-
- // Check properties
- if len(file) == 0 {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename)
- return nil, false
- }
-
- // If it's disabled, we're just checking syntax
- if !enabled {
- return nil, true
- }
-
- xlw := NewXMLLogWriter(file, rotate)
- xlw.SetRotateLines(maxrecords)
- xlw.SetRotateSize(maxsize)
- xlw.SetRotateDaily(daily)
- return xlw, true
-}
-
-func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) {
- endpoint := ""
- protocol := "udp"
-
- // Parse properties
- for _, prop := range props {
- switch prop.Name {
- case "endpoint":
- endpoint = strings.Trim(prop.Value, " \r\n")
- case "protocol":
- protocol = strings.Trim(prop.Value, " \r\n")
- default:
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
- }
- }
-
- // Check properties
- if len(endpoint) == 0 {
- fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename)
- return nil, false
- }
-
- // If it's disabled, we're just checking syntax
- if !enabled {
- return nil, true
- }
-
- return NewSocketLogWriter(protocol, endpoint), true
-}
diff --git a/vendor/github.com/alecthomas/log4go/filelog.go b/vendor/github.com/alecthomas/log4go/filelog.go
deleted file mode 100644
index a5ae87809..000000000
--- a/vendor/github.com/alecthomas/log4go/filelog.go
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
-
-package log4go
-
-import (
- "bytes"
- "fmt"
- "io"
- "os"
- "time"
-)
-
-// This log writer sends output to a file
-type FileLogWriter struct {
- rec chan *LogRecord
- rot chan bool
- done chan bool
-
- // The opened file
- filename string
- file *os.File
-
- // The logging format
- format string
-
- // File header/trailer
- header, trailer string
-
- // Rotate at linecount
- maxlines int
- maxlines_curlines int
-
- // Rotate at size
- maxsize int
- maxsize_cursize int
-
- // Rotate daily
- daily bool
- daily_opendate int
-
- // Keep old logfiles (.001, .002, etc)
- rotate bool
- maxbackup int
-}
-
-// This is the FileLogWriter's output method
-func (w *FileLogWriter) LogWrite(rec *LogRecord) {
- w.rec <- rec
-}
-
-func (w *FileLogWriter) Close() {
- close(w.rec)
- <-w.done
-}
-
-// NewFileLogWriter creates a new LogWriter which writes to the given file and
-// has rotation enabled if rotate is true.
-//
-// If rotate is true, any time a new log file is opened, the old one is renamed
-// with a .### extension to preserve it. The various Set* methods can be used
-// to configure log rotation based on lines, size, and daily.
-//
-// The standard log-line format is:
-// [%D %T] [%L] (%S) %M
-func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
- w := &FileLogWriter{
- rec: make(chan *LogRecord, LogBufferLength),
- rot: make(chan bool),
- done: make(chan bool),
- filename: fname,
- format: "[%D %T] [%L] (%S) %M",
- rotate: rotate,
- maxbackup: 999,
- }
-
- // open the file for the first time
- if err := w.intRotate(); err != nil {
- fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
- return nil
- }
-
- go func() {
- defer func() {
- if w.file != nil {
- fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
- w.file.Sync()
- w.file.Close()
- }
- close(w.done)
- }()
-
- for {
- select {
- case <-w.rot:
- if err := w.intRotate(); err != nil {
- fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
- return
- }
- case rec, ok := <-w.rec:
- if !ok {
- return
- }
- now := time.Now()
- if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
- (w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
- (w.daily && now.Day() != w.daily_opendate) {
- if err := w.intRotate(); err != nil {
- fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
- return
- }
- }
-
- // Perform the write
- n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
- if err != nil {
- fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
- return
- }
-
- // Update the counts
- w.maxlines_curlines++
- w.maxsize_cursize += n
- }
- }
- }()
-
- return w
-}
-
-// Request that the logs rotate
-func (w *FileLogWriter) Rotate() {
- w.rot <- true
-}
-
-// If this is called in a threaded context, it MUST be synchronized
-func (w *FileLogWriter) intRotate() error {
- // Close any log file that may be open
- if w.file != nil {
- fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
- w.file.Close()
- }
-
- // If we are keeping log files, move it to the next available number
- if w.rotate {
- _, err := os.Lstat(w.filename)
- if err == nil { // file exists
- // Find the next available number
- num := 1
- fname := ""
- if w.daily && time.Now().Day() != w.daily_opendate {
- yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
-
- for ; err == nil && num <= 999; num++ {
- fname = w.filename + fmt.Sprintf(".%s.%03d", yesterday, num)
- _, err = os.Lstat(fname)
- }
- // return error if the last file checked still existed
- if err == nil {
- return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
- }
- } else {
- num = w.maxbackup - 1
- for ; num >= 1; num-- {
- fname = w.filename + fmt.Sprintf(".%d", num)
- nfname := w.filename + fmt.Sprintf(".%d", num+1)
- _, err = os.Lstat(fname)
- if err == nil {
- os.Rename(fname, nfname)
- }
- }
- }
-
- w.file.Close()
- // Rename the file to its newfound home
- err = os.Rename(w.filename, fname)
- if err != nil {
- return fmt.Errorf("Rotate: %s\n", err)
- }
- }
- }
-
- lineCount := 0
- byteCount := 0
-
- // If the file exists, open for reading and set our line/byte counts
- // On failure, just assume the file doesn't exist
- if fd, err := os.OpenFile(w.filename, os.O_RDONLY, 0440); err == nil {
- lineCount, _ = lineCounter(fd)
-
- fi, err := fd.Stat()
- if err == nil {
- byteCount = int(fi.Size())
- }
-
- fd.Close()
- }
-
- fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
- if err != nil {
- return err
- }
- w.file = fd
-
- now := time.Now()
- fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))
-
- // Set the daily open date to the current date
- w.daily_opendate = now.Day()
-
- // initialize rotation values
- w.maxlines_curlines = lineCount
- w.maxsize_cursize = byteCount
-
- return nil
-}
-
-// Taken from https://stackoverflow.com/a/24563853
-func lineCounter(r io.Reader) (int, error) {
- buf := make([]byte, 32*1024)
- count := 0
- lineSep := []byte{'\n'}
-
- for {
- c, err := r.Read(buf)
- count += bytes.Count(buf[:c], lineSep)
-
- switch {
- case err == io.EOF:
- return count, nil
-
- case err != nil:
- return count, err
- }
- }
-}
-
-// Set the logging format (chainable). Must be called before the first log
-// message is written.
-func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
- w.format = format
- return w
-}
-
-// Set the logfile header and footer (chainable). Must be called before the first log
-// message is written. These are formatted similar to the FormatLogRecord (e.g.
-// you can use %D and %T in your header/footer for date and time).
-func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
- w.header, w.trailer = head, foot
- if w.maxlines_curlines == 0 {
- fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
- }
- return w
-}
-
-// Set rotate at linecount (chainable). Must be called before the first log
-// message is written.
-func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
- //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
- w.maxlines = maxlines
- return w
-}
-
-// Set rotate at size (chainable). Must be called before the first log message
-// is written.
-func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
- //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
- w.maxsize = maxsize
- return w
-}
-
-// Set rotate daily (chainable). Must be called before the first log message is
-// written.
-func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
- //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
- w.daily = daily
- return w
-}
-
-// Set max backup files. Must be called before the first log message
-// is written.
-func (w *FileLogWriter) SetRotateMaxBackup(maxbackup int) *FileLogWriter {
- w.maxbackup = maxbackup
- return w
-}
-
-// SetRotate changes whether or not the old logs are kept. (chainable) Must be
-// called before the first log message is written. If rotate is false, the
-// files are overwritten; otherwise, they are rotated to another file before the
-// new log is opened.
-func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
- //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
- w.rotate = rotate
- return w
-}
-
-// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
-// output XML record log messages instead of line-based ones.
-func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
- return NewFileLogWriter(fname, rotate).SetFormat(
- ` <record level="%L">
- <timestamp>%D %T</timestamp>
- <source>%S</source>
- <message>%M</message>
- </record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
-}
diff --git a/vendor/github.com/alecthomas/log4go/log4go.go b/vendor/github.com/alecthomas/log4go/log4go.go
deleted file mode 100644
index 822e890cc..000000000
--- a/vendor/github.com/alecthomas/log4go/log4go.go
+++ /dev/null
@@ -1,484 +0,0 @@
-// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
-
-// Package log4go provides level-based and highly configurable logging.
-//
-// Enhanced Logging
-//
-// This is inspired by the logging functionality in Java. Essentially, you create a Logger
-// object and create output filters for it. You can send whatever you want to the Logger,
-// and it will filter that based on your settings and send it to the outputs. This way, you
-// can put as much debug code in your program as you want, and when you're done you can filter
-// out the mundane messages so only the important ones show up.
-//
-// Utility functions are provided to make life easier. Here is some example code to get started:
-//
-// log := log4go.NewLogger()
-// log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter())
-// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
-// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
-//
-// The first two lines can be combined with the utility NewDefaultLogger:
-//
-// log := log4go.NewDefaultLogger(log4go.DEBUG)
-// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
-// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
-//
-// Usage notes:
-// - The ConsoleLogWriter does not display the source of the message to standard
-// output, but the FileLogWriter does.
-// - The utility functions (Info, Debug, Warn, etc) derive their source from the
-// calling function, and this incurs extra overhead.
-//
-// Changes from 2.0:
-// - The external interface has remained mostly stable, but a lot of the
-// internals have been changed, so if you depended on any of this or created
-// your own LogWriter, then you will probably have to update your code. In
-// particular, Logger is now a map and ConsoleLogWriter is now a channel
-// behind-the-scenes, and the LogWrite method no longer has return values.
-//
-// Future work: (please let me know if you think I should work on any of these particularly)
-// - Log file rotation
-// - Logging configuration files ala log4j
-// - Have the ability to remove filters?
-// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows
-// for another method of logging
-// - Add an XML filter type
-package log4go
-
-import (
- "errors"
- "fmt"
- "os"
- "runtime"
- "strings"
- "time"
-)
-
-// Version information
-const (
- L4G_VERSION = "log4go-v3.0.1"
- L4G_MAJOR = 3
- L4G_MINOR = 0
- L4G_BUILD = 1
-)
-
-/****** Constants ******/
-
-// These are the integer logging levels used by the logger
-type Level int
-
-const (
- FINEST Level = iota
- FINE
- DEBUG
- TRACE
- INFO
- WARNING
- ERROR
- CRITICAL
-)
-
-// Logging level strings
-var (
- levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
-)
-
-func (l Level) String() string {
- if l < 0 || int(l) > len(levelStrings) {
- return "UNKNOWN"
- }
- return levelStrings[int(l)]
-}
-
-/****** Variables ******/
-var (
- // LogBufferLength specifies how many log messages a particular log4go
- // logger can buffer at a time before writing them.
- LogBufferLength = 32
-)
-
-/****** LogRecord ******/
-
-// A LogRecord contains all of the pertinent information for each message
-type LogRecord struct {
- Level Level // The log level
- Created time.Time // The time at which the log message was created (nanoseconds)
- Source string // The message source
- Message string // The log message
-}
-
-/****** LogWriter ******/
-
-// This is an interface for anything that should be able to write logs
-type LogWriter interface {
- // This will be called to log a LogRecord message.
- LogWrite(rec *LogRecord)
-
- // This should clean up anything lingering about the LogWriter, as it is called before
- // the LogWriter is removed. LogWrite should not be called after Close.
- Close()
-}
-
-/****** Logger ******/
-
-// A Filter represents the log level below which no log records are written to
-// the associated LogWriter.
-type Filter struct {
- Level Level
- LogWriter
-}
-
-// A Logger represents a collection of Filters through which log messages are
-// written.
-type Logger map[string]*Filter
-
-// Create a new logger.
-//
-// DEPRECATED: Use make(Logger) instead.
-func NewLogger() Logger {
- os.Stderr.WriteString("warning: use of deprecated NewLogger\n")
- return make(Logger)
-}
-
-// Create a new logger with a "stdout" filter configured to send log messages at
-// or above lvl to standard output.
-//
-// DEPRECATED: use NewDefaultLogger instead.
-func NewConsoleLogger(lvl Level) Logger {
- os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n")
- return Logger{
- "stdout": &Filter{lvl, NewConsoleLogWriter()},
- }
-}
-
-// Create a new logger with a "stdout" filter configured to send log messages at
-// or above lvl to standard output.
-func NewDefaultLogger(lvl Level) Logger {
- return Logger{
- "stdout": &Filter{lvl, NewConsoleLogWriter()},
- }
-}
-
-// Closes all log writers in preparation for exiting the program or a
-// reconfiguration of logging. Calling this is not really imperative, unless
-// you want to guarantee that all log messages are written. Close removes
-// all filters (and thus all LogWriters) from the logger.
-func (log Logger) Close() {
- // Close all open loggers
- for name, filt := range log {
- filt.Close()
- delete(log, name)
- }
-}
-
-// Add a new LogWriter to the Logger which will only log messages at lvl or
-// higher. This function should not be called from multiple goroutines.
-// Returns the logger for chaining.
-func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger {
- log[name] = &Filter{lvl, writer}
- return log
-}
-
-/******* Logging *******/
-// Send a formatted log message internally
-func (log Logger) intLogf(lvl Level, format string, args ...interface{}) {
- skip := true
-
- // Determine if any logging will be done
- for _, filt := range log {
- if lvl >= filt.Level {
- skip = false
- break
- }
- }
- if skip {
- return
- }
-
- // Determine caller func
- pc, _, lineno, ok := runtime.Caller(2)
- src := ""
- if ok {
- src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
- }
-
- msg := format
- if len(args) > 0 {
- msg = fmt.Sprintf(format, args...)
- }
-
- // Make the log record
- rec := &LogRecord{
- Level: lvl,
- Created: time.Now(),
- Source: src,
- Message: msg,
- }
-
- // Dispatch the logs
- for _, filt := range log {
- if lvl < filt.Level {
- continue
- }
- filt.LogWrite(rec)
- }
-}
-
-// Send a closure log message internally
-func (log Logger) intLogc(lvl Level, closure func() string) {
- skip := true
-
- // Determine if any logging will be done
- for _, filt := range log {
- if lvl >= filt.Level {
- skip = false
- break
- }
- }
- if skip {
- return
- }
-
- // Determine caller func
- pc, _, lineno, ok := runtime.Caller(2)
- src := ""
- if ok {
- src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
- }
-
- // Make the log record
- rec := &LogRecord{
- Level: lvl,
- Created: time.Now(),
- Source: src,
- Message: closure(),
- }
-
- // Dispatch the logs
- for _, filt := range log {
- if lvl < filt.Level {
- continue
- }
- filt.LogWrite(rec)
- }
-}
-
-// Send a log message with manual level, source, and message.
-func (log Logger) Log(lvl Level, source, message string) {
- skip := true
-
- // Determine if any logging will be done
- for _, filt := range log {
- if lvl >= filt.Level {
- skip = false
- break
- }
- }
- if skip {
- return
- }
-
- // Make the log record
- rec := &LogRecord{
- Level: lvl,
- Created: time.Now(),
- Source: source,
- Message: message,
- }
-
- // Dispatch the logs
- for _, filt := range log {
- if lvl < filt.Level {
- continue
- }
- filt.LogWrite(rec)
- }
-}
-
-// Logf logs a formatted log message at the given log level, using the caller as
-// its source.
-func (log Logger) Logf(lvl Level, format string, args ...interface{}) {
- log.intLogf(lvl, format, args...)
-}
-
-// Logc logs a string returned by the closure at the given log level, using the caller as
-// its source. If no log message would be written, the closure is never called.
-func (log Logger) Logc(lvl Level, closure func() string) {
- log.intLogc(lvl, closure)
-}
-
-// Finest logs a message at the finest log level.
-// See Debug for an explanation of the arguments.
-func (log Logger) Finest(arg0 interface{}, args ...interface{}) {
- const (
- lvl = FINEST
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- log.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- log.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Fine logs a message at the fine log level.
-// See Debug for an explanation of the arguments.
-func (log Logger) Fine(arg0 interface{}, args ...interface{}) {
- const (
- lvl = FINE
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- log.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- log.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Debug is a utility method for debug log messages.
-// The behavior of Debug depends on the first argument:
-// - arg0 is a string
-// When given a string as the first argument, this behaves like Logf but with
-// the DEBUG log level: the first argument is interpreted as a format for the
-// latter arguments.
-// - arg0 is a func()string
-// When given a closure of type func()string, this logs the string returned by
-// the closure iff it will be logged. The closure runs at most one time.
-// - arg0 is interface{}
-// When given anything else, the log message will be each of the arguments
-// formatted with %v and separated by spaces (ala Sprint).
-func (log Logger) Debug(arg0 interface{}, args ...interface{}) {
- const (
- lvl = DEBUG
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- log.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- log.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Trace logs a message at the trace log level.
-// See Debug for an explanation of the arguments.
-func (log Logger) Trace(arg0 interface{}, args ...interface{}) {
- const (
- lvl = TRACE
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- log.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- log.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Info logs a message at the info log level.
-// See Debug for an explanation of the arguments.
-func (log Logger) Info(arg0 interface{}, args ...interface{}) {
- const (
- lvl = INFO
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- log.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- log.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Warn logs a message at the warning log level and returns the formatted error.
-// At the warning level and higher, there is no performance benefit if the
-// message is not actually logged, because all formats are processed and all
-// closures are executed to format the error message.
-// See Debug for further explanation of the arguments.
-func (log Logger) Warn(arg0 interface{}, args ...interface{}) error {
- const (
- lvl = WARNING
- )
- var msg string
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- msg = fmt.Sprintf(first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- msg = first()
- default:
- // Build a format string so that it will be similar to Sprint
- msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
- }
- log.intLogf(lvl, msg)
- return errors.New(msg)
-}
-
-// Error logs a message at the error log level and returns the formatted error,
-// See Warn for an explanation of the performance and Debug for an explanation
-// of the parameters.
-func (log Logger) Error(arg0 interface{}, args ...interface{}) error {
- const (
- lvl = ERROR
- )
- var msg string
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- msg = fmt.Sprintf(first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- msg = first()
- default:
- // Build a format string so that it will be similar to Sprint
- msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
- }
- log.intLogf(lvl, msg)
- return errors.New(msg)
-}
-
-// Critical logs a message at the critical log level and returns the formatted error,
-// See Warn for an explanation of the performance and Debug for an explanation
-// of the parameters.
-func (log Logger) Critical(arg0 interface{}, args ...interface{}) error {
- const (
- lvl = CRITICAL
- )
- var msg string
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- msg = fmt.Sprintf(first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- msg = first()
- default:
- // Build a format string so that it will be similar to Sprint
- msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
- }
- log.intLogf(lvl, msg)
- return errors.New(msg)
-}
diff --git a/vendor/github.com/alecthomas/log4go/pattlog.go b/vendor/github.com/alecthomas/log4go/pattlog.go
deleted file mode 100644
index 98632e4da..000000000
--- a/vendor/github.com/alecthomas/log4go/pattlog.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
-
-package log4go
-
-import (
- "bytes"
- "fmt"
- "io"
- "strings"
- "sync"
-)
-
-const (
- FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M"
- FORMAT_SHORT = "[%t %d] [%L] %M"
- FORMAT_ABBREV = "[%L] %M"
-)
-
-type formatCacheType struct {
- LastUpdateSeconds int64
- shortTime, shortDate string
- longTime, longDate string
-}
-
-var formatCache = &formatCacheType{}
-var mutex sync.Mutex
-
-// Known format codes:
-// %T - Time (15:04:05 MST)
-// %t - Time (15:04)
-// %D - Date (2006/01/02)
-// %d - Date (01/02/06)
-// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-// %S - Source
-// %M - Message
-// Ignores unknown formats
-// Recommended: "[%D %T] [%L] (%S) %M"
-func FormatLogRecord(format string, rec *LogRecord) string {
- if rec == nil {
- return "<nil>"
- }
- if len(format) == 0 {
- return ""
- }
-
- out := bytes.NewBuffer(make([]byte, 0, 64))
- secs := rec.Created.UnixNano() / 1e9
-
- mutex.Lock()
- cache := *formatCache
- if cache.LastUpdateSeconds != secs {
- month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year()
- hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second()
- zone, _ := rec.Created.Zone()
- updated := &formatCacheType{
- LastUpdateSeconds: secs,
- shortTime: fmt.Sprintf("%02d:%02d", hour, minute),
- shortDate: fmt.Sprintf("%02d/%02d/%02d", day, month, year%100),
- longTime: fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone),
- longDate: fmt.Sprintf("%04d/%02d/%02d", year, month, day),
- }
- cache = *updated
- formatCache = updated
- }
- mutex.Unlock()
-
- // Split the string into pieces by % signs
- pieces := bytes.Split([]byte(format), []byte{'%'})
-
- // Iterate over the pieces, replacing known formats
- for i, piece := range pieces {
- if i > 0 && len(piece) > 0 {
- switch piece[0] {
- case 'T':
- out.WriteString(cache.longTime)
- case 't':
- out.WriteString(cache.shortTime)
- case 'D':
- out.WriteString(cache.longDate)
- case 'd':
- out.WriteString(cache.shortDate)
- case 'L':
- out.WriteString(levelStrings[rec.Level])
- case 'S':
- out.WriteString(rec.Source)
- case 's':
- slice := strings.Split(rec.Source, "/")
- out.WriteString(slice[len(slice)-1])
- case 'M':
- out.WriteString(rec.Message)
- }
- if len(piece) > 1 {
- out.Write(piece[1:])
- }
- } else if len(piece) > 0 {
- out.Write(piece)
- }
- }
- out.WriteByte('\n')
-
- return out.String()
-}
-
-// This is the standard writer that prints to standard output.
-type FormatLogWriter chan *LogRecord
-
-// This creates a new FormatLogWriter
-func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter {
- records := make(FormatLogWriter, LogBufferLength)
- go records.run(out, format)
- return records
-}
-
-func (w FormatLogWriter) run(out io.Writer, format string) {
- for rec := range w {
- fmt.Fprint(out, FormatLogRecord(format, rec))
- }
-}
-
-// This is the FormatLogWriter's output method. This will block if the output
-// buffer is full.
-func (w FormatLogWriter) LogWrite(rec *LogRecord) {
- w <- rec
-}
-
-// Close stops the logger from sending messages to standard output. Attempts to
-// send log messages to this logger after a Close have undefined behavior.
-func (w FormatLogWriter) Close() {
- close(w)
-}
diff --git a/vendor/github.com/alecthomas/log4go/socklog.go b/vendor/github.com/alecthomas/log4go/socklog.go
deleted file mode 100644
index 1d224a99d..000000000
--- a/vendor/github.com/alecthomas/log4go/socklog.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
-
-package log4go
-
-import (
- "encoding/json"
- "fmt"
- "net"
- "os"
-)
-
-// This log writer sends output to a socket
-type SocketLogWriter chan *LogRecord
-
-// This is the SocketLogWriter's output method
-func (w SocketLogWriter) LogWrite(rec *LogRecord) {
- w <- rec
-}
-
-func (w SocketLogWriter) Close() {
- close(w)
-}
-
-func NewSocketLogWriter(proto, hostport string) SocketLogWriter {
- sock, err := net.Dial(proto, hostport)
- if err != nil {
- fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err)
- return nil
- }
-
- w := SocketLogWriter(make(chan *LogRecord, LogBufferLength))
-
- go func() {
- defer func() {
- if sock != nil && proto == "tcp" {
- sock.Close()
- }
- }()
-
- for rec := range w {
- // Marshall into JSON
- js, err := json.Marshal(rec)
- if err != nil {
- fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
- return
- }
-
- _, err = sock.Write(js)
- if err != nil {
- fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
- return
- }
- }
- }()
-
- return w
-}
diff --git a/vendor/github.com/alecthomas/log4go/termlog.go b/vendor/github.com/alecthomas/log4go/termlog.go
deleted file mode 100644
index 8a941e269..000000000
--- a/vendor/github.com/alecthomas/log4go/termlog.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
-
-package log4go
-
-import (
- "fmt"
- "io"
- "os"
- "time"
-)
-
-var stdout io.Writer = os.Stdout
-
-// This is the standard writer that prints to standard output.
-type ConsoleLogWriter struct {
- format string
- w chan *LogRecord
-}
-
-// This creates a new ConsoleLogWriter
-func NewConsoleLogWriter() *ConsoleLogWriter {
- consoleWriter := &ConsoleLogWriter{
- format: "[%T %D] [%L] (%S) %M",
- w: make(chan *LogRecord, LogBufferLength),
- }
- go consoleWriter.run(stdout)
- return consoleWriter
-}
-func (c *ConsoleLogWriter) SetFormat(format string) {
- c.format = format
-}
-func (c *ConsoleLogWriter) run(out io.Writer) {
- for rec := range c.w {
- fmt.Fprint(out, FormatLogRecord(c.format, rec))
- }
-}
-
-// This is the ConsoleLogWriter's output method. This will block if the output
-// buffer is full.
-func (c *ConsoleLogWriter) LogWrite(rec *LogRecord) {
- c.w <- rec
-}
-
-// Close stops the logger from sending messages to standard output. Attempts to
-// send log messages to this logger after a Close have undefined behavior.
-func (c *ConsoleLogWriter) Close() {
- close(c.w)
- time.Sleep(50 * time.Millisecond) // Try to give console I/O time to complete
-}
diff --git a/vendor/github.com/alecthomas/log4go/wrapper.go b/vendor/github.com/alecthomas/log4go/wrapper.go
deleted file mode 100644
index 2ae222b0c..000000000
--- a/vendor/github.com/alecthomas/log4go/wrapper.go
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
-
-package log4go
-
-import (
- "errors"
- "fmt"
- "os"
- "strings"
-)
-
-var (
- Global Logger
-)
-
-func init() {
- Global = NewDefaultLogger(DEBUG)
-}
-
-// Wrapper for (*Logger).LoadConfiguration
-func LoadConfiguration(filename string) {
- Global.LoadConfiguration(filename)
-}
-
-// Wrapper for (*Logger).AddFilter
-func AddFilter(name string, lvl Level, writer LogWriter) {
- Global.AddFilter(name, lvl, writer)
-}
-
-// Wrapper for (*Logger).Close (closes and removes all logwriters)
-func Close() {
- Global.Close()
-}
-
-func Crash(args ...interface{}) {
- if len(args) > 0 {
- Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...)
- }
- panic(args)
-}
-
-// Logs the given message and crashes the program
-func Crashf(format string, args ...interface{}) {
- Global.intLogf(CRITICAL, format, args...)
- Global.Close() // so that hopefully the messages get logged
- panic(fmt.Sprintf(format, args...))
-}
-
-// Compatibility with `log`
-func Exit(args ...interface{}) {
- if len(args) > 0 {
- Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
- }
- Global.Close() // so that hopefully the messages get logged
- os.Exit(0)
-}
-
-// Compatibility with `log`
-func Exitf(format string, args ...interface{}) {
- Global.intLogf(ERROR, format, args...)
- Global.Close() // so that hopefully the messages get logged
- os.Exit(0)
-}
-
-// Compatibility with `log`
-func Stderr(args ...interface{}) {
- if len(args) > 0 {
- Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
- }
-}
-
-// Compatibility with `log`
-func Stderrf(format string, args ...interface{}) {
- Global.intLogf(ERROR, format, args...)
-}
-
-// Compatibility with `log`
-func Stdout(args ...interface{}) {
- if len(args) > 0 {
- Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...)
- }
-}
-
-// Compatibility with `log`
-func Stdoutf(format string, args ...interface{}) {
- Global.intLogf(INFO, format, args...)
-}
-
-// Send a log message manually
-// Wrapper for (*Logger).Log
-func Log(lvl Level, source, message string) {
- Global.Log(lvl, source, message)
-}
-
-// Send a formatted log message easily
-// Wrapper for (*Logger).Logf
-func Logf(lvl Level, format string, args ...interface{}) {
- Global.intLogf(lvl, format, args...)
-}
-
-// Send a closure log message
-// Wrapper for (*Logger).Logc
-func Logc(lvl Level, closure func() string) {
- Global.intLogc(lvl, closure)
-}
-
-// Utility for finest log messages (see Debug() for parameter explanation)
-// Wrapper for (*Logger).Finest
-func Finest(arg0 interface{}, args ...interface{}) {
- const (
- lvl = FINEST
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- Global.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- Global.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Utility for fine log messages (see Debug() for parameter explanation)
-// Wrapper for (*Logger).Fine
-func Fine(arg0 interface{}, args ...interface{}) {
- const (
- lvl = FINE
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- Global.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- Global.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Utility for debug log messages
-// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments)
-// When given a closure of type func()string, this logs the string returned by the closure iff it will be logged. The closure runs at most one time.
-// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint).
-// Wrapper for (*Logger).Debug
-func Debug(arg0 interface{}, args ...interface{}) {
- const (
- lvl = DEBUG
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- Global.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- Global.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Utility for trace log messages (see Debug() for parameter explanation)
-// Wrapper for (*Logger).Trace
-func Trace(arg0 interface{}, args ...interface{}) {
- const (
- lvl = TRACE
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- Global.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- Global.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Utility for info log messages (see Debug() for parameter explanation)
-// Wrapper for (*Logger).Info
-func Info(arg0 interface{}, args ...interface{}) {
- const (
- lvl = INFO
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- Global.intLogf(lvl, first, args...)
- case func() string:
- // Log the closure (no other arguments used)
- Global.intLogc(lvl, first)
- default:
- // Build a format string so that it will be similar to Sprint
- Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
- }
-}
-
-// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
-// These functions will execute a closure exactly once, to build the error message for the return
-// Wrapper for (*Logger).Warn
-func Warn(arg0 interface{}, args ...interface{}) error {
- const (
- lvl = WARNING
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- Global.intLogf(lvl, first, args...)
- return errors.New(fmt.Sprintf(first, args...))
- case func() string:
- // Log the closure (no other arguments used)
- str := first()
- Global.intLogf(lvl, "%s", str)
- return errors.New(str)
- default:
- // Build a format string so that it will be similar to Sprint
- Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
- return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
- }
- return nil
-}
-
-// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
-// These functions will execute a closure exactly once, to build the error message for the return
-// Wrapper for (*Logger).Error
-func Error(arg0 interface{}, args ...interface{}) error {
- const (
- lvl = ERROR
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- Global.intLogf(lvl, first, args...)
- return errors.New(fmt.Sprintf(first, args...))
- case func() string:
- // Log the closure (no other arguments used)
- str := first()
- Global.intLogf(lvl, "%s", str)
- return errors.New(str)
- default:
- // Build a format string so that it will be similar to Sprint
- Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
- return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
- }
- return nil
-}
-
-// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
-// These functions will execute a closure exactly once, to build the error message for the return
-// Wrapper for (*Logger).Critical
-func Critical(arg0 interface{}, args ...interface{}) error {
- const (
- lvl = CRITICAL
- )
- switch first := arg0.(type) {
- case string:
- // Use the string as a format string
- Global.intLogf(lvl, first, args...)
- return errors.New(fmt.Sprintf(first, args...))
- case func() string:
- // Log the closure (no other arguments used)
- str := first()
- Global.intLogf(lvl, "%s", str)
- return errors.New(str)
- default:
- // Build a format string so that it will be similar to Sprint
- Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
- return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
- }
- return nil
-}
diff --git a/vendor/go.uber.org/atomic/.codecov.yml b/vendor/go.uber.org/atomic/.codecov.yml
new file mode 100644
index 000000000..6d4d1be7b
--- /dev/null
+++ b/vendor/go.uber.org/atomic/.codecov.yml
@@ -0,0 +1,15 @@
+coverage:
+ range: 80..100
+ round: down
+ precision: 2
+
+ status:
+ project: # measuring the overall project coverage
+ default: # context, you can create multiple ones with custom titles
+ enabled: yes # must be yes|true to enable this status
+ target: 100 # specify the target coverage for each commit status
+ # option: "auto" (must increase from parent commit or pull request base)
+ # option: "X%" a static target percentage to hit
+ if_not_found: success # if parent is not found report status as success, error, or failure
+ if_ci_failed: error # if ci fails report status as success, error, or failure
+
diff --git a/vendor/go.uber.org/atomic/.gitignore b/vendor/go.uber.org/atomic/.gitignore
new file mode 100644
index 000000000..0a4504f11
--- /dev/null
+++ b/vendor/go.uber.org/atomic/.gitignore
@@ -0,0 +1,11 @@
+.DS_Store
+/vendor
+/cover
+cover.out
+lint.log
+
+# Binaries
+*.test
+
+# Profiling output
+*.prof
diff --git a/vendor/go.uber.org/atomic/.travis.yml b/vendor/go.uber.org/atomic/.travis.yml
new file mode 100644
index 000000000..58957222a
--- /dev/null
+++ b/vendor/go.uber.org/atomic/.travis.yml
@@ -0,0 +1,23 @@
+sudo: false
+language: go
+go_import_path: go.uber.org/atomic
+
+go:
+ - 1.7
+ - 1.8
+ - 1.9
+
+cache:
+ directories:
+ - vendor
+
+install:
+ - make install_ci
+
+script:
+ - make test_ci
+ - scripts/test-ubergo.sh
+ - make lint
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/go.uber.org/atomic/LICENSE.txt b/vendor/go.uber.org/atomic/LICENSE.txt
new file mode 100644
index 000000000..8765c9fbc
--- /dev/null
+++ b/vendor/go.uber.org/atomic/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2016 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/go.uber.org/atomic/Makefile b/vendor/go.uber.org/atomic/Makefile
new file mode 100644
index 000000000..dfc63d9db
--- /dev/null
+++ b/vendor/go.uber.org/atomic/Makefile
@@ -0,0 +1,64 @@
+PACKAGES := $(shell glide nv)
+# Many Go tools take file globs or directories as arguments instead of packages.
+PACKAGE_FILES ?= *.go
+
+
+# The linting tools evolve with each Go version, so run them only on the latest
+# stable release.
+GO_VERSION := $(shell go version | cut -d " " -f 3)
+GO_MINOR_VERSION := $(word 2,$(subst ., ,$(GO_VERSION)))
+LINTABLE_MINOR_VERSIONS := 7 8
+ifneq ($(filter $(LINTABLE_MINOR_VERSIONS),$(GO_MINOR_VERSION)),)
+SHOULD_LINT := true
+endif
+
+
+export GO15VENDOREXPERIMENT=1
+
+
+.PHONY: build
+build:
+ go build -i $(PACKAGES)
+
+
+.PHONY: install
+install:
+ glide --version || go get github.com/Masterminds/glide
+ glide install
+
+
+.PHONY: test
+test:
+ go test -cover -race $(PACKAGES)
+
+
+.PHONY: install_ci
+install_ci: install
+ go get github.com/wadey/gocovmerge
+ go get github.com/mattn/goveralls
+ go get golang.org/x/tools/cmd/cover
+ifdef SHOULD_LINT
+ go get github.com/golang/lint/golint
+endif
+
+.PHONY: lint
+lint:
+ifdef SHOULD_LINT
+ @rm -rf lint.log
+ @echo "Checking formatting..."
+ @gofmt -d -s $(PACKAGE_FILES) 2>&1 | tee lint.log
+ @echo "Checking vet..."
+ @$(foreach dir,$(PACKAGE_FILES),go tool vet $(dir) 2>&1 | tee -a lint.log;)
+ @echo "Checking lint..."
+ @$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;)
+ @echo "Checking for unresolved FIXMEs..."
+ @git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log
+ @[ ! -s lint.log ]
+else
+ @echo "Skipping linters on" $(GO_VERSION)
+endif
+
+
+.PHONY: test_ci
+test_ci: install_ci build
+ ./scripts/cover.sh $(shell go list $(PACKAGES))
diff --git a/vendor/go.uber.org/atomic/README.md b/vendor/go.uber.org/atomic/README.md
new file mode 100644
index 000000000..6505abf65
--- /dev/null
+++ b/vendor/go.uber.org/atomic/README.md
@@ -0,0 +1,36 @@
+# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard]
+
+Simple wrappers for primitive types to enforce atomic access.
+
+## Installation
+`go get -u go.uber.org/atomic`
+
+## Usage
+The standard library's `sync/atomic` is powerful, but it's easy to forget which
+variables must be accessed atomically. `go.uber.org/atomic` preserves all the
+functionality of the standard library, but wraps the primitive types to
+provide a safer, more convenient API.
+
+```go
+var atom atomic.Uint32
+atom.Store(42)
+atom.Sub(2)
+atom.CAS(40, 11)
+```
+
+See the [documentation][doc] for a complete API specification.
+
+## Development Status
+Stable.
+
+<hr>
+Released under the [MIT License](LICENSE.txt).
+
+[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
+[doc]: https://godoc.org/go.uber.org/atomic
+[ci-img]: https://travis-ci.org/uber-go/atomic.svg?branch=master
+[ci]: https://travis-ci.org/uber-go/atomic
+[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
+[cov]: https://codecov.io/gh/uber-go/atomic
+[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic
+[reportcard]: https://goreportcard.com/report/go.uber.org/atomic
diff --git a/vendor/go.uber.org/atomic/atomic.go b/vendor/go.uber.org/atomic/atomic.go
new file mode 100644
index 000000000..1ca50dc34
--- /dev/null
+++ b/vendor/go.uber.org/atomic/atomic.go
@@ -0,0 +1,309 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package atomic provides simple wrappers around numerics to enforce atomic
+// access.
+package atomic
+
+import (
+ "math"
+ "sync/atomic"
+)
+
+// Int32 is an atomic wrapper around an int32.
+type Int32 struct{ v int32 }
+
+// NewInt32 creates an Int32.
+func NewInt32(i int32) *Int32 {
+ return &Int32{i}
+}
+
+// Load atomically loads the wrapped value.
+func (i *Int32) Load() int32 {
+ return atomic.LoadInt32(&i.v)
+}
+
+// Add atomically adds to the wrapped int32 and returns the new value.
+func (i *Int32) Add(n int32) int32 {
+ return atomic.AddInt32(&i.v, n)
+}
+
+// Sub atomically subtracts from the wrapped int32 and returns the new value.
+func (i *Int32) Sub(n int32) int32 {
+ return atomic.AddInt32(&i.v, -n)
+}
+
+// Inc atomically increments the wrapped int32 and returns the new value.
+func (i *Int32) Inc() int32 {
+ return i.Add(1)
+}
+
+// Dec atomically decrements the wrapped int32 and returns the new value.
+func (i *Int32) Dec() int32 {
+ return i.Sub(1)
+}
+
+// CAS is an atomic compare-and-swap.
+func (i *Int32) CAS(old, new int32) bool {
+ return atomic.CompareAndSwapInt32(&i.v, old, new)
+}
+
+// Store atomically stores the passed value.
+func (i *Int32) Store(n int32) {
+ atomic.StoreInt32(&i.v, n)
+}
+
+// Swap atomically swaps the wrapped int32 and returns the old value.
+func (i *Int32) Swap(n int32) int32 {
+ return atomic.SwapInt32(&i.v, n)
+}
+
+// Int64 is an atomic wrapper around an int64.
+type Int64 struct{ v int64 }
+
+// NewInt64 creates an Int64.
+func NewInt64(i int64) *Int64 {
+ return &Int64{i}
+}
+
+// Load atomically loads the wrapped value.
+func (i *Int64) Load() int64 {
+ return atomic.LoadInt64(&i.v)
+}
+
+// Add atomically adds to the wrapped int64 and returns the new value.
+func (i *Int64) Add(n int64) int64 {
+ return atomic.AddInt64(&i.v, n)
+}
+
+// Sub atomically subtracts from the wrapped int64 and returns the new value.
+func (i *Int64) Sub(n int64) int64 {
+ return atomic.AddInt64(&i.v, -n)
+}
+
+// Inc atomically increments the wrapped int64 and returns the new value.
+func (i *Int64) Inc() int64 {
+ return i.Add(1)
+}
+
+// Dec atomically decrements the wrapped int64 and returns the new value.
+func (i *Int64) Dec() int64 {
+ return i.Sub(1)
+}
+
+// CAS is an atomic compare-and-swap.
+func (i *Int64) CAS(old, new int64) bool {
+ return atomic.CompareAndSwapInt64(&i.v, old, new)
+}
+
+// Store atomically stores the passed value.
+func (i *Int64) Store(n int64) {
+ atomic.StoreInt64(&i.v, n)
+}
+
+// Swap atomically swaps the wrapped int64 and returns the old value.
+func (i *Int64) Swap(n int64) int64 {
+ return atomic.SwapInt64(&i.v, n)
+}
+
+// Uint32 is an atomic wrapper around an uint32.
+type Uint32 struct{ v uint32 }
+
+// NewUint32 creates a Uint32.
+func NewUint32(i uint32) *Uint32 {
+ return &Uint32{i}
+}
+
+// Load atomically loads the wrapped value.
+func (i *Uint32) Load() uint32 {
+ return atomic.LoadUint32(&i.v)
+}
+
+// Add atomically adds to the wrapped uint32 and returns the new value.
+func (i *Uint32) Add(n uint32) uint32 {
+ return atomic.AddUint32(&i.v, n)
+}
+
+// Sub atomically subtracts from the wrapped uint32 and returns the new value.
+func (i *Uint32) Sub(n uint32) uint32 {
+ return atomic.AddUint32(&i.v, ^(n - 1))
+}
+
+// Inc atomically increments the wrapped uint32 and returns the new value.
+func (i *Uint32) Inc() uint32 {
+ return i.Add(1)
+}
+
+// Dec atomically decrements the wrapped int32 and returns the new value.
+func (i *Uint32) Dec() uint32 {
+ return i.Sub(1)
+}
+
+// CAS is an atomic compare-and-swap.
+func (i *Uint32) CAS(old, new uint32) bool {
+ return atomic.CompareAndSwapUint32(&i.v, old, new)
+}
+
+// Store atomically stores the passed value.
+func (i *Uint32) Store(n uint32) {
+ atomic.StoreUint32(&i.v, n)
+}
+
+// Swap atomically swaps the wrapped uint32 and returns the old value.
+func (i *Uint32) Swap(n uint32) uint32 {
+ return atomic.SwapUint32(&i.v, n)
+}
+
+// Uint64 is an atomic wrapper around a uint64.
+type Uint64 struct{ v uint64 }
+
+// NewUint64 creates a Uint64.
+func NewUint64(i uint64) *Uint64 {
+ return &Uint64{i}
+}
+
+// Load atomically loads the wrapped value.
+func (i *Uint64) Load() uint64 {
+ return atomic.LoadUint64(&i.v)
+}
+
+// Add atomically adds to the wrapped uint64 and returns the new value.
+func (i *Uint64) Add(n uint64) uint64 {
+ return atomic.AddUint64(&i.v, n)
+}
+
+// Sub atomically subtracts from the wrapped uint64 and returns the new value.
+func (i *Uint64) Sub(n uint64) uint64 {
+ return atomic.AddUint64(&i.v, ^(n - 1))
+}
+
+// Inc atomically increments the wrapped uint64 and returns the new value.
+func (i *Uint64) Inc() uint64 {
+ return i.Add(1)
+}
+
+// Dec atomically decrements the wrapped uint64 and returns the new value.
+func (i *Uint64) Dec() uint64 {
+ return i.Sub(1)
+}
+
+// CAS is an atomic compare-and-swap.
+func (i *Uint64) CAS(old, new uint64) bool {
+ return atomic.CompareAndSwapUint64(&i.v, old, new)
+}
+
+// Store atomically stores the passed value.
+func (i *Uint64) Store(n uint64) {
+ atomic.StoreUint64(&i.v, n)
+}
+
+// Swap atomically swaps the wrapped uint64 and returns the old value.
+func (i *Uint64) Swap(n uint64) uint64 {
+ return atomic.SwapUint64(&i.v, n)
+}
+
+// Bool is an atomic Boolean.
+type Bool struct{ v uint32 }
+
+// NewBool creates a Bool.
+func NewBool(initial bool) *Bool {
+ return &Bool{boolToInt(initial)}
+}
+
+// Load atomically loads the Boolean.
+func (b *Bool) Load() bool {
+ return truthy(atomic.LoadUint32(&b.v))
+}
+
+// CAS is an atomic compare-and-swap.
+func (b *Bool) CAS(old, new bool) bool {
+ return atomic.CompareAndSwapUint32(&b.v, boolToInt(old), boolToInt(new))
+}
+
+// Store atomically stores the passed value.
+func (b *Bool) Store(new bool) {
+ atomic.StoreUint32(&b.v, boolToInt(new))
+}
+
+// Swap sets the given value and returns the previous value.
+func (b *Bool) Swap(new bool) bool {
+ return truthy(atomic.SwapUint32(&b.v, boolToInt(new)))
+}
+
+// Toggle atomically negates the Boolean and returns the previous value.
+func (b *Bool) Toggle() bool {
+ return truthy(atomic.AddUint32(&b.v, 1) - 1)
+}
+
+func truthy(n uint32) bool {
+ return n&1 == 1
+}
+
+func boolToInt(b bool) uint32 {
+ if b {
+ return 1
+ }
+ return 0
+}
+
+// Float64 is an atomic wrapper around float64.
+type Float64 struct {
+ v uint64
+}
+
+// NewFloat64 creates a Float64.
+func NewFloat64(f float64) *Float64 {
+ return &Float64{math.Float64bits(f)}
+}
+
+// Load atomically loads the wrapped value.
+func (f *Float64) Load() float64 {
+ return math.Float64frombits(atomic.LoadUint64(&f.v))
+}
+
+// Store atomically stores the passed value.
+func (f *Float64) Store(s float64) {
+ atomic.StoreUint64(&f.v, math.Float64bits(s))
+}
+
+// Add atomically adds to the wrapped float64 and returns the new value.
+func (f *Float64) Add(s float64) float64 {
+ for {
+ old := f.Load()
+ new := old + s
+ if f.CAS(old, new) {
+ return new
+ }
+ }
+}
+
+// Sub atomically subtracts from the wrapped float64 and returns the new value.
+func (f *Float64) Sub(s float64) float64 {
+ return f.Add(-s)
+}
+
+// CAS is an atomic compare-and-swap.
+func (f *Float64) CAS(old, new float64) bool {
+ return atomic.CompareAndSwapUint64(&f.v, math.Float64bits(old), math.Float64bits(new))
+}
+
+// Value shadows the type of the same name from sync/atomic
+// https://godoc.org/sync/atomic#Value
+type Value struct{ atomic.Value }
diff --git a/vendor/go.uber.org/atomic/glide.lock b/vendor/go.uber.org/atomic/glide.lock
new file mode 100644
index 000000000..3c72c5997
--- /dev/null
+++ b/vendor/go.uber.org/atomic/glide.lock
@@ -0,0 +1,17 @@
+hash: f14d51408e3e0e4f73b34e4039484c78059cd7fc5f4996fdd73db20dc8d24f53
+updated: 2016-10-27T00:10:51.16960137-07:00
+imports: []
+testImports:
+- name: github.com/davecgh/go-spew
+ version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
+ subpackages:
+ - spew
+- name: github.com/pmezard/go-difflib
+ version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
+ subpackages:
+ - difflib
+- name: github.com/stretchr/testify
+ version: d77da356e56a7428ad25149ca77381849a6a5232
+ subpackages:
+ - assert
+ - require
diff --git a/vendor/go.uber.org/atomic/glide.yaml b/vendor/go.uber.org/atomic/glide.yaml
new file mode 100644
index 000000000..4cf608ec0
--- /dev/null
+++ b/vendor/go.uber.org/atomic/glide.yaml
@@ -0,0 +1,6 @@
+package: go.uber.org/atomic
+testImport:
+- package: github.com/stretchr/testify
+ subpackages:
+ - assert
+ - require
diff --git a/vendor/go.uber.org/atomic/string.go b/vendor/go.uber.org/atomic/string.go
new file mode 100644
index 000000000..ede8136fa
--- /dev/null
+++ b/vendor/go.uber.org/atomic/string.go
@@ -0,0 +1,49 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package atomic
+
+// String is an atomic type-safe wrapper around Value for strings.
+type String struct{ v Value }
+
+// NewString creates a String.
+func NewString(str string) *String {
+ s := &String{}
+ if str != "" {
+ s.Store(str)
+ }
+ return s
+}
+
+// Load atomically loads the wrapped string.
+func (s *String) Load() string {
+ v := s.v.Load()
+ if v == nil {
+ return ""
+ }
+ return v.(string)
+}
+
+// Store atomically stores the passed string.
+// Note: Converting the string to an interface{} to store in the Value
+// requires an allocation.
+func (s *String) Store(str string) {
+ s.v.Store(str)
+}
diff --git a/vendor/go.uber.org/multierr/.codecov.yml b/vendor/go.uber.org/multierr/.codecov.yml
new file mode 100644
index 000000000..6d4d1be7b
--- /dev/null
+++ b/vendor/go.uber.org/multierr/.codecov.yml
@@ -0,0 +1,15 @@
+coverage:
+ range: 80..100
+ round: down
+ precision: 2
+
+ status:
+ project: # measuring the overall project coverage
+ default: # context, you can create multiple ones with custom titles
+ enabled: yes # must be yes|true to enable this status
+ target: 100 # specify the target coverage for each commit status
+ # option: "auto" (must increase from parent commit or pull request base)
+ # option: "X%" a static target percentage to hit
+ if_not_found: success # if parent is not found report status as success, error, or failure
+ if_ci_failed: error # if ci fails report status as success, error, or failure
+
diff --git a/vendor/go.uber.org/multierr/.gitignore b/vendor/go.uber.org/multierr/.gitignore
new file mode 100644
index 000000000..61ead8666
--- /dev/null
+++ b/vendor/go.uber.org/multierr/.gitignore
@@ -0,0 +1 @@
+/vendor
diff --git a/vendor/go.uber.org/multierr/.travis.yml b/vendor/go.uber.org/multierr/.travis.yml
new file mode 100644
index 000000000..5ffa8fed4
--- /dev/null
+++ b/vendor/go.uber.org/multierr/.travis.yml
@@ -0,0 +1,33 @@
+sudo: false
+language: go
+go_import_path: go.uber.org/multierr
+
+env:
+ global:
+ - GO15VENDOREXPERIMENT=1
+
+go:
+ - 1.7
+ - 1.8
+ - tip
+
+cache:
+ directories:
+ - vendor
+
+before_install:
+- go version
+
+install:
+- |
+ set -e
+ make install_ci
+
+script:
+- |
+ set -e
+ make lint
+ make test_ci
+
+after_success:
+- bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/go.uber.org/multierr/CHANGELOG.md b/vendor/go.uber.org/multierr/CHANGELOG.md
new file mode 100644
index 000000000..898445d06
--- /dev/null
+++ b/vendor/go.uber.org/multierr/CHANGELOG.md
@@ -0,0 +1,28 @@
+Releases
+========
+
+v1.1.0 (2017-06-30)
+===================
+
+- Added an `Errors(error) []error` function to extract the underlying list of
+ errors for a multierr error.
+
+
+v1.0.0 (2017-05-31)
+===================
+
+No changes since v0.2.0. This release is committing to making no breaking
+changes to the current API in the 1.X series.
+
+
+v0.2.0 (2017-04-11)
+===================
+
+- Repeatedly appending to the same error is now faster due to fewer
+ allocations.
+
+
+v0.1.0 (2017-31-03)
+===================
+
+- Initial release
diff --git a/vendor/go.uber.org/multierr/LICENSE.txt b/vendor/go.uber.org/multierr/LICENSE.txt
new file mode 100644
index 000000000..858e02475
--- /dev/null
+++ b/vendor/go.uber.org/multierr/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2017 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/go.uber.org/multierr/Makefile b/vendor/go.uber.org/multierr/Makefile
new file mode 100644
index 000000000..a7437d061
--- /dev/null
+++ b/vendor/go.uber.org/multierr/Makefile
@@ -0,0 +1,74 @@
+export GO15VENDOREXPERIMENT=1
+
+PACKAGES := $(shell glide nv)
+
+GO_FILES := $(shell \
+ find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
+ -o -name '*.go' -print | cut -b3-)
+
+.PHONY: install
+install:
+ glide --version || go get github.com/Masterminds/glide
+ glide install
+
+.PHONY: build
+build:
+ go build -i $(PACKAGES)
+
+.PHONY: test
+test:
+ go test -cover -race $(PACKAGES)
+
+.PHONY: gofmt
+gofmt:
+ $(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
+ @gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
+ @[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false)
+
+.PHONY: govet
+govet:
+ $(eval VET_LOG := $(shell mktemp -t govet.XXXXX))
+ @go vet $(PACKAGES) 2>&1 \
+ | grep -v '^exit status' > $(VET_LOG) || true
+ @[ ! -s "$(VET_LOG)" ] || (echo "govet failed:" | cat - $(VET_LOG) && false)
+
+.PHONY: golint
+golint:
+ @go get github.com/golang/lint/golint
+ $(eval LINT_LOG := $(shell mktemp -t golint.XXXXX))
+ @cat /dev/null > $(LINT_LOG)
+ @$(foreach pkg, $(PACKAGES), golint $(pkg) >> $(LINT_LOG) || true;)
+ @[ ! -s "$(LINT_LOG)" ] || (echo "golint failed:" | cat - $(LINT_LOG) && false)
+
+.PHONY: staticcheck
+staticcheck:
+ @go get honnef.co/go/tools/cmd/staticcheck
+ $(eval STATICCHECK_LOG := $(shell mktemp -t staticcheck.XXXXX))
+ @staticcheck $(PACKAGES) 2>&1 > $(STATICCHECK_LOG) || true
+ @[ ! -s "$(STATICCHECK_LOG)" ] || (echo "staticcheck failed:" | cat - $(STATICCHECK_LOG) && false)
+
+.PHONY: lint
+lint: gofmt govet golint staticcheck
+
+.PHONY: cover
+cover:
+ ./scripts/cover.sh $(shell go list $(PACKAGES))
+ go tool cover -html=cover.out -o cover.html
+
+update-license:
+ @go get go.uber.org/tools/update-license
+ @update-license \
+ $(shell go list -json $(PACKAGES) | \
+ jq -r '.Dir + "/" + (.GoFiles | .[])')
+
+##############################################################################
+
+.PHONY: install_ci
+install_ci: install
+ go get github.com/wadey/gocovmerge
+ go get github.com/mattn/goveralls
+ go get golang.org/x/tools/cmd/cover
+
+.PHONY: test_ci
+test_ci: install_ci
+ ./scripts/cover.sh $(shell go list $(PACKAGES))
diff --git a/vendor/go.uber.org/multierr/README.md b/vendor/go.uber.org/multierr/README.md
new file mode 100644
index 000000000..065088f64
--- /dev/null
+++ b/vendor/go.uber.org/multierr/README.md
@@ -0,0 +1,23 @@
+# multierr [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
+
+`multierr` allows combining one or more Go `error`s together.
+
+## Installation
+
+ go get -u go.uber.org/multierr
+
+## Status
+
+Stable: No breaking changes will be made before 2.0.
+
+-------------------------------------------------------------------------------
+
+Released under the [MIT License].
+
+[MIT License]: LICENSE.txt
+[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg
+[doc]: https://godoc.org/go.uber.org/multierr
+[ci-img]: https://travis-ci.org/uber-go/multierr.svg?branch=master
+[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
+[ci]: https://travis-ci.org/uber-go/multierr
+[cov]: https://codecov.io/gh/uber-go/multierr
diff --git a/vendor/go.uber.org/multierr/error.go b/vendor/go.uber.org/multierr/error.go
new file mode 100644
index 000000000..de6ce4736
--- /dev/null
+++ b/vendor/go.uber.org/multierr/error.go
@@ -0,0 +1,401 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package multierr allows combining one or more errors together.
+//
+// Overview
+//
+// Errors can be combined with the use of the Combine function.
+//
+// multierr.Combine(
+// reader.Close(),
+// writer.Close(),
+// conn.Close(),
+// )
+//
+// If only two errors are being combined, the Append function may be used
+// instead.
+//
+// err = multierr.Combine(reader.Close(), writer.Close())
+//
+// This makes it possible to record resource cleanup failures from deferred
+// blocks with the help of named return values.
+//
+// func sendRequest(req Request) (err error) {
+// conn, err := openConnection()
+// if err != nil {
+// return err
+// }
+// defer func() {
+// err = multierr.Append(err, conn.Close())
+// }()
+// // ...
+// }
+//
+// The underlying list of errors for a returned error object may be retrieved
+// with the Errors function.
+//
+// errors := multierr.Errors(err)
+// if len(errors) > 0 {
+// fmt.Println("The following errors occurred:")
+// }
+//
+// Advanced Usage
+//
+// Errors returned by Combine and Append MAY implement the following
+// interface.
+//
+// type errorGroup interface {
+// // Returns a slice containing the underlying list of errors.
+// //
+// // This slice MUST NOT be modified by the caller.
+// Errors() []error
+// }
+//
+// Note that if you need access to list of errors behind a multierr error, you
+// should prefer using the Errors function. That said, if you need cheap
+// read-only access to the underlying errors slice, you can attempt to cast
+// the error to this interface. You MUST handle the failure case gracefully
+// because errors returned by Combine and Append are not guaranteed to
+// implement this interface.
+//
+// var errors []error
+// group, ok := err.(errorGroup)
+// if ok {
+// errors = group.Errors()
+// } else {
+// errors = []error{err}
+// }
+package multierr // import "go.uber.org/multierr"
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+
+ "go.uber.org/atomic"
+)
+
+var (
+ // Separator for single-line error messages.
+ _singlelineSeparator = []byte("; ")
+
+ _newline = []byte("\n")
+
+ // Prefix for multi-line messages
+ _multilinePrefix = []byte("the following errors occurred:")
+
+ // Prefix for the first and following lines of an item in a list of
+ // multi-line error messages.
+ //
+ // For example, if a single item is:
+ //
+ // foo
+ // bar
+ //
+ // It will become,
+ //
+ // - foo
+ // bar
+ _multilineSeparator = []byte("\n - ")
+ _multilineIndent = []byte(" ")
+)
+
+// _bufferPool is a pool of bytes.Buffers.
+var _bufferPool = sync.Pool{
+ New: func() interface{} {
+ return &bytes.Buffer{}
+ },
+}
+
+type errorGroup interface {
+ Errors() []error
+}
+
+// Errors returns a slice containing zero or more errors that the supplied
+// error is composed of. If the error is nil, the returned slice is empty.
+//
+// err := multierr.Append(r.Close(), w.Close())
+// errors := multierr.Errors(err)
+//
+// If the error is not composed of other errors, the returned slice contains
+// just the error that was passed in.
+//
+// Callers of this function are free to modify the returned slice.
+func Errors(err error) []error {
+ if err == nil {
+ return nil
+ }
+
+ // Note that we're casting to multiError, not errorGroup. Our contract is
+ // that returned errors MAY implement errorGroup. Errors, however, only
+ // has special behavior for multierr-specific error objects.
+ //
+ // This behavior can be expanded in the future but I think it's prudent to
+ // start with as little as possible in terms of contract and possibility
+ // of misuse.
+ eg, ok := err.(*multiError)
+ if !ok {
+ return []error{err}
+ }
+
+ errors := eg.Errors()
+ result := make([]error, len(errors))
+ copy(result, errors)
+ return result
+}
+
+// multiError is an error that holds one or more errors.
+//
+// An instance of this is guaranteed to be non-empty and flattened. That is,
+// none of the errors inside multiError are other multiErrors.
+//
+// multiError formats to a semi-colon delimited list of error messages with
+// %v and with a more readable multi-line format with %+v.
+type multiError struct {
+ copyNeeded atomic.Bool
+ errors []error
+}
+
+var _ errorGroup = (*multiError)(nil)
+
+// Errors returns the list of underlying errors.
+//
+// This slice MUST NOT be modified.
+func (merr *multiError) Errors() []error {
+ if merr == nil {
+ return nil
+ }
+ return merr.errors
+}
+
+func (merr *multiError) Error() string {
+ if merr == nil {
+ return ""
+ }
+
+ buff := _bufferPool.Get().(*bytes.Buffer)
+ buff.Reset()
+
+ merr.writeSingleline(buff)
+
+ result := buff.String()
+ _bufferPool.Put(buff)
+ return result
+}
+
+func (merr *multiError) Format(f fmt.State, c rune) {
+ if c == 'v' && f.Flag('+') {
+ merr.writeMultiline(f)
+ } else {
+ merr.writeSingleline(f)
+ }
+}
+
+func (merr *multiError) writeSingleline(w io.Writer) {
+ first := true
+ for _, item := range merr.errors {
+ if first {
+ first = false
+ } else {
+ w.Write(_singlelineSeparator)
+ }
+ io.WriteString(w, item.Error())
+ }
+}
+
+func (merr *multiError) writeMultiline(w io.Writer) {
+ w.Write(_multilinePrefix)
+ for _, item := range merr.errors {
+ w.Write(_multilineSeparator)
+ writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
+ }
+}
+
+// Writes s to the writer with the given prefix added before each line after
+// the first.
+func writePrefixLine(w io.Writer, prefix []byte, s string) {
+ first := true
+ for len(s) > 0 {
+ if first {
+ first = false
+ } else {
+ w.Write(prefix)
+ }
+
+ idx := strings.IndexByte(s, '\n')
+ if idx < 0 {
+ idx = len(s) - 1
+ }
+
+ io.WriteString(w, s[:idx+1])
+ s = s[idx+1:]
+ }
+}
+
+type inspectResult struct {
+ // Number of top-level non-nil errors
+ Count int
+
+ // Total number of errors including multiErrors
+ Capacity int
+
+ // Index of the first non-nil error in the list. Value is meaningless if
+ // Count is zero.
+ FirstErrorIdx int
+
+ // Whether the list contains at least one multiError
+ ContainsMultiError bool
+}
+
+// Inspects the given slice of errors so that we can efficiently allocate
+// space for it.
+func inspect(errors []error) (res inspectResult) {
+ first := true
+ for i, err := range errors {
+ if err == nil {
+ continue
+ }
+
+ res.Count++
+ if first {
+ first = false
+ res.FirstErrorIdx = i
+ }
+
+ if merr, ok := err.(*multiError); ok {
+ res.Capacity += len(merr.errors)
+ res.ContainsMultiError = true
+ } else {
+ res.Capacity++
+ }
+ }
+ return
+}
+
+// fromSlice converts the given list of errors into a single error.
+func fromSlice(errors []error) error {
+ res := inspect(errors)
+ switch res.Count {
+ case 0:
+ return nil
+ case 1:
+ // only one non-nil entry
+ return errors[res.FirstErrorIdx]
+ case len(errors):
+ if !res.ContainsMultiError {
+ // already flat
+ return &multiError{errors: errors}
+ }
+ }
+
+ nonNilErrs := make([]error, 0, res.Capacity)
+ for _, err := range errors[res.FirstErrorIdx:] {
+ if err == nil {
+ continue
+ }
+
+ if nested, ok := err.(*multiError); ok {
+ nonNilErrs = append(nonNilErrs, nested.errors...)
+ } else {
+ nonNilErrs = append(nonNilErrs, err)
+ }
+ }
+
+ return &multiError{errors: nonNilErrs}
+}
+
+// Combine combines the passed errors into a single error.
+//
+// If zero arguments were passed or if all items are nil, a nil error is
+// returned.
+//
+// Combine(nil, nil) // == nil
+//
+// If only a single error was passed, it is returned as-is.
+//
+// Combine(err) // == err
+//
+// Combine skips over nil arguments so this function may be used to combine
+// together errors from operations that fail independently of each other.
+//
+// multierr.Combine(
+// reader.Close(),
+// writer.Close(),
+// pipe.Close(),
+// )
+//
+// If any of the passed errors is a multierr error, it will be flattened along
+// with the other errors.
+//
+// multierr.Combine(multierr.Combine(err1, err2), err3)
+// // is the same as
+// multierr.Combine(err1, err2, err3)
+//
+// The returned error formats into a readable multi-line error message if
+// formatted with %+v.
+//
+// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
+func Combine(errors ...error) error {
+ return fromSlice(errors)
+}
+
+// Append appends the given errors together. Either value may be nil.
+//
+// This function is a specialization of Combine for the common case where
+// there are only two errors.
+//
+// err = multierr.Append(reader.Close(), writer.Close())
+//
+// The following pattern may also be used to record failure of deferred
+// operations without losing information about the original error.
+//
+// func doSomething(..) (err error) {
+// f := acquireResource()
+// defer func() {
+// err = multierr.Append(err, f.Close())
+// }()
+func Append(left error, right error) error {
+ switch {
+ case left == nil:
+ return right
+ case right == nil:
+ return left
+ }
+
+ if _, ok := right.(*multiError); !ok {
+ if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
+ // Common case where the error on the left is constantly being
+ // appended to.
+ errs := append(l.errors, right)
+ return &multiError{errors: errs}
+ } else if !ok {
+ // Both errors are single errors.
+ return &multiError{errors: []error{left, right}}
+ }
+ }
+
+ // Either right or both, left and right, are multiErrors. Rely on usual
+ // expensive logic.
+ errors := [2]error{left, right}
+ return fromSlice(errors[0:])
+}
diff --git a/vendor/go.uber.org/multierr/glide.lock b/vendor/go.uber.org/multierr/glide.lock
new file mode 100644
index 000000000..f9ea94c33
--- /dev/null
+++ b/vendor/go.uber.org/multierr/glide.lock
@@ -0,0 +1,19 @@
+hash: b53b5e9a84b9cb3cc4b2d0499e23da2feca1eec318ce9bb717ecf35bf24bf221
+updated: 2017-04-10T13:34:45.671678062-07:00
+imports:
+- name: go.uber.org/atomic
+ version: 3b8db5e93c4c02efbc313e17b2e796b0914a01fb
+testImports:
+- name: github.com/davecgh/go-spew
+ version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
+ subpackages:
+ - spew
+- name: github.com/pmezard/go-difflib
+ version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
+ subpackages:
+ - difflib
+- name: github.com/stretchr/testify
+ version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
+ subpackages:
+ - assert
+ - require
diff --git a/vendor/go.uber.org/multierr/glide.yaml b/vendor/go.uber.org/multierr/glide.yaml
new file mode 100644
index 000000000..6ef084ec2
--- /dev/null
+++ b/vendor/go.uber.org/multierr/glide.yaml
@@ -0,0 +1,8 @@
+package: go.uber.org/multierr
+import:
+- package: go.uber.org/atomic
+ version: ^1
+testImport:
+- package: github.com/stretchr/testify
+ subpackages:
+ - assert
diff --git a/vendor/go.uber.org/zap/.codecov.yml b/vendor/go.uber.org/zap/.codecov.yml
new file mode 100644
index 000000000..8e5ca7d3e
--- /dev/null
+++ b/vendor/go.uber.org/zap/.codecov.yml
@@ -0,0 +1,17 @@
+coverage:
+ range: 80..100
+ round: down
+ precision: 2
+
+ status:
+ project: # measuring the overall project coverage
+ default: # context, you can create multiple ones with custom titles
+ enabled: yes # must be yes|true to enable this status
+ target: 95% # specify the target coverage for each commit status
+ # option: "auto" (must increase from parent commit or pull request base)
+ # option: "X%" a static target percentage to hit
+ if_not_found: success # if parent is not found report status as success, error, or failure
+ if_ci_failed: error # if ci fails report status as success, error, or failure
+ignore:
+ - internal/readme/readme.go
+
diff --git a/vendor/go.uber.org/zap/.gitignore b/vendor/go.uber.org/zap/.gitignore
new file mode 100644
index 000000000..08fbde6ce
--- /dev/null
+++ b/vendor/go.uber.org/zap/.gitignore
@@ -0,0 +1,28 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+vendor
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+*.pprof
+*.out
+*.log
diff --git a/vendor/go.uber.org/zap/.readme.tmpl b/vendor/go.uber.org/zap/.readme.tmpl
new file mode 100644
index 000000000..550dcda12
--- /dev/null
+++ b/vendor/go.uber.org/zap/.readme.tmpl
@@ -0,0 +1,108 @@
+# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
+
+Blazing fast, structured, leveled logging in Go.
+
+## Installation
+
+`go get -u go.uber.org/zap`
+
+Note that zap only supports the two most recent minor versions of Go.
+
+## Quick Start
+
+In contexts where performance is nice, but not critical, use the
+`SugaredLogger`. It's 4-10x faster than than other structured logging
+packages and includes both structured and `printf`-style APIs.
+
+```go
+logger, _ := zap.NewProduction()
+defer logger.Sync() // flushes buffer, if any
+sugar := logger.Sugar()
+sugar.Infow("failed to fetch URL",
+ // Structured context as loosely typed key-value pairs.
+ "url", url,
+ "attempt", 3,
+ "backoff", time.Second,
+)
+sugar.Infof("Failed to fetch URL: %s", url)
+```
+
+When performance and type safety are critical, use the `Logger`. It's even
+faster than the `SugaredLogger` and allocates far less, but it only supports
+structured logging.
+
+```go
+logger, _ := zap.NewProduction()
+defer logger.Sync()
+logger.Info("failed to fetch URL",
+ // Structured context as strongly typed Field values.
+ zap.String("url", url),
+ zap.Int("attempt", 3),
+ zap.Duration("backoff", time.Second),
+)
+```
+
+See the [documentation][doc] and [FAQ](FAQ.md) for more details.
+
+## Performance
+
+For applications that log in the hot path, reflection-based serialization and
+string formatting are prohibitively expensive &mdash; they're CPU-intensive
+and make many small allocations. Put differently, using `encoding/json` and
+`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.
+
+Zap takes a different approach. It includes a reflection-free, zero-allocation
+JSON encoder, and the base `Logger` strives to avoid serialization overhead
+and allocations wherever possible. By building the high-level `SugaredLogger`
+on that foundation, zap lets users *choose* when they need to count every
+allocation and when they'd prefer a more familiar, loosely typed API.
+
+As measured by its own [benchmarking suite][], not only is zap more performant
+than comparable structured logging packages &mdash; it's also faster than the
+standard library. Like all benchmarks, take these with a grain of salt.<sup
+id="anchor-versions">[1](#footnote-versions)</sup>
+
+Log a message and 10 fields:
+
+{{.BenchmarkAddingFields}}
+
+Log a message with a logger that already has 10 fields of context:
+
+{{.BenchmarkAccumulatedContext}}
+
+Log a static string, without any context or `printf`-style templating:
+
+{{.BenchmarkWithoutFields}}
+
+## Development Status: Stable
+
+All APIs are finalized, and no breaking changes will be made in the 1.x series
+of releases. Users of semver-aware dependency management systems should pin
+zap to `^1`.
+
+## Contributing
+
+We encourage and support an active, healthy community of contributors &mdash;
+including you! Details are in the [contribution guide](CONTRIBUTING.md) and
+the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on
+issues and pull requests, but you can also report any negative conduct to
+oss-conduct@uber.com. That email list is a private, safe space; even the zap
+maintainers don't have access, so don't hesitate to hold us to a high
+standard.
+
+<hr>
+
+Released under the [MIT License](LICENSE.txt).
+
+<sup id="footnote-versions">1</sup> In particular, keep in mind that we may be
+benchmarking against slightly older versions of other packages. Versions are
+pinned in zap's [glide.lock][] file. [↩](#anchor-versions)
+
+[doc-img]: https://godoc.org/go.uber.org/zap?status.svg
+[doc]: https://godoc.org/go.uber.org/zap
+[ci-img]: https://travis-ci.org/uber-go/zap.svg?branch=master
+[ci]: https://travis-ci.org/uber-go/zap
+[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
+[cov]: https://codecov.io/gh/uber-go/zap
+[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
+[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock
diff --git a/vendor/go.uber.org/zap/.travis.yml b/vendor/go.uber.org/zap/.travis.yml
new file mode 100644
index 000000000..da1100b7d
--- /dev/null
+++ b/vendor/go.uber.org/zap/.travis.yml
@@ -0,0 +1,21 @@
+language: go
+sudo: false
+go:
+ - "1.9"
+ - "1.10"
+go_import_path: go.uber.org/zap
+env:
+ global:
+ - TEST_TIMEOUT_SCALE=10
+cache:
+ directories:
+ - vendor
+install:
+ - make dependencies
+script:
+ - make lint
+ - make test
+ - make bench
+after_success:
+ - make cover
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md
new file mode 100644
index 000000000..be28291d5
--- /dev/null
+++ b/vendor/go.uber.org/zap/CHANGELOG.md
@@ -0,0 +1,286 @@
+# Changelog
+
+## v1.8.0 (13 Apr 2018)
+
+Enhancements:
+* [#508][]: Make log level configurable when redirecting the standard
+ library's logger.
+* [#518][]: Add a logger that writes to a `*testing.TB`.
+* [#577][]: Add a top-level alias for `zapcore.Field` to clean up GoDoc.
+
+Bugfixes:
+* [#574][]: Add a missing import comment to `go.uber.org/zap/buffer`.
+
+Thanks to @DiSiqueira and @djui for their contributions to this release.
+
+## v1.7.1 (25 Sep 2017)
+
+Bugfixes:
+* [#504][]: Store strings when using AddByteString with the map encoder.
+
+## v1.7.0 (21 Sep 2017)
+
+Enhancements:
+
+* [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user
+ to specify the level of the logged messages.
+
+## v1.6.0 (30 Aug 2017)
+
+Enhancements:
+
+* [#491][]: Omit zap stack frames from stacktraces.
+* [#490][]: Add a `ContextMap` method to observer logs for simpler
+ field validation in tests.
+
+## v1.5.0 (22 Jul 2017)
+
+Enhancements:
+
+* [#460][] and [#470][]: Support errors produced by `go.uber.org/multierr`.
+* [#465][]: Support user-supplied encoders for logger names.
+
+Bugfixes:
+
+* [#477][]: Fix a bug that incorrectly truncated deep stacktraces.
+
+Thanks to @richard-tunein and @pavius for their contributions to this release.
+
+## v1.4.1 (08 Jun 2017)
+
+This release fixes two bugs.
+
+Bugfixes:
+
+* [#435][]: Support a variety of case conventions when unmarshaling levels.
+* [#444][]: Fix a panic in the observer.
+
+## v1.4.0 (12 May 2017)
+
+This release adds a few small features and is fully backward-compatible.
+
+Enhancements:
+
+* [#424][]: Add a `LineEnding` field to `EncoderConfig`, allowing users to
+ override the Unix-style default.
+* [#425][]: Preserve time zones when logging times.
+* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a
+ variety of operations a bit simpler.
+
+## v1.3.0 (25 Apr 2017)
+
+This release adds an enhancement to zap's testing helpers as well as the
+ability to marshal an AtomicLevel. It is fully backward-compatible.
+
+Enhancements:
+
+* [#415][]: Add a substring-filtering helper to zap's observer. This is
+ particularly useful when testing the `SugaredLogger`.
+* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`.
+
+## v1.2.0 (13 Apr 2017)
+
+This release adds a gRPC compatibility wrapper. It is fully backward-compatible.
+
+Enhancements:
+
+* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements
+ `grpclog.Logger`.
+
+## v1.1.0 (31 Mar 2017)
+
+This release fixes two bugs and adds some enhancements to zap's testing helpers.
+It is fully backward-compatible.
+
+Bugfixes:
+
+* [#385][]: Fix caller path trimming on Windows.
+* [#396][]: Fix a panic when attempting to use non-existent directories with
+ zap's configuration struct.
+
+Enhancements:
+
+* [#386][]: Add filtering helpers to zaptest's observing logger.
+
+Thanks to @moitias for contributing to this release.
+
+## v1.0.0 (14 Mar 2017)
+
+This is zap's first stable release. All exported APIs are now final, and no
+further breaking changes will be made in the 1.x release series. Anyone using a
+semver-aware dependency manager should now pin to `^1`.
+
+Breaking changes:
+
+* [#366][]: Add byte-oriented APIs to encoders to log UTF-8 encoded text without
+ casting from `[]byte` to `string`.
+* [#364][]: To support buffering outputs, add `Sync` methods to `zapcore.Core`,
+ `zap.Logger`, and `zap.SugaredLogger`.
+* [#371][]: Rename the `testutils` package to `zaptest`, which is less likely to
+ clash with other testing helpers.
+
+Bugfixes:
+
+* [#362][]: Make the ISO8601 time formatters fixed-width, which is friendlier
+ for tab-separated console output.
+* [#369][]: Remove the automatic locks in `zapcore.NewCore`, which allows zap to
+ work with concurrency-safe `WriteSyncer` implementations.
+* [#347][]: Stop reporting errors when trying to `fsync` standard out on Linux
+ systems.
+* [#373][]: Report the correct caller from zap's standard library
+ interoperability wrappers.
+
+Enhancements:
+
+* [#348][]: Add a registry allowing third-party encodings to work with zap's
+ built-in `Config`.
+* [#327][]: Make the representation of logger callers configurable (like times,
+ levels, and durations).
+* [#376][]: Allow third-party encoders to use their own buffer pools, which
+ removes the last performance advantage that zap's encoders have over plugins.
+* [#346][]: Add `CombineWriteSyncers`, a convenience function to tee multiple
+ `WriteSyncer`s and lock the result.
+* [#365][]: Make zap's stacktraces compatible with mid-stack inlining (coming in
+ Go 1.9).
+* [#372][]: Export zap's observing logger as `zaptest/observer`. This makes it
+ easier for particularly punctilious users to unit test their application's
+ logging.
+
+Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their
+contributions to this release.
+
+## v1.0.0-rc.3 (7 Mar 2017)
+
+This is the third release candidate for zap's stable release. There are no
+breaking changes.
+
+Bugfixes:
+
+* [#339][]: Byte slices passed to `zap.Any` are now correctly treated as binary blobs
+ rather than `[]uint8`.
+
+Enhancements:
+
+* [#307][]: Users can opt into colored output for log levels.
+* [#353][]: In addition to hijacking the output of the standard library's
+ package-global logging functions, users can now construct a zap-backed
+ `log.Logger` instance.
+* [#311][]: Frames from common runtime functions and some of zap's internal
+ machinery are now omitted from stacktraces.
+
+Thanks to @ansel1 and @suyash for their contributions to this release.
+
+## v1.0.0-rc.2 (21 Feb 2017)
+
+This is the second release candidate for zap's stable release. It includes two
+breaking changes.
+
+Breaking changes:
+
+* [#316][]: Zap's global loggers are now fully concurrency-safe
+ (previously, users had to ensure that `ReplaceGlobals` was called before the
+ loggers were in use). However, they must now be accessed via the `L()` and
+ `S()` functions. Users can update their projects with
+
+ ```
+ gofmt -r "zap.L -> zap.L()" -w .
+ gofmt -r "zap.S -> zap.S()" -w .
+ ```
+* [#309][] and [#317][]: RC1 was mistakenly shipped with invalid
+ JSON and YAML struct tags on all config structs. This release fixes the tags
+ and adds static analysis to prevent similar bugs in the future.
+
+Bugfixes:
+
+* [#321][]: Redirecting the standard library's `log` output now
+ correctly reports the logger's caller.
+
+Enhancements:
+
+* [#325][] and [#333][]: Zap now transparently supports non-standard, rich
+ errors like those produced by `github.com/pkg/errors`.
+* [#326][]: Though `New(nil)` continues to return a no-op logger, `NewNop()` is
+ now preferred. Users can update their projects with `gofmt -r 'zap.New(nil) ->
+ zap.NewNop()' -w .`.
+* [#300][]: Incorrectly importing zap as `github.com/uber-go/zap` now returns a
+ more informative error.
+
+Thanks to @skipor and @chapsuk for their contributions to this release.
+
+## v1.0.0-rc.1 (14 Feb 2017)
+
+This is the first release candidate for zap's stable release. There are multiple
+breaking changes and improvements from the pre-release version. Most notably:
+
+* **Zap's import path is now "go.uber.org/zap"** &mdash; all users will
+ need to update their code.
+* User-facing types and functions remain in the `zap` package. Code relevant
+ largely to extension authors is now in the `zapcore` package.
+* The `zapcore.Core` type makes it easy for third-party packages to use zap's
+ internals but provide a different user-facing API.
+* `Logger` is now a concrete type instead of an interface.
+* A less verbose (though slower) logging API is included by default.
+* Package-global loggers `L` and `S` are included.
+* A human-friendly console encoder is included.
+* A declarative config struct allows common logger configurations to be managed
+ as configuration instead of code.
+* Sampling is more accurate, and doesn't depend on the standard library's shared
+ timer heap.
+
+## v0.1.0-beta.1 (6 Feb 2017)
+
+This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and
+upgrade at their leisure. Since this is the first tagged release, there are no
+backward compatibility concerns and all functionality is new.
+
+Early zap adopters should pin to the 0.1.x minor version until they're ready to
+upgrade to the upcoming stable release.
+
+[#316]: https://github.com/uber-go/zap/pull/316
+[#309]: https://github.com/uber-go/zap/pull/309
+[#317]: https://github.com/uber-go/zap/pull/317
+[#321]: https://github.com/uber-go/zap/pull/321
+[#325]: https://github.com/uber-go/zap/pull/325
+[#333]: https://github.com/uber-go/zap/pull/333
+[#326]: https://github.com/uber-go/zap/pull/326
+[#300]: https://github.com/uber-go/zap/pull/300
+[#339]: https://github.com/uber-go/zap/pull/339
+[#307]: https://github.com/uber-go/zap/pull/307
+[#353]: https://github.com/uber-go/zap/pull/353
+[#311]: https://github.com/uber-go/zap/pull/311
+[#366]: https://github.com/uber-go/zap/pull/366
+[#364]: https://github.com/uber-go/zap/pull/364
+[#371]: https://github.com/uber-go/zap/pull/371
+[#362]: https://github.com/uber-go/zap/pull/362
+[#369]: https://github.com/uber-go/zap/pull/369
+[#347]: https://github.com/uber-go/zap/pull/347
+[#373]: https://github.com/uber-go/zap/pull/373
+[#348]: https://github.com/uber-go/zap/pull/348
+[#327]: https://github.com/uber-go/zap/pull/327
+[#376]: https://github.com/uber-go/zap/pull/376
+[#346]: https://github.com/uber-go/zap/pull/346
+[#365]: https://github.com/uber-go/zap/pull/365
+[#372]: https://github.com/uber-go/zap/pull/372
+[#385]: https://github.com/uber-go/zap/pull/385
+[#396]: https://github.com/uber-go/zap/pull/396
+[#386]: https://github.com/uber-go/zap/pull/386
+[#402]: https://github.com/uber-go/zap/pull/402
+[#415]: https://github.com/uber-go/zap/pull/415
+[#416]: https://github.com/uber-go/zap/pull/416
+[#424]: https://github.com/uber-go/zap/pull/424
+[#425]: https://github.com/uber-go/zap/pull/425
+[#431]: https://github.com/uber-go/zap/pull/431
+[#435]: https://github.com/uber-go/zap/pull/435
+[#444]: https://github.com/uber-go/zap/pull/444
+[#477]: https://github.com/uber-go/zap/pull/477
+[#465]: https://github.com/uber-go/zap/pull/465
+[#460]: https://github.com/uber-go/zap/pull/460
+[#470]: https://github.com/uber-go/zap/pull/470
+[#487]: https://github.com/uber-go/zap/pull/487
+[#490]: https://github.com/uber-go/zap/pull/490
+[#491]: https://github.com/uber-go/zap/pull/491
+[#504]: https://github.com/uber-go/zap/pull/504
+[#508]: https://github.com/uber-go/zap/pull/508
+[#518]: https://github.com/uber-go/zap/pull/518
+[#577]: https://github.com/uber-go/zap/pull/577
+[#574]: https://github.com/uber-go/zap/pull/574
diff --git a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..e327d9aa5
--- /dev/null
+++ b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
@@ -0,0 +1,75 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age,
+body size, disability, ethnicity, gender identity and expression, level of
+experience, nationality, personal appearance, race, religion, or sexual
+identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an
+appointed representative at an online or offline event. Representation of a
+project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at oss-conduct@uber.com. The project
+team will review and investigate all complaints, and will respond in a way
+that it deems appropriate to the circumstances. The project team is obligated
+to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 1.4, available at
+[http://contributor-covenant.org/version/1/4][version].
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/vendor/go.uber.org/zap/CONTRIBUTING.md b/vendor/go.uber.org/zap/CONTRIBUTING.md
new file mode 100644
index 000000000..9454bbaf0
--- /dev/null
+++ b/vendor/go.uber.org/zap/CONTRIBUTING.md
@@ -0,0 +1,81 @@
+# Contributing
+
+We'd love your help making zap the very best structured logging library in Go!
+
+If you'd like to add new exported APIs, please [open an issue][open-issue]
+describing your proposal &mdash; discussing API changes ahead of time makes
+pull request review much smoother. In your issue, pull request, and any other
+communications, please remember to treat your fellow contributors with
+respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously.
+
+Note that you'll need to sign [Uber's Contributor License Agreement][cla]
+before we can accept any of your contributions. If necessary, a bot will remind
+you to accept the CLA when you open your pull request.
+
+## Setup
+
+[Fork][fork], then clone the repository:
+
+```
+mkdir -p $GOPATH/src/go.uber.org
+cd $GOPATH/src/go.uber.org
+git clone git@github.com:your_github_username/zap.git
+cd zap
+git remote add upstream https://github.com/uber-go/zap.git
+git fetch upstream
+```
+
+Install zap's dependencies:
+
+```
+make dependencies
+```
+
+Make sure that the tests and the linters pass:
+
+```
+make test
+make lint
+```
+
+If you're not using the minor version of Go specified in the Makefile's
+`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is
+fine, but it means that you'll only discover lint failures after you open your
+pull request.
+
+## Making Changes
+
+Start by creating a new branch for your changes:
+
+```
+cd $GOPATH/src/go.uber.org/zap
+git checkout master
+git fetch upstream
+git rebase upstream/master
+git checkout -b cool_new_feature
+```
+
+Make your changes, then ensure that `make lint` and `make test` still pass. If
+you're satisfied with your changes, push them to your fork.
+
+```
+git push origin cool_new_feature
+```
+
+Then use the GitHub UI to open a pull request.
+
+At this point, you're waiting on us to review your changes. We *try* to respond
+to issues and pull requests within a few business days, and we may suggest some
+improvements or alternatives. Once your changes are approved, one of the
+project maintainers will merge them.
+
+We're much more likely to approve your changes if you:
+
+* Add tests for new functionality.
+* Write a [good commit message][commit-message].
+* Maintain backward compatibility.
+
+[fork]: https://github.com/uber-go/zap/fork
+[open-issue]: https://github.com/uber-go/zap/issues/new
+[cla]: https://cla-assistant.io/uber-go/zap
+[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
diff --git a/vendor/go.uber.org/zap/FAQ.md b/vendor/go.uber.org/zap/FAQ.md
new file mode 100644
index 000000000..90ddfe17a
--- /dev/null
+++ b/vendor/go.uber.org/zap/FAQ.md
@@ -0,0 +1,154 @@
+# Frequently Asked Questions
+
+## Design
+
+### Why spend so much effort on logger performance?
+
+Of course, most applications won't notice the impact of a slow logger: they
+already take tens or hundreds of milliseconds for each operation, so an extra
+millisecond doesn't matter.
+
+On the other hand, why *not* make structured logging fast? The `SugaredLogger`
+isn't any harder to use than other logging packages, and the `Logger` makes
+structured logging possible in performance-sensitive contexts. Across a fleet
+of Go microservices, making each application even slightly more efficient adds
+up quickly.
+
+### Why aren't `Logger` and `SugaredLogger` interfaces?
+
+Unlike the familiar `io.Writer` and `http.Handler`, `Logger` and
+`SugaredLogger` interfaces would include *many* methods. As [Rob Pike points
+out][go-proverbs], "The bigger the interface, the weaker the abstraction."
+Interfaces are also rigid &mdash; *any* change requires releasing a new major
+version, since it breaks all third-party implementations.
+
+Making the `Logger` and `SugaredLogger` concrete types doesn't sacrifice much
+abstraction, and it lets us add methods without introducing breaking changes.
+Your applications should define and depend upon an interface that includes
+just the methods you use.
+
+### Why sample application logs?
+
+Applications often experience runs of errors, either because of a bug or
+because of a misbehaving user. Logging errors is usually a good idea, but it
+can easily make this bad situation worse: not only is your application coping
+with a flood of errors, it's also spending extra CPU cycles and I/O logging
+those errors. Since writes are typically serialized, logging limits throughput
+when you need it most.
+
+Sampling fixes this problem by dropping repetitive log entries. Under normal
+conditions, your application writes out every entry. When similar entries are
+logged hundreds or thousands of times each second, though, zap begins dropping
+duplicates to preserve throughput.
+
+### Why do the structured logging APIs take a message in addition to fields?
+
+Subjectively, we find it helpful to accompany structured context with a brief
+description. This isn't critical during development, but it makes debugging
+and operating unfamiliar systems much easier.
+
+More concretely, zap's sampling algorithm uses the message to identify
+duplicate entries. In our experience, this is a practical middle ground
+between random sampling (which often drops the exact entry that you need while
+debugging) and hashing the complete entry (which is prohibitively expensive).
+
+### Why include package-global loggers?
+
+Since so many other logging packages include a global logger, many
+applications aren't designed to accept loggers as explicit parameters.
+Changing function signatures is often a breaking change, so zap includes
+global loggers to simplify migration.
+
+Avoid them where possible.
+
+### Why include dedicated Panic and Fatal log levels?
+
+In general, application code should handle errors gracefully instead of using
+`panic` or `os.Exit`. However, every rule has exceptions, and it's common to
+crash when an error is truly unrecoverable. To avoid losing any information
+&mdash; especially the reason for the crash &mdash; the logger must flush any
+buffered entries before the process exits.
+
+Zap makes this easy by offering `Panic` and `Fatal` logging methods that
+automatically flush before exiting. Of course, this doesn't guarantee that
+logs will never be lost, but it eliminates a common error.
+
+See the discussion in uber-go/zap#207 for more details.
+
+### What's `DPanic`?
+
+`DPanic` stands for "panic in development." In development, it logs at
+`PanicLevel`; otherwise, it logs at `ErrorLevel`. `DPanic` makes it easier to
+catch errors that are theoretically possible, but shouldn't actually happen,
+*without* crashing in production.
+
+If you've ever written code like this, you need `DPanic`:
+
+```go
+if err != nil {
+ panic(fmt.Sprintf("shouldn't ever get here: %v", err))
+}
+```
+
+## Installation
+
+### What does the error `expects import "go.uber.org/zap"` mean?
+
+Either zap was installed incorrectly or you're referencing the wrong package
+name in your code.
+
+Zap's source code happens to be hosted on GitHub, but the [import
+path][import-path] is `go.uber.org/zap`. This gives us, the project
+maintainers, the freedom to move the source code if necessary. However, it
+means that you need to take a little care when installing and using the
+package.
+
+If you follow two simple rules, everything should work: install zap with `go
+get -u go.uber.org/zap`, and always import it in your code with `import
+"go.uber.org/zap"`. Your code shouldn't contain *any* references to
+`github.com/uber-go/zap`.
+
+## Usage
+
+### Does zap support log rotation?
+
+Zap doesn't natively support rotating log files, since we prefer to leave this
+to an external program like `logrotate`.
+
+However, it's easy to integrate a log rotation package like
+[`gopkg.in/natefinch/lumberjack.v2`][lumberjack] as a `zapcore.WriteSyncer`.
+
+```go
+// lumberjack.Logger is already safe for concurrent use, so we don't need to
+// lock it.
+w := zapcore.AddSync(&lumberjack.Logger{
+ Filename: "/var/log/myapp/foo.log",
+ MaxSize: 500, // megabytes
+ MaxBackups: 3,
+ MaxAge: 28, // days
+})
+core := zapcore.NewCore(
+ zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
+ w,
+ zap.InfoLevel,
+)
+logger := zap.New(core)
+```
+
+## Extensions
+
+We'd love to support every logging need within zap itself, but we're only
+familiar with a handful of log ingestion systems, flag-parsing packages, and
+the like. Rather than merging code that we can't effectively debug and
+support, we'd rather grow an ecosystem of zap extensions.
+
+We're aware of the following extensions, but haven't used them ourselves:
+
+| Package | Integration |
+| --- | --- |
+| `github.com/tchap/zapext` | Sentry, syslog |
+| `github.com/fgrosse/zaptest` | Ginkgo |
+
+[go-proverbs]: https://go-proverbs.github.io/
+[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths
+[lumberjack]: https://godoc.org/gopkg.in/natefinch/lumberjack.v2
diff --git a/vendor/go.uber.org/zap/LICENSE.txt b/vendor/go.uber.org/zap/LICENSE.txt
new file mode 100644
index 000000000..6652bed45
--- /dev/null
+++ b/vendor/go.uber.org/zap/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2016-2017 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile
new file mode 100644
index 000000000..ef7893b3b
--- /dev/null
+++ b/vendor/go.uber.org/zap/Makefile
@@ -0,0 +1,76 @@
+export GO15VENDOREXPERIMENT=1
+
+BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
+PKGS ?= $(shell glide novendor)
+# Many Go tools take file globs or directories as arguments instead of packages.
+PKG_FILES ?= *.go zapcore benchmarks buffer zapgrpc zaptest zaptest/observer internal/bufferpool internal/exit internal/color internal/ztest
+
+# The linting tools evolve with each Go version, so run them only on the latest
+# stable release.
+GO_VERSION := $(shell go version | cut -d " " -f 3)
+GO_MINOR_VERSION := $(word 2,$(subst ., ,$(GO_VERSION)))
+LINTABLE_MINOR_VERSIONS := 10
+ifneq ($(filter $(LINTABLE_MINOR_VERSIONS),$(GO_MINOR_VERSION)),)
+SHOULD_LINT := true
+endif
+
+
+.PHONY: all
+all: lint test
+
+.PHONY: dependencies
+dependencies:
+ @echo "Installing Glide and locked dependencies..."
+ glide --version || go get -u -f github.com/Masterminds/glide
+ glide install
+ @echo "Installing test dependencies..."
+ go install ./vendor/github.com/axw/gocov/gocov
+ go install ./vendor/github.com/mattn/goveralls
+ifdef SHOULD_LINT
+ @echo "Installing golint..."
+ go install ./vendor/github.com/golang/lint/golint
+else
+ @echo "Not installing golint, since we don't expect to lint on" $(GO_VERSION)
+endif
+
+# Disable printf-like invocation checking due to testify.assert.Error()
+VET_RULES := -printf=false
+
+.PHONY: lint
+lint:
+ifdef SHOULD_LINT
+ @rm -rf lint.log
+ @echo "Checking formatting..."
+ @gofmt -d -s $(PKG_FILES) 2>&1 | tee lint.log
+ @echo "Installing test dependencies for vet..."
+ @go test -i $(PKGS)
+ @echo "Checking vet..."
+ @$(foreach dir,$(PKG_FILES),go tool vet $(VET_RULES) $(dir) 2>&1 | tee -a lint.log;)
+ @echo "Checking lint..."
+ @$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;)
+ @echo "Checking for unresolved FIXMEs..."
+ @git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log
+ @echo "Checking for license headers..."
+ @./check_license.sh | tee -a lint.log
+ @[ ! -s lint.log ]
+else
+ @echo "Skipping linters on" $(GO_VERSION)
+endif
+
+.PHONY: test
+test:
+ go test -race $(PKGS)
+
+.PHONY: cover
+cover:
+ ./scripts/cover.sh $(PKGS)
+
+.PHONY: bench
+BENCH ?= .
+bench:
+ @$(foreach pkg,$(PKGS),go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) $(pkg);)
+
+.PHONY: updatereadme
+updatereadme:
+ rm -f README.md
+ cat .readme.tmpl | go run internal/readme/readme.go > README.md
diff --git a/vendor/go.uber.org/zap/README.md b/vendor/go.uber.org/zap/README.md
new file mode 100644
index 000000000..4b2bb9d8b
--- /dev/null
+++ b/vendor/go.uber.org/zap/README.md
@@ -0,0 +1,136 @@
+# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
+
+Blazing fast, structured, leveled logging in Go.
+
+## Installation
+
+`go get -u go.uber.org/zap`
+
+Note that zap only supports the two most recent minor versions of Go.
+
+## Quick Start
+
+In contexts where performance is nice, but not critical, use the
+`SugaredLogger`. It's 4-10x faster than than other structured logging
+packages and includes both structured and `printf`-style APIs.
+
+```go
+logger, _ := zap.NewProduction()
+defer logger.Sync() // flushes buffer, if any
+sugar := logger.Sugar()
+sugar.Infow("failed to fetch URL",
+ // Structured context as loosely typed key-value pairs.
+ "url", url,
+ "attempt", 3,
+ "backoff", time.Second,
+)
+sugar.Infof("Failed to fetch URL: %s", url)
+```
+
+When performance and type safety are critical, use the `Logger`. It's even
+faster than the `SugaredLogger` and allocates far less, but it only supports
+structured logging.
+
+```go
+logger, _ := zap.NewProduction()
+defer logger.Sync()
+logger.Info("failed to fetch URL",
+ // Structured context as strongly typed Field values.
+ zap.String("url", url),
+ zap.Int("attempt", 3),
+ zap.Duration("backoff", time.Second),
+)
+```
+
+See the [documentation][doc] and [FAQ](FAQ.md) for more details.
+
+## Performance
+
+For applications that log in the hot path, reflection-based serialization and
+string formatting are prohibitively expensive &mdash; they're CPU-intensive
+and make many small allocations. Put differently, using `encoding/json` and
+`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.
+
+Zap takes a different approach. It includes a reflection-free, zero-allocation
+JSON encoder, and the base `Logger` strives to avoid serialization overhead
+and allocations wherever possible. By building the high-level `SugaredLogger`
+on that foundation, zap lets users *choose* when they need to count every
+allocation and when they'd prefer a more familiar, loosely typed API.
+
+As measured by its own [benchmarking suite][], not only is zap more performant
+than comparable structured logging packages &mdash; it's also faster than the
+standard library. Like all benchmarks, take these with a grain of salt.<sup
+id="anchor-versions">[1](#footnote-versions)</sup>
+
+Log a message and 10 fields:
+
+| Package | Time | Objects Allocated |
+| :--- | :---: | :---: |
+| :zap: zap | 3131 ns/op | 5 allocs/op |
+| :zap: zap (sugared) | 4173 ns/op | 21 allocs/op |
+| zerolog | 16154 ns/op | 90 allocs/op |
+| lion | 16341 ns/op | 111 allocs/op |
+| go-kit | 17049 ns/op | 126 allocs/op |
+| logrus | 23662 ns/op | 142 allocs/op |
+| log15 | 36351 ns/op | 149 allocs/op |
+| apex/log | 42530 ns/op | 126 allocs/op |
+
+Log a message with a logger that already has 10 fields of context:
+
+| Package | Time | Objects Allocated |
+| :--- | :---: | :---: |
+| :zap: zap | 380 ns/op | 0 allocs/op |
+| :zap: zap (sugared) | 564 ns/op | 2 allocs/op |
+| zerolog | 321 ns/op | 0 allocs/op |
+| lion | 7092 ns/op | 39 allocs/op |
+| go-kit | 20226 ns/op | 115 allocs/op |
+| logrus | 22312 ns/op | 130 allocs/op |
+| log15 | 28788 ns/op | 79 allocs/op |
+| apex/log | 42063 ns/op | 115 allocs/op |
+
+Log a static string, without any context or `printf`-style templating:
+
+| Package | Time | Objects Allocated |
+| :--- | :---: | :---: |
+| :zap: zap | 361 ns/op | 0 allocs/op |
+| :zap: zap (sugared) | 534 ns/op | 2 allocs/op |
+| zerolog | 323 ns/op | 0 allocs/op |
+| standard library | 575 ns/op | 2 allocs/op |
+| go-kit | 922 ns/op | 13 allocs/op |
+| lion | 1413 ns/op | 10 allocs/op |
+| logrus | 2291 ns/op | 27 allocs/op |
+| apex/log | 3690 ns/op | 11 allocs/op |
+| log15 | 5954 ns/op | 26 allocs/op |
+
+## Development Status: Stable
+
+All APIs are finalized, and no breaking changes will be made in the 1.x series
+of releases. Users of semver-aware dependency management systems should pin
+zap to `^1`.
+
+## Contributing
+
+We encourage and support an active, healthy community of contributors &mdash;
+including you! Details are in the [contribution guide](CONTRIBUTING.md) and
+the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on
+issues and pull requests, but you can also report any negative conduct to
+oss-conduct@uber.com. That email list is a private, safe space; even the zap
+maintainers don't have access, so don't hesitate to hold us to a high
+standard.
+
+<hr>
+
+Released under the [MIT License](LICENSE.txt).
+
+<sup id="footnote-versions">1</sup> In particular, keep in mind that we may be
+benchmarking against slightly older versions of other packages. Versions are
+pinned in zap's [glide.lock][] file. [↩](#anchor-versions)
+
+[doc-img]: https://godoc.org/go.uber.org/zap?status.svg
+[doc]: https://godoc.org/go.uber.org/zap
+[ci-img]: https://travis-ci.org/uber-go/zap.svg?branch=master
+[ci]: https://travis-ci.org/uber-go/zap
+[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
+[cov]: https://codecov.io/gh/uber-go/zap
+[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
+[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock
diff --git a/vendor/go.uber.org/zap/array.go b/vendor/go.uber.org/zap/array.go
new file mode 100644
index 000000000..5be3704a3
--- /dev/null
+++ b/vendor/go.uber.org/zap/array.go
@@ -0,0 +1,320 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// Array constructs a field with the given key and ArrayMarshaler. It provides
+// a flexible, but still type-safe and efficient, way to add array-like types
+// to the logging context. The struct's MarshalLogArray method is called lazily.
+func Array(key string, val zapcore.ArrayMarshaler) Field {
+ return Field{Key: key, Type: zapcore.ArrayMarshalerType, Interface: val}
+}
+
+// Bools constructs a field that carries a slice of bools.
+func Bools(key string, bs []bool) Field {
+ return Array(key, bools(bs))
+}
+
+// ByteStrings constructs a field that carries a slice of []byte, each of which
+// must be UTF-8 encoded text.
+func ByteStrings(key string, bss [][]byte) Field {
+ return Array(key, byteStringsArray(bss))
+}
+
+// Complex128s constructs a field that carries a slice of complex numbers.
+func Complex128s(key string, nums []complex128) Field {
+ return Array(key, complex128s(nums))
+}
+
+// Complex64s constructs a field that carries a slice of complex numbers.
+func Complex64s(key string, nums []complex64) Field {
+ return Array(key, complex64s(nums))
+}
+
+// Durations constructs a field that carries a slice of time.Durations.
+func Durations(key string, ds []time.Duration) Field {
+ return Array(key, durations(ds))
+}
+
+// Float64s constructs a field that carries a slice of floats.
+func Float64s(key string, nums []float64) Field {
+ return Array(key, float64s(nums))
+}
+
+// Float32s constructs a field that carries a slice of floats.
+func Float32s(key string, nums []float32) Field {
+ return Array(key, float32s(nums))
+}
+
+// Ints constructs a field that carries a slice of integers.
+func Ints(key string, nums []int) Field {
+ return Array(key, ints(nums))
+}
+
+// Int64s constructs a field that carries a slice of integers.
+func Int64s(key string, nums []int64) Field {
+ return Array(key, int64s(nums))
+}
+
+// Int32s constructs a field that carries a slice of integers.
+func Int32s(key string, nums []int32) Field {
+ return Array(key, int32s(nums))
+}
+
+// Int16s constructs a field that carries a slice of integers.
+func Int16s(key string, nums []int16) Field {
+ return Array(key, int16s(nums))
+}
+
+// Int8s constructs a field that carries a slice of integers.
+func Int8s(key string, nums []int8) Field {
+ return Array(key, int8s(nums))
+}
+
+// Strings constructs a field that carries a slice of strings.
+func Strings(key string, ss []string) Field {
+ return Array(key, stringArray(ss))
+}
+
+// Times constructs a field that carries a slice of time.Times.
+func Times(key string, ts []time.Time) Field {
+ return Array(key, times(ts))
+}
+
+// Uints constructs a field that carries a slice of unsigned integers.
+func Uints(key string, nums []uint) Field {
+ return Array(key, uints(nums))
+}
+
+// Uint64s constructs a field that carries a slice of unsigned integers.
+func Uint64s(key string, nums []uint64) Field {
+ return Array(key, uint64s(nums))
+}
+
+// Uint32s constructs a field that carries a slice of unsigned integers.
+func Uint32s(key string, nums []uint32) Field {
+ return Array(key, uint32s(nums))
+}
+
+// Uint16s constructs a field that carries a slice of unsigned integers.
+func Uint16s(key string, nums []uint16) Field {
+ return Array(key, uint16s(nums))
+}
+
+// Uint8s constructs a field that carries a slice of unsigned integers.
+func Uint8s(key string, nums []uint8) Field {
+ return Array(key, uint8s(nums))
+}
+
+// Uintptrs constructs a field that carries a slice of pointer addresses.
+func Uintptrs(key string, us []uintptr) Field {
+ return Array(key, uintptrs(us))
+}
+
+// Errors constructs a field that carries a slice of errors.
+func Errors(key string, errs []error) Field {
+ return Array(key, errArray(errs))
+}
+
+type bools []bool
+
+func (bs bools) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range bs {
+ arr.AppendBool(bs[i])
+ }
+ return nil
+}
+
+type byteStringsArray [][]byte
+
+func (bss byteStringsArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range bss {
+ arr.AppendByteString(bss[i])
+ }
+ return nil
+}
+
+type complex128s []complex128
+
+func (nums complex128s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendComplex128(nums[i])
+ }
+ return nil
+}
+
+type complex64s []complex64
+
+func (nums complex64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendComplex64(nums[i])
+ }
+ return nil
+}
+
+type durations []time.Duration
+
+func (ds durations) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range ds {
+ arr.AppendDuration(ds[i])
+ }
+ return nil
+}
+
+type float64s []float64
+
+func (nums float64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendFloat64(nums[i])
+ }
+ return nil
+}
+
+type float32s []float32
+
+func (nums float32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendFloat32(nums[i])
+ }
+ return nil
+}
+
+type ints []int
+
+func (nums ints) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt(nums[i])
+ }
+ return nil
+}
+
+type int64s []int64
+
+func (nums int64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt64(nums[i])
+ }
+ return nil
+}
+
+type int32s []int32
+
+func (nums int32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt32(nums[i])
+ }
+ return nil
+}
+
+type int16s []int16
+
+func (nums int16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt16(nums[i])
+ }
+ return nil
+}
+
+type int8s []int8
+
+func (nums int8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt8(nums[i])
+ }
+ return nil
+}
+
+type stringArray []string
+
+func (ss stringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range ss {
+ arr.AppendString(ss[i])
+ }
+ return nil
+}
+
+type times []time.Time
+
+func (ts times) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range ts {
+ arr.AppendTime(ts[i])
+ }
+ return nil
+}
+
+type uints []uint
+
+func (nums uints) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint(nums[i])
+ }
+ return nil
+}
+
+type uint64s []uint64
+
+func (nums uint64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint64(nums[i])
+ }
+ return nil
+}
+
+type uint32s []uint32
+
+func (nums uint32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint32(nums[i])
+ }
+ return nil
+}
+
+type uint16s []uint16
+
+func (nums uint16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint16(nums[i])
+ }
+ return nil
+}
+
+type uint8s []uint8
+
+func (nums uint8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint8(nums[i])
+ }
+ return nil
+}
+
+type uintptrs []uintptr
+
+func (nums uintptrs) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUintptr(nums[i])
+ }
+ return nil
+}
diff --git a/vendor/go.uber.org/zap/buffer/buffer.go b/vendor/go.uber.org/zap/buffer/buffer.go
new file mode 100644
index 000000000..d15f7fdb3
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/buffer.go
@@ -0,0 +1,106 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package buffer provides a thin wrapper around a byte slice. Unlike the
+// standard library's bytes.Buffer, it supports a portion of the strconv
+// package's zero-allocation formatters.
+package buffer // import "go.uber.org/zap/buffer"
+
+import "strconv"
+
+const _size = 1024 // by default, create 1 KiB buffers
+
+// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so
+// the only way to construct one is via a Pool.
+type Buffer struct {
+ bs []byte
+ pool Pool
+}
+
+// AppendByte writes a single byte to the Buffer.
+func (b *Buffer) AppendByte(v byte) {
+ b.bs = append(b.bs, v)
+}
+
+// AppendString writes a string to the Buffer.
+func (b *Buffer) AppendString(s string) {
+ b.bs = append(b.bs, s...)
+}
+
+// AppendInt appends an integer to the underlying buffer (assuming base 10).
+func (b *Buffer) AppendInt(i int64) {
+ b.bs = strconv.AppendInt(b.bs, i, 10)
+}
+
+// AppendUint appends an unsigned integer to the underlying buffer (assuming
+// base 10).
+func (b *Buffer) AppendUint(i uint64) {
+ b.bs = strconv.AppendUint(b.bs, i, 10)
+}
+
+// AppendBool appends a bool to the underlying buffer.
+func (b *Buffer) AppendBool(v bool) {
+ b.bs = strconv.AppendBool(b.bs, v)
+}
+
+// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN
+// or +/- Inf.
+func (b *Buffer) AppendFloat(f float64, bitSize int) {
+ b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize)
+}
+
+// Len returns the length of the underlying byte slice.
+func (b *Buffer) Len() int {
+ return len(b.bs)
+}
+
+// Cap returns the capacity of the underlying byte slice.
+func (b *Buffer) Cap() int {
+ return cap(b.bs)
+}
+
+// Bytes returns a mutable reference to the underlying byte slice.
+func (b *Buffer) Bytes() []byte {
+ return b.bs
+}
+
+// String returns a string copy of the underlying byte slice.
+func (b *Buffer) String() string {
+ return string(b.bs)
+}
+
+// Reset resets the underlying byte slice. Subsequent writes re-use the slice's
+// backing array.
+func (b *Buffer) Reset() {
+ b.bs = b.bs[:0]
+}
+
+// Write implements io.Writer.
+func (b *Buffer) Write(bs []byte) (int, error) {
+ b.bs = append(b.bs, bs...)
+ return len(bs), nil
+}
+
+// Free returns the Buffer to its Pool.
+//
+// Callers must not retain references to the Buffer after calling Free.
+func (b *Buffer) Free() {
+ b.pool.put(b)
+}
diff --git a/vendor/go.uber.org/zap/buffer/pool.go b/vendor/go.uber.org/zap/buffer/pool.go
new file mode 100644
index 000000000..8fb3e202c
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/pool.go
@@ -0,0 +1,49 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package buffer
+
+import "sync"
+
+// A Pool is a type-safe wrapper around a sync.Pool.
+type Pool struct {
+ p *sync.Pool
+}
+
+// NewPool constructs a new Pool.
+func NewPool() Pool {
+ return Pool{p: &sync.Pool{
+ New: func() interface{} {
+ return &Buffer{bs: make([]byte, 0, _size)}
+ },
+ }}
+}
+
+// Get retrieves a Buffer from the pool, creating one if necessary.
+func (p Pool) Get() *Buffer {
+ buf := p.p.Get().(*Buffer)
+ buf.Reset()
+ buf.pool = p
+ return buf
+}
+
+func (p Pool) put(buf *Buffer) {
+ p.p.Put(buf)
+}
diff --git a/vendor/go.uber.org/zap/check_license.sh b/vendor/go.uber.org/zap/check_license.sh
new file mode 100755
index 000000000..345ac8b89
--- /dev/null
+++ b/vendor/go.uber.org/zap/check_license.sh
@@ -0,0 +1,17 @@
+#!/bin/bash -e
+
+ERROR_COUNT=0
+while read -r file
+do
+ case "$(head -1 "${file}")" in
+ *"Copyright (c) "*" Uber Technologies, Inc.")
+ # everything's cool
+ ;;
+ *)
+ echo "$file is missing license header."
+ (( ERROR_COUNT++ ))
+ ;;
+ esac
+done < <(git ls-files "*\.go")
+
+exit $ERROR_COUNT
diff --git a/vendor/go.uber.org/zap/config.go b/vendor/go.uber.org/zap/config.go
new file mode 100644
index 000000000..dae130303
--- /dev/null
+++ b/vendor/go.uber.org/zap/config.go
@@ -0,0 +1,243 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "sort"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// SamplingConfig sets a sampling strategy for the logger. Sampling caps the
+// global CPU and I/O load that logging puts on your process while attempting
+// to preserve a representative subset of your logs.
+//
+// Values configured here are per-second. See zapcore.NewSampler for details.
+type SamplingConfig struct {
+ Initial int `json:"initial" yaml:"initial"`
+ Thereafter int `json:"thereafter" yaml:"thereafter"`
+}
+
+// Config offers a declarative way to construct a logger. It doesn't do
+// anything that can't be done with New, Options, and the various
+// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to
+// toggle common options.
+//
+// Note that Config intentionally supports only the most common options. More
+// unusual logging setups (logging to network connections or message queues,
+// splitting output between multiple files, etc.) are possible, but require
+// direct use of the zapcore package. For sample code, see the package-level
+// BasicConfiguration and AdvancedConfiguration examples.
+//
+// For an example showing runtime log level changes, see the documentation for
+// AtomicLevel.
+type Config struct {
+ // Level is the minimum enabled logging level. Note that this is a dynamic
+ // level, so calling Config.Level.SetLevel will atomically change the log
+ // level of all loggers descended from this config.
+ Level AtomicLevel `json:"level" yaml:"level"`
+ // Development puts the logger in development mode, which changes the
+ // behavior of DPanicLevel and takes stacktraces more liberally.
+ Development bool `json:"development" yaml:"development"`
+ // DisableCaller stops annotating logs with the calling function's file
+ // name and line number. By default, all logs are annotated.
+ DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
+ // DisableStacktrace completely disables automatic stacktrace capturing. By
+ // default, stacktraces are captured for WarnLevel and above logs in
+ // development and ErrorLevel and above in production.
+ DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
+ // Sampling sets a sampling policy. A nil SamplingConfig disables sampling.
+ Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
+ // Encoding sets the logger's encoding. Valid values are "json" and
+ // "console", as well as any third-party encodings registered via
+ // RegisterEncoder.
+ Encoding string `json:"encoding" yaml:"encoding"`
+ // EncoderConfig sets options for the chosen encoder. See
+ // zapcore.EncoderConfig for details.
+ EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
+ // OutputPaths is a list of paths to write logging output to. See Open for
+ // details.
+ OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
+ // ErrorOutputPaths is a list of paths to write internal logger errors to.
+ // The default is standard error.
+ //
+ // Note that this setting only affects internal errors; for sample code that
+ // sends error-level logs to a different location from info- and debug-level
+ // logs, see the package-level AdvancedConfiguration example.
+ ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
+ // InitialFields is a collection of fields to add to the root logger.
+ InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
+}
+
+// NewProductionEncoderConfig returns an opinionated EncoderConfig for
+// production environments.
+func NewProductionEncoderConfig() zapcore.EncoderConfig {
+ return zapcore.EncoderConfig{
+ TimeKey: "ts",
+ LevelKey: "level",
+ NameKey: "logger",
+ CallerKey: "caller",
+ MessageKey: "msg",
+ StacktraceKey: "stacktrace",
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ EncodeTime: zapcore.EpochTimeEncoder,
+ EncodeDuration: zapcore.SecondsDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ }
+}
+
+// NewProductionConfig is a reasonable production logging configuration.
+// Logging is enabled at InfoLevel and above.
+//
+// It uses a JSON encoder, writes to standard error, and enables sampling.
+// Stacktraces are automatically included on logs of ErrorLevel and above.
+func NewProductionConfig() Config {
+ return Config{
+ Level: NewAtomicLevelAt(InfoLevel),
+ Development: false,
+ Sampling: &SamplingConfig{
+ Initial: 100,
+ Thereafter: 100,
+ },
+ Encoding: "json",
+ EncoderConfig: NewProductionEncoderConfig(),
+ OutputPaths: []string{"stderr"},
+ ErrorOutputPaths: []string{"stderr"},
+ }
+}
+
+// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
+// development environments.
+func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
+ return zapcore.EncoderConfig{
+ // Keys can be anything except the empty string.
+ TimeKey: "T",
+ LevelKey: "L",
+ NameKey: "N",
+ CallerKey: "C",
+ MessageKey: "M",
+ StacktraceKey: "S",
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeLevel: zapcore.CapitalLevelEncoder,
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeDuration: zapcore.StringDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ }
+}
+
+// NewDevelopmentConfig is a reasonable development logging configuration.
+// Logging is enabled at DebugLevel and above.
+//
+// It enables development mode (which makes DPanicLevel logs panic), uses a
+// console encoder, writes to standard error, and disables sampling.
+// Stacktraces are automatically included on logs of WarnLevel and above.
+func NewDevelopmentConfig() Config {
+ return Config{
+ Level: NewAtomicLevelAt(DebugLevel),
+ Development: true,
+ Encoding: "console",
+ EncoderConfig: NewDevelopmentEncoderConfig(),
+ OutputPaths: []string{"stderr"},
+ ErrorOutputPaths: []string{"stderr"},
+ }
+}
+
+// Build constructs a logger from the Config and Options.
+func (cfg Config) Build(opts ...Option) (*Logger, error) {
+ enc, err := cfg.buildEncoder()
+ if err != nil {
+ return nil, err
+ }
+
+ sink, errSink, err := cfg.openSinks()
+ if err != nil {
+ return nil, err
+ }
+
+ log := New(
+ zapcore.NewCore(enc, sink, cfg.Level),
+ cfg.buildOptions(errSink)...,
+ )
+ if len(opts) > 0 {
+ log = log.WithOptions(opts...)
+ }
+ return log, nil
+}
+
+func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
+ opts := []Option{ErrorOutput(errSink)}
+
+ if cfg.Development {
+ opts = append(opts, Development())
+ }
+
+ if !cfg.DisableCaller {
+ opts = append(opts, AddCaller())
+ }
+
+ stackLevel := ErrorLevel
+ if cfg.Development {
+ stackLevel = WarnLevel
+ }
+ if !cfg.DisableStacktrace {
+ opts = append(opts, AddStacktrace(stackLevel))
+ }
+
+ if cfg.Sampling != nil {
+ opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core {
+ return zapcore.NewSampler(core, time.Second, int(cfg.Sampling.Initial), int(cfg.Sampling.Thereafter))
+ }))
+ }
+
+ if len(cfg.InitialFields) > 0 {
+ fs := make([]Field, 0, len(cfg.InitialFields))
+ keys := make([]string, 0, len(cfg.InitialFields))
+ for k := range cfg.InitialFields {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ fs = append(fs, Any(k, cfg.InitialFields[k]))
+ }
+ opts = append(opts, Fields(fs...))
+ }
+
+ return opts
+}
+
+func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
+ sink, closeOut, err := Open(cfg.OutputPaths...)
+ if err != nil {
+ return nil, nil, err
+ }
+ errSink, _, err := Open(cfg.ErrorOutputPaths...)
+ if err != nil {
+ closeOut()
+ return nil, nil, err
+ }
+ return sink, errSink, nil
+}
+
+func (cfg Config) buildEncoder() (zapcore.Encoder, error) {
+ return newEncoder(cfg.Encoding, cfg.EncoderConfig)
+}
diff --git a/vendor/go.uber.org/zap/doc.go b/vendor/go.uber.org/zap/doc.go
new file mode 100644
index 000000000..3f16a8d45
--- /dev/null
+++ b/vendor/go.uber.org/zap/doc.go
@@ -0,0 +1,113 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package zap provides fast, structured, leveled logging.
+//
+// For applications that log in the hot path, reflection-based serialization
+// and string formatting are prohibitively expensive - they're CPU-intensive
+// and make many small allocations. Put differently, using json.Marshal and
+// fmt.Fprintf to log tons of interface{} makes your application slow.
+//
+// Zap takes a different approach. It includes a reflection-free,
+// zero-allocation JSON encoder, and the base Logger strives to avoid
+// serialization overhead and allocations wherever possible. By building the
+// high-level SugaredLogger on that foundation, zap lets users choose when
+// they need to count every allocation and when they'd prefer a more familiar,
+// loosely typed API.
+//
+// Choosing a Logger
+//
+// In contexts where performance is nice, but not critical, use the
+// SugaredLogger. It's 4-10x faster than other structured logging packages and
+// supports both structured and printf-style logging. Like log15 and go-kit,
+// the SugaredLogger's structured logging APIs are loosely typed and accept a
+// variadic number of key-value pairs. (For more advanced use cases, they also
+// accept strongly typed fields - see the SugaredLogger.With documentation for
+// details.)
+// sugar := zap.NewExample().Sugar()
+// defer sugar.Sync()
+// sugar.Infow("failed to fetch URL",
+// "url", "http://example.com",
+// "attempt", 3,
+// "backoff", time.Second,
+// )
+// sugar.Printf("failed to fetch URL: %s", "http://example.com")
+//
+// By default, loggers are unbuffered. However, since zap's low-level APIs
+// allow buffering, calling Sync before letting your process exit is a good
+// habit.
+//
+// In the rare contexts where every microsecond and every allocation matter,
+// use the Logger. It's even faster than the SugaredLogger and allocates far
+// less, but it only supports strongly-typed, structured logging.
+// logger := zap.NewExample()
+// defer logger.Sync()
+// logger.Info("failed to fetch URL",
+// zap.String("url", "http://example.com"),
+// zap.Int("attempt", 3),
+// zap.Duration("backoff", time.Second),
+// )
+//
+// Choosing between the Logger and SugaredLogger doesn't need to be an
+// application-wide decision: converting between the two is simple and
+// inexpensive.
+// logger := zap.NewExample()
+// defer logger.Sync()
+// sugar := logger.Sugar()
+// plain := sugar.Desugar()
+//
+// Configuring Zap
+//
+// The simplest way to build a Logger is to use zap's opinionated presets:
+// NewExample, NewProduction, and NewDevelopment. These presets build a logger
+// with a single function call:
+// logger, err := zap.NewProduction()
+// if err != nil {
+// log.Fatalf("can't initialize zap logger: %v", err)
+// }
+// defer logger.Sync()
+//
+// Presets are fine for small projects, but larger projects and organizations
+// naturally require a bit more customization. For most users, zap's Config
+// struct strikes the right balance between flexibility and convenience. See
+// the package-level BasicConfiguration example for sample code.
+//
+// More unusual configurations (splitting output between files, sending logs
+// to a message queue, etc.) are possible, but require direct use of
+// go.uber.org/zap/zapcore. See the package-level AdvancedConfiguration
+// example for sample code.
+//
+// Extending Zap
+//
+// The zap package itself is a relatively thin wrapper around the interfaces
+// in go.uber.org/zap/zapcore. Extending zap to support a new encoding (e.g.,
+// BSON), a new log sink (e.g., Kafka), or something more exotic (perhaps an
+// exception aggregation service, like Sentry or Rollbar) typically requires
+// implementing the zapcore.Encoder, zapcore.WriteSyncer, or zapcore.Core
+// interfaces. See the zapcore documentation for details.
+//
+// Similarly, package authors can use the high-performance Encoder and Core
+// implementations in the zapcore package to build their own loggers.
+//
+// Frequently Asked Questions
+//
+// An FAQ covering everything from installation errors to design decisions is
+// available at https://github.com/uber-go/zap/blob/master/FAQ.md.
+package zap // import "go.uber.org/zap"
diff --git a/vendor/go.uber.org/zap/encoder.go b/vendor/go.uber.org/zap/encoder.go
new file mode 100644
index 000000000..2e9d3c341
--- /dev/null
+++ b/vendor/go.uber.org/zap/encoder.go
@@ -0,0 +1,75 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+
+ "go.uber.org/zap/zapcore"
+)
+
+var (
+ errNoEncoderNameSpecified = errors.New("no encoder name specified")
+
+ _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){
+ "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ return zapcore.NewConsoleEncoder(encoderConfig), nil
+ },
+ "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ return zapcore.NewJSONEncoder(encoderConfig), nil
+ },
+ }
+ _encoderMutex sync.RWMutex
+)
+
+// RegisterEncoder registers an encoder constructor, which the Config struct
+// can then reference. By default, the "json" and "console" encoders are
+// registered.
+//
+// Attempting to register an encoder whose name is already taken returns an
+// error.
+func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error {
+ _encoderMutex.Lock()
+ defer _encoderMutex.Unlock()
+ if name == "" {
+ return errNoEncoderNameSpecified
+ }
+ if _, ok := _encoderNameToConstructor[name]; ok {
+ return fmt.Errorf("encoder already registered for name %q", name)
+ }
+ _encoderNameToConstructor[name] = constructor
+ return nil
+}
+
+func newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ _encoderMutex.RLock()
+ defer _encoderMutex.RUnlock()
+ if name == "" {
+ return nil, errNoEncoderNameSpecified
+ }
+ constructor, ok := _encoderNameToConstructor[name]
+ if !ok {
+ return nil, fmt.Errorf("no encoder registered for name %q", name)
+ }
+ return constructor(encoderConfig)
+}
diff --git a/vendor/go.uber.org/zap/error.go b/vendor/go.uber.org/zap/error.go
new file mode 100644
index 000000000..65982a51e
--- /dev/null
+++ b/vendor/go.uber.org/zap/error.go
@@ -0,0 +1,80 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "sync"
+
+ "go.uber.org/zap/zapcore"
+)
+
+var _errArrayElemPool = sync.Pool{New: func() interface{} {
+ return &errArrayElem{}
+}}
+
+// Error is shorthand for the common idiom NamedError("error", err).
+func Error(err error) Field {
+ return NamedError("error", err)
+}
+
+// NamedError constructs a field that lazily stores err.Error() under the
+// provided key. Errors which also implement fmt.Formatter (like those produced
+// by github.com/pkg/errors) will also have their verbose representation stored
+// under key+"Verbose". If passed a nil error, the field is a no-op.
+//
+// For the common case in which the key is simply "error", the Error function
+// is shorter and less repetitive.
+func NamedError(key string, err error) Field {
+ if err == nil {
+ return Skip()
+ }
+ return Field{Key: key, Type: zapcore.ErrorType, Interface: err}
+}
+
+type errArray []error
+
+func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range errs {
+ if errs[i] == nil {
+ continue
+ }
+ // To represent each error as an object with an "error" attribute and
+ // potentially an "errorVerbose" attribute, we need to wrap it in a
+ // type that implements LogObjectMarshaler. To prevent this from
+ // allocating, pool the wrapper type.
+ elem := _errArrayElemPool.Get().(*errArrayElem)
+ elem.error = errs[i]
+ arr.AppendObject(elem)
+ elem.error = nil
+ _errArrayElemPool.Put(elem)
+ }
+ return nil
+}
+
+type errArrayElem struct {
+ error
+}
+
+func (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ // Re-use the error field's logic, which supports non-standard error types.
+ Error(e.error).AddTo(enc)
+ return nil
+}
diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go
new file mode 100644
index 000000000..5130e1347
--- /dev/null
+++ b/vendor/go.uber.org/zap/field.go
@@ -0,0 +1,310 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "fmt"
+ "math"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// Field is an alias for Field. Aliasing this type dramatically
+// improves the navigability of this package's API documentation.
+type Field = zapcore.Field
+
+// Skip constructs a no-op field, which is often useful when handling invalid
+// inputs in other Field constructors.
+func Skip() Field {
+ return Field{Type: zapcore.SkipType}
+}
+
+// Binary constructs a field that carries an opaque binary blob.
+//
+// Binary data is serialized in an encoding-appropriate format. For example,
+// zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text,
+// use ByteString.
+func Binary(key string, val []byte) Field {
+ return Field{Key: key, Type: zapcore.BinaryType, Interface: val}
+}
+
+// Bool constructs a field that carries a bool.
+func Bool(key string, val bool) Field {
+ var ival int64
+ if val {
+ ival = 1
+ }
+ return Field{Key: key, Type: zapcore.BoolType, Integer: ival}
+}
+
+// ByteString constructs a field that carries UTF-8 encoded text as a []byte.
+// To log opaque binary blobs (which aren't necessarily valid UTF-8), use
+// Binary.
+func ByteString(key string, val []byte) Field {
+ return Field{Key: key, Type: zapcore.ByteStringType, Interface: val}
+}
+
+// Complex128 constructs a field that carries a complex number. Unlike most
+// numeric fields, this costs an allocation (to convert the complex128 to
+// interface{}).
+func Complex128(key string, val complex128) Field {
+ return Field{Key: key, Type: zapcore.Complex128Type, Interface: val}
+}
+
+// Complex64 constructs a field that carries a complex number. Unlike most
+// numeric fields, this costs an allocation (to convert the complex64 to
+// interface{}).
+func Complex64(key string, val complex64) Field {
+ return Field{Key: key, Type: zapcore.Complex64Type, Interface: val}
+}
+
+// Float64 constructs a field that carries a float64. The way the
+// floating-point value is represented is encoder-dependent, so marshaling is
+// necessarily lazy.
+func Float64(key string, val float64) Field {
+ return Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))}
+}
+
+// Float32 constructs a field that carries a float32. The way the
+// floating-point value is represented is encoder-dependent, so marshaling is
+// necessarily lazy.
+func Float32(key string, val float32) Field {
+ return Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))}
+}
+
+// Int constructs a field with the given key and value.
+func Int(key string, val int) Field {
+ return Int64(key, int64(val))
+}
+
+// Int64 constructs a field with the given key and value.
+func Int64(key string, val int64) Field {
+ return Field{Key: key, Type: zapcore.Int64Type, Integer: val}
+}
+
+// Int32 constructs a field with the given key and value.
+func Int32(key string, val int32) Field {
+ return Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)}
+}
+
+// Int16 constructs a field with the given key and value.
+func Int16(key string, val int16) Field {
+ return Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)}
+}
+
+// Int8 constructs a field with the given key and value.
+func Int8(key string, val int8) Field {
+ return Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)}
+}
+
+// String constructs a field with the given key and value.
+func String(key string, val string) Field {
+ return Field{Key: key, Type: zapcore.StringType, String: val}
+}
+
+// Uint constructs a field with the given key and value.
+func Uint(key string, val uint) Field {
+ return Uint64(key, uint64(val))
+}
+
+// Uint64 constructs a field with the given key and value.
+func Uint64(key string, val uint64) Field {
+ return Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)}
+}
+
+// Uint32 constructs a field with the given key and value.
+func Uint32(key string, val uint32) Field {
+ return Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)}
+}
+
+// Uint16 constructs a field with the given key and value.
+func Uint16(key string, val uint16) Field {
+ return Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)}
+}
+
+// Uint8 constructs a field with the given key and value.
+func Uint8(key string, val uint8) Field {
+ return Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)}
+}
+
+// Uintptr constructs a field with the given key and value.
+func Uintptr(key string, val uintptr) Field {
+ return Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)}
+}
+
+// Reflect constructs a field with the given key and an arbitrary object. It uses
+// an encoding-appropriate, reflection-based function to lazily serialize nearly
+// any object into the logging context, but it's relatively slow and
+// allocation-heavy. Outside tests, Any is always a better choice.
+//
+// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect
+// includes the error message in the final log output.
+func Reflect(key string, val interface{}) Field {
+ return Field{Key: key, Type: zapcore.ReflectType, Interface: val}
+}
+
+// Namespace creates a named, isolated scope within the logger's context. All
+// subsequent fields will be added to the new namespace.
+//
+// This helps prevent key collisions when injecting loggers into sub-components
+// or third-party libraries.
+func Namespace(key string) Field {
+ return Field{Key: key, Type: zapcore.NamespaceType}
+}
+
+// Stringer constructs a field with the given key and the output of the value's
+// String method. The Stringer's String method is called lazily.
+func Stringer(key string, val fmt.Stringer) Field {
+ return Field{Key: key, Type: zapcore.StringerType, Interface: val}
+}
+
+// Time constructs a Field with the given key and value. The encoder
+// controls how the time is serialized.
+func Time(key string, val time.Time) Field {
+ return Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()}
+}
+
+// Stack constructs a field that stores a stacktrace of the current goroutine
+// under provided key. Keep in mind that taking a stacktrace is eager and
+// expensive (relatively speaking); this function both makes an allocation and
+// takes about two microseconds.
+func Stack(key string) Field {
+ // Returning the stacktrace as a string costs an allocation, but saves us
+ // from expanding the zapcore.Field union struct to include a byte slice. Since
+ // taking a stacktrace is already so expensive (~10us), the extra allocation
+ // is okay.
+ return String(key, takeStacktrace())
+}
+
+// Duration constructs a field with the given key and value. The encoder
+// controls how the duration is serialized.
+func Duration(key string, val time.Duration) Field {
+ return Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)}
+}
+
+// Object constructs a field with the given key and ObjectMarshaler. It
+// provides a flexible, but still type-safe and efficient, way to add map- or
+// struct-like user-defined types to the logging context. The struct's
+// MarshalLogObject method is called lazily.
+func Object(key string, val zapcore.ObjectMarshaler) Field {
+ return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val}
+}
+
+// Any takes a key and an arbitrary value and chooses the best way to represent
+// them as a field, falling back to a reflection-based approach only if
+// necessary.
+//
+// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between
+// them. To minimize surprises, []byte values are treated as binary blobs, byte
+// values are treated as uint8, and runes are always treated as integers.
+func Any(key string, value interface{}) Field {
+ switch val := value.(type) {
+ case zapcore.ObjectMarshaler:
+ return Object(key, val)
+ case zapcore.ArrayMarshaler:
+ return Array(key, val)
+ case bool:
+ return Bool(key, val)
+ case []bool:
+ return Bools(key, val)
+ case complex128:
+ return Complex128(key, val)
+ case []complex128:
+ return Complex128s(key, val)
+ case complex64:
+ return Complex64(key, val)
+ case []complex64:
+ return Complex64s(key, val)
+ case float64:
+ return Float64(key, val)
+ case []float64:
+ return Float64s(key, val)
+ case float32:
+ return Float32(key, val)
+ case []float32:
+ return Float32s(key, val)
+ case int:
+ return Int(key, val)
+ case []int:
+ return Ints(key, val)
+ case int64:
+ return Int64(key, val)
+ case []int64:
+ return Int64s(key, val)
+ case int32:
+ return Int32(key, val)
+ case []int32:
+ return Int32s(key, val)
+ case int16:
+ return Int16(key, val)
+ case []int16:
+ return Int16s(key, val)
+ case int8:
+ return Int8(key, val)
+ case []int8:
+ return Int8s(key, val)
+ case string:
+ return String(key, val)
+ case []string:
+ return Strings(key, val)
+ case uint:
+ return Uint(key, val)
+ case []uint:
+ return Uints(key, val)
+ case uint64:
+ return Uint64(key, val)
+ case []uint64:
+ return Uint64s(key, val)
+ case uint32:
+ return Uint32(key, val)
+ case []uint32:
+ return Uint32s(key, val)
+ case uint16:
+ return Uint16(key, val)
+ case []uint16:
+ return Uint16s(key, val)
+ case uint8:
+ return Uint8(key, val)
+ case []byte:
+ return Binary(key, val)
+ case uintptr:
+ return Uintptr(key, val)
+ case []uintptr:
+ return Uintptrs(key, val)
+ case time.Time:
+ return Time(key, val)
+ case []time.Time:
+ return Times(key, val)
+ case time.Duration:
+ return Duration(key, val)
+ case []time.Duration:
+ return Durations(key, val)
+ case error:
+ return NamedError(key, val)
+ case []error:
+ return Errors(key, val)
+ case fmt.Stringer:
+ return Stringer(key, val)
+ default:
+ return Reflect(key, val)
+ }
+}
diff --git a/vendor/go.uber.org/zap/flag.go b/vendor/go.uber.org/zap/flag.go
new file mode 100644
index 000000000..131287507
--- /dev/null
+++ b/vendor/go.uber.org/zap/flag.go
@@ -0,0 +1,39 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "flag"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// LevelFlag uses the standard library's flag.Var to declare a global flag
+// with the specified name, default, and usage guidance. The returned value is
+// a pointer to the value of the flag.
+//
+// If you don't want to use the flag package's global state, you can use any
+// non-nil *Level as a flag.Value with your own *flag.FlagSet.
+func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level {
+ lvl := defaultLevel
+ flag.Var(&lvl, name, usage)
+ return &lvl
+}
diff --git a/vendor/go.uber.org/zap/glide.lock b/vendor/go.uber.org/zap/glide.lock
new file mode 100644
index 000000000..881b462c0
--- /dev/null
+++ b/vendor/go.uber.org/zap/glide.lock
@@ -0,0 +1,76 @@
+hash: f073ba522c06c88ea3075bde32a8aaf0969a840a66cab6318a0897d141ffee92
+updated: 2017-07-22T18:06:49.598185334-07:00
+imports:
+- name: go.uber.org/atomic
+ version: 4e336646b2ef9fc6e47be8e21594178f98e5ebcf
+- name: go.uber.org/multierr
+ version: 3c4937480c32f4c13a875a1829af76c98ca3d40a
+testImports:
+- name: github.com/apex/log
+ version: d9b960447bfa720077b2da653cc79e533455b499
+ subpackages:
+ - handlers/json
+- name: github.com/axw/gocov
+ version: 3a69a0d2a4ef1f263e2d92b041a69593d6964fe8
+ subpackages:
+ - gocov
+- name: github.com/davecgh/go-spew
+ version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
+ subpackages:
+ - spew
+- name: github.com/fatih/color
+ version: 62e9147c64a1ed519147b62a56a14e83e2be02c1
+- name: github.com/go-kit/kit
+ version: e10f5bf035be9af21fd5b2fb4469d5716c6ab07d
+ subpackages:
+ - log
+- name: github.com/go-logfmt/logfmt
+ version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5
+- name: github.com/go-stack/stack
+ version: 54be5f394ed2c3e19dac9134a40a95ba5a017f7b
+- name: github.com/golang/lint
+ version: c5fb716d6688a859aae56d26d3e6070808df29f7
+ subpackages:
+ - golint
+- name: github.com/kr/logfmt
+ version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
+- name: github.com/mattn/go-colorable
+ version: 3fa8c76f9daed4067e4a806fb7e4dc86455c6d6a
+- name: github.com/mattn/go-isatty
+ version: fc9e8d8ef48496124e79ae0df75490096eccf6fe
+- name: github.com/mattn/goveralls
+ version: 6efce81852ad1b7567c17ad71b03aeccc9dd9ae0
+- name: github.com/pborman/uuid
+ version: e790cca94e6cc75c7064b1332e63811d4aae1a53
+- name: github.com/pkg/errors
+ version: 645ef00459ed84a119197bfb8d8205042c6df63d
+- name: github.com/pmezard/go-difflib
+ version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
+ subpackages:
+ - difflib
+- name: github.com/rs/zerolog
+ version: eed4c2b94d945e0b2456ad6aa518a443986b5f22
+- name: github.com/satori/go.uuid
+ version: 5bf94b69c6b68ee1b541973bb8e1144db23a194b
+- name: github.com/sirupsen/logrus
+ version: 7dd06bf38e1e13df288d471a57d5adbac106be9e
+- name: github.com/stretchr/testify
+ version: f6abca593680b2315d2075e0f5e2a9751e3f431a
+ subpackages:
+ - assert
+ - require
+- name: go.pedge.io/lion
+ version: 87958e8713f1fa138d993087133b97e976642159
+- name: golang.org/x/sys
+ version: c4489faa6e5ab84c0ef40d6ee878f7a030281f0f
+ subpackages:
+ - unix
+- name: golang.org/x/tools
+ version: 496819729719f9d07692195e0a94d6edd2251389
+ subpackages:
+ - cover
+- name: gopkg.in/inconshreveable/log15.v2
+ version: b105bd37f74e5d9dc7b6ad7806715c7a2b83fd3f
+ subpackages:
+ - stack
+ - term
diff --git a/vendor/go.uber.org/zap/glide.yaml b/vendor/go.uber.org/zap/glide.yaml
new file mode 100644
index 000000000..94412594c
--- /dev/null
+++ b/vendor/go.uber.org/zap/glide.yaml
@@ -0,0 +1,35 @@
+package: go.uber.org/zap
+license: MIT
+import:
+- package: go.uber.org/atomic
+ version: ^1
+- package: go.uber.org/multierr
+ version: ^1
+testImport:
+- package: github.com/satori/go.uuid
+- package: github.com/sirupsen/logrus
+- package: github.com/apex/log
+ subpackages:
+ - handlers/json
+- package: github.com/go-kit/kit
+ subpackages:
+ - log
+- package: github.com/stretchr/testify
+ subpackages:
+ - assert
+ - require
+- package: gopkg.in/inconshreveable/log15.v2
+- package: github.com/mattn/goveralls
+- package: github.com/pborman/uuid
+- package: github.com/pkg/errors
+- package: go.pedge.io/lion
+- package: github.com/rs/zerolog
+- package: golang.org/x/tools
+ subpackages:
+ - cover
+- package: github.com/golang/lint
+ subpackages:
+ - golint
+- package: github.com/axw/gocov
+ subpackages:
+ - gocov
diff --git a/vendor/go.uber.org/zap/global.go b/vendor/go.uber.org/zap/global.go
new file mode 100644
index 000000000..d02232e39
--- /dev/null
+++ b/vendor/go.uber.org/zap/global.go
@@ -0,0 +1,169 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "os"
+ "sync"
+
+ "go.uber.org/zap/zapcore"
+)
+
+const (
+ _stdLogDefaultDepth = 2
+ _loggerWriterDepth = 2
+ _programmerErrorTemplate = "You've found a bug in zap! Please file a bug at " +
+ "https://github.com/uber-go/zap/issues/new and reference this error: %v"
+)
+
+var (
+ _globalMu sync.RWMutex
+ _globalL = NewNop()
+ _globalS = _globalL.Sugar()
+)
+
+// L returns the global Logger, which can be reconfigured with ReplaceGlobals.
+// It's safe for concurrent use.
+func L() *Logger {
+ _globalMu.RLock()
+ l := _globalL
+ _globalMu.RUnlock()
+ return l
+}
+
+// S returns the global SugaredLogger, which can be reconfigured with
+// ReplaceGlobals. It's safe for concurrent use.
+func S() *SugaredLogger {
+ _globalMu.RLock()
+ s := _globalS
+ _globalMu.RUnlock()
+ return s
+}
+
+// ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a
+// function to restore the original values. It's safe for concurrent use.
+func ReplaceGlobals(logger *Logger) func() {
+ _globalMu.Lock()
+ prev := _globalL
+ _globalL = logger
+ _globalS = logger.Sugar()
+ _globalMu.Unlock()
+ return func() { ReplaceGlobals(prev) }
+}
+
+// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at
+// InfoLevel. To redirect the standard library's package-global logging
+// functions, use RedirectStdLog instead.
+func NewStdLog(l *Logger) *log.Logger {
+ logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
+ f := logger.Info
+ return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */)
+}
+
+// NewStdLogAt returns *log.Logger which writes to supplied zap logger at
+// required level.
+func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) {
+ logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
+ logFunc, err := levelToFunc(logger, level)
+ if err != nil {
+ return nil, err
+ }
+ return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil
+}
+
+// RedirectStdLog redirects output from the standard library's package-global
+// logger to the supplied logger at InfoLevel. Since zap already handles caller
+// annotations, timestamps, etc., it automatically disables the standard
+// library's annotations and prefixing.
+//
+// It returns a function to restore the original prefix and flags and reset the
+// standard library's output to os.Stderr.
+func RedirectStdLog(l *Logger) func() {
+ f, err := redirectStdLogAt(l, InfoLevel)
+ if err != nil {
+ // Can't get here, since passing InfoLevel to redirectStdLogAt always
+ // works.
+ panic(fmt.Sprintf(_programmerErrorTemplate, err))
+ }
+ return f
+}
+
+// RedirectStdLogAt redirects output from the standard library's package-global
+// logger to the supplied logger at the specified level. Since zap already
+// handles caller annotations, timestamps, etc., it automatically disables the
+// standard library's annotations and prefixing.
+//
+// It returns a function to restore the original prefix and flags and reset the
+// standard library's output to os.Stderr.
+func RedirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {
+ return redirectStdLogAt(l, level)
+}
+
+func redirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {
+ flags := log.Flags()
+ prefix := log.Prefix()
+ log.SetFlags(0)
+ log.SetPrefix("")
+ logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
+ logFunc, err := levelToFunc(logger, level)
+ if err != nil {
+ return nil, err
+ }
+ log.SetOutput(&loggerWriter{logFunc})
+ return func() {
+ log.SetFlags(flags)
+ log.SetPrefix(prefix)
+ log.SetOutput(os.Stderr)
+ }, nil
+}
+
+func levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) {
+ switch lvl {
+ case DebugLevel:
+ return logger.Debug, nil
+ case InfoLevel:
+ return logger.Info, nil
+ case WarnLevel:
+ return logger.Warn, nil
+ case ErrorLevel:
+ return logger.Error, nil
+ case DPanicLevel:
+ return logger.DPanic, nil
+ case PanicLevel:
+ return logger.Panic, nil
+ case FatalLevel:
+ return logger.Fatal, nil
+ }
+ return nil, fmt.Errorf("unrecognized level: %q", lvl)
+}
+
+type loggerWriter struct {
+ logFunc func(msg string, fields ...Field)
+}
+
+func (l *loggerWriter) Write(p []byte) (int, error) {
+ p = bytes.TrimSpace(p)
+ l.logFunc(string(p))
+ return len(p), nil
+}
diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go
new file mode 100644
index 000000000..f171c3847
--- /dev/null
+++ b/vendor/go.uber.org/zap/http_handler.go
@@ -0,0 +1,81 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// ServeHTTP is a simple JSON endpoint that can report on or change the current
+// logging level.
+//
+// GET requests return a JSON description of the current logging level. PUT
+// requests change the logging level and expect a payload like:
+// {"level":"info"}
+//
+// It's perfectly safe to change the logging level while a program is running.
+func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ type errorResponse struct {
+ Error string `json:"error"`
+ }
+ type payload struct {
+ Level *zapcore.Level `json:"level"`
+ }
+
+ enc := json.NewEncoder(w)
+
+ switch r.Method {
+
+ case "GET":
+ current := lvl.Level()
+ enc.Encode(payload{Level: &current})
+
+ case "PUT":
+ var req payload
+
+ if errmess := func() string {
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ return fmt.Sprintf("Request body must be well-formed JSON: %v", err)
+ }
+ if req.Level == nil {
+ return "Must specify a logging level."
+ }
+ return ""
+ }(); errmess != "" {
+ w.WriteHeader(http.StatusBadRequest)
+ enc.Encode(errorResponse{Error: errmess})
+ return
+ }
+
+ lvl.SetLevel(*req.Level)
+ enc.Encode(req)
+
+ default:
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ enc.Encode(errorResponse{
+ Error: "Only GET and PUT are supported.",
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go b/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go
new file mode 100644
index 000000000..dad583aaa
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go
@@ -0,0 +1,31 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package bufferpool houses zap's shared internal buffer pool. Third-party
+// packages can recreate the same functionality with buffers.NewPool.
+package bufferpool
+
+import "go.uber.org/zap/buffer"
+
+var (
+ _pool = buffer.NewPool()
+ // Get retrieves a buffer from the pool, creating one if necessary.
+ Get = _pool.Get
+)
diff --git a/vendor/go.uber.org/zap/internal/color/color.go b/vendor/go.uber.org/zap/internal/color/color.go
new file mode 100644
index 000000000..c4d5d02ab
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/color/color.go
@@ -0,0 +1,44 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package color adds coloring functionality for TTY output.
+package color
+
+import "fmt"
+
+// Foreground colors.
+const (
+ Black Color = iota + 30
+ Red
+ Green
+ Yellow
+ Blue
+ Magenta
+ Cyan
+ White
+)
+
+// Color represents a text color.
+type Color uint8
+
+// Add adds the coloring to the given string.
+func (c Color) Add(s string) string {
+ return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s)
+}
diff --git a/vendor/go.uber.org/zap/internal/exit/exit.go b/vendor/go.uber.org/zap/internal/exit/exit.go
new file mode 100644
index 000000000..dfc5b05fe
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/exit/exit.go
@@ -0,0 +1,64 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package exit provides stubs so that unit tests can exercise code that calls
+// os.Exit(1).
+package exit
+
+import "os"
+
+var real = func() { os.Exit(1) }
+
+// Exit normally terminates the process by calling os.Exit(1). If the package
+// is stubbed, it instead records a call in the testing spy.
+func Exit() {
+ real()
+}
+
+// A StubbedExit is a testing fake for os.Exit.
+type StubbedExit struct {
+ Exited bool
+ prev func()
+}
+
+// Stub substitutes a fake for the call to os.Exit(1).
+func Stub() *StubbedExit {
+ s := &StubbedExit{prev: real}
+ real = s.exit
+ return s
+}
+
+// WithStub runs the supplied function with Exit stubbed. It returns the stub
+// used, so that users can test whether the process would have crashed.
+func WithStub(f func()) *StubbedExit {
+ s := Stub()
+ defer s.Unstub()
+ f()
+ return s
+}
+
+// Unstub restores the previous exit function.
+func (se *StubbedExit) Unstub() {
+ real = se.prev
+}
+
+func (se *StubbedExit) exit() {
+ se.Exited = true
+}
diff --git a/vendor/go.uber.org/zap/level.go b/vendor/go.uber.org/zap/level.go
new file mode 100644
index 000000000..3567a9a1e
--- /dev/null
+++ b/vendor/go.uber.org/zap/level.go
@@ -0,0 +1,132 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "go.uber.org/atomic"
+ "go.uber.org/zap/zapcore"
+)
+
+const (
+ // DebugLevel logs are typically voluminous, and are usually disabled in
+ // production.
+ DebugLevel = zapcore.DebugLevel
+ // InfoLevel is the default logging priority.
+ InfoLevel = zapcore.InfoLevel
+ // WarnLevel logs are more important than Info, but don't need individual
+ // human review.
+ WarnLevel = zapcore.WarnLevel
+ // ErrorLevel logs are high-priority. If an application is running smoothly,
+ // it shouldn't generate any error-level logs.
+ ErrorLevel = zapcore.ErrorLevel
+ // DPanicLevel logs are particularly important errors. In development the
+ // logger panics after writing the message.
+ DPanicLevel = zapcore.DPanicLevel
+ // PanicLevel logs a message, then panics.
+ PanicLevel = zapcore.PanicLevel
+ // FatalLevel logs a message, then calls os.Exit(1).
+ FatalLevel = zapcore.FatalLevel
+)
+
+// LevelEnablerFunc is a convenient way to implement zapcore.LevelEnabler with
+// an anonymous function.
+//
+// It's particularly useful when splitting log output between different
+// outputs (e.g., standard error and standard out). For sample code, see the
+// package-level AdvancedConfiguration example.
+type LevelEnablerFunc func(zapcore.Level) bool
+
+// Enabled calls the wrapped function.
+func (f LevelEnablerFunc) Enabled(lvl zapcore.Level) bool { return f(lvl) }
+
+// An AtomicLevel is an atomically changeable, dynamic logging level. It lets
+// you safely change the log level of a tree of loggers (the root logger and
+// any children created by adding context) at runtime.
+//
+// The AtomicLevel itself is an http.Handler that serves a JSON endpoint to
+// alter its level.
+//
+// AtomicLevels must be created with the NewAtomicLevel constructor to allocate
+// their internal atomic pointer.
+type AtomicLevel struct {
+ l *atomic.Int32
+}
+
+// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging
+// enabled.
+func NewAtomicLevel() AtomicLevel {
+ return AtomicLevel{
+ l: atomic.NewInt32(int32(InfoLevel)),
+ }
+}
+
+// NewAtomicLevelAt is a convenience function that creates an AtomicLevel
+// and then calls SetLevel with the given level.
+func NewAtomicLevelAt(l zapcore.Level) AtomicLevel {
+ a := NewAtomicLevel()
+ a.SetLevel(l)
+ return a
+}
+
+// Enabled implements the zapcore.LevelEnabler interface, which allows the
+// AtomicLevel to be used in place of traditional static levels.
+func (lvl AtomicLevel) Enabled(l zapcore.Level) bool {
+ return lvl.Level().Enabled(l)
+}
+
+// Level returns the minimum enabled log level.
+func (lvl AtomicLevel) Level() zapcore.Level {
+ return zapcore.Level(int8(lvl.l.Load()))
+}
+
+// SetLevel alters the logging level.
+func (lvl AtomicLevel) SetLevel(l zapcore.Level) {
+ lvl.l.Store(int32(l))
+}
+
+// String returns the string representation of the underlying Level.
+func (lvl AtomicLevel) String() string {
+ return lvl.Level().String()
+}
+
+// UnmarshalText unmarshals the text to an AtomicLevel. It uses the same text
+// representations as the static zapcore.Levels ("debug", "info", "warn",
+// "error", "dpanic", "panic", and "fatal").
+func (lvl *AtomicLevel) UnmarshalText(text []byte) error {
+ if lvl.l == nil {
+ lvl.l = &atomic.Int32{}
+ }
+
+ var l zapcore.Level
+ if err := l.UnmarshalText(text); err != nil {
+ return err
+ }
+
+ lvl.SetLevel(l)
+ return nil
+}
+
+// MarshalText marshals the AtomicLevel to a byte slice. It uses the same
+// text representation as the static zapcore.Levels ("debug", "info", "warn",
+// "error", "dpanic", "panic", and "fatal").
+func (lvl AtomicLevel) MarshalText() (text []byte, err error) {
+ return lvl.Level().MarshalText()
+}
diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go
new file mode 100644
index 000000000..dc8f6e3a4
--- /dev/null
+++ b/vendor/go.uber.org/zap/logger.go
@@ -0,0 +1,305 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "runtime"
+ "strings"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// A Logger provides fast, leveled, structured logging. All methods are safe
+// for concurrent use.
+//
+// The Logger is designed for contexts in which every microsecond and every
+// allocation matters, so its API intentionally favors performance and type
+// safety over brevity. For most applications, the SugaredLogger strikes a
+// better balance between performance and ergonomics.
+type Logger struct {
+ core zapcore.Core
+
+ development bool
+ name string
+ errorOutput zapcore.WriteSyncer
+
+ addCaller bool
+ addStack zapcore.LevelEnabler
+
+ callerSkip int
+}
+
+// New constructs a new Logger from the provided zapcore.Core and Options. If
+// the passed zapcore.Core is nil, it falls back to using a no-op
+// implementation.
+//
+// This is the most flexible way to construct a Logger, but also the most
+// verbose. For typical use cases, the highly-opinionated presets
+// (NewProduction, NewDevelopment, and NewExample) or the Config struct are
+// more convenient.
+//
+// For sample code, see the package-level AdvancedConfiguration example.
+func New(core zapcore.Core, options ...Option) *Logger {
+ if core == nil {
+ return NewNop()
+ }
+ log := &Logger{
+ core: core,
+ errorOutput: zapcore.Lock(os.Stderr),
+ addStack: zapcore.FatalLevel + 1,
+ }
+ return log.WithOptions(options...)
+}
+
+// NewNop returns a no-op Logger. It never writes out logs or internal errors,
+// and it never runs user-defined hooks.
+//
+// Using WithOptions to replace the Core or error output of a no-op Logger can
+// re-enable logging.
+func NewNop() *Logger {
+ return &Logger{
+ core: zapcore.NewNopCore(),
+ errorOutput: zapcore.AddSync(ioutil.Discard),
+ addStack: zapcore.FatalLevel + 1,
+ }
+}
+
+// NewProduction builds a sensible production Logger that writes InfoLevel and
+// above logs to standard error as JSON.
+//
+// It's a shortcut for NewProductionConfig().Build(...Option).
+func NewProduction(options ...Option) (*Logger, error) {
+ return NewProductionConfig().Build(options...)
+}
+
+// NewDevelopment builds a development Logger that writes DebugLevel and above
+// logs to standard error in a human-friendly format.
+//
+// It's a shortcut for NewDevelopmentConfig().Build(...Option).
+func NewDevelopment(options ...Option) (*Logger, error) {
+ return NewDevelopmentConfig().Build(options...)
+}
+
+// NewExample builds a Logger that's designed for use in zap's testable
+// examples. It writes DebugLevel and above logs to standard out as JSON, but
+// omits the timestamp and calling function to keep example output
+// short and deterministic.
+func NewExample(options ...Option) *Logger {
+ encoderCfg := zapcore.EncoderConfig{
+ MessageKey: "msg",
+ LevelKey: "level",
+ NameKey: "logger",
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeDuration: zapcore.StringDurationEncoder,
+ }
+ core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel)
+ return New(core).WithOptions(options...)
+}
+
+// Sugar wraps the Logger to provide a more ergonomic, but slightly slower,
+// API. Sugaring a Logger is quite inexpensive, so it's reasonable for a
+// single application to use both Loggers and SugaredLoggers, converting
+// between them on the boundaries of performance-sensitive code.
+func (log *Logger) Sugar() *SugaredLogger {
+ core := log.clone()
+ core.callerSkip += 2
+ return &SugaredLogger{core}
+}
+
+// Named adds a new path segment to the logger's name. Segments are joined by
+// periods. By default, Loggers are unnamed.
+func (log *Logger) Named(s string) *Logger {
+ if s == "" {
+ return log
+ }
+ l := log.clone()
+ if log.name == "" {
+ l.name = s
+ } else {
+ l.name = strings.Join([]string{l.name, s}, ".")
+ }
+ return l
+}
+
+// WithOptions clones the current Logger, applies the supplied Options, and
+// returns the resulting Logger. It's safe to use concurrently.
+func (log *Logger) WithOptions(opts ...Option) *Logger {
+ c := log.clone()
+ for _, opt := range opts {
+ opt.apply(c)
+ }
+ return c
+}
+
+// With creates a child logger and adds structured context to it. Fields added
+// to the child don't affect the parent, and vice versa.
+func (log *Logger) With(fields ...Field) *Logger {
+ if len(fields) == 0 {
+ return log
+ }
+ l := log.clone()
+ l.core = l.core.With(fields)
+ return l
+}
+
+// Check returns a CheckedEntry if logging a message at the specified level
+// is enabled. It's a completely optional optimization; in high-performance
+// applications, Check can help avoid allocating a slice to hold fields.
+func (log *Logger) Check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
+ return log.check(lvl, msg)
+}
+
+// Debug logs a message at DebugLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Debug(msg string, fields ...Field) {
+ if ce := log.check(DebugLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Info logs a message at InfoLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Info(msg string, fields ...Field) {
+ if ce := log.check(InfoLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Warn logs a message at WarnLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Warn(msg string, fields ...Field) {
+ if ce := log.check(WarnLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Error logs a message at ErrorLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Error(msg string, fields ...Field) {
+ if ce := log.check(ErrorLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// DPanic logs a message at DPanicLevel. The message includes any fields
+// passed at the log site, as well as any fields accumulated on the logger.
+//
+// If the logger is in development mode, it then panics (DPanic means
+// "development panic"). This is useful for catching errors that are
+// recoverable, but shouldn't ever happen.
+func (log *Logger) DPanic(msg string, fields ...Field) {
+ if ce := log.check(DPanicLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Panic logs a message at PanicLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+//
+// The logger then panics, even if logging at PanicLevel is disabled.
+func (log *Logger) Panic(msg string, fields ...Field) {
+ if ce := log.check(PanicLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Fatal logs a message at FatalLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+//
+// The logger then calls os.Exit(1), even if logging at FatalLevel is
+// disabled.
+func (log *Logger) Fatal(msg string, fields ...Field) {
+ if ce := log.check(FatalLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Sync calls the underlying Core's Sync method, flushing any buffered log
+// entries. Applications should take care to call Sync before exiting.
+func (log *Logger) Sync() error {
+ return log.core.Sync()
+}
+
+// Core returns the Logger's underlying zapcore.Core.
+func (log *Logger) Core() zapcore.Core {
+ return log.core
+}
+
+func (log *Logger) clone() *Logger {
+ copy := *log
+ return &copy
+}
+
+func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
+ // check must always be called directly by a method in the Logger interface
+ // (e.g., Check, Info, Fatal).
+ const callerSkipOffset = 2
+
+ // Create basic checked entry thru the core; this will be non-nil if the
+ // log message will actually be written somewhere.
+ ent := zapcore.Entry{
+ LoggerName: log.name,
+ Time: time.Now(),
+ Level: lvl,
+ Message: msg,
+ }
+ ce := log.core.Check(ent, nil)
+ willWrite := ce != nil
+
+ // Set up any required terminal behavior.
+ switch ent.Level {
+ case zapcore.PanicLevel:
+ ce = ce.Should(ent, zapcore.WriteThenPanic)
+ case zapcore.FatalLevel:
+ ce = ce.Should(ent, zapcore.WriteThenFatal)
+ case zapcore.DPanicLevel:
+ if log.development {
+ ce = ce.Should(ent, zapcore.WriteThenPanic)
+ }
+ }
+
+ // Only do further annotation if we're going to write this message; checked
+ // entries that exist only for terminal behavior don't benefit from
+ // annotation.
+ if !willWrite {
+ return ce
+ }
+
+ // Thread the error output through to the CheckedEntry.
+ ce.ErrorOutput = log.errorOutput
+ if log.addCaller {
+ ce.Entry.Caller = zapcore.NewEntryCaller(runtime.Caller(log.callerSkip + callerSkipOffset))
+ if !ce.Entry.Caller.Defined {
+ fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", time.Now().UTC())
+ log.errorOutput.Sync()
+ }
+ }
+ if log.addStack.Enabled(ce.Entry.Level) {
+ ce.Entry.Stack = Stack("").String
+ }
+
+ return ce
+}
diff --git a/vendor/go.uber.org/zap/options.go b/vendor/go.uber.org/zap/options.go
new file mode 100644
index 000000000..7a6b0fca1
--- /dev/null
+++ b/vendor/go.uber.org/zap/options.go
@@ -0,0 +1,109 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import "go.uber.org/zap/zapcore"
+
+// An Option configures a Logger.
+type Option interface {
+ apply(*Logger)
+}
+
+// optionFunc wraps a func so it satisfies the Option interface.
+type optionFunc func(*Logger)
+
+func (f optionFunc) apply(log *Logger) {
+ f(log)
+}
+
+// WrapCore wraps or replaces the Logger's underlying zapcore.Core.
+func WrapCore(f func(zapcore.Core) zapcore.Core) Option {
+ return optionFunc(func(log *Logger) {
+ log.core = f(log.core)
+ })
+}
+
+// Hooks registers functions which will be called each time the Logger writes
+// out an Entry. Repeated use of Hooks is additive.
+//
+// Hooks are useful for simple side effects, like capturing metrics for the
+// number of emitted logs. More complex side effects, including anything that
+// requires access to the Entry's structured fields, should be implemented as
+// a zapcore.Core instead. See zapcore.RegisterHooks for details.
+func Hooks(hooks ...func(zapcore.Entry) error) Option {
+ return optionFunc(func(log *Logger) {
+ log.core = zapcore.RegisterHooks(log.core, hooks...)
+ })
+}
+
+// Fields adds fields to the Logger.
+func Fields(fs ...Field) Option {
+ return optionFunc(func(log *Logger) {
+ log.core = log.core.With(fs)
+ })
+}
+
+// ErrorOutput sets the destination for errors generated by the Logger. Note
+// that this option only affects internal errors; for sample code that sends
+// error-level logs to a different location from info- and debug-level logs,
+// see the package-level AdvancedConfiguration example.
+//
+// The supplied WriteSyncer must be safe for concurrent use. The Open and
+// zapcore.Lock functions are the simplest ways to protect files with a mutex.
+func ErrorOutput(w zapcore.WriteSyncer) Option {
+ return optionFunc(func(log *Logger) {
+ log.errorOutput = w
+ })
+}
+
+// Development puts the logger in development mode, which makes DPanic-level
+// logs panic instead of simply logging an error.
+func Development() Option {
+ return optionFunc(func(log *Logger) {
+ log.development = true
+ })
+}
+
+// AddCaller configures the Logger to annotate each message with the filename
+// and line number of zap's caller.
+func AddCaller() Option {
+ return optionFunc(func(log *Logger) {
+ log.addCaller = true
+ })
+}
+
+// AddCallerSkip increases the number of callers skipped by caller annotation
+// (as enabled by the AddCaller option). When building wrappers around the
+// Logger and SugaredLogger, supplying this Option prevents zap from always
+// reporting the wrapper code as the caller.
+func AddCallerSkip(skip int) Option {
+ return optionFunc(func(log *Logger) {
+ log.callerSkip += skip
+ })
+}
+
+// AddStacktrace configures the Logger to record a stack trace for all messages at
+// or above a given level.
+func AddStacktrace(lvl zapcore.LevelEnabler) Option {
+ return optionFunc(func(log *Logger) {
+ log.addStack = lvl
+ })
+}
diff --git a/vendor/go.uber.org/zap/stacktrace.go b/vendor/go.uber.org/zap/stacktrace.go
new file mode 100644
index 000000000..100fac216
--- /dev/null
+++ b/vendor/go.uber.org/zap/stacktrace.go
@@ -0,0 +1,126 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "runtime"
+ "strings"
+ "sync"
+
+ "go.uber.org/zap/internal/bufferpool"
+)
+
+const _zapPackage = "go.uber.org/zap"
+
+var (
+ _stacktracePool = sync.Pool{
+ New: func() interface{} {
+ return newProgramCounters(64)
+ },
+ }
+
+ // We add "." and "/" suffixes to the package name to ensure we only match
+ // the exact package and not any package with the same prefix.
+ _zapStacktracePrefixes = addPrefix(_zapPackage, ".", "/")
+ _zapStacktraceVendorContains = addPrefix("/vendor/", _zapStacktracePrefixes...)
+)
+
+func takeStacktrace() string {
+ buffer := bufferpool.Get()
+ defer buffer.Free()
+ programCounters := _stacktracePool.Get().(*programCounters)
+ defer _stacktracePool.Put(programCounters)
+
+ var numFrames int
+ for {
+ // Skip the call to runtime.Counters and takeStacktrace so that the
+ // program counters start at the caller of takeStacktrace.
+ numFrames = runtime.Callers(2, programCounters.pcs)
+ if numFrames < len(programCounters.pcs) {
+ break
+ }
+ // Don't put the too-short counter slice back into the pool; this lets
+ // the pool adjust if we consistently take deep stacktraces.
+ programCounters = newProgramCounters(len(programCounters.pcs) * 2)
+ }
+
+ i := 0
+ skipZapFrames := true // skip all consecutive zap frames at the beginning.
+ frames := runtime.CallersFrames(programCounters.pcs[:numFrames])
+
+ // Note: On the last iteration, frames.Next() returns false, with a valid
+ // frame, but we ignore this frame. The last frame is a a runtime frame which
+ // adds noise, since it's only either runtime.main or runtime.goexit.
+ for frame, more := frames.Next(); more; frame, more = frames.Next() {
+ if skipZapFrames && isZapFrame(frame.Function) {
+ continue
+ } else {
+ skipZapFrames = false
+ }
+
+ if i != 0 {
+ buffer.AppendByte('\n')
+ }
+ i++
+ buffer.AppendString(frame.Function)
+ buffer.AppendByte('\n')
+ buffer.AppendByte('\t')
+ buffer.AppendString(frame.File)
+ buffer.AppendByte(':')
+ buffer.AppendInt(int64(frame.Line))
+ }
+
+ return buffer.String()
+}
+
+func isZapFrame(function string) bool {
+ for _, prefix := range _zapStacktracePrefixes {
+ if strings.HasPrefix(function, prefix) {
+ return true
+ }
+ }
+
+ // We can't use a prefix match here since the location of the vendor
+ // directory affects the prefix. Instead we do a contains match.
+ for _, contains := range _zapStacktraceVendorContains {
+ if strings.Contains(function, contains) {
+ return true
+ }
+ }
+
+ return false
+}
+
+type programCounters struct {
+ pcs []uintptr
+}
+
+func newProgramCounters(size int) *programCounters {
+ return &programCounters{make([]uintptr, size)}
+}
+
+func addPrefix(prefix string, ss ...string) []string {
+ withPrefix := make([]string, len(ss))
+ for i, s := range ss {
+ withPrefix[i] = prefix + s
+ }
+ return withPrefix
+}
diff --git a/vendor/go.uber.org/zap/sugar.go b/vendor/go.uber.org/zap/sugar.go
new file mode 100644
index 000000000..77ca227f4
--- /dev/null
+++ b/vendor/go.uber.org/zap/sugar.go
@@ -0,0 +1,304 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "fmt"
+
+ "go.uber.org/zap/zapcore"
+
+ "go.uber.org/multierr"
+)
+
+const (
+ _oddNumberErrMsg = "Ignored key without a value."
+ _nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys."
+)
+
+// A SugaredLogger wraps the base Logger functionality in a slower, but less
+// verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar
+// method.
+//
+// Unlike the Logger, the SugaredLogger doesn't insist on structured logging.
+// For each log level, it exposes three methods: one for loosely-typed
+// structured logging, one for println-style formatting, and one for
+// printf-style formatting. For example, SugaredLoggers can produce InfoLevel
+// output with Infow ("info with" structured context), Info, or Infof.
+type SugaredLogger struct {
+ base *Logger
+}
+
+// Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring
+// is quite inexpensive, so it's reasonable for a single application to use
+// both Loggers and SugaredLoggers, converting between them on the boundaries
+// of performance-sensitive code.
+func (s *SugaredLogger) Desugar() *Logger {
+ base := s.base.clone()
+ base.callerSkip -= 2
+ return base
+}
+
+// Named adds a sub-scope to the logger's name. See Logger.Named for details.
+func (s *SugaredLogger) Named(name string) *SugaredLogger {
+ return &SugaredLogger{base: s.base.Named(name)}
+}
+
+// With adds a variadic number of fields to the logging context. It accepts a
+// mix of strongly-typed Field objects and loosely-typed key-value pairs. When
+// processing pairs, the first element of the pair is used as the field key
+// and the second as the field value.
+//
+// For example,
+// sugaredLogger.With(
+// "hello", "world",
+// "failure", errors.New("oh no"),
+// Stack(),
+// "count", 42,
+// "user", User{Name: "alice"},
+// )
+// is the equivalent of
+// unsugared.With(
+// String("hello", "world"),
+// String("failure", "oh no"),
+// Stack(),
+// Int("count", 42),
+// Object("user", User{Name: "alice"}),
+// )
+//
+// Note that the keys in key-value pairs should be strings. In development,
+// passing a non-string key panics. In production, the logger is more
+// forgiving: a separate error is logged, but the key-value pair is skipped
+// and execution continues. Passing an orphaned key triggers similar behavior:
+// panics in development and errors in production.
+func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger {
+ return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)}
+}
+
+// Debug uses fmt.Sprint to construct and log a message.
+func (s *SugaredLogger) Debug(args ...interface{}) {
+ s.log(DebugLevel, "", args, nil)
+}
+
+// Info uses fmt.Sprint to construct and log a message.
+func (s *SugaredLogger) Info(args ...interface{}) {
+ s.log(InfoLevel, "", args, nil)
+}
+
+// Warn uses fmt.Sprint to construct and log a message.
+func (s *SugaredLogger) Warn(args ...interface{}) {
+ s.log(WarnLevel, "", args, nil)
+}
+
+// Error uses fmt.Sprint to construct and log a message.
+func (s *SugaredLogger) Error(args ...interface{}) {
+ s.log(ErrorLevel, "", args, nil)
+}
+
+// DPanic uses fmt.Sprint to construct and log a message. In development, the
+// logger then panics. (See DPanicLevel for details.)
+func (s *SugaredLogger) DPanic(args ...interface{}) {
+ s.log(DPanicLevel, "", args, nil)
+}
+
+// Panic uses fmt.Sprint to construct and log a message, then panics.
+func (s *SugaredLogger) Panic(args ...interface{}) {
+ s.log(PanicLevel, "", args, nil)
+}
+
+// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit.
+func (s *SugaredLogger) Fatal(args ...interface{}) {
+ s.log(FatalLevel, "", args, nil)
+}
+
+// Debugf uses fmt.Sprintf to log a templated message.
+func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
+ s.log(DebugLevel, template, args, nil)
+}
+
+// Infof uses fmt.Sprintf to log a templated message.
+func (s *SugaredLogger) Infof(template string, args ...interface{}) {
+ s.log(InfoLevel, template, args, nil)
+}
+
+// Warnf uses fmt.Sprintf to log a templated message.
+func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
+ s.log(WarnLevel, template, args, nil)
+}
+
+// Errorf uses fmt.Sprintf to log a templated message.
+func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
+ s.log(ErrorLevel, template, args, nil)
+}
+
+// DPanicf uses fmt.Sprintf to log a templated message. In development, the
+// logger then panics. (See DPanicLevel for details.)
+func (s *SugaredLogger) DPanicf(template string, args ...interface{}) {
+ s.log(DPanicLevel, template, args, nil)
+}
+
+// Panicf uses fmt.Sprintf to log a templated message, then panics.
+func (s *SugaredLogger) Panicf(template string, args ...interface{}) {
+ s.log(PanicLevel, template, args, nil)
+}
+
+// Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit.
+func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
+ s.log(FatalLevel, template, args, nil)
+}
+
+// Debugw logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+//
+// When debug-level logging is disabled, this is much faster than
+// s.With(keysAndValues).Debug(msg)
+func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {
+ s.log(DebugLevel, msg, nil, keysAndValues)
+}
+
+// Infow logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) {
+ s.log(InfoLevel, msg, nil, keysAndValues)
+}
+
+// Warnw logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) {
+ s.log(WarnLevel, msg, nil, keysAndValues)
+}
+
+// Errorw logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {
+ s.log(ErrorLevel, msg, nil, keysAndValues)
+}
+
+// DPanicw logs a message with some additional context. In development, the
+// logger then panics. (See DPanicLevel for details.) The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) {
+ s.log(DPanicLevel, msg, nil, keysAndValues)
+}
+
+// Panicw logs a message with some additional context, then panics. The
+// variadic key-value pairs are treated as they are in With.
+func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) {
+ s.log(PanicLevel, msg, nil, keysAndValues)
+}
+
+// Fatalw logs a message with some additional context, then calls os.Exit. The
+// variadic key-value pairs are treated as they are in With.
+func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
+ s.log(FatalLevel, msg, nil, keysAndValues)
+}
+
+// Sync flushes any buffered log entries.
+func (s *SugaredLogger) Sync() error {
+ return s.base.Sync()
+}
+
+func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
+ // If logging at this level is completely disabled, skip the overhead of
+ // string formatting.
+ if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
+ return
+ }
+
+ // Format with Sprint, Sprintf, or neither.
+ msg := template
+ if msg == "" && len(fmtArgs) > 0 {
+ msg = fmt.Sprint(fmtArgs...)
+ } else if msg != "" && len(fmtArgs) > 0 {
+ msg = fmt.Sprintf(template, fmtArgs...)
+ }
+
+ if ce := s.base.Check(lvl, msg); ce != nil {
+ ce.Write(s.sweetenFields(context)...)
+ }
+}
+
+func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
+ if len(args) == 0 {
+ return nil
+ }
+
+ // Allocate enough space for the worst case; if users pass only structured
+ // fields, we shouldn't penalize them with extra allocations.
+ fields := make([]Field, 0, len(args))
+ var invalid invalidPairs
+
+ for i := 0; i < len(args); {
+ // This is a strongly-typed field. Consume it and move on.
+ if f, ok := args[i].(Field); ok {
+ fields = append(fields, f)
+ i++
+ continue
+ }
+
+ // Make sure this element isn't a dangling key.
+ if i == len(args)-1 {
+ s.base.DPanic(_oddNumberErrMsg, Any("ignored", args[i]))
+ break
+ }
+
+ // Consume this value and the next, treating them as a key-value pair. If the
+ // key isn't a string, add this pair to the slice of invalid pairs.
+ key, val := args[i], args[i+1]
+ if keyStr, ok := key.(string); !ok {
+ // Subsequent errors are likely, so allocate once up front.
+ if cap(invalid) == 0 {
+ invalid = make(invalidPairs, 0, len(args)/2)
+ }
+ invalid = append(invalid, invalidPair{i, key, val})
+ } else {
+ fields = append(fields, Any(keyStr, val))
+ }
+ i += 2
+ }
+
+ // If we encountered any invalid key-value pairs, log an error.
+ if len(invalid) > 0 {
+ s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid))
+ }
+ return fields
+}
+
+type invalidPair struct {
+ position int
+ key, value interface{}
+}
+
+func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddInt64("position", int64(p.position))
+ Any("key", p.key).AddTo(enc)
+ Any("value", p.value).AddTo(enc)
+ return nil
+}
+
+type invalidPairs []invalidPair
+
+func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error {
+ var err error
+ for i := range ps {
+ err = multierr.Append(err, enc.AppendObject(ps[i]))
+ }
+ return err
+}
diff --git a/vendor/go.uber.org/zap/time.go b/vendor/go.uber.org/zap/time.go
new file mode 100644
index 000000000..c5a1f1622
--- /dev/null
+++ b/vendor/go.uber.org/zap/time.go
@@ -0,0 +1,27 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import "time"
+
+func timeToMillis(t time.Time) int64 {
+ return t.UnixNano() / int64(time.Millisecond)
+}
diff --git a/vendor/go.uber.org/zap/writer.go b/vendor/go.uber.org/zap/writer.go
new file mode 100644
index 000000000..16f55ce48
--- /dev/null
+++ b/vendor/go.uber.org/zap/writer.go
@@ -0,0 +1,96 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "io/ioutil"
+ "os"
+
+ "go.uber.org/zap/zapcore"
+
+ "go.uber.org/multierr"
+)
+
+// Open is a high-level wrapper that takes a variadic number of paths, opens or
+// creates each of the specified files, and combines them into a locked
+// WriteSyncer. It also returns any error encountered and a function to close
+// any opened files.
+//
+// Passing no paths returns a no-op WriteSyncer. The special paths "stdout" and
+// "stderr" are interpreted as os.Stdout and os.Stderr, respectively.
+func Open(paths ...string) (zapcore.WriteSyncer, func(), error) {
+ writers, close, err := open(paths)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ writer := CombineWriteSyncers(writers...)
+ return writer, close, nil
+}
+
+func open(paths []string) ([]zapcore.WriteSyncer, func(), error) {
+ var openErr error
+ writers := make([]zapcore.WriteSyncer, 0, len(paths))
+ files := make([]*os.File, 0, len(paths))
+ close := func() {
+ for _, f := range files {
+ f.Close()
+ }
+ }
+ for _, path := range paths {
+ switch path {
+ case "stdout":
+ writers = append(writers, os.Stdout)
+ // Don't close standard out.
+ continue
+ case "stderr":
+ writers = append(writers, os.Stderr)
+ // Don't close standard error.
+ continue
+ }
+ f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
+ openErr = multierr.Append(openErr, err)
+ if err == nil {
+ writers = append(writers, f)
+ files = append(files, f)
+ }
+ }
+
+ if openErr != nil {
+ close()
+ return writers, nil, openErr
+ }
+
+ return writers, close, nil
+}
+
+// CombineWriteSyncers is a utility that combines multiple WriteSyncers into a
+// single, locked WriteSyncer. If no inputs are supplied, it returns a no-op
+// WriteSyncer.
+//
+// It's provided purely as a convenience; the result is no different from
+// using zapcore.NewMultiWriteSyncer and zapcore.Lock individually.
+func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer {
+ if len(writers) == 0 {
+ return zapcore.AddSync(ioutil.Discard)
+ }
+ return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...))
+}
diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go
new file mode 100644
index 000000000..b7875966f
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/console_encoder.go
@@ -0,0 +1,147 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "fmt"
+ "sync"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+)
+
+var _sliceEncoderPool = sync.Pool{
+ New: func() interface{} {
+ return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)}
+ },
+}
+
+func getSliceEncoder() *sliceArrayEncoder {
+ return _sliceEncoderPool.Get().(*sliceArrayEncoder)
+}
+
+func putSliceEncoder(e *sliceArrayEncoder) {
+ e.elems = e.elems[:0]
+ _sliceEncoderPool.Put(e)
+}
+
+type consoleEncoder struct {
+ *jsonEncoder
+}
+
+// NewConsoleEncoder creates an encoder whose output is designed for human -
+// rather than machine - consumption. It serializes the core log entry data
+// (message, level, timestamp, etc.) in a plain-text format and leaves the
+// structured context as JSON.
+//
+// Note that although the console encoder doesn't use the keys specified in the
+// encoder configuration, it will omit any element whose key is set to the empty
+// string.
+func NewConsoleEncoder(cfg EncoderConfig) Encoder {
+ return consoleEncoder{newJSONEncoder(cfg, true)}
+}
+
+func (c consoleEncoder) Clone() Encoder {
+ return consoleEncoder{c.jsonEncoder.Clone().(*jsonEncoder)}
+}
+
+func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {
+ line := bufferpool.Get()
+
+ // We don't want the entry's metadata to be quoted and escaped (if it's
+ // encoded as strings), which means that we can't use the JSON encoder. The
+ // simplest option is to use the memory encoder and fmt.Fprint.
+ //
+ // If this ever becomes a performance bottleneck, we can implement
+ // ArrayEncoder for our plain-text format.
+ arr := getSliceEncoder()
+ if c.TimeKey != "" && c.EncodeTime != nil {
+ c.EncodeTime(ent.Time, arr)
+ }
+ if c.LevelKey != "" && c.EncodeLevel != nil {
+ c.EncodeLevel(ent.Level, arr)
+ }
+ if ent.LoggerName != "" && c.NameKey != "" {
+ nameEncoder := c.EncodeName
+
+ if nameEncoder == nil {
+ // Fall back to FullNameEncoder for backward compatibility.
+ nameEncoder = FullNameEncoder
+ }
+
+ nameEncoder(ent.LoggerName, arr)
+ }
+ if ent.Caller.Defined && c.CallerKey != "" && c.EncodeCaller != nil {
+ c.EncodeCaller(ent.Caller, arr)
+ }
+ for i := range arr.elems {
+ if i > 0 {
+ line.AppendByte('\t')
+ }
+ fmt.Fprint(line, arr.elems[i])
+ }
+ putSliceEncoder(arr)
+
+ // Add the message itself.
+ if c.MessageKey != "" {
+ c.addTabIfNecessary(line)
+ line.AppendString(ent.Message)
+ }
+
+ // Add any structured context.
+ c.writeContext(line, fields)
+
+ // If there's no stacktrace key, honor that; this allows users to force
+ // single-line output.
+ if ent.Stack != "" && c.StacktraceKey != "" {
+ line.AppendByte('\n')
+ line.AppendString(ent.Stack)
+ }
+
+ if c.LineEnding != "" {
+ line.AppendString(c.LineEnding)
+ } else {
+ line.AppendString(DefaultLineEnding)
+ }
+ return line, nil
+}
+
+func (c consoleEncoder) writeContext(line *buffer.Buffer, extra []Field) {
+ context := c.jsonEncoder.Clone().(*jsonEncoder)
+ defer context.buf.Free()
+
+ addFields(context, extra)
+ context.closeOpenNamespaces()
+ if context.buf.Len() == 0 {
+ return
+ }
+
+ c.addTabIfNecessary(line)
+ line.AppendByte('{')
+ line.Write(context.buf.Bytes())
+ line.AppendByte('}')
+}
+
+func (c consoleEncoder) addTabIfNecessary(line *buffer.Buffer) {
+ if line.Len() > 0 {
+ line.AppendByte('\t')
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/core.go b/vendor/go.uber.org/zap/zapcore/core.go
new file mode 100644
index 000000000..a1ef8b034
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/core.go
@@ -0,0 +1,113 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+// Core is a minimal, fast logger interface. It's designed for library authors
+// to wrap in a more user-friendly API.
+type Core interface {
+ LevelEnabler
+
+ // With adds structured context to the Core.
+ With([]Field) Core
+ // Check determines whether the supplied Entry should be logged (using the
+ // embedded LevelEnabler and possibly some extra logic). If the entry
+ // should be logged, the Core adds itself to the CheckedEntry and returns
+ // the result.
+ //
+ // Callers must use Check before calling Write.
+ Check(Entry, *CheckedEntry) *CheckedEntry
+ // Write serializes the Entry and any Fields supplied at the log site and
+ // writes them to their destination.
+ //
+ // If called, Write should always log the Entry and Fields; it should not
+ // replicate the logic of Check.
+ Write(Entry, []Field) error
+ // Sync flushes buffered logs (if any).
+ Sync() error
+}
+
+type nopCore struct{}
+
+// NewNopCore returns a no-op Core.
+func NewNopCore() Core { return nopCore{} }
+func (nopCore) Enabled(Level) bool { return false }
+func (n nopCore) With([]Field) Core { return n }
+func (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce }
+func (nopCore) Write(Entry, []Field) error { return nil }
+func (nopCore) Sync() error { return nil }
+
+// NewCore creates a Core that writes logs to a WriteSyncer.
+func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core {
+ return &ioCore{
+ LevelEnabler: enab,
+ enc: enc,
+ out: ws,
+ }
+}
+
+type ioCore struct {
+ LevelEnabler
+ enc Encoder
+ out WriteSyncer
+}
+
+func (c *ioCore) With(fields []Field) Core {
+ clone := c.clone()
+ addFields(clone.enc, fields)
+ return clone
+}
+
+func (c *ioCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ if c.Enabled(ent.Level) {
+ return ce.AddCore(ent, c)
+ }
+ return ce
+}
+
+func (c *ioCore) Write(ent Entry, fields []Field) error {
+ buf, err := c.enc.EncodeEntry(ent, fields)
+ if err != nil {
+ return err
+ }
+ _, err = c.out.Write(buf.Bytes())
+ buf.Free()
+ if err != nil {
+ return err
+ }
+ if ent.Level > ErrorLevel {
+ // Since we may be crashing the program, sync the output. Ignore Sync
+ // errors, pending a clean solution to issue #370.
+ c.Sync()
+ }
+ return nil
+}
+
+func (c *ioCore) Sync() error {
+ return c.out.Sync()
+}
+
+func (c *ioCore) clone() *ioCore {
+ return &ioCore{
+ LevelEnabler: c.LevelEnabler,
+ enc: c.enc.Clone(),
+ out: c.out,
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/doc.go b/vendor/go.uber.org/zap/zapcore/doc.go
new file mode 100644
index 000000000..31000e91f
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/doc.go
@@ -0,0 +1,24 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package zapcore defines and implements the low-level interfaces upon which
+// zap is built. By providing alternate implementations of these interfaces,
+// external packages can extend zap's capabilities.
+package zapcore // import "go.uber.org/zap/zapcore"
diff --git a/vendor/go.uber.org/zap/zapcore/encoder.go b/vendor/go.uber.org/zap/zapcore/encoder.go
new file mode 100644
index 000000000..f0509522b
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/encoder.go
@@ -0,0 +1,348 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "time"
+
+ "go.uber.org/zap/buffer"
+)
+
+// DefaultLineEnding defines the default line ending when writing logs.
+// Alternate line endings specified in EncoderConfig can override this
+// behavior.
+const DefaultLineEnding = "\n"
+
+// A LevelEncoder serializes a Level to a primitive type.
+type LevelEncoder func(Level, PrimitiveArrayEncoder)
+
+// LowercaseLevelEncoder serializes a Level to a lowercase string. For example,
+// InfoLevel is serialized to "info".
+func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ enc.AppendString(l.String())
+}
+
+// LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring.
+// For example, InfoLevel is serialized to "info" and colored blue.
+func LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ s, ok := _levelToLowercaseColorString[l]
+ if !ok {
+ s = _unknownLevelColor.Add(l.String())
+ }
+ enc.AppendString(s)
+}
+
+// CapitalLevelEncoder serializes a Level to an all-caps string. For example,
+// InfoLevel is serialized to "INFO".
+func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ enc.AppendString(l.CapitalString())
+}
+
+// CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color.
+// For example, InfoLevel is serialized to "INFO" and colored blue.
+func CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ s, ok := _levelToCapitalColorString[l]
+ if !ok {
+ s = _unknownLevelColor.Add(l.CapitalString())
+ }
+ enc.AppendString(s)
+}
+
+// UnmarshalText unmarshals text to a LevelEncoder. "capital" is unmarshaled to
+// CapitalLevelEncoder, "coloredCapital" is unmarshaled to CapitalColorLevelEncoder,
+// "colored" is unmarshaled to LowercaseColorLevelEncoder, and anything else
+// is unmarshaled to LowercaseLevelEncoder.
+func (e *LevelEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "capital":
+ *e = CapitalLevelEncoder
+ case "capitalColor":
+ *e = CapitalColorLevelEncoder
+ case "color":
+ *e = LowercaseColorLevelEncoder
+ default:
+ *e = LowercaseLevelEncoder
+ }
+ return nil
+}
+
+// A TimeEncoder serializes a time.Time to a primitive type.
+type TimeEncoder func(time.Time, PrimitiveArrayEncoder)
+
+// EpochTimeEncoder serializes a time.Time to a floating-point number of seconds
+// since the Unix epoch.
+func EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ nanos := t.UnixNano()
+ sec := float64(nanos) / float64(time.Second)
+ enc.AppendFloat64(sec)
+}
+
+// EpochMillisTimeEncoder serializes a time.Time to a floating-point number of
+// milliseconds since the Unix epoch.
+func EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ nanos := t.UnixNano()
+ millis := float64(nanos) / float64(time.Millisecond)
+ enc.AppendFloat64(millis)
+}
+
+// EpochNanosTimeEncoder serializes a time.Time to an integer number of
+// nanoseconds since the Unix epoch.
+func EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ enc.AppendInt64(t.UnixNano())
+}
+
+// ISO8601TimeEncoder serializes a time.Time to an ISO8601-formatted string
+// with millisecond precision.
+func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ enc.AppendString(t.Format("2006-01-02T15:04:05.000Z0700"))
+}
+
+// UnmarshalText unmarshals text to a TimeEncoder. "iso8601" and "ISO8601" are
+// unmarshaled to ISO8601TimeEncoder, "millis" is unmarshaled to
+// EpochMillisTimeEncoder, and anything else is unmarshaled to EpochTimeEncoder.
+func (e *TimeEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "iso8601", "ISO8601":
+ *e = ISO8601TimeEncoder
+ case "millis":
+ *e = EpochMillisTimeEncoder
+ case "nanos":
+ *e = EpochNanosTimeEncoder
+ default:
+ *e = EpochTimeEncoder
+ }
+ return nil
+}
+
+// A DurationEncoder serializes a time.Duration to a primitive type.
+type DurationEncoder func(time.Duration, PrimitiveArrayEncoder)
+
+// SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed.
+func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendFloat64(float64(d) / float64(time.Second))
+}
+
+// NanosDurationEncoder serializes a time.Duration to an integer number of
+// nanoseconds elapsed.
+func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendInt64(int64(d))
+}
+
+// StringDurationEncoder serializes a time.Duration using its built-in String
+// method.
+func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendString(d.String())
+}
+
+// UnmarshalText unmarshals text to a DurationEncoder. "string" is unmarshaled
+// to StringDurationEncoder, and anything else is unmarshaled to
+// NanosDurationEncoder.
+func (e *DurationEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "string":
+ *e = StringDurationEncoder
+ case "nanos":
+ *e = NanosDurationEncoder
+ default:
+ *e = SecondsDurationEncoder
+ }
+ return nil
+}
+
+// A CallerEncoder serializes an EntryCaller to a primitive type.
+type CallerEncoder func(EntryCaller, PrimitiveArrayEncoder)
+
+// FullCallerEncoder serializes a caller in /full/path/to/package/file:line
+// format.
+func FullCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
+ // TODO: consider using a byte-oriented API to save an allocation.
+ enc.AppendString(caller.String())
+}
+
+// ShortCallerEncoder serializes a caller in package/file:line format, trimming
+// all but the final directory from the full path.
+func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
+ // TODO: consider using a byte-oriented API to save an allocation.
+ enc.AppendString(caller.TrimmedPath())
+}
+
+// UnmarshalText unmarshals text to a CallerEncoder. "full" is unmarshaled to
+// FullCallerEncoder and anything else is unmarshaled to ShortCallerEncoder.
+func (e *CallerEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "full":
+ *e = FullCallerEncoder
+ default:
+ *e = ShortCallerEncoder
+ }
+ return nil
+}
+
+// A NameEncoder serializes a period-separated logger name to a primitive
+// type.
+type NameEncoder func(string, PrimitiveArrayEncoder)
+
+// FullNameEncoder serializes the logger name as-is.
+func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {
+ enc.AppendString(loggerName)
+}
+
+// UnmarshalText unmarshals text to a NameEncoder. Currently, everything is
+// unmarshaled to FullNameEncoder.
+func (e *NameEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "full":
+ *e = FullNameEncoder
+ default:
+ *e = FullNameEncoder
+ }
+ return nil
+}
+
+// An EncoderConfig allows users to configure the concrete encoders supplied by
+// zapcore.
+type EncoderConfig struct {
+ // Set the keys used for each log entry. If any key is empty, that portion
+ // of the entry is omitted.
+ MessageKey string `json:"messageKey" yaml:"messageKey"`
+ LevelKey string `json:"levelKey" yaml:"levelKey"`
+ TimeKey string `json:"timeKey" yaml:"timeKey"`
+ NameKey string `json:"nameKey" yaml:"nameKey"`
+ CallerKey string `json:"callerKey" yaml:"callerKey"`
+ StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
+ LineEnding string `json:"lineEnding" yaml:"lineEnding"`
+ // Configure the primitive representations of common complex types. For
+ // example, some users may want all time.Times serialized as floating-point
+ // seconds since epoch, while others may prefer ISO8601 strings.
+ EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"`
+ EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"`
+ EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
+ EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"`
+ // Unlike the other primitive type encoders, EncodeName is optional. The
+ // zero value falls back to FullNameEncoder.
+ EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
+}
+
+// ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a
+// map- or struct-like object to the logging context. Like maps, ObjectEncoders
+// aren't safe for concurrent use (though typical use shouldn't require locks).
+type ObjectEncoder interface {
+ // Logging-specific marshalers.
+ AddArray(key string, marshaler ArrayMarshaler) error
+ AddObject(key string, marshaler ObjectMarshaler) error
+
+ // Built-in types.
+ AddBinary(key string, value []byte) // for arbitrary bytes
+ AddByteString(key string, value []byte) // for UTF-8 encoded bytes
+ AddBool(key string, value bool)
+ AddComplex128(key string, value complex128)
+ AddComplex64(key string, value complex64)
+ AddDuration(key string, value time.Duration)
+ AddFloat64(key string, value float64)
+ AddFloat32(key string, value float32)
+ AddInt(key string, value int)
+ AddInt64(key string, value int64)
+ AddInt32(key string, value int32)
+ AddInt16(key string, value int16)
+ AddInt8(key string, value int8)
+ AddString(key, value string)
+ AddTime(key string, value time.Time)
+ AddUint(key string, value uint)
+ AddUint64(key string, value uint64)
+ AddUint32(key string, value uint32)
+ AddUint16(key string, value uint16)
+ AddUint8(key string, value uint8)
+ AddUintptr(key string, value uintptr)
+
+ // AddReflected uses reflection to serialize arbitrary objects, so it's slow
+ // and allocation-heavy.
+ AddReflected(key string, value interface{}) error
+ // OpenNamespace opens an isolated namespace where all subsequent fields will
+ // be added. Applications can use namespaces to prevent key collisions when
+ // injecting loggers into sub-components or third-party libraries.
+ OpenNamespace(key string)
+}
+
+// ArrayEncoder is a strongly-typed, encoding-agnostic interface for adding
+// array-like objects to the logging context. Of note, it supports mixed-type
+// arrays even though they aren't typical in Go. Like slices, ArrayEncoders
+// aren't safe for concurrent use (though typical use shouldn't require locks).
+type ArrayEncoder interface {
+ // Built-in types.
+ PrimitiveArrayEncoder
+
+ // Time-related types.
+ AppendDuration(time.Duration)
+ AppendTime(time.Time)
+
+ // Logging-specific marshalers.
+ AppendArray(ArrayMarshaler) error
+ AppendObject(ObjectMarshaler) error
+
+ // AppendReflected uses reflection to serialize arbitrary objects, so it's
+ // slow and allocation-heavy.
+ AppendReflected(value interface{}) error
+}
+
+// PrimitiveArrayEncoder is the subset of the ArrayEncoder interface that deals
+// only in Go's built-in types. It's included only so that Duration- and
+// TimeEncoders cannot trigger infinite recursion.
+type PrimitiveArrayEncoder interface {
+ // Built-in types.
+ AppendBool(bool)
+ AppendByteString([]byte) // for UTF-8 encoded bytes
+ AppendComplex128(complex128)
+ AppendComplex64(complex64)
+ AppendFloat64(float64)
+ AppendFloat32(float32)
+ AppendInt(int)
+ AppendInt64(int64)
+ AppendInt32(int32)
+ AppendInt16(int16)
+ AppendInt8(int8)
+ AppendString(string)
+ AppendUint(uint)
+ AppendUint64(uint64)
+ AppendUint32(uint32)
+ AppendUint16(uint16)
+ AppendUint8(uint8)
+ AppendUintptr(uintptr)
+}
+
+// Encoder is a format-agnostic interface for all log entry marshalers. Since
+// log encoders don't need to support the same wide range of use cases as
+// general-purpose marshalers, it's possible to make them faster and
+// lower-allocation.
+//
+// Implementations of the ObjectEncoder interface's methods can, of course,
+// freely modify the receiver. However, the Clone and EncodeEntry methods will
+// be called concurrently and shouldn't modify the receiver.
+type Encoder interface {
+ ObjectEncoder
+
+ // Clone copies the encoder, ensuring that adding fields to the copy doesn't
+ // affect the original.
+ Clone() Encoder
+
+ // EncodeEntry encodes an entry and fields, along with any accumulated
+ // context, into a byte buffer and returns it.
+ EncodeEntry(Entry, []Field) (*buffer.Buffer, error)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go
new file mode 100644
index 000000000..7d9893f33
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/entry.go
@@ -0,0 +1,257 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+ "time"
+
+ "go.uber.org/zap/internal/bufferpool"
+ "go.uber.org/zap/internal/exit"
+
+ "go.uber.org/multierr"
+)
+
+var (
+ _cePool = sync.Pool{New: func() interface{} {
+ // Pre-allocate some space for cores.
+ return &CheckedEntry{
+ cores: make([]Core, 4),
+ }
+ }}
+)
+
+func getCheckedEntry() *CheckedEntry {
+ ce := _cePool.Get().(*CheckedEntry)
+ ce.reset()
+ return ce
+}
+
+func putCheckedEntry(ce *CheckedEntry) {
+ if ce == nil {
+ return
+ }
+ _cePool.Put(ce)
+}
+
+// NewEntryCaller makes an EntryCaller from the return signature of
+// runtime.Caller.
+func NewEntryCaller(pc uintptr, file string, line int, ok bool) EntryCaller {
+ if !ok {
+ return EntryCaller{}
+ }
+ return EntryCaller{
+ PC: pc,
+ File: file,
+ Line: line,
+ Defined: true,
+ }
+}
+
+// EntryCaller represents the caller of a logging function.
+type EntryCaller struct {
+ Defined bool
+ PC uintptr
+ File string
+ Line int
+}
+
+// String returns the full path and line number of the caller.
+func (ec EntryCaller) String() string {
+ return ec.FullPath()
+}
+
+// FullPath returns a /full/path/to/package/file:line description of the
+// caller.
+func (ec EntryCaller) FullPath() string {
+ if !ec.Defined {
+ return "undefined"
+ }
+ buf := bufferpool.Get()
+ buf.AppendString(ec.File)
+ buf.AppendByte(':')
+ buf.AppendInt(int64(ec.Line))
+ caller := buf.String()
+ buf.Free()
+ return caller
+}
+
+// TrimmedPath returns a package/file:line description of the caller,
+// preserving only the leaf directory name and file name.
+func (ec EntryCaller) TrimmedPath() string {
+ if !ec.Defined {
+ return "undefined"
+ }
+ // nb. To make sure we trim the path correctly on Windows too, we
+ // counter-intuitively need to use '/' and *not* os.PathSeparator here,
+ // because the path given originates from Go stdlib, specifically
+ // runtime.Caller() which (as of Mar/17) returns forward slashes even on
+ // Windows.
+ //
+ // See https://github.com/golang/go/issues/3335
+ // and https://github.com/golang/go/issues/18151
+ //
+ // for discussion on the issue on Go side.
+ //
+ // Find the last separator.
+ //
+ idx := strings.LastIndexByte(ec.File, '/')
+ if idx == -1 {
+ return ec.FullPath()
+ }
+ // Find the penultimate separator.
+ idx = strings.LastIndexByte(ec.File[:idx], '/')
+ if idx == -1 {
+ return ec.FullPath()
+ }
+ buf := bufferpool.Get()
+ // Keep everything after the penultimate separator.
+ buf.AppendString(ec.File[idx+1:])
+ buf.AppendByte(':')
+ buf.AppendInt(int64(ec.Line))
+ caller := buf.String()
+ buf.Free()
+ return caller
+}
+
+// An Entry represents a complete log message. The entry's structured context
+// is already serialized, but the log level, time, message, and call site
+// information are available for inspection and modification.
+//
+// Entries are pooled, so any functions that accept them MUST be careful not to
+// retain references to them.
+type Entry struct {
+ Level Level
+ Time time.Time
+ LoggerName string
+ Message string
+ Caller EntryCaller
+ Stack string
+}
+
+// CheckWriteAction indicates what action to take after a log entry is
+// processed. Actions are ordered in increasing severity.
+type CheckWriteAction uint8
+
+const (
+ // WriteThenNoop indicates that nothing special needs to be done. It's the
+ // default behavior.
+ WriteThenNoop CheckWriteAction = iota
+ // WriteThenPanic causes a panic after Write.
+ WriteThenPanic
+ // WriteThenFatal causes a fatal os.Exit after Write.
+ WriteThenFatal
+)
+
+// CheckedEntry is an Entry together with a collection of Cores that have
+// already agreed to log it.
+//
+// CheckedEntry references should be created by calling AddCore or Should on a
+// nil *CheckedEntry. References are returned to a pool after Write, and MUST
+// NOT be retained after calling their Write method.
+type CheckedEntry struct {
+ Entry
+ ErrorOutput WriteSyncer
+ dirty bool // best-effort detection of pool misuse
+ should CheckWriteAction
+ cores []Core
+}
+
+func (ce *CheckedEntry) reset() {
+ ce.Entry = Entry{}
+ ce.ErrorOutput = nil
+ ce.dirty = false
+ ce.should = WriteThenNoop
+ for i := range ce.cores {
+ // don't keep references to cores
+ ce.cores[i] = nil
+ }
+ ce.cores = ce.cores[:0]
+}
+
+// Write writes the entry to the stored Cores, returns any errors, and returns
+// the CheckedEntry reference to a pool for immediate re-use. Finally, it
+// executes any required CheckWriteAction.
+func (ce *CheckedEntry) Write(fields ...Field) {
+ if ce == nil {
+ return
+ }
+
+ if ce.dirty {
+ if ce.ErrorOutput != nil {
+ // Make a best effort to detect unsafe re-use of this CheckedEntry.
+ // If the entry is dirty, log an internal error; because the
+ // CheckedEntry is being used after it was returned to the pool,
+ // the message may be an amalgamation from multiple call sites.
+ fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", time.Now(), ce.Entry)
+ ce.ErrorOutput.Sync()
+ }
+ return
+ }
+ ce.dirty = true
+
+ var err error
+ for i := range ce.cores {
+ err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields))
+ }
+ if ce.ErrorOutput != nil {
+ if err != nil {
+ fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", time.Now(), err)
+ ce.ErrorOutput.Sync()
+ }
+ }
+
+ should, msg := ce.should, ce.Message
+ putCheckedEntry(ce)
+
+ switch should {
+ case WriteThenPanic:
+ panic(msg)
+ case WriteThenFatal:
+ exit.Exit()
+ }
+}
+
+// AddCore adds a Core that has agreed to log this CheckedEntry. It's intended to be
+// used by Core.Check implementations, and is safe to call on nil CheckedEntry
+// references.
+func (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry {
+ if ce == nil {
+ ce = getCheckedEntry()
+ ce.Entry = ent
+ }
+ ce.cores = append(ce.cores, core)
+ return ce
+}
+
+// Should sets this CheckedEntry's CheckWriteAction, which controls whether a
+// Core will panic or fatal after writing this log entry. Like AddCore, it's
+// safe to call on nil CheckedEntry references.
+func (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry {
+ if ce == nil {
+ ce = getCheckedEntry()
+ ce.Entry = ent
+ }
+ ce.should = should
+ return ce
+}
diff --git a/vendor/go.uber.org/zap/zapcore/error.go b/vendor/go.uber.org/zap/zapcore/error.go
new file mode 100644
index 000000000..a67c7bacc
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/error.go
@@ -0,0 +1,120 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "fmt"
+ "sync"
+)
+
+// Encodes the given error into fields of an object. A field with the given
+// name is added for the error message.
+//
+// If the error implements fmt.Formatter, a field with the name ${key}Verbose
+// is also added with the full verbose error message.
+//
+// Finally, if the error implements errorGroup (from go.uber.org/multierr) or
+// causer (from github.com/pkg/errors), a ${key}Causes field is added with an
+// array of objects containing the errors this error was comprised of.
+//
+// {
+// "error": err.Error(),
+// "errorVerbose": fmt.Sprintf("%+v", err),
+// "errorCauses": [
+// ...
+// ],
+// }
+func encodeError(key string, err error, enc ObjectEncoder) error {
+ basic := err.Error()
+ enc.AddString(key, basic)
+
+ switch e := err.(type) {
+ case errorGroup:
+ return enc.AddArray(key+"Causes", errArray(e.Errors()))
+ case fmt.Formatter:
+ verbose := fmt.Sprintf("%+v", e)
+ if verbose != basic {
+ // This is a rich error type, like those produced by
+ // github.com/pkg/errors.
+ enc.AddString(key+"Verbose", verbose)
+ }
+ }
+ return nil
+}
+
+type errorGroup interface {
+ // Provides read-only access to the underlying list of errors, preferably
+ // without causing any allocs.
+ Errors() []error
+}
+
+type causer interface {
+ // Provides access to the error that caused this error.
+ Cause() error
+}
+
+// Note that errArry and errArrayElem are very similar to the version
+// implemented in the top-level error.go file. We can't re-use this because
+// that would require exporting errArray as part of the zapcore API.
+
+// Encodes a list of errors using the standard error encoding logic.
+type errArray []error
+
+func (errs errArray) MarshalLogArray(arr ArrayEncoder) error {
+ for i := range errs {
+ if errs[i] == nil {
+ continue
+ }
+
+ el := newErrArrayElem(errs[i])
+ arr.AppendObject(el)
+ el.Free()
+ }
+ return nil
+}
+
+var _errArrayElemPool = sync.Pool{New: func() interface{} {
+ return &errArrayElem{}
+}}
+
+// Encodes any error into a {"error": ...} re-using the same errors logic.
+//
+// May be passed in place of an array to build a single-element array.
+type errArrayElem struct{ err error }
+
+func newErrArrayElem(err error) *errArrayElem {
+ e := _errArrayElemPool.Get().(*errArrayElem)
+ e.err = err
+ return e
+}
+
+func (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error {
+ return arr.AppendObject(e)
+}
+
+func (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error {
+ return encodeError("error", e.err, enc)
+}
+
+func (e *errArrayElem) Free() {
+ e.err = nil
+ _errArrayElemPool.Put(e)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/field.go b/vendor/go.uber.org/zap/zapcore/field.go
new file mode 100644
index 000000000..6a5e33e2f
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/field.go
@@ -0,0 +1,201 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "reflect"
+ "time"
+)
+
+// A FieldType indicates which member of the Field union struct should be used
+// and how it should be serialized.
+type FieldType uint8
+
+const (
+ // UnknownType is the default field type. Attempting to add it to an encoder will panic.
+ UnknownType FieldType = iota
+ // ArrayMarshalerType indicates that the field carries an ArrayMarshaler.
+ ArrayMarshalerType
+ // ObjectMarshalerType indicates that the field carries an ObjectMarshaler.
+ ObjectMarshalerType
+ // BinaryType indicates that the field carries an opaque binary blob.
+ BinaryType
+ // BoolType indicates that the field carries a bool.
+ BoolType
+ // ByteStringType indicates that the field carries UTF-8 encoded bytes.
+ ByteStringType
+ // Complex128Type indicates that the field carries a complex128.
+ Complex128Type
+ // Complex64Type indicates that the field carries a complex128.
+ Complex64Type
+ // DurationType indicates that the field carries a time.Duration.
+ DurationType
+ // Float64Type indicates that the field carries a float64.
+ Float64Type
+ // Float32Type indicates that the field carries a float32.
+ Float32Type
+ // Int64Type indicates that the field carries an int64.
+ Int64Type
+ // Int32Type indicates that the field carries an int32.
+ Int32Type
+ // Int16Type indicates that the field carries an int16.
+ Int16Type
+ // Int8Type indicates that the field carries an int8.
+ Int8Type
+ // StringType indicates that the field carries a string.
+ StringType
+ // TimeType indicates that the field carries a time.Time.
+ TimeType
+ // Uint64Type indicates that the field carries a uint64.
+ Uint64Type
+ // Uint32Type indicates that the field carries a uint32.
+ Uint32Type
+ // Uint16Type indicates that the field carries a uint16.
+ Uint16Type
+ // Uint8Type indicates that the field carries a uint8.
+ Uint8Type
+ // UintptrType indicates that the field carries a uintptr.
+ UintptrType
+ // ReflectType indicates that the field carries an interface{}, which should
+ // be serialized using reflection.
+ ReflectType
+ // NamespaceType signals the beginning of an isolated namespace. All
+ // subsequent fields should be added to the new namespace.
+ NamespaceType
+ // StringerType indicates that the field carries a fmt.Stringer.
+ StringerType
+ // ErrorType indicates that the field carries an error.
+ ErrorType
+ // SkipType indicates that the field is a no-op.
+ SkipType
+)
+
+// A Field is a marshaling operation used to add a key-value pair to a logger's
+// context. Most fields are lazily marshaled, so it's inexpensive to add fields
+// to disabled debug-level log statements.
+type Field struct {
+ Key string
+ Type FieldType
+ Integer int64
+ String string
+ Interface interface{}
+}
+
+// AddTo exports a field through the ObjectEncoder interface. It's primarily
+// useful to library authors, and shouldn't be necessary in most applications.
+func (f Field) AddTo(enc ObjectEncoder) {
+ var err error
+
+ switch f.Type {
+ case ArrayMarshalerType:
+ err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))
+ case ObjectMarshalerType:
+ err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))
+ case BinaryType:
+ enc.AddBinary(f.Key, f.Interface.([]byte))
+ case BoolType:
+ enc.AddBool(f.Key, f.Integer == 1)
+ case ByteStringType:
+ enc.AddByteString(f.Key, f.Interface.([]byte))
+ case Complex128Type:
+ enc.AddComplex128(f.Key, f.Interface.(complex128))
+ case Complex64Type:
+ enc.AddComplex64(f.Key, f.Interface.(complex64))
+ case DurationType:
+ enc.AddDuration(f.Key, time.Duration(f.Integer))
+ case Float64Type:
+ enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer)))
+ case Float32Type:
+ enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer)))
+ case Int64Type:
+ enc.AddInt64(f.Key, f.Integer)
+ case Int32Type:
+ enc.AddInt32(f.Key, int32(f.Integer))
+ case Int16Type:
+ enc.AddInt16(f.Key, int16(f.Integer))
+ case Int8Type:
+ enc.AddInt8(f.Key, int8(f.Integer))
+ case StringType:
+ enc.AddString(f.Key, f.String)
+ case TimeType:
+ if f.Interface != nil {
+ enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location)))
+ } else {
+ // Fall back to UTC if location is nil.
+ enc.AddTime(f.Key, time.Unix(0, f.Integer))
+ }
+ case Uint64Type:
+ enc.AddUint64(f.Key, uint64(f.Integer))
+ case Uint32Type:
+ enc.AddUint32(f.Key, uint32(f.Integer))
+ case Uint16Type:
+ enc.AddUint16(f.Key, uint16(f.Integer))
+ case Uint8Type:
+ enc.AddUint8(f.Key, uint8(f.Integer))
+ case UintptrType:
+ enc.AddUintptr(f.Key, uintptr(f.Integer))
+ case ReflectType:
+ err = enc.AddReflected(f.Key, f.Interface)
+ case NamespaceType:
+ enc.OpenNamespace(f.Key)
+ case StringerType:
+ enc.AddString(f.Key, f.Interface.(fmt.Stringer).String())
+ case ErrorType:
+ encodeError(f.Key, f.Interface.(error), enc)
+ case SkipType:
+ break
+ default:
+ panic(fmt.Sprintf("unknown field type: %v", f))
+ }
+
+ if err != nil {
+ enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error())
+ }
+}
+
+// Equals returns whether two fields are equal. For non-primitive types such as
+// errors, marshalers, or reflect types, it uses reflect.DeepEqual.
+func (f Field) Equals(other Field) bool {
+ if f.Type != other.Type {
+ return false
+ }
+ if f.Key != other.Key {
+ return false
+ }
+
+ switch f.Type {
+ case BinaryType, ByteStringType:
+ return bytes.Equal(f.Interface.([]byte), other.Interface.([]byte))
+ case ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType:
+ return reflect.DeepEqual(f.Interface, other.Interface)
+ default:
+ return f == other
+ }
+}
+
+func addFields(enc ObjectEncoder, fields []Field) {
+ for i := range fields {
+ fields[i].AddTo(enc)
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/hook.go b/vendor/go.uber.org/zap/zapcore/hook.go
new file mode 100644
index 000000000..5db4afb30
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/hook.go
@@ -0,0 +1,68 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "go.uber.org/multierr"
+
+type hooked struct {
+ Core
+ funcs []func(Entry) error
+}
+
+// RegisterHooks wraps a Core and runs a collection of user-defined callback
+// hooks each time a message is logged. Execution of the callbacks is blocking.
+//
+// This offers users an easy way to register simple callbacks (e.g., metrics
+// collection) without implementing the full Core interface.
+func RegisterHooks(core Core, hooks ...func(Entry) error) Core {
+ funcs := append([]func(Entry) error{}, hooks...)
+ return &hooked{
+ Core: core,
+ funcs: funcs,
+ }
+}
+
+func (h *hooked) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ // Let the wrapped Core decide whether to log this message or not. This
+ // also gives the downstream a chance to register itself directly with the
+ // CheckedEntry.
+ if downstream := h.Core.Check(ent, ce); downstream != nil {
+ return downstream.AddCore(ent, h)
+ }
+ return ce
+}
+
+func (h *hooked) With(fields []Field) Core {
+ return &hooked{
+ Core: h.Core.With(fields),
+ funcs: h.funcs,
+ }
+}
+
+func (h *hooked) Write(ent Entry, _ []Field) error {
+ // Since our downstream had a chance to register itself directly with the
+ // CheckedMessage, we don't need to call it here.
+ var err error
+ for i := range h.funcs {
+ err = multierr.Append(err, h.funcs[i](ent))
+ }
+ return err
+}
diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder.go b/vendor/go.uber.org/zap/zapcore/json_encoder.go
new file mode 100644
index 000000000..1006ba2b1
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/json_encoder.go
@@ -0,0 +1,480 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "math"
+ "sync"
+ "time"
+ "unicode/utf8"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+)
+
+// For JSON-escaping; see jsonEncoder.safeAddString below.
+const _hex = "0123456789abcdef"
+
+var _jsonPool = sync.Pool{New: func() interface{} {
+ return &jsonEncoder{}
+}}
+
+func getJSONEncoder() *jsonEncoder {
+ return _jsonPool.Get().(*jsonEncoder)
+}
+
+func putJSONEncoder(enc *jsonEncoder) {
+ enc.EncoderConfig = nil
+ enc.buf = nil
+ enc.spaced = false
+ enc.openNamespaces = 0
+ _jsonPool.Put(enc)
+}
+
+type jsonEncoder struct {
+ *EncoderConfig
+ buf *buffer.Buffer
+ spaced bool // include spaces after colons and commas
+ openNamespaces int
+}
+
+// NewJSONEncoder creates a fast, low-allocation JSON encoder. The encoder
+// appropriately escapes all field keys and values.
+//
+// Note that the encoder doesn't deduplicate keys, so it's possible to produce
+// a message like
+// {"foo":"bar","foo":"baz"}
+// This is permitted by the JSON specification, but not encouraged. Many
+// libraries will ignore duplicate key-value pairs (typically keeping the last
+// pair) when unmarshaling, but users should attempt to avoid adding duplicate
+// keys.
+func NewJSONEncoder(cfg EncoderConfig) Encoder {
+ return newJSONEncoder(cfg, false)
+}
+
+func newJSONEncoder(cfg EncoderConfig, spaced bool) *jsonEncoder {
+ return &jsonEncoder{
+ EncoderConfig: &cfg,
+ buf: bufferpool.Get(),
+ spaced: spaced,
+ }
+}
+
+func (enc *jsonEncoder) AddArray(key string, arr ArrayMarshaler) error {
+ enc.addKey(key)
+ return enc.AppendArray(arr)
+}
+
+func (enc *jsonEncoder) AddObject(key string, obj ObjectMarshaler) error {
+ enc.addKey(key)
+ return enc.AppendObject(obj)
+}
+
+func (enc *jsonEncoder) AddBinary(key string, val []byte) {
+ enc.AddString(key, base64.StdEncoding.EncodeToString(val))
+}
+
+func (enc *jsonEncoder) AddByteString(key string, val []byte) {
+ enc.addKey(key)
+ enc.AppendByteString(val)
+}
+
+func (enc *jsonEncoder) AddBool(key string, val bool) {
+ enc.addKey(key)
+ enc.AppendBool(val)
+}
+
+func (enc *jsonEncoder) AddComplex128(key string, val complex128) {
+ enc.addKey(key)
+ enc.AppendComplex128(val)
+}
+
+func (enc *jsonEncoder) AddDuration(key string, val time.Duration) {
+ enc.addKey(key)
+ enc.AppendDuration(val)
+}
+
+func (enc *jsonEncoder) AddFloat64(key string, val float64) {
+ enc.addKey(key)
+ enc.AppendFloat64(val)
+}
+
+func (enc *jsonEncoder) AddInt64(key string, val int64) {
+ enc.addKey(key)
+ enc.AppendInt64(val)
+}
+
+func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error {
+ marshaled, err := json.Marshal(obj)
+ if err != nil {
+ return err
+ }
+ enc.addKey(key)
+ _, err = enc.buf.Write(marshaled)
+ return err
+}
+
+func (enc *jsonEncoder) OpenNamespace(key string) {
+ enc.addKey(key)
+ enc.buf.AppendByte('{')
+ enc.openNamespaces++
+}
+
+func (enc *jsonEncoder) AddString(key, val string) {
+ enc.addKey(key)
+ enc.AppendString(val)
+}
+
+func (enc *jsonEncoder) AddTime(key string, val time.Time) {
+ enc.addKey(key)
+ enc.AppendTime(val)
+}
+
+func (enc *jsonEncoder) AddUint64(key string, val uint64) {
+ enc.addKey(key)
+ enc.AppendUint64(val)
+}
+
+func (enc *jsonEncoder) AppendArray(arr ArrayMarshaler) error {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('[')
+ err := arr.MarshalLogArray(enc)
+ enc.buf.AppendByte(']')
+ return err
+}
+
+func (enc *jsonEncoder) AppendObject(obj ObjectMarshaler) error {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('{')
+ err := obj.MarshalLogObject(enc)
+ enc.buf.AppendByte('}')
+ return err
+}
+
+func (enc *jsonEncoder) AppendBool(val bool) {
+ enc.addElementSeparator()
+ enc.buf.AppendBool(val)
+}
+
+func (enc *jsonEncoder) AppendByteString(val []byte) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.safeAddByteString(val)
+ enc.buf.AppendByte('"')
+}
+
+func (enc *jsonEncoder) AppendComplex128(val complex128) {
+ enc.addElementSeparator()
+ // Cast to a platform-independent, fixed-size type.
+ r, i := float64(real(val)), float64(imag(val))
+ enc.buf.AppendByte('"')
+ // Because we're always in a quoted string, we can use strconv without
+ // special-casing NaN and +/-Inf.
+ enc.buf.AppendFloat(r, 64)
+ enc.buf.AppendByte('+')
+ enc.buf.AppendFloat(i, 64)
+ enc.buf.AppendByte('i')
+ enc.buf.AppendByte('"')
+}
+
+func (enc *jsonEncoder) AppendDuration(val time.Duration) {
+ cur := enc.buf.Len()
+ enc.EncodeDuration(val, enc)
+ if cur == enc.buf.Len() {
+ // User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep
+ // JSON valid.
+ enc.AppendInt64(int64(val))
+ }
+}
+
+func (enc *jsonEncoder) AppendInt64(val int64) {
+ enc.addElementSeparator()
+ enc.buf.AppendInt(val)
+}
+
+func (enc *jsonEncoder) AppendReflected(val interface{}) error {
+ marshaled, err := json.Marshal(val)
+ if err != nil {
+ return err
+ }
+ enc.addElementSeparator()
+ _, err = enc.buf.Write(marshaled)
+ return err
+}
+
+func (enc *jsonEncoder) AppendString(val string) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.safeAddString(val)
+ enc.buf.AppendByte('"')
+}
+
+func (enc *jsonEncoder) AppendTime(val time.Time) {
+ cur := enc.buf.Len()
+ enc.EncodeTime(val, enc)
+ if cur == enc.buf.Len() {
+ // User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep
+ // output JSON valid.
+ enc.AppendInt64(val.UnixNano())
+ }
+}
+
+func (enc *jsonEncoder) AppendUint64(val uint64) {
+ enc.addElementSeparator()
+ enc.buf.AppendUint(val)
+}
+
+func (enc *jsonEncoder) AddComplex64(k string, v complex64) { enc.AddComplex128(k, complex128(v)) }
+func (enc *jsonEncoder) AddFloat32(k string, v float32) { enc.AddFloat64(k, float64(v)) }
+func (enc *jsonEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AppendComplex64(v complex64) { enc.AppendComplex128(complex128(v)) }
+func (enc *jsonEncoder) AppendFloat64(v float64) { enc.appendFloat(v, 64) }
+func (enc *jsonEncoder) AppendFloat32(v float32) { enc.appendFloat(float64(v), 32) }
+func (enc *jsonEncoder) AppendInt(v int) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendInt32(v int32) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendInt16(v int16) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendInt8(v int8) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendUint(v uint) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUint32(v uint32) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUint16(v uint16) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUint8(v uint8) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUintptr(v uintptr) { enc.AppendUint64(uint64(v)) }
+
+func (enc *jsonEncoder) Clone() Encoder {
+ clone := enc.clone()
+ clone.buf.Write(enc.buf.Bytes())
+ return clone
+}
+
+func (enc *jsonEncoder) clone() *jsonEncoder {
+ clone := getJSONEncoder()
+ clone.EncoderConfig = enc.EncoderConfig
+ clone.spaced = enc.spaced
+ clone.openNamespaces = enc.openNamespaces
+ clone.buf = bufferpool.Get()
+ return clone
+}
+
+func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {
+ final := enc.clone()
+ final.buf.AppendByte('{')
+
+ if final.LevelKey != "" {
+ final.addKey(final.LevelKey)
+ cur := final.buf.Len()
+ final.EncodeLevel(ent.Level, final)
+ if cur == final.buf.Len() {
+ // User-supplied EncodeLevel was a no-op. Fall back to strings to keep
+ // output JSON valid.
+ final.AppendString(ent.Level.String())
+ }
+ }
+ if final.TimeKey != "" {
+ final.AddTime(final.TimeKey, ent.Time)
+ }
+ if ent.LoggerName != "" && final.NameKey != "" {
+ final.addKey(final.NameKey)
+ cur := final.buf.Len()
+ nameEncoder := final.EncodeName
+
+ // if no name encoder provided, fall back to FullNameEncoder for backwards
+ // compatibility
+ if nameEncoder == nil {
+ nameEncoder = FullNameEncoder
+ }
+
+ nameEncoder(ent.LoggerName, final)
+ if cur == final.buf.Len() {
+ // User-supplied EncodeName was a no-op. Fall back to strings to
+ // keep output JSON valid.
+ final.AppendString(ent.LoggerName)
+ }
+ }
+ if ent.Caller.Defined && final.CallerKey != "" {
+ final.addKey(final.CallerKey)
+ cur := final.buf.Len()
+ final.EncodeCaller(ent.Caller, final)
+ if cur == final.buf.Len() {
+ // User-supplied EncodeCaller was a no-op. Fall back to strings to
+ // keep output JSON valid.
+ final.AppendString(ent.Caller.String())
+ }
+ }
+ if final.MessageKey != "" {
+ final.addKey(enc.MessageKey)
+ final.AppendString(ent.Message)
+ }
+ if enc.buf.Len() > 0 {
+ final.addElementSeparator()
+ final.buf.Write(enc.buf.Bytes())
+ }
+ addFields(final, fields)
+ final.closeOpenNamespaces()
+ if ent.Stack != "" && final.StacktraceKey != "" {
+ final.AddString(final.StacktraceKey, ent.Stack)
+ }
+ final.buf.AppendByte('}')
+ if final.LineEnding != "" {
+ final.buf.AppendString(final.LineEnding)
+ } else {
+ final.buf.AppendString(DefaultLineEnding)
+ }
+
+ ret := final.buf
+ putJSONEncoder(final)
+ return ret, nil
+}
+
+func (enc *jsonEncoder) truncate() {
+ enc.buf.Reset()
+}
+
+func (enc *jsonEncoder) closeOpenNamespaces() {
+ for i := 0; i < enc.openNamespaces; i++ {
+ enc.buf.AppendByte('}')
+ }
+}
+
+func (enc *jsonEncoder) addKey(key string) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.safeAddString(key)
+ enc.buf.AppendByte('"')
+ enc.buf.AppendByte(':')
+ if enc.spaced {
+ enc.buf.AppendByte(' ')
+ }
+}
+
+func (enc *jsonEncoder) addElementSeparator() {
+ last := enc.buf.Len() - 1
+ if last < 0 {
+ return
+ }
+ switch enc.buf.Bytes()[last] {
+ case '{', '[', ':', ',', ' ':
+ return
+ default:
+ enc.buf.AppendByte(',')
+ if enc.spaced {
+ enc.buf.AppendByte(' ')
+ }
+ }
+}
+
+func (enc *jsonEncoder) appendFloat(val float64, bitSize int) {
+ enc.addElementSeparator()
+ switch {
+ case math.IsNaN(val):
+ enc.buf.AppendString(`"NaN"`)
+ case math.IsInf(val, 1):
+ enc.buf.AppendString(`"+Inf"`)
+ case math.IsInf(val, -1):
+ enc.buf.AppendString(`"-Inf"`)
+ default:
+ enc.buf.AppendFloat(val, bitSize)
+ }
+}
+
+// safeAddString JSON-escapes a string and appends it to the internal buffer.
+// Unlike the standard library's encoder, it doesn't attempt to protect the
+// user from browser vulnerabilities or JSONP-related problems.
+func (enc *jsonEncoder) safeAddString(s string) {
+ for i := 0; i < len(s); {
+ if enc.tryAddRuneSelf(s[i]) {
+ i++
+ continue
+ }
+ r, size := utf8.DecodeRuneInString(s[i:])
+ if enc.tryAddRuneError(r, size) {
+ i++
+ continue
+ }
+ enc.buf.AppendString(s[i : i+size])
+ i += size
+ }
+}
+
+// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte.
+func (enc *jsonEncoder) safeAddByteString(s []byte) {
+ for i := 0; i < len(s); {
+ if enc.tryAddRuneSelf(s[i]) {
+ i++
+ continue
+ }
+ r, size := utf8.DecodeRune(s[i:])
+ if enc.tryAddRuneError(r, size) {
+ i++
+ continue
+ }
+ enc.buf.Write(s[i : i+size])
+ i += size
+ }
+}
+
+// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte.
+func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool {
+ if b >= utf8.RuneSelf {
+ return false
+ }
+ if 0x20 <= b && b != '\\' && b != '"' {
+ enc.buf.AppendByte(b)
+ return true
+ }
+ switch b {
+ case '\\', '"':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte(b)
+ case '\n':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte('n')
+ case '\r':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte('r')
+ case '\t':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte('t')
+ default:
+ // Encode bytes < 0x20, except for the escape sequences above.
+ enc.buf.AppendString(`\u00`)
+ enc.buf.AppendByte(_hex[b>>4])
+ enc.buf.AppendByte(_hex[b&0xF])
+ }
+ return true
+}
+
+func (enc *jsonEncoder) tryAddRuneError(r rune, size int) bool {
+ if r == utf8.RuneError && size == 1 {
+ enc.buf.AppendString(`\ufffd`)
+ return true
+ }
+ return false
+}
diff --git a/vendor/go.uber.org/zap/zapcore/level.go b/vendor/go.uber.org/zap/zapcore/level.go
new file mode 100644
index 000000000..e575c9f43
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/level.go
@@ -0,0 +1,175 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+)
+
+var errUnmarshalNilLevel = errors.New("can't unmarshal a nil *Level")
+
+// A Level is a logging priority. Higher levels are more important.
+type Level int8
+
+const (
+ // DebugLevel logs are typically voluminous, and are usually disabled in
+ // production.
+ DebugLevel Level = iota - 1
+ // InfoLevel is the default logging priority.
+ InfoLevel
+ // WarnLevel logs are more important than Info, but don't need individual
+ // human review.
+ WarnLevel
+ // ErrorLevel logs are high-priority. If an application is running smoothly,
+ // it shouldn't generate any error-level logs.
+ ErrorLevel
+ // DPanicLevel logs are particularly important errors. In development the
+ // logger panics after writing the message.
+ DPanicLevel
+ // PanicLevel logs a message, then panics.
+ PanicLevel
+ // FatalLevel logs a message, then calls os.Exit(1).
+ FatalLevel
+
+ _minLevel = DebugLevel
+ _maxLevel = FatalLevel
+)
+
+// String returns a lower-case ASCII representation of the log level.
+func (l Level) String() string {
+ switch l {
+ case DebugLevel:
+ return "debug"
+ case InfoLevel:
+ return "info"
+ case WarnLevel:
+ return "warn"
+ case ErrorLevel:
+ return "error"
+ case DPanicLevel:
+ return "dpanic"
+ case PanicLevel:
+ return "panic"
+ case FatalLevel:
+ return "fatal"
+ default:
+ return fmt.Sprintf("Level(%d)", l)
+ }
+}
+
+// CapitalString returns an all-caps ASCII representation of the log level.
+func (l Level) CapitalString() string {
+ // Printing levels in all-caps is common enough that we should export this
+ // functionality.
+ switch l {
+ case DebugLevel:
+ return "DEBUG"
+ case InfoLevel:
+ return "INFO"
+ case WarnLevel:
+ return "WARN"
+ case ErrorLevel:
+ return "ERROR"
+ case DPanicLevel:
+ return "DPANIC"
+ case PanicLevel:
+ return "PANIC"
+ case FatalLevel:
+ return "FATAL"
+ default:
+ return fmt.Sprintf("LEVEL(%d)", l)
+ }
+}
+
+// MarshalText marshals the Level to text. Note that the text representation
+// drops the -Level suffix (see example).
+func (l Level) MarshalText() ([]byte, error) {
+ return []byte(l.String()), nil
+}
+
+// UnmarshalText unmarshals text to a level. Like MarshalText, UnmarshalText
+// expects the text representation of a Level to drop the -Level suffix (see
+// example).
+//
+// In particular, this makes it easy to configure logging levels using YAML,
+// TOML, or JSON files.
+func (l *Level) UnmarshalText(text []byte) error {
+ if l == nil {
+ return errUnmarshalNilLevel
+ }
+ if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) {
+ return fmt.Errorf("unrecognized level: %q", text)
+ }
+ return nil
+}
+
+func (l *Level) unmarshalText(text []byte) bool {
+ switch string(text) {
+ case "debug", "DEBUG":
+ *l = DebugLevel
+ case "info", "INFO", "": // make the zero value useful
+ *l = InfoLevel
+ case "warn", "WARN":
+ *l = WarnLevel
+ case "error", "ERROR":
+ *l = ErrorLevel
+ case "dpanic", "DPANIC":
+ *l = DPanicLevel
+ case "panic", "PANIC":
+ *l = PanicLevel
+ case "fatal", "FATAL":
+ *l = FatalLevel
+ default:
+ return false
+ }
+ return true
+}
+
+// Set sets the level for the flag.Value interface.
+func (l *Level) Set(s string) error {
+ return l.UnmarshalText([]byte(s))
+}
+
+// Get gets the level for the flag.Getter interface.
+func (l *Level) Get() interface{} {
+ return *l
+}
+
+// Enabled returns true if the given level is at or above this level.
+func (l Level) Enabled(lvl Level) bool {
+ return lvl >= l
+}
+
+// LevelEnabler decides whether a given logging level is enabled when logging a
+// message.
+//
+// Enablers are intended to be used to implement deterministic filters;
+// concerns like sampling are better implemented as a Core.
+//
+// Each concrete Level value implements a static LevelEnabler which returns
+// true for itself and all higher logging levels. For example WarnLevel.Enabled()
+// will return true for WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, and
+// FatalLevel, but return false for InfoLevel and DebugLevel.
+type LevelEnabler interface {
+ Enabled(Level) bool
+}
diff --git a/vendor/go.uber.org/zap/zapcore/level_strings.go b/vendor/go.uber.org/zap/zapcore/level_strings.go
new file mode 100644
index 000000000..7af8dadcb
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/level_strings.go
@@ -0,0 +1,46 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "go.uber.org/zap/internal/color"
+
+var (
+ _levelToColor = map[Level]color.Color{
+ DebugLevel: color.Magenta,
+ InfoLevel: color.Blue,
+ WarnLevel: color.Yellow,
+ ErrorLevel: color.Red,
+ DPanicLevel: color.Red,
+ PanicLevel: color.Red,
+ FatalLevel: color.Red,
+ }
+ _unknownLevelColor = color.Red
+
+ _levelToLowercaseColorString = make(map[Level]string, len(_levelToColor))
+ _levelToCapitalColorString = make(map[Level]string, len(_levelToColor))
+)
+
+func init() {
+ for level, color := range _levelToColor {
+ _levelToLowercaseColorString[level] = color.Add(level.String())
+ _levelToCapitalColorString[level] = color.Add(level.CapitalString())
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/marshaler.go b/vendor/go.uber.org/zap/zapcore/marshaler.go
new file mode 100644
index 000000000..2627a653d
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/marshaler.go
@@ -0,0 +1,53 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+// ObjectMarshaler allows user-defined types to efficiently add themselves to the
+// logging context, and to selectively omit information which shouldn't be
+// included in logs (e.g., passwords).
+type ObjectMarshaler interface {
+ MarshalLogObject(ObjectEncoder) error
+}
+
+// ObjectMarshalerFunc is a type adapter that turns a function into an
+// ObjectMarshaler.
+type ObjectMarshalerFunc func(ObjectEncoder) error
+
+// MarshalLogObject calls the underlying function.
+func (f ObjectMarshalerFunc) MarshalLogObject(enc ObjectEncoder) error {
+ return f(enc)
+}
+
+// ArrayMarshaler allows user-defined types to efficiently add themselves to the
+// logging context, and to selectively omit information which shouldn't be
+// included in logs (e.g., passwords).
+type ArrayMarshaler interface {
+ MarshalLogArray(ArrayEncoder) error
+}
+
+// ArrayMarshalerFunc is a type adapter that turns a function into an
+// ArrayMarshaler.
+type ArrayMarshalerFunc func(ArrayEncoder) error
+
+// MarshalLogArray calls the underlying function.
+func (f ArrayMarshalerFunc) MarshalLogArray(enc ArrayEncoder) error {
+ return f(enc)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/memory_encoder.go b/vendor/go.uber.org/zap/zapcore/memory_encoder.go
new file mode 100644
index 000000000..5c46bc13d
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/memory_encoder.go
@@ -0,0 +1,179 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "time"
+
+// MapObjectEncoder is an ObjectEncoder backed by a simple
+// map[string]interface{}. It's not fast enough for production use, but it's
+// helpful in tests.
+type MapObjectEncoder struct {
+ // Fields contains the entire encoded log context.
+ Fields map[string]interface{}
+ // cur is a pointer to the namespace we're currently writing to.
+ cur map[string]interface{}
+}
+
+// NewMapObjectEncoder creates a new map-backed ObjectEncoder.
+func NewMapObjectEncoder() *MapObjectEncoder {
+ m := make(map[string]interface{})
+ return &MapObjectEncoder{
+ Fields: m,
+ cur: m,
+ }
+}
+
+// AddArray implements ObjectEncoder.
+func (m *MapObjectEncoder) AddArray(key string, v ArrayMarshaler) error {
+ arr := &sliceArrayEncoder{}
+ err := v.MarshalLogArray(arr)
+ m.cur[key] = arr.elems
+ return err
+}
+
+// AddObject implements ObjectEncoder.
+func (m *MapObjectEncoder) AddObject(k string, v ObjectMarshaler) error {
+ newMap := NewMapObjectEncoder()
+ m.cur[k] = newMap.Fields
+ return v.MarshalLogObject(newMap)
+}
+
+// AddBinary implements ObjectEncoder.
+func (m *MapObjectEncoder) AddBinary(k string, v []byte) { m.cur[k] = v }
+
+// AddByteString implements ObjectEncoder.
+func (m *MapObjectEncoder) AddByteString(k string, v []byte) { m.cur[k] = string(v) }
+
+// AddBool implements ObjectEncoder.
+func (m *MapObjectEncoder) AddBool(k string, v bool) { m.cur[k] = v }
+
+// AddDuration implements ObjectEncoder.
+func (m MapObjectEncoder) AddDuration(k string, v time.Duration) { m.cur[k] = v }
+
+// AddComplex128 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddComplex128(k string, v complex128) { m.cur[k] = v }
+
+// AddComplex64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddComplex64(k string, v complex64) { m.cur[k] = v }
+
+// AddFloat64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddFloat64(k string, v float64) { m.cur[k] = v }
+
+// AddFloat32 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddFloat32(k string, v float32) { m.cur[k] = v }
+
+// AddInt implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt(k string, v int) { m.cur[k] = v }
+
+// AddInt64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt64(k string, v int64) { m.cur[k] = v }
+
+// AddInt32 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt32(k string, v int32) { m.cur[k] = v }
+
+// AddInt16 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt16(k string, v int16) { m.cur[k] = v }
+
+// AddInt8 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt8(k string, v int8) { m.cur[k] = v }
+
+// AddString implements ObjectEncoder.
+func (m *MapObjectEncoder) AddString(k string, v string) { m.cur[k] = v }
+
+// AddTime implements ObjectEncoder.
+func (m MapObjectEncoder) AddTime(k string, v time.Time) { m.cur[k] = v }
+
+// AddUint implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint(k string, v uint) { m.cur[k] = v }
+
+// AddUint64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint64(k string, v uint64) { m.cur[k] = v }
+
+// AddUint32 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint32(k string, v uint32) { m.cur[k] = v }
+
+// AddUint16 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint16(k string, v uint16) { m.cur[k] = v }
+
+// AddUint8 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint8(k string, v uint8) { m.cur[k] = v }
+
+// AddUintptr implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUintptr(k string, v uintptr) { m.cur[k] = v }
+
+// AddReflected implements ObjectEncoder.
+func (m *MapObjectEncoder) AddReflected(k string, v interface{}) error {
+ m.cur[k] = v
+ return nil
+}
+
+// OpenNamespace implements ObjectEncoder.
+func (m *MapObjectEncoder) OpenNamespace(k string) {
+ ns := make(map[string]interface{})
+ m.cur[k] = ns
+ m.cur = ns
+}
+
+// sliceArrayEncoder is an ArrayEncoder backed by a simple []interface{}. Like
+// the MapObjectEncoder, it's not designed for production use.
+type sliceArrayEncoder struct {
+ elems []interface{}
+}
+
+func (s *sliceArrayEncoder) AppendArray(v ArrayMarshaler) error {
+ enc := &sliceArrayEncoder{}
+ err := v.MarshalLogArray(enc)
+ s.elems = append(s.elems, enc.elems)
+ return err
+}
+
+func (s *sliceArrayEncoder) AppendObject(v ObjectMarshaler) error {
+ m := NewMapObjectEncoder()
+ err := v.MarshalLogObject(m)
+ s.elems = append(s.elems, m.Fields)
+ return err
+}
+
+func (s *sliceArrayEncoder) AppendReflected(v interface{}) error {
+ s.elems = append(s.elems, v)
+ return nil
+}
+
+func (s *sliceArrayEncoder) AppendBool(v bool) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendByteString(v []byte) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendComplex128(v complex128) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendComplex64(v complex64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendDuration(v time.Duration) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendFloat64(v float64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendFloat32(v float32) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt(v int) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt64(v int64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt32(v int32) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt16(v int16) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt8(v int8) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendString(v string) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendTime(v time.Time) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint(v uint) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint64(v uint64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint32(v uint32) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint16(v uint16) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint8(v uint8) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUintptr(v uintptr) { s.elems = append(s.elems, v) }
diff --git a/vendor/go.uber.org/zap/zapcore/sampler.go b/vendor/go.uber.org/zap/zapcore/sampler.go
new file mode 100644
index 000000000..e31641863
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/sampler.go
@@ -0,0 +1,134 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "time"
+
+ "go.uber.org/atomic"
+)
+
+const (
+ _numLevels = _maxLevel - _minLevel + 1
+ _countersPerLevel = 4096
+)
+
+type counter struct {
+ resetAt atomic.Int64
+ counter atomic.Uint64
+}
+
+type counters [_numLevels][_countersPerLevel]counter
+
+func newCounters() *counters {
+ return &counters{}
+}
+
+func (cs *counters) get(lvl Level, key string) *counter {
+ i := lvl - _minLevel
+ j := fnv32a(key) % _countersPerLevel
+ return &cs[i][j]
+}
+
+// fnv32a, adapted from "hash/fnv", but without a []byte(string) alloc
+func fnv32a(s string) uint32 {
+ const (
+ offset32 = 2166136261
+ prime32 = 16777619
+ )
+ hash := uint32(offset32)
+ for i := 0; i < len(s); i++ {
+ hash ^= uint32(s[i])
+ hash *= prime32
+ }
+ return hash
+}
+
+func (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 {
+ tn := t.UnixNano()
+ resetAfter := c.resetAt.Load()
+ if resetAfter > tn {
+ return c.counter.Inc()
+ }
+
+ c.counter.Store(1)
+
+ newResetAfter := tn + tick.Nanoseconds()
+ if !c.resetAt.CAS(resetAfter, newResetAfter) {
+ // We raced with another goroutine trying to reset, and it also reset
+ // the counter to 1, so we need to reincrement the counter.
+ return c.counter.Inc()
+ }
+
+ return 1
+}
+
+type sampler struct {
+ Core
+
+ counts *counters
+ tick time.Duration
+ first, thereafter uint64
+}
+
+// NewSampler creates a Core that samples incoming entries, which caps the CPU
+// and I/O load of logging while attempting to preserve a representative subset
+// of your logs.
+//
+// Zap samples by logging the first N entries with a given level and message
+// each tick. If more Entries with the same level and message are seen during
+// the same interval, every Mth message is logged and the rest are dropped.
+//
+// Keep in mind that zap's sampling implementation is optimized for speed over
+// absolute precision; under load, each tick may be slightly over- or
+// under-sampled.
+func NewSampler(core Core, tick time.Duration, first, thereafter int) Core {
+ return &sampler{
+ Core: core,
+ tick: tick,
+ counts: newCounters(),
+ first: uint64(first),
+ thereafter: uint64(thereafter),
+ }
+}
+
+func (s *sampler) With(fields []Field) Core {
+ return &sampler{
+ Core: s.Core.With(fields),
+ tick: s.tick,
+ counts: s.counts,
+ first: s.first,
+ thereafter: s.thereafter,
+ }
+}
+
+func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ if !s.Enabled(ent.Level) {
+ return ce
+ }
+
+ counter := s.counts.get(ent.Level, ent.Message)
+ n := counter.IncCheckReset(ent.Time, s.tick)
+ if n > s.first && (n-s.first)%s.thereafter != 0 {
+ return ce
+ }
+ return s.Core.Check(ent, ce)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/tee.go b/vendor/go.uber.org/zap/zapcore/tee.go
new file mode 100644
index 000000000..07a32eef9
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/tee.go
@@ -0,0 +1,81 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "go.uber.org/multierr"
+
+type multiCore []Core
+
+// NewTee creates a Core that duplicates log entries into two or more
+// underlying Cores.
+//
+// Calling it with a single Core returns the input unchanged, and calling
+// it with no input returns a no-op Core.
+func NewTee(cores ...Core) Core {
+ switch len(cores) {
+ case 0:
+ return NewNopCore()
+ case 1:
+ return cores[0]
+ default:
+ return multiCore(cores)
+ }
+}
+
+func (mc multiCore) With(fields []Field) Core {
+ clone := make(multiCore, len(mc))
+ for i := range mc {
+ clone[i] = mc[i].With(fields)
+ }
+ return clone
+}
+
+func (mc multiCore) Enabled(lvl Level) bool {
+ for i := range mc {
+ if mc[i].Enabled(lvl) {
+ return true
+ }
+ }
+ return false
+}
+
+func (mc multiCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ for i := range mc {
+ ce = mc[i].Check(ent, ce)
+ }
+ return ce
+}
+
+func (mc multiCore) Write(ent Entry, fields []Field) error {
+ var err error
+ for i := range mc {
+ err = multierr.Append(err, mc[i].Write(ent, fields))
+ }
+ return err
+}
+
+func (mc multiCore) Sync() error {
+ var err error
+ for i := range mc {
+ err = multierr.Append(err, mc[i].Sync())
+ }
+ return err
+}
diff --git a/vendor/go.uber.org/zap/zapcore/write_syncer.go b/vendor/go.uber.org/zap/zapcore/write_syncer.go
new file mode 100644
index 000000000..209e25fe2
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/write_syncer.go
@@ -0,0 +1,123 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "io"
+ "sync"
+
+ "go.uber.org/multierr"
+)
+
+// A WriteSyncer is an io.Writer that can also flush any buffered data. Note
+// that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer.
+type WriteSyncer interface {
+ io.Writer
+ Sync() error
+}
+
+// AddSync converts an io.Writer to a WriteSyncer. It attempts to be
+// intelligent: if the concrete type of the io.Writer implements WriteSyncer,
+// we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync.
+func AddSync(w io.Writer) WriteSyncer {
+ switch w := w.(type) {
+ case WriteSyncer:
+ return w
+ default:
+ return writerWrapper{w}
+ }
+}
+
+type lockedWriteSyncer struct {
+ sync.Mutex
+ ws WriteSyncer
+}
+
+// Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In
+// particular, *os.Files must be locked before use.
+func Lock(ws WriteSyncer) WriteSyncer {
+ if _, ok := ws.(*lockedWriteSyncer); ok {
+ // no need to layer on another lock
+ return ws
+ }
+ return &lockedWriteSyncer{ws: ws}
+}
+
+func (s *lockedWriteSyncer) Write(bs []byte) (int, error) {
+ s.Lock()
+ n, err := s.ws.Write(bs)
+ s.Unlock()
+ return n, err
+}
+
+func (s *lockedWriteSyncer) Sync() error {
+ s.Lock()
+ err := s.ws.Sync()
+ s.Unlock()
+ return err
+}
+
+type writerWrapper struct {
+ io.Writer
+}
+
+func (w writerWrapper) Sync() error {
+ return nil
+}
+
+type multiWriteSyncer []WriteSyncer
+
+// NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes
+// and sync calls, much like io.MultiWriter.
+func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer {
+ if len(ws) == 1 {
+ return ws[0]
+ }
+ // Copy to protect against https://github.com/golang/go/issues/7809
+ return multiWriteSyncer(append([]WriteSyncer(nil), ws...))
+}
+
+// See https://golang.org/src/io/multi.go
+// When not all underlying syncers write the same number of bytes,
+// the smallest number is returned even though Write() is called on
+// all of them.
+func (ws multiWriteSyncer) Write(p []byte) (int, error) {
+ var writeErr error
+ nWritten := 0
+ for _, w := range ws {
+ n, err := w.Write(p)
+ writeErr = multierr.Append(writeErr, err)
+ if nWritten == 0 && n != 0 {
+ nWritten = n
+ } else if n < nWritten {
+ nWritten = n
+ }
+ }
+ return nWritten, writeErr
+}
+
+func (ws multiWriteSyncer) Sync() error {
+ var err error
+ for _, w := range ws {
+ err = multierr.Append(err, w.Sync())
+ }
+ return err
+}
diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/.gitignore b/vendor/gopkg.in/natefinch/lumberjack.v2/.gitignore
new file mode 100644
index 000000000..836562412
--- /dev/null
+++ b/vendor/gopkg.in/natefinch/lumberjack.v2/.gitignore
@@ -0,0 +1,23 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/LICENSE b/vendor/gopkg.in/natefinch/lumberjack.v2/LICENSE
new file mode 100644
index 000000000..c3d4cc307
--- /dev/null
+++ b/vendor/gopkg.in/natefinch/lumberjack.v2/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Nate Finch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/README.md b/vendor/gopkg.in/natefinch/lumberjack.v2/README.md
new file mode 100644
index 000000000..9e9715453
--- /dev/null
+++ b/vendor/gopkg.in/natefinch/lumberjack.v2/README.md
@@ -0,0 +1,174 @@
+# lumberjack [![GoDoc](https://godoc.org/gopkg.in/natefinch/lumberjack.v2?status.png)](https://godoc.org/gopkg.in/natefinch/lumberjack.v2) [![Build Status](https://drone.io/github.com/natefinch/lumberjack/status.png)](https://drone.io/github.com/natefinch/lumberjack/latest) [![Build status](https://ci.appveyor.com/api/projects/status/00gchpxtg4gkrt5d)](https://ci.appveyor.com/project/natefinch/lumberjack) [![Coverage Status](https://coveralls.io/repos/natefinch/lumberjack/badge.svg?branch=v2.0)](https://coveralls.io/r/natefinch/lumberjack?branch=v2.0)
+
+### Lumberjack is a Go package for writing logs to rolling files.
+
+Package lumberjack provides a rolling logger.
+
+Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
+thusly:
+
+ import "gopkg.in/natefinch/lumberjack.v2"
+
+The package name remains simply lumberjack, and the code resides at
+https://github.com/natefinch/lumberjack under the v2.0 branch.
+
+Lumberjack is intended to be one part of a logging infrastructure.
+It is not an all-in-one solution, but instead is a pluggable
+component at the bottom of the logging stack that simply controls the files
+to which logs are written.
+
+Lumberjack plays well with any logging package that can write to an
+io.Writer, including the standard library's log package.
+
+Lumberjack assumes that only one process is writing to the output files.
+Using the same lumberjack configuration from multiple processes on the same
+machine will result in improper behavior.
+
+
+**Example**
+
+To use lumberjack with the standard library's log package, just pass it into the SetOutput function when your application starts.
+
+Code:
+
+```go
+log.SetOutput(&lumberjack.Logger{
+ Filename: "/var/log/myapp/foo.log",
+ MaxSize: 500, // megabytes
+ MaxBackups: 3,
+ MaxAge: 28, //days
+})
+```
+
+
+
+## type Logger
+``` go
+type Logger struct {
+ // Filename is the file to write logs to. Backup log files will be retained
+ // in the same directory. It uses <processname>-lumberjack.log in
+ // os.TempDir() if empty.
+ Filename string `json:"filename" yaml:"filename"`
+
+ // MaxSize is the maximum size in megabytes of the log file before it gets
+ // rotated. It defaults to 100 megabytes.
+ MaxSize int `json:"maxsize" yaml:"maxsize"`
+
+ // MaxAge is the maximum number of days to retain old log files based on the
+ // timestamp encoded in their filename. Note that a day is defined as 24
+ // hours and may not exactly correspond to calendar days due to daylight
+ // savings, leap seconds, etc. The default is not to remove old log files
+ // based on age.
+ MaxAge int `json:"maxage" yaml:"maxage"`
+
+ // MaxBackups is the maximum number of old log files to retain. The default
+ // is to retain all old log files (though MaxAge may still cause them to get
+ // deleted.)
+ MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
+
+ // LocalTime determines if the time used for formatting the timestamps in
+ // backup files is the computer's local time. The default is to use UTC
+ // time.
+ LocalTime bool `json:"localtime" yaml:"localtime"`
+ // contains filtered or unexported fields
+}
+```
+Logger is an io.WriteCloser that writes to the specified filename.
+
+Logger opens or creates the logfile on first Write. If the file exists and
+is less than MaxSize megabytes, lumberjack will open and append to that file.
+If the file exists and its size is >= MaxSize megabytes, the file is renamed
+by putting the current time in a timestamp in the name immediately before the
+file's extension (or the end of the filename if there's no extension). A new
+log file is then created using original filename.
+
+Whenever a write would cause the current log file exceed MaxSize megabytes,
+the current file is closed, renamed, and a new log file created with the
+original name. Thus, the filename you give Logger is always the "current" log
+file.
+
+Backups use the log file name given to Logger, in the form `name-timestamp.ext`
+where name is the filename without the extension, timestamp is the time at which
+the log was rotated formatted with the time.Time format of
+`2006-01-02T15-04-05.000` and the extension is the original extension. For
+example, if your Logger.Filename is `/var/log/foo/server.log`, a backup created
+at 6:30pm on Nov 11 2016 would use the filename
+`/var/log/foo/server-2016-11-04T18-30-00.000.log`
+
+### Cleaning Up Old Log Files
+Whenever a new logfile gets created, old log files may be deleted. The most
+recent files according to the encoded timestamp will be retained, up to a
+number equal to MaxBackups (or all of them if MaxBackups is 0). Any files
+with an encoded timestamp older than MaxAge days are deleted, regardless of
+MaxBackups. Note that the time encoded in the timestamp is the rotation
+time, which may differ from the last time that file was written to.
+
+If MaxBackups and MaxAge are both 0, no old log files will be deleted.
+
+
+
+
+
+
+
+
+
+
+
+### func (\*Logger) Close
+``` go
+func (l *Logger) Close() error
+```
+Close implements io.Closer, and closes the current logfile.
+
+
+
+### func (\*Logger) Rotate
+``` go
+func (l *Logger) Rotate() error
+```
+Rotate causes Logger to close the existing log file and immediately create a
+new one. This is a helper function for applications that want to initiate
+rotations outside of the normal rotation rules, such as in response to
+SIGHUP. After rotating, this initiates a cleanup of old log files according
+to the normal rules.
+
+**Example**
+
+Example of how to rotate in response to SIGHUP.
+
+Code:
+
+```go
+l := &lumberjack.Logger{}
+log.SetOutput(l)
+c := make(chan os.Signal, 1)
+signal.Notify(c, syscall.SIGHUP)
+
+go func() {
+ for {
+ <-c
+ l.Rotate()
+ }
+}()
+```
+
+### func (\*Logger) Write
+``` go
+func (l *Logger) Write(p []byte) (n int, err error)
+```
+Write implements io.Writer. If a write would cause the log file to be larger
+than MaxSize, the file is closed, renamed to include a timestamp of the
+current time, and a new log file is created using the original log file name.
+If the length of the write is greater than MaxSize, an error is returned.
+
+
+
+
+
+
+
+
+
+- - -
+Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/chown.go b/vendor/gopkg.in/natefinch/lumberjack.v2/chown.go
new file mode 100644
index 000000000..11d066972
--- /dev/null
+++ b/vendor/gopkg.in/natefinch/lumberjack.v2/chown.go
@@ -0,0 +1,11 @@
+// +build !linux
+
+package lumberjack
+
+import (
+ "os"
+)
+
+func chown(_ string, _ os.FileInfo) error {
+ return nil
+}
diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/chown_linux.go b/vendor/gopkg.in/natefinch/lumberjack.v2/chown_linux.go
new file mode 100644
index 000000000..2758ec9ce
--- /dev/null
+++ b/vendor/gopkg.in/natefinch/lumberjack.v2/chown_linux.go
@@ -0,0 +1,19 @@
+package lumberjack
+
+import (
+ "os"
+ "syscall"
+)
+
+// os_Chown is a var so we can mock it out during tests.
+var os_Chown = os.Chown
+
+func chown(name string, info os.FileInfo) error {
+ f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode())
+ if err != nil {
+ return err
+ }
+ f.Close()
+ stat := info.Sys().(*syscall.Stat_t)
+ return os_Chown(name, int(stat.Uid), int(stat.Gid))
+}
diff --git a/vendor/gopkg.in/natefinch/lumberjack.v2/lumberjack.go b/vendor/gopkg.in/natefinch/lumberjack.v2/lumberjack.go
new file mode 100644
index 000000000..ca19da440
--- /dev/null
+++ b/vendor/gopkg.in/natefinch/lumberjack.v2/lumberjack.go
@@ -0,0 +1,541 @@
+// Package lumberjack provides a rolling logger.
+//
+// Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
+// thusly:
+//
+// import "gopkg.in/natefinch/lumberjack.v2"
+//
+// The package name remains simply lumberjack, and the code resides at
+// https://github.com/natefinch/lumberjack under the v2.0 branch.
+//
+// Lumberjack is intended to be one part of a logging infrastructure.
+// It is not an all-in-one solution, but instead is a pluggable
+// component at the bottom of the logging stack that simply controls the files
+// to which logs are written.
+//
+// Lumberjack plays well with any logging package that can write to an
+// io.Writer, including the standard library's log package.
+//
+// Lumberjack assumes that only one process is writing to the output files.
+// Using the same lumberjack configuration from multiple processes on the same
+// machine will result in improper behavior.
+package lumberjack
+
+import (
+ "compress/gzip"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+)
+
+const (
+ backupTimeFormat = "2006-01-02T15-04-05.000"
+ compressSuffix = ".gz"
+ defaultMaxSize = 100
+)
+
+// ensure we always implement io.WriteCloser
+var _ io.WriteCloser = (*Logger)(nil)
+
+// Logger is an io.WriteCloser that writes to the specified filename.
+//
+// Logger opens or creates the logfile on first Write. If the file exists and
+// is less than MaxSize megabytes, lumberjack will open and append to that file.
+// If the file exists and its size is >= MaxSize megabytes, the file is renamed
+// by putting the current time in a timestamp in the name immediately before the
+// file's extension (or the end of the filename if there's no extension). A new
+// log file is then created using original filename.
+//
+// Whenever a write would cause the current log file exceed MaxSize megabytes,
+// the current file is closed, renamed, and a new log file created with the
+// original name. Thus, the filename you give Logger is always the "current" log
+// file.
+//
+// Backups use the log file name given to Logger, in the form
+// `name-timestamp.ext` where name is the filename without the extension,
+// timestamp is the time at which the log was rotated formatted with the
+// time.Time format of `2006-01-02T15-04-05.000` and the extension is the
+// original extension. For example, if your Logger.Filename is
+// `/var/log/foo/server.log`, a backup created at 6:30pm on Nov 11 2016 would
+// use the filename `/var/log/foo/server-2016-11-04T18-30-00.000.log`
+//
+// Cleaning Up Old Log Files
+//
+// Whenever a new logfile gets created, old log files may be deleted. The most
+// recent files according to the encoded timestamp will be retained, up to a
+// number equal to MaxBackups (or all of them if MaxBackups is 0). Any files
+// with an encoded timestamp older than MaxAge days are deleted, regardless of
+// MaxBackups. Note that the time encoded in the timestamp is the rotation
+// time, which may differ from the last time that file was written to.
+//
+// If MaxBackups and MaxAge are both 0, no old log files will be deleted.
+type Logger struct {
+ // Filename is the file to write logs to. Backup log files will be retained
+ // in the same directory. It uses <processname>-lumberjack.log in
+ // os.TempDir() if empty.
+ Filename string `json:"filename" yaml:"filename"`
+
+ // MaxSize is the maximum size in megabytes of the log file before it gets
+ // rotated. It defaults to 100 megabytes.
+ MaxSize int `json:"maxsize" yaml:"maxsize"`
+
+ // MaxAge is the maximum number of days to retain old log files based on the
+ // timestamp encoded in their filename. Note that a day is defined as 24
+ // hours and may not exactly correspond to calendar days due to daylight
+ // savings, leap seconds, etc. The default is not to remove old log files
+ // based on age.
+ MaxAge int `json:"maxage" yaml:"maxage"`
+
+ // MaxBackups is the maximum number of old log files to retain. The default
+ // is to retain all old log files (though MaxAge may still cause them to get
+ // deleted.)
+ MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
+
+ // LocalTime determines if the time used for formatting the timestamps in
+ // backup files is the computer's local time. The default is to use UTC
+ // time.
+ LocalTime bool `json:"localtime" yaml:"localtime"`
+
+ // Compress determines if the rotated log files should be compressed
+ // using gzip.
+ Compress bool `json:"compress" yaml:"compress"`
+
+ size int64
+ file *os.File
+ mu sync.Mutex
+
+ millCh chan bool
+ startMill sync.Once
+}
+
+var (
+ // currentTime exists so it can be mocked out by tests.
+ currentTime = time.Now
+
+ // os_Stat exists so it can be mocked out by tests.
+ os_Stat = os.Stat
+
+ // megabyte is the conversion factor between MaxSize and bytes. It is a
+ // variable so tests can mock it out and not need to write megabytes of data
+ // to disk.
+ megabyte = 1024 * 1024
+)
+
+// Write implements io.Writer. If a write would cause the log file to be larger
+// than MaxSize, the file is closed, renamed to include a timestamp of the
+// current time, and a new log file is created using the original log file name.
+// If the length of the write is greater than MaxSize, an error is returned.
+func (l *Logger) Write(p []byte) (n int, err error) {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+
+ writeLen := int64(len(p))
+ if writeLen > l.max() {
+ return 0, fmt.Errorf(
+ "write length %d exceeds maximum file size %d", writeLen, l.max(),
+ )
+ }
+
+ if l.file == nil {
+ if err = l.openExistingOrNew(len(p)); err != nil {
+ return 0, err
+ }
+ }
+
+ if l.size+writeLen > l.max() {
+ if err := l.rotate(); err != nil {
+ return 0, err
+ }
+ }
+
+ n, err = l.file.Write(p)
+ l.size += int64(n)
+
+ return n, err
+}
+
+// Close implements io.Closer, and closes the current logfile.
+func (l *Logger) Close() error {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ return l.close()
+}
+
+// close closes the file if it is open.
+func (l *Logger) close() error {
+ if l.file == nil {
+ return nil
+ }
+ err := l.file.Close()
+ l.file = nil
+ return err
+}
+
+// Rotate causes Logger to close the existing log file and immediately create a
+// new one. This is a helper function for applications that want to initiate
+// rotations outside of the normal rotation rules, such as in response to
+// SIGHUP. After rotating, this initiates compression and removal of old log
+// files according to the configuration.
+func (l *Logger) Rotate() error {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ return l.rotate()
+}
+
+// rotate closes the current file, moves it aside with a timestamp in the name,
+// (if it exists), opens a new file with the original filename, and then runs
+// post-rotation processing and removal.
+func (l *Logger) rotate() error {
+ if err := l.close(); err != nil {
+ return err
+ }
+ if err := l.openNew(); err != nil {
+ return err
+ }
+ l.mill()
+ return nil
+}
+
+// openNew opens a new log file for writing, moving any old log file out of the
+// way. This methods assumes the file has already been closed.
+func (l *Logger) openNew() error {
+ err := os.MkdirAll(l.dir(), 0744)
+ if err != nil {
+ return fmt.Errorf("can't make directories for new logfile: %s", err)
+ }
+
+ name := l.filename()
+ mode := os.FileMode(0644)
+ info, err := os_Stat(name)
+ if err == nil {
+ // Copy the mode off the old logfile.
+ mode = info.Mode()
+ // move the existing file
+ newname := backupName(name, l.LocalTime)
+ if err := os.Rename(name, newname); err != nil {
+ return fmt.Errorf("can't rename log file: %s", err)
+ }
+
+ // this is a no-op anywhere but linux
+ if err := chown(name, info); err != nil {
+ return err
+ }
+ }
+
+ // we use truncate here because this should only get called when we've moved
+ // the file ourselves. if someone else creates the file in the meantime,
+ // just wipe out the contents.
+ f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
+ if err != nil {
+ return fmt.Errorf("can't open new logfile: %s", err)
+ }
+ l.file = f
+ l.size = 0
+ return nil
+}
+
+// backupName creates a new filename from the given name, inserting a timestamp
+// between the filename and the extension, using the local time if requested
+// (otherwise UTC).
+func backupName(name string, local bool) string {
+ dir := filepath.Dir(name)
+ filename := filepath.Base(name)
+ ext := filepath.Ext(filename)
+ prefix := filename[:len(filename)-len(ext)]
+ t := currentTime()
+ if !local {
+ t = t.UTC()
+ }
+
+ timestamp := t.Format(backupTimeFormat)
+ return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext))
+}
+
+// openExistingOrNew opens the logfile if it exists and if the current write
+// would not put it over MaxSize. If there is no such file or the write would
+// put it over the MaxSize, a new file is created.
+func (l *Logger) openExistingOrNew(writeLen int) error {
+ l.mill()
+
+ filename := l.filename()
+ info, err := os_Stat(filename)
+ if os.IsNotExist(err) {
+ return l.openNew()
+ }
+ if err != nil {
+ return fmt.Errorf("error getting log file info: %s", err)
+ }
+
+ if info.Size()+int64(writeLen) >= l.max() {
+ return l.rotate()
+ }
+
+ file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644)
+ if err != nil {
+ // if we fail to open the old log file for some reason, just ignore
+ // it and open a new log file.
+ return l.openNew()
+ }
+ l.file = file
+ l.size = info.Size()
+ return nil
+}
+
+// genFilename generates the name of the logfile from the current time.
+func (l *Logger) filename() string {
+ if l.Filename != "" {
+ return l.Filename
+ }
+ name := filepath.Base(os.Args[0]) + "-lumberjack.log"
+ return filepath.Join(os.TempDir(), name)
+}
+
+// millRunOnce performs compression and removal of stale log files.
+// Log files are compressed if enabled via configuration and old log
+// files are removed, keeping at most l.MaxBackups files, as long as
+// none of them are older than MaxAge.
+func (l *Logger) millRunOnce() error {
+ if l.MaxBackups == 0 && l.MaxAge == 0 && !l.Compress {
+ return nil
+ }
+
+ files, err := l.oldLogFiles()
+ if err != nil {
+ return err
+ }
+
+ var compress, remove []logInfo
+
+ if l.MaxBackups > 0 && l.MaxBackups < len(files) {
+ preserved := make(map[string]bool)
+ var remaining []logInfo
+ for _, f := range files {
+ // Only count the uncompressed log file or the
+ // compressed log file, not both.
+ fn := f.Name()
+ if strings.HasSuffix(fn, compressSuffix) {
+ fn = fn[:len(fn)-len(compressSuffix)]
+ }
+ preserved[fn] = true
+
+ if len(preserved) > l.MaxBackups {
+ remove = append(remove, f)
+ } else {
+ remaining = append(remaining, f)
+ }
+ }
+ files = remaining
+ }
+ if l.MaxAge > 0 {
+ diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge))
+ cutoff := currentTime().Add(-1 * diff)
+
+ var remaining []logInfo
+ for _, f := range files {
+ if f.timestamp.Before(cutoff) {
+ remove = append(remove, f)
+ } else {
+ remaining = append(remaining, f)
+ }
+ }
+ files = remaining
+ }
+
+ if l.Compress {
+ for _, f := range files {
+ if !strings.HasSuffix(f.Name(), compressSuffix) {
+ compress = append(compress, f)
+ }
+ }
+ }
+
+ for _, f := range remove {
+ errRemove := os.Remove(filepath.Join(l.dir(), f.Name()))
+ if err == nil && errRemove != nil {
+ err = errRemove
+ }
+ }
+ for _, f := range compress {
+ fn := filepath.Join(l.dir(), f.Name())
+ errCompress := compressLogFile(fn, fn+compressSuffix)
+ if err == nil && errCompress != nil {
+ err = errCompress
+ }
+ }
+
+ return err
+}
+
+// millRun runs in a goroutine to manage post-rotation compression and removal
+// of old log files.
+func (l *Logger) millRun() {
+ for _ = range l.millCh {
+ // what am I going to do, log this?
+ _ = l.millRunOnce()
+ }
+}
+
+// mill performs post-rotation compression and removal of stale log files,
+// starting the mill goroutine if necessary.
+func (l *Logger) mill() {
+ l.startMill.Do(func() {
+ l.millCh = make(chan bool, 1)
+ go l.millRun()
+ })
+ select {
+ case l.millCh <- true:
+ default:
+ }
+}
+
+// oldLogFiles returns the list of backup log files stored in the same
+// directory as the current log file, sorted by ModTime
+func (l *Logger) oldLogFiles() ([]logInfo, error) {
+ files, err := ioutil.ReadDir(l.dir())
+ if err != nil {
+ return nil, fmt.Errorf("can't read log file directory: %s", err)
+ }
+ logFiles := []logInfo{}
+
+ prefix, ext := l.prefixAndExt()
+
+ for _, f := range files {
+ if f.IsDir() {
+ continue
+ }
+ if t, err := l.timeFromName(f.Name(), prefix, ext); err == nil {
+ logFiles = append(logFiles, logInfo{t, f})
+ continue
+ }
+ if t, err := l.timeFromName(f.Name(), prefix, ext+compressSuffix); err == nil {
+ logFiles = append(logFiles, logInfo{t, f})
+ continue
+ }
+ // error parsing means that the suffix at the end was not generated
+ // by lumberjack, and therefore it's not a backup file.
+ }
+
+ sort.Sort(byFormatTime(logFiles))
+
+ return logFiles, nil
+}
+
+// timeFromName extracts the formatted time from the filename by stripping off
+// the filename's prefix and extension. This prevents someone's filename from
+// confusing time.parse.
+func (l *Logger) timeFromName(filename, prefix, ext string) (time.Time, error) {
+ if !strings.HasPrefix(filename, prefix) {
+ return time.Time{}, errors.New("mismatched prefix")
+ }
+ if !strings.HasSuffix(filename, ext) {
+ return time.Time{}, errors.New("mismatched extension")
+ }
+ ts := filename[len(prefix) : len(filename)-len(ext)]
+ return time.Parse(backupTimeFormat, ts)
+}
+
+// max returns the maximum size in bytes of log files before rolling.
+func (l *Logger) max() int64 {
+ if l.MaxSize == 0 {
+ return int64(defaultMaxSize * megabyte)
+ }
+ return int64(l.MaxSize) * int64(megabyte)
+}
+
+// dir returns the directory for the current filename.
+func (l *Logger) dir() string {
+ return filepath.Dir(l.filename())
+}
+
+// prefixAndExt returns the filename part and extension part from the Logger's
+// filename.
+func (l *Logger) prefixAndExt() (prefix, ext string) {
+ filename := filepath.Base(l.filename())
+ ext = filepath.Ext(filename)
+ prefix = filename[:len(filename)-len(ext)] + "-"
+ return prefix, ext
+}
+
+// compressLogFile compresses the given log file, removing the
+// uncompressed log file if successful.
+func compressLogFile(src, dst string) (err error) {
+ f, err := os.Open(src)
+ if err != nil {
+ return fmt.Errorf("failed to open log file: %v", err)
+ }
+ defer f.Close()
+
+ fi, err := os_Stat(src)
+ if err != nil {
+ return fmt.Errorf("failed to stat log file: %v", err)
+ }
+
+ if err := chown(dst, fi); err != nil {
+ return fmt.Errorf("failed to chown compressed log file: %v", err)
+ }
+
+ // If this file already exists, we presume it was created by
+ // a previous attempt to compress the log file.
+ gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode())
+ if err != nil {
+ return fmt.Errorf("failed to open compressed log file: %v", err)
+ }
+ defer gzf.Close()
+
+ gz := gzip.NewWriter(gzf)
+
+ defer func() {
+ if err != nil {
+ os.Remove(dst)
+ err = fmt.Errorf("failed to compress log file: %v", err)
+ }
+ }()
+
+ if _, err := io.Copy(gz, f); err != nil {
+ return err
+ }
+ if err := gz.Close(); err != nil {
+ return err
+ }
+ if err := gzf.Close(); err != nil {
+ return err
+ }
+
+ if err := f.Close(); err != nil {
+ return err
+ }
+ if err := os.Remove(src); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// logInfo is a convenience struct to return the filename and its embedded
+// timestamp.
+type logInfo struct {
+ timestamp time.Time
+ os.FileInfo
+}
+
+// byFormatTime sorts by newest time formatted in the name.
+type byFormatTime []logInfo
+
+func (b byFormatTime) Less(i, j int) bool {
+ return b[i].timestamp.After(b[j].timestamp)
+}
+
+func (b byFormatTime) Swap(i, j int) {
+ b[i], b[j] = b[j], b[i]
+}
+
+func (b byFormatTime) Len() int {
+ return len(b)
+}
diff --git a/web/web.go b/web/web.go
index c9d318397..56a5ab6ac 100644
--- a/web/web.go
+++ b/web/web.go
@@ -4,6 +4,7 @@
package web
import (
+ "fmt"
"net/http"
"path/filepath"
"strings"
@@ -11,20 +12,20 @@ import (
"github.com/NYTimes/gziphandler"
"github.com/avct/uasurfer"
- l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/api"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
func Init(api3 *api.API) {
- l4g.Debug(utils.T("web.init.debug"))
+ mlog.Debug("Initializing web routes")
mainrouter := api3.BaseRoutes.Root
if *api3.App.Config().ServiceSettings.WebserverMode != "disabled" {
staticDir, _ := utils.FindDir(model.CLIENT_DIR)
- l4g.Debug("Using client directory at %v", staticDir)
+ mlog.Debug(fmt.Sprintf("Using client directory at %v", staticDir))
staticHandler := staticHandler(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
pluginHandler := pluginHandler(api3.App.Config, http.StripPrefix("/static/plugins/", http.FileServer(http.Dir(*api3.App.Config().PluginSettings.ClientDirectory))))
diff --git a/web/web_test.go b/web/web_test.go
index 09460b3b0..12099709e 100644
--- a/web/web_test.go
+++ b/web/web_test.go
@@ -11,6 +11,7 @@ import (
"github.com/mattermost/mattermost-server/api"
"github.com/mattermost/mattermost-server/api4"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/store/sqlstore"
@@ -150,6 +151,15 @@ func TestIncomingWebhook(t *testing.T) {
}
func TestMain(m *testing.M) {
+ // Setup a global logger to catch tests logging outside of app context
+ // The global logger will be stomped by apps initalizing 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()
status := 0
diff --git a/wsapi/status.go b/wsapi/status.go
index c3d323053..113bb7ed5 100644
--- a/wsapi/status.go
+++ b/wsapi/status.go
@@ -4,8 +4,7 @@
package wsapi
import (
- l4g "github.com/alecthomas/log4go"
-
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
)
@@ -22,7 +21,7 @@ func (api *API) getStatuses(req *model.WebSocketRequest) (map[string]interface{}
func (api *API) getStatusesByIds(req *model.WebSocketRequest) (map[string]interface{}, *model.AppError) {
var userIds []string
if userIds = model.ArrayFromInterface(req.Data["user_ids"]); len(userIds) == 0 {
- l4g.Error(model.StringInterfaceToJson(req.Data))
+ mlog.Error(model.StringInterfaceToJson(req.Data))
return nil, NewInvalidWebSocketParamError(req.Action, "user_ids")
}
diff --git a/wsapi/websocket_handler.go b/wsapi/websocket_handler.go
index 0208de140..dc6bd06d9 100644
--- a/wsapi/websocket_handler.go
+++ b/wsapi/websocket_handler.go
@@ -4,11 +4,11 @@
package wsapi
import (
- l4g "github.com/alecthomas/log4go"
-
+ "fmt"
"net/http"
"github.com/mattermost/mattermost-server/app"
+ "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
)
@@ -23,11 +23,11 @@ type webSocketHandler struct {
}
func (wh webSocketHandler) ServeWebSocket(conn *app.WebConn, r *model.WebSocketRequest) {
- l4g.Debug("websocket: %s", r.Action)
+ mlog.Debug(fmt.Sprintf("websocket: %s", r.Action))
session, sessionErr := wh.app.GetSession(conn.GetSessionToken())
if sessionErr != nil {
- l4g.Error(utils.T("api.web_socket_handler.log.error"), "websocket", r.Action, r.Seq, conn.UserId, sessionErr.SystemMessage(utils.T), sessionErr.Error())
+ mlog.Error(fmt.Sprintf("%v:%v seq=%v uid=%v %v [details: %v]", "websocket", r.Action, r.Seq, conn.UserId, sessionErr.SystemMessage(utils.T), sessionErr.Error()))
sessionErr.DetailedError = ""
errResp := model.NewWebSocketError(r.Seq, sessionErr)
@@ -43,7 +43,7 @@ func (wh webSocketHandler) ServeWebSocket(conn *app.WebConn, r *model.WebSocketR
var err *model.AppError
if data, err = wh.handlerFunc(r); err != nil {
- l4g.Error(utils.T("api.web_socket_handler.log.error"), "websocket", r.Action, r.Seq, r.Session.UserId, err.SystemMessage(utils.T), err.DetailedError)
+ mlog.Error(fmt.Sprintf("%v:%v seq=%v uid=%v %v [details: %v]", "websocket", r.Action, r.Seq, r.Session.UserId, err.SystemMessage(utils.T), err.DetailedError))
err.DetailedError = ""
errResp := model.NewWebSocketError(r.Seq, err)