Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slack-19.0: add structured logging #449

Merged
merged 9 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,12 @@ require (
github.com/kr/text v0.2.0
github.com/mitchellh/mapstructure v1.5.0
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249
github.com/slok/noglog v0.2.0
github.com/spf13/afero v1.11.0
github.com/spf13/jwalterweatherman v1.1.0
github.com/xlab/treeprint v1.2.0
go.uber.org/goleak v1.3.0
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/sync v0.6.0
gonum.org/v1/gonum v0.14.0
Expand Down Expand Up @@ -183,7 +185,6 @@ require (
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sjmudd/stopwatch v0.1.1 h1:x45OvxFB5OtCkjvYtzRF5fWB857Jzjjk84Oyd5C5ebw=
github.com/sjmudd/stopwatch v0.1.1/go.mod h1:BLw0oIQJ1YLXBO/q9ufK/SgnKBVIkC2qrm6uy78Zw6U=
github.com/slok/noglog v0.2.0 h1:1czu4l2EoJ8L92UwdSXXa1Y+c5TIjFAFm2P+mjej95E=
github.com/slok/noglog v0.2.0/go.mod h1:TfKxwpEZPT+UA83bQ6RME146k0MM4e8mwHLf6bhcGDI=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
Expand Down
1 change: 1 addition & 0 deletions go/flags/endtoend/mysqlctl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Flags:
--service_map strings comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice
--socket_file string Local unix socket file to listen on
--stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1)
--structured-logging enable structured logging
--table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class
--tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid.
--tablet_uid uint32 Tablet UID. (default 41983)
Expand Down
1 change: 1 addition & 0 deletions go/flags/endtoend/mysqlctld.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ Flags:
--shutdown-wait-time duration How long to wait for mysqld shutdown (default 5m0s)
--socket_file string Local unix socket file to listen on
--stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1)
--structured-logging enable structured logging
--table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class
--tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid.
--tablet_uid uint32 Tablet UID (default 41983)
Expand Down
1 change: 1 addition & 0 deletions go/flags/endtoend/vtcombo.txt
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ Flags:
--stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1)
--stream_buffer_size int the number of bytes sent from vtgate for each stream call. It's recommended to keep this value in sync with vttablet's query-server-config-stream-buffer-size. (default 32768)
--stream_health_buffer_size uint max streaming health entries to buffer per streaming health client (default 20)
--structured-logging enable structured logging
--table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class
--table_gc_lifecycle string States for a DROP TABLE garbage collection cycle. Default is 'hold,purge,evac,drop', use any subset ('drop' implicitly always included) (default "hold,purge,evac,drop")
--tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid.
Expand Down
1 change: 1 addition & 0 deletions go/flags/endtoend/vtctld.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ Flags:
--stats_drop_variables string Variables to be dropped from the list of exported variables.
--stats_emit_period duration Interval between emitting stats to all registered backends (default 1m0s)
--stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1)
--structured-logging enable structured logging
--table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class
--tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid.
--tablet_grpc_ca string the server ca to use to validate servers when connecting
Expand Down
1 change: 1 addition & 0 deletions go/flags/endtoend/vtgate.txt
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ Flags:
--statsd_sample_rate float Sample rate for statsd metrics (default 1)
--stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1)
--stream_buffer_size int the number of bytes sent from vtgate for each stream call. It's recommended to keep this value in sync with vttablet's query-server-config-stream-buffer-size. (default 32768)
--structured-logging enable structured logging
--table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class
--tablet_filters strings Specifies a comma-separated list of 'keyspace|shard_name or keyrange' values to filter the tablets to watch.
--tablet_grpc_ca string the server ca to use to validate servers when connecting
Expand Down
1 change: 1 addition & 0 deletions go/flags/endtoend/vtgateclienttest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Flags:
--security_policy string the name of a registered security policy to use for controlling access to URLs - empty means allow all for anyone (built-in policies: deny-all, read-only)
--service_map strings comma separated list of services to enable (or disable if prefixed with '-') Example: grpc-queryservice
--stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1)
--structured-logging enable structured logging
--table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class
--v Level log level for V logs
-v, --version print binary version
Expand Down
1 change: 1 addition & 0 deletions go/flags/endtoend/vtorc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ Flags:
--stats_drop_variables string Variables to be dropped from the list of exported variables.
--stats_emit_period duration Interval between emitting stats to all registered backends (default 1m0s)
--stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1)
--structured-logging enable structured logging
--table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class
--tablet_manager_grpc_ca string the server ca to use to validate servers when connecting
--tablet_manager_grpc_cert string the cert to use to connect
Expand Down
1 change: 1 addition & 0 deletions go/flags/endtoend/vttablet.txt
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ Flags:
--statsd_sample_rate float Sample rate for statsd metrics (default 1)
--stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1)
--stream_health_buffer_size uint max streaming health entries to buffer per streaming health client (default 20)
--structured-logging enable structured logging
--table-acl-config string path to table access checker config file; send SIGHUP to reload this file
--table-acl-config-reload-interval duration Ticker to reload ACLs. Duration flag, format e.g.: 30s. Default: do not reload
--table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class
Expand Down
1 change: 1 addition & 0 deletions go/flags/endtoend/vttestserver.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ Flags:
--sql-max-length-errors int truncate queries in error logs to the given length (default unlimited)
--sql-max-length-ui int truncate queries in debug UIs to the given length (default 512) (default 512)
--stderrthreshold severityFlag logs at or above this threshold go to stderr (default 1)
--structured-logging enable structured logging
--table-refresh-interval int interval in milliseconds to refresh tables in status page with refreshRequired class
--tablet_dir string The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid.
--tablet_hostname string The hostname to use for the tablet otherwise it will be derived from OS' hostname (default "localhost")
Expand Down
47 changes: 47 additions & 0 deletions go/vt/logutil/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ import (
"sync"
"time"

noglog "github.com/slok/noglog"
"go.uber.org/zap"

"vitess.io/vitess/go/protoutil"
"vitess.io/vitess/go/vt/log"
logutilpb "vitess.io/vitess/go/vt/proto/logutil"
)

