diff --git a/cmd/client/commandv2/logs.go b/cmd/client/commandv2/logs.go index 7ef1afe353..fb03d677a2 100644 --- a/cmd/client/commandv2/logs.go +++ b/cmd/client/commandv2/logs.go @@ -66,5 +66,47 @@ func LogsCmd() *cobra.Command { } cmd.Flags().IntVar(&n, "tail", 500, "Lines of recent log file to display. Defaults to 500, use -1 to show all lines") cmd.Flags().BoolVarP(&follow, "follow", "f", false, "Specify if the logs should be streamed.") + cmd.AddCommand(setLogLevelCmd()) + cmd.AddCommand(getLogLevelCmd()) + return cmd +} + +func setLogLevelCmd() *cobra.Command { + examples := []general.Example{ + {Desc: "Set log level to info", Command: "egctl logs set-level info"}, + {Desc: "Set log level to debug", Command: "egctl logs set-level debug"}, + } + + cmd := &cobra.Command{ + Use: "set-level", + Short: "Set Easegress log level", + Example: createMultiExample(examples), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + level := args[0] + p := general.LogsLevelURL + "/" + level + if _, err := general.HandleRequest(http.MethodPut, p, nil); err != nil { + general.ExitWithError(err) + } + fmt.Println("Set log level to", level) + }, + } + return cmd +} + +func getLogLevelCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "get-level", + Short: "Get Easegress log level", + Example: createExample("Get current log level.", "egctl logs get-level"), + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + body, err := general.HandleRequest(http.MethodGet, general.LogsLevelURL, nil) + if err != nil { + general.ExitWithError(err) + } + fmt.Println(string(body)) + }, + } return cmd } diff --git a/cmd/client/general/urls.go b/cmd/client/general/urls.go index d079913146..07a19a676e 100644 --- a/cmd/client/general/urls.go +++ b/cmd/client/general/urls.go @@ -69,6 +69,8 @@ const ( // LogsURL is the URL of logs. LogsURL = APIURL + "/logs" + // LogsLevelURL is the URL of logs level. + LogsLevelURL = APIURL + "/logs/level" // HTTPProtocol is prefix for HTTP protocol HTTPProtocol = "http://" diff --git a/pkg/api/logs.go b/pkg/api/logs.go index fb9e5a1a52..aeb3a16f9a 100644 --- a/pkg/api/logs.go +++ b/pkg/api/logs.go @@ -24,9 +24,12 @@ import ( "net/http" "os" "strconv" + "strings" "github.com/fsnotify/fsnotify" + "github.com/go-chi/chi/v5" "github.com/megaease/easegress/v2/pkg/logger" + "go.uber.org/zap" ) func (s *Server) logsAPIEntries() []*Entry { @@ -36,6 +39,16 @@ func (s *Server) logsAPIEntries() []*Entry { Method: "GET", Handler: s.getLogs, }, + { + Path: "/logs/level/{level}", + Method: "PUT", + Handler: s.setLogLevel, + }, + { + Path: "/logs/level", + Method: "GET", + Handler: s.getLogLevel, + }, } } @@ -76,6 +89,30 @@ func newLogFile(r *http.Request, filePath string) (*logFile, error) { }, nil } +func (s *Server) getLogLevel(w http.ResponseWriter, r *http.Request) { + level := logger.GetLogLevel() + w.WriteHeader(http.StatusOK) + w.Write([]byte(level)) +} + +func (s *Server) setLogLevel(w http.ResponseWriter, r *http.Request) { + level := chi.URLParam(r, "level") + if level == "" { + HandleAPIError(w, r, http.StatusBadRequest, errors.New("level is required")) + return + } + level = strings.ToLower(level) + if level == "debug" { + logger.SetLogLevel(zap.DebugLevel) + } else if level == "info" { + logger.SetLogLevel(zap.InfoLevel) + } else { + HandleAPIError(w, r, http.StatusBadRequest, fmt.Errorf("invalid level %s, only support to set log level to info or debug", level)) + return + } + w.WriteHeader(http.StatusOK) +} + func (s *Server) getLogs(w http.ResponseWriter, r *http.Request) { flusher := w.(http.Flusher) var err error diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 53e54e7478..3026ba57f8 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -35,8 +35,17 @@ import ( "github.com/megaease/easegress/v2/pkg/util/fasttime" ) +func init() { + globalLogLevel = zap.NewAtomicLevel() + globalLogLevel.SetLevel(zap.InfoLevel) +} + // Init initializes logger. func Init(opt *option.Options) { + if opt.Debug { + globalLogLevel.SetLevel(zap.DebugLevel) + } + initDefault(opt) initHTTPFilter(opt) initRestAPI(opt) @@ -92,10 +101,20 @@ var ( httpFilterAccessLogger *zap.SugaredLogger httpFilterDumpLogger *zap.SugaredLogger restAPILogger *zap.SugaredLogger + globalLogLevel zap.AtomicLevel stdoutLogPath string ) +// SetLogLevel sets log level. Only support debug and info. +func SetLogLevel(level zapcore.Level) { + globalLogLevel.SetLevel(level) +} + +func GetLogLevel() string { + return globalLogLevel.String() +} + // GetLogPath returns the path of stdout log. func GetLogPath() string { return stdoutLogPath @@ -105,15 +124,8 @@ func GetLogPath() string { func EtcdClientLoggerConfig(opt *option.Options, filename string) *zap.Config { encoderConfig := defaultEncoderConfig() - level := zap.NewAtomicLevel() - if opt.Debug { - level.SetLevel(zapcore.DebugLevel) - } else { - level.SetLevel(zapcore.InfoLevel) - } - cfg := &zap.Config{ - Level: level, + Level: globalLogLevel, Encoding: "console", EncoderConfig: encoderConfig, OutputPaths: []string{"stdout"}, @@ -149,11 +161,6 @@ func defaultEncoderConfig() zapcore.EncoderConfig { func initDefault(opt *option.Options) { encoderConfig := defaultEncoderConfig() - lowestLevel := zap.InfoLevel - if opt.Debug { - lowestLevel = zap.DebugLevel - } - var err error var gressLF io.Writer = os.Stdout if opt.AbsLogDir != "" { @@ -167,11 +174,11 @@ func initDefault(opt *option.Options) { opts := []zap.Option{zap.AddCaller(), zap.AddCallerSkip(1)} stderrSyncer := zapcore.AddSync(os.Stderr) - stderrCore := zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), stderrSyncer, lowestLevel) + stderrCore := zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), stderrSyncer, globalLogLevel) stderrLogger = zap.New(stderrCore, opts...).Sugar() gressSyncer := zapcore.AddSync(gressLF) - gressCore := zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), gressSyncer, lowestLevel) + gressCore := zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), gressSyncer, globalLogLevel) gressLogger = zap.New(gressCore, opts...).Sugar() defaultCore := gressCore