From b3abeafcc4f71821d9f90691f713e7a108be1f31 Mon Sep 17 00:00:00 2001 From: Ewout Prangsma Date: Tue, 26 Jun 2018 12:14:05 +0200 Subject: [PATCH] Capture glog output and redirect to standard logging service --- deps/github.com/golang/glog/glog_redirect.go | 89 ++++++++++++++++++++ main.go | 24 ++++-- pkg/logging/logger.go | 31 +++++++ 3 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 deps/github.com/golang/glog/glog_redirect.go diff --git a/deps/github.com/golang/glog/glog_redirect.go b/deps/github.com/golang/glog/glog_redirect.go new file mode 100644 index 000000000..b8a4d78d0 --- /dev/null +++ b/deps/github.com/golang/glog/glog_redirect.go @@ -0,0 +1,89 @@ +// +// DISCLAIMER +// +// Copyright 2018 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Ewout Prangsma +// + +// +// Note: This code is added to the standard glog package. +// It has to be here because it needs package level +// access to some members. +// Do not remove this when updating the vendored glog package! +// + +package glog + +import "strings" + +type LogLevel int + +const ( + // Make sure these constants end up having the same indexes as the severity constants + LogLevelInfo LogLevel = iota + LogLevelWarning + LogLevelError + LogLevelFatal +) + +// redirectWriter wraps a callback that is called when data is written to it. +type redirectWriter struct { + cb func(level LogLevel, message string) + level LogLevel +} + +func (w *redirectWriter) Flush() error { + return nil +} + +func (w *redirectWriter) Sync() error { + return nil +} + +func (w *redirectWriter) Write(p []byte) (n int, err error) { + msg := string(p) + if msg[len(msg)-1] == '\n' { + msg = msg[:len(msg)-1] + } + if idx := strings.IndexByte(msg, ']'); idx > 0 { + msg = strings.TrimSpace(msg[idx+1:]) + } + w.cb(w.level, msg) + return len(p), nil +} + +// RedirectOutput redirects output of the given logging to the given callback. +func (l *loggingT) RedirectOutput(cb func(level LogLevel, message string)) { + l.mu.Lock() + defer l.mu.Unlock() + + l.toStderr = false + l.alsoToStderr = false + for i := range logging.file { + logging.file[i] = &redirectWriter{ + cb: cb, + level: LogLevel(i), + } + } + return +} + +// RedirectOutput redirects output of thestandard logging to the given callback. +func RedirectOutput(cb func(level LogLevel, message string)) { + logging.RedirectOutput(cb) +} diff --git a/main.go b/main.go index d93bb5d62..63232fb38 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ import ( "net/http" "os" "strconv" + "strings" "time" "github.com/pkg/errors" @@ -116,12 +117,25 @@ func cmdUsage(cmd *cobra.Command, args []string) { // Run the operator func cmdMainRun(cmd *cobra.Command, args []string) { + // Get environment + namespace := os.Getenv(constants.EnvOperatorPodNamespace) + name := os.Getenv(constants.EnvOperatorPodName) + ip := os.Getenv(constants.EnvOperatorPodIP) + + // Prepare log service goflag.CommandLine.Parse([]string{"-logtostderr"}) var err error logService, err = logging.NewService(logLevel) if err != nil { cliLog.Fatal().Err(err).Msg("Failed to initialize log service") } + logService.ConfigureRootLogger(func(log zerolog.Logger) zerolog.Logger { + podNameParts := strings.Split(name, "-") + operatorID := podNameParts[len(podNameParts)-1] + cliLog = cliLog.With().Str("operator-id", operatorID).Logger() + return log.With().Str("operator-id", operatorID).Logger() + }) + logService.CaptureGLog(logService.MustGetLogger("glog")) // Check operating mode if !operatorOptions.enableDeployment && !operatorOptions.enableDeploymentReplication && !operatorOptions.enableStorage { @@ -129,18 +143,18 @@ func cmdMainRun(cmd *cobra.Command, args []string) { } // Log version - cliLog.Info().Msgf("Starting arangodb-operator, version %s build %s", projectVersion, projectBuild) + cliLog.Info(). + Str("pod-name", name). + Str("pod-namespace", namespace). + Msgf("Starting arangodb-operator, version %s build %s", projectVersion, projectBuild) - // Get environment - namespace := os.Getenv(constants.EnvOperatorPodNamespace) + // Check environment if len(namespace) == 0 { cliLog.Fatal().Msgf("%s environment variable missing", constants.EnvOperatorPodNamespace) } - name := os.Getenv(constants.EnvOperatorPodName) if len(name) == 0 { cliLog.Fatal().Msgf("%s environment variable missing", constants.EnvOperatorPodName) } - ip := os.Getenv(constants.EnvOperatorPodIP) if len(ip) == 0 { cliLog.Fatal().Msgf("%s environment variable missing", constants.EnvOperatorPodIP) } diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go index 991799f33..25857c03a 100644 --- a/pkg/logging/logger.go +++ b/pkg/logging/logger.go @@ -28,6 +28,7 @@ import ( "strings" "sync" + "github.com/golang/glog" "github.com/rs/zerolog" ) @@ -47,6 +48,10 @@ type Service interface { MustGetLogger(name string) zerolog.Logger // MustSetLevel sets the log level for the component with given name to given level. MustSetLevel(name, level string) + // ConfigureRootLogger calls the given callback to modify the root logger. + ConfigureRootLogger(cb func(rootLog zerolog.Logger) zerolog.Logger) + // CaptureGLog configures glog to write to the given logger + CaptureGLog(log zerolog.Logger) } // loggingService implements Service @@ -83,6 +88,32 @@ func NewService(defaultLevel string) (Service, error) { return s, nil } +// ConfigureRootLogger calls the given callback to modify the root logger. +func (s *loggingService) ConfigureRootLogger(cb func(rootLog zerolog.Logger) zerolog.Logger) { + s.mutex.Lock() + defer s.mutex.Unlock() + + s.rootLog = cb(s.rootLog) +} + +// CaptureGLog configures glog to write to the given logger +func (s *loggingService) CaptureGLog(log zerolog.Logger) { + glog.RedirectOutput(func(level glog.LogLevel, msg string) { + var e *zerolog.Event + switch level { + case glog.LogLevelWarning: + e = log.WithLevel(zerolog.WarnLevel) + case glog.LogLevelError: + e = log.WithLevel(zerolog.ErrorLevel) + case glog.LogLevelFatal: + e = log.WithLevel(zerolog.FatalLevel) + default: + e = log.WithLevel(zerolog.InfoLevel) + } + e.Msg(msg) + }) +} + // MustGetLogger creates a logger with given name func (s *loggingService) MustGetLogger(name string) zerolog.Logger { s.mutex.Lock()