diff --git a/integration/main_test.go b/integration/main_test.go index 7827c913..2f1c8a46 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -2,6 +2,7 @@ package integration import ( "bufio" + "bytes" "crypto/tls" "encoding/json" "errors" @@ -618,6 +619,78 @@ var _ = Describe("Router Integration", func() { Consistently(contentsFunc).ShouldNot(ContainSubstring("Component Router registered successfully")) }) + Context("It starts up a debugserver", func() { + var ( + testState *testState + contentsFunc func() string = func() string { + return string(gorouterSession.Out.Contents()) + } + ) + + BeforeEach(func() { + + testState = NewTestState() + testState.cfg.DebugAddr = "127.0.0.1:17017" + gorouterSession = testState.StartGorouter() + }) + + It("can change the debugserver's logging level", func() { + + Consistently(contentsFunc).ShouldNot(ContainSubstring(`{log_level":0,"timestamp"`)) + + request, err := http.NewRequest("PUT", fmt.Sprintf("http://%s/log-level", testState.cfg.DebugAddr), bytes.NewBufferString("debug")) + Expect(err).NotTo(HaveOccurred()) + + response, err := http.DefaultClient.Do(request) + Expect(err).NotTo(HaveOccurred()) + + Expect(response.StatusCode).To(Equal(http.StatusOK)) + response.Body.Close() + + Consistently(contentsFunc).Should(ContainSubstring(`{"log_level":0,"timestamp"`)) + + // And back to info level + gorouterSession.Out.Clear() + request, err = http.NewRequest("PUT", fmt.Sprintf("http://%s/log-level", testState.cfg.DebugAddr), bytes.NewBufferString("info")) + Expect(err).NotTo(HaveOccurred()) + + response, err = http.DefaultClient.Do(request) + Expect(err).NotTo(HaveOccurred()) + + Expect(response.StatusCode).To(Equal(http.StatusOK)) + response.Body.Close() + + //Terminate everything just to generate some info logs + testState.StopAndCleanup() + + Consistently(contentsFunc).ShouldNot(ContainSubstring(`{"log_level":0,"timestamp"`)) + Eventually(contentsFunc).Should(ContainSubstring(`{"log_level":1,"timestamp"`)) + + }) + + It("Does not accept invalid debug levels", func() { + + Consistently(contentsFunc).ShouldNot(ContainSubstring(`{log_level":0,"timestamp"`)) + + gorouterSession.Out.Clear() + + request, err := http.NewRequest("PUT", fmt.Sprintf("http://%s/log-level", testState.cfg.DebugAddr), bytes.NewBufferString("meow")) + Expect(err).NotTo(HaveOccurred()) + + response, err := http.DefaultClient.Do(request) + Expect(err).NotTo(HaveOccurred()) + + Expect(response.StatusCode).To(Equal(http.StatusOK)) + response.Body.Close() + + Expect(gorouterSession.ExitCode()).To(Equal(-1)) + + Consistently(contentsFunc).ShouldNot(ContainSubstring(`{"log_level":0,"timestamp"`)) + Eventually(contentsFunc).Should(ContainSubstring(`{"log_level":1,"timestamp"`)) + }) + + }) + Describe("loggregator metrics emitted", func() { var ( fakeMetron test_util.FakeMetron diff --git a/logger/logger.go b/logger/logger.go index 9b4d6a20..267797ca 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -8,13 +8,14 @@ import ( "sync" "time" + "code.cloudfoundry.org/lager/v3" "go.uber.org/zap" "go.uber.org/zap/exp/zapslog" "go.uber.org/zap/zapcore" ) var ( - conf dynamicLoggingConfig + Conf DynamicLoggingConfig baseLogger *slog.Logger writeSyncer = &dynamicWriter{w: os.Stdout} mutex sync.Mutex @@ -23,7 +24,7 @@ var ( /* dynamicLoggingConfig holds dynamic configuration for the time encoding and logging level. */ -type dynamicLoggingConfig struct { +type DynamicLoggingConfig struct { encoding string level zap.AtomicLevel } @@ -74,10 +75,10 @@ SetTimeEncoder dynamically sets the time encoder at runtime: All other values: The encoder is set to an Epoch encoder */ func SetTimeEncoder(enc string) { - conf.encoding = enc + Conf.encoding = enc } -func (e *dynamicLoggingConfig) encodeTime(t time.Time, pae zapcore.PrimitiveArrayEncoder) { +func (e *DynamicLoggingConfig) encodeTime(t time.Time, pae zapcore.PrimitiveArrayEncoder) { switch e.encoding { case "rfc3339": RFC3339Formatter()(t, pae) @@ -91,11 +92,33 @@ SetLoggingLevel dynamically sets the logging level at runtime. See https://githu for possible logging levels. */ func SetLoggingLevel(level string) { + mutex.Lock() + defer mutex.Unlock() zapLevel, err := zapcore.ParseLevel(level) if err != nil { panic(err) } - conf.level.SetLevel(zapLevel) + Conf.level.SetLevel(zapLevel) +} + +// This exists to be able to export the logging level configs to the debugserver +func (loggingConf DynamicLoggingConfig) SetMinLevel(level lager.LogLevel) { + Conf.level.SetLevel(toZapLevel(level)) +} + +func toZapLevel(level lager.LogLevel) zapcore.Level { + switch level { + case lager.DEBUG: + return zapcore.DebugLevel + case lager.INFO: + return zapcore.InfoLevel + case lager.ERROR: + return zapcore.ErrorLevel + case lager.FATAL: + return zapcore.FatalLevel + default: + return zapcore.InfoLevel + } } type Logger interface { @@ -108,14 +131,14 @@ timestamp format and writeSyncer. func initializeLogger() *slog.Logger { zapLevel := zap.InfoLevel - conf = dynamicLoggingConfig{encoding: "epoch", level: zap.NewAtomicLevelAt(zapLevel)} + Conf = DynamicLoggingConfig{encoding: "epoch", level: zap.NewAtomicLevelAt(zapLevel)} zapConfig := zapcore.EncoderConfig{ MessageKey: "message", LevelKey: "log_level", EncodeLevel: numberLevelFormatter, TimeKey: "timestamp", - EncodeTime: conf.encodeTime, + EncodeTime: Conf.encodeTime, EncodeCaller: zapcore.ShortCallerEncoder, StacktraceKey: "stack_trace", } @@ -123,7 +146,7 @@ func initializeLogger() *slog.Logger { zapCore := zapcore.NewCore( zapcore.NewJSONEncoder(zapConfig), writeSyncer, - conf.level, + Conf.level, ) zapHandler := zapslog.NewHandler(zapCore, zapslog.WithCaller(true)) diff --git a/main.go b/main.go index a50ead57..030e2a8e 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,6 @@ import ( "code.cloudfoundry.org/clock" "code.cloudfoundry.org/debugserver" mr "code.cloudfoundry.org/go-metric-registry" - "code.cloudfoundry.org/lager/v3" "code.cloudfoundry.org/tlsconfig" "github.com/cloudfoundry/dropsonde" "github.com/cloudfoundry/dropsonde/metric_sender" @@ -107,8 +106,7 @@ func main() { } if c.DebugAddr != "" { - reconfigurableSink := lager.ReconfigurableSink{} - _, err = debugserver.Run(c.DebugAddr, &reconfigurableSink) + _, err = debugserver.Run(c.DebugAddr, &grlog.Conf) if err != nil { logger.Error("failed-to-start-debug-server", grlog.ErrAttr(err)) }