Expand Down Expand Up @@ -383,3 +387,46 @@ func fileAndLine(depth int) (string, int64) {
}
return file, int64(line)
}

type StructuredLogger zap.SugaredLogger

// SetStructuredLogger in-place noglog replacement with Zap's logger.
func SetStructuredLogger(conf *zap.Config) (vtSLogger *zap.SugaredLogger, err error) {
var l *zap.Logger

// Use the passed configuration instead of the default configuration
if conf == nil {
defaultProdConf := zap.NewProductionConfig()
conf = &defaultProdConf
}

// Build configuration and generate a sugared logger
l, err = conf.Build()
vtSLogger = l.Sugar()

noglog.SetLogger(&noglog.LoggerFunc{
DebugfFunc: func(f string, a ...interface{}) { vtSLogger.Debugf(f, a...) },
InfofFunc: func(f string, a ...interface{}) { vtSLogger.Infof(f, a...) },
WarnfFunc: func(f string, a ...interface{}) { vtSLogger.Warnf(f, a...) },
ErrorfFunc: func(f string, a ...interface{}) { vtSLogger.Errorf(f, a...) },
})

log.Flush = noglog.Flush
log.Info = noglog.Info
log.Infof = noglog.Infof
log.InfoDepth = noglog.InfoDepth
log.Warning = noglog.Warning
log.Warningf = noglog.Warningf
log.WarningDepth = noglog.WarningDepth
log.Error = noglog.Error
log.Errorf = noglog.Errorf
log.ErrorDepth = noglog.ErrorDepth
log.Exit = noglog.Exit
log.Exitf = noglog.Exitf
log.ExitDepth = noglog.ExitDepth
log.Fatal = noglog.Fatal
log.Fatalf = noglog.Fatalf
log.FatalDepth = noglog.FatalDepth

return
}
108 changes: 108 additions & 0 deletions go/vt/logutil/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,18 @@ limitations under the License.
package logutil

import (
"bytes"
"encoding/json"
"net/url"
"testing"
"time"

"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"

vtlog "vitess.io/vitess/go/vt/log"

"vitess.io/vitess/go/protoutil"
"vitess.io/vitess/go/race"
logutilpb "vitess.io/vitess/go/vt/proto/logutil"
Expand Down Expand Up @@ -152,3 +161,102 @@ func TestTeeLogger(t *testing.T) {
}
}
}

// MemorySink implements zap.Sink by writing all messages to a buffer.
// It's used to capture the logs.
type MemorySink struct {
*bytes.Buffer
}

// Implement Close and Sync as no-ops to satisfy the interface. The Write
// method is provided by the embedded buffer.
func (s *MemorySink) Close() error { return nil }
func (s *MemorySink) Sync() error { return nil }

func SetupLoggerWithMemSink() (sink *MemorySink, err error) {
// Create a sink instance, and register it with zap for the "memory" protocol.
sink = &MemorySink{new(bytes.Buffer)}
err = zap.RegisterSink("memory", func(*url.URL) (zap.Sink, error) {
return sink, nil
})
if err != nil {
return nil, err
}

testLoggerConf := NewMemorySinkConfig()
_, err = SetStructuredLogger(&testLoggerConf)
if err != nil {
return nil, err
}

return
}

func NewMemorySinkConfig() zap.Config {
return zap.Config{
Level: zap.NewAtomicLevelAt(zap.InfoLevel),
Development: false,
Sampling: &zap.SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: "json",
EncoderConfig: zap.NewProductionEncoderConfig(),
OutputPaths: []string{"memory://"},
ErrorOutputPaths: []string{"memory://"},
}
}

func TestStructuredLogger_Replacing_glog(t *testing.T) {
type logMsg struct {
Level string `json:"level"`
Msg string `json:"msg"`
}

type testCase struct {
name string
logLevel zapcore.Level
}

dummyLogMessage := "testing log"
testCases := []testCase{
{"log info", zap.InfoLevel},
{"log warn", zap.WarnLevel},
{"log error", zap.ErrorLevel},
}

sink, err := SetupLoggerWithMemSink()
assert.NoError(t, err)

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var loggingFunc func(format string, args ...interface{})
var expectedLevel string

switch tc.logLevel {
case zapcore.InfoLevel:
loggingFunc = vtlog.Infof
expectedLevel = "info"
case zapcore.ErrorLevel:
loggingFunc = vtlog.Errorf
expectedLevel = "error"
case zapcore.WarnLevel:
loggingFunc = vtlog.Warningf
expectedLevel = "warn"
}

loggingFunc(dummyLogMessage)

// Unmarshal the captured log. This means we're getting a struct log.
actualLog := logMsg{}
err = json.Unmarshal(sink.Bytes(), &actualLog)
assert.NoError(t, err)
// Reset the sink so that it'll contain one log per test case.
sink.Reset()

assert.Equal(t, expectedLevel, actualLog.Level)
assert.Equal(t, dummyLogMessage, actualLog.Msg)

})
}
}
14 changes: 13 additions & 1 deletion go/vt/servenv/servenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ var (
maxStackSize = 64 * 1024 * 1024
initStartTime time.Time // time when tablet init started: for debug purposes to time how long a tablet init takes
tableRefreshInterval int
useStructuredLogger bool
)

// RegisterFlags installs the flags used by Init, Run, and RunDefault.
Expand All @@ -98,7 +99,10 @@ func RegisterFlags() {
fs.IntVar(&tableRefreshInterval, "table-refresh-interval", tableRefreshInterval, "interval in milliseconds to refresh tables in status page with refreshRequired class")

// pid_file.go
fs.StringVar(&pidFile, "pid_file", pidFile, "If set, the process will write its pid to the named file, and delete it on graceful shutdown.")
fs.StringVar(&pidFile, "pid_file", pidFile, "If set, the process will write its pid to the named file, and delete it on graceful shutdown.") // Logging

// Logging
fs.BoolVar(&useStructuredLogger, "structured-logging", useStructuredLogger, "enable structured logging")
})
}

Expand Down Expand Up @@ -285,6 +289,14 @@ func ParseFlags(cmd string) {
os.Exit(0)
}

if useStructuredLogger {
// Replace glog logger with zap logger
_, err := logutil.SetStructuredLogger(nil)
if err != nil {
log.Exitf("error while setting the structured logger: %s", err)
}
}

args := fs.Args()
if len(args) > 0 {
_flag.Usage()
Expand Down
Loading