diff --git a/.golangci.yml b/.golangci.yml index 0a8e951e..a2bedb2c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -31,4 +31,4 @@ linters: linters-settings: errcheck: - ignore: fmt:.*,io:Close + ignore: fmt:.*,io:Close,github.com/sacloud/autoscaler/logging:.* diff --git a/commands/flags/global_flags.go b/commands/flags/global_flags.go new file mode 100644 index 00000000..16f45190 --- /dev/null +++ b/commands/flags/global_flags.go @@ -0,0 +1,58 @@ +// Copyright 2021 The sacloud Authors +// +// 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. + +package flags + +import ( + "fmt" + "os" + + "github.com/sacloud/autoscaler/log" + + "github.com/sacloud/autoscaler/validate" + "github.com/spf13/cobra" +) + +type flags struct { + LogLevel string `name:"log-level" validate:"required,oneof=error warn info debug"` + LogFormat string `name:"log-format" validate:"required,oneof=logfmt json"` +} + +var global = &flags{ + LogLevel: "info", + LogFormat: "logfmt", +} + +func SetFlags(cmd *cobra.Command) { + cmd.PersistentFlags().StringVarP(&global.LogLevel, "log-level", "", global.LogLevel, "Level of logging to be output. options: [ error | warn | info | debug ]") + cmd.PersistentFlags().StringVarP(&global.LogFormat, "log-format", "", global.LogFormat, "Format of logging to be output. options: [ logfmt | json ]") +} + +func ValidateFlags(cmd *cobra.Command, args []string) error { + err := validate.Struct(global) + if err != nil { + fmt.Println(err) + } + return err +} + +func NewLogger() *log.Logger { + return log.NewLogger(&log.LoggerOption{ + Writer: os.Stderr, + JSON: global.LogFormat == "json", + TimeStamp: true, + Caller: false, + Level: log.Level(global.LogLevel), + }) +} diff --git a/commands/inputs/alertmanager/cmd.go b/commands/inputs/alertmanager/cmd.go index 5a3470e0..56289d1f 100644 --- a/commands/inputs/alertmanager/cmd.go +++ b/commands/inputs/alertmanager/cmd.go @@ -15,6 +15,8 @@ package alertmanager import ( + "github.com/sacloud/autoscaler/commands/flags" + "github.com/sacloud/autoscaler/defaults" "github.com/sacloud/autoscaler/inputs" "github.com/sacloud/autoscaler/inputs/alertmanager" "github.com/spf13/cobra" @@ -26,12 +28,16 @@ var Command = &cobra.Command{ RunE: run, } -var server = &alertmanager.Input{} +var ( + dest string + address string +) func init() { - inputs.Init(Command, server) + Command.Flags().StringVarP(&dest, "dest", "", defaults.CoreSocketAddr, "URL of gRPC endpoint of AutoScaler Core") + Command.Flags().StringVarP(&address, "addr", "", ":3001", "the TCP address for the server to listen on") } func run(cmd *cobra.Command, args []string) error { - return inputs.Serve(server) + return inputs.Serve(alertmanager.NewInput(dest, address, flags.NewLogger())) } diff --git a/commands/inputs/direct/cmd.go b/commands/inputs/direct/cmd.go index 52a500c5..adb1ef2e 100644 --- a/commands/inputs/direct/cmd.go +++ b/commands/inputs/direct/cmd.go @@ -79,6 +79,7 @@ func run(cmd *cobra.Command, args []string) error { return err } + // 単発の出力のためlog(標準エラー)ではなく標準出力に書く fmt.Printf("status: %s, job-id: %s", res.Status, res.ScalingJobId) if res.Message != "" { fmt.Printf(", message: %s", res.Message) diff --git a/commands/inputs/grafana/cmd.go b/commands/inputs/grafana/cmd.go index 9ff9da46..06c8f75b 100644 --- a/commands/inputs/grafana/cmd.go +++ b/commands/inputs/grafana/cmd.go @@ -15,6 +15,8 @@ package grafana import ( + "github.com/sacloud/autoscaler/commands/flags" + "github.com/sacloud/autoscaler/defaults" "github.com/sacloud/autoscaler/inputs" "github.com/sacloud/autoscaler/inputs/grafana" "github.com/spf13/cobra" @@ -26,12 +28,16 @@ var Command = &cobra.Command{ RunE: run, } -var server = &grafana.Input{} +var ( + dest string + address string +) func init() { - inputs.Init(Command, server) + Command.Flags().StringVarP(&dest, "dest", "", defaults.CoreSocketAddr, "URL of gRPC endpoint of AutoScaler Core") + Command.Flags().StringVarP(&address, "addr", "", ":3001", "the TCP address for the server to listen on") } func run(cmd *cobra.Command, args []string) error { - return inputs.Serve(server) + return inputs.Serve(grafana.NewInput(dest, address, flags.NewLogger())) } diff --git a/commands/root.go b/commands/root.go index 3179ba11..498fa0e6 100644 --- a/commands/root.go +++ b/commands/root.go @@ -18,6 +18,7 @@ import ( "os" "github.com/sacloud/autoscaler/commands/completion" + "github.com/sacloud/autoscaler/commands/flags" "github.com/sacloud/autoscaler/commands/inputs" "github.com/sacloud/autoscaler/commands/server" "github.com/sacloud/autoscaler/commands/version" @@ -25,10 +26,11 @@ import ( ) var rootCmd = &cobra.Command{ - Use: "autoscaler", - Short: "autoscaler is a tool for managing the scale of resources on SAKURA cloud", - SilenceUsage: true, - SilenceErrors: false, + Use: "autoscaler", + Short: "autoscaler is a tool for managing the scale of resources on SAKURA cloud", + PersistentPreRunE: flags.ValidateFlags, + SilenceUsage: true, + SilenceErrors: false, } var subCommands = []*cobra.Command{ @@ -39,6 +41,7 @@ var subCommands = []*cobra.Command{ } func init() { + flags.SetFlags(rootCmd) rootCmd.AddCommand(subCommands...) } diff --git a/commands/server/start/cmd.go b/commands/server/start/cmd.go index e1c51c3d..beda831d 100644 --- a/commands/server/start/cmd.go +++ b/commands/server/start/cmd.go @@ -19,6 +19,7 @@ import ( "os/signal" "syscall" + "github.com/sacloud/autoscaler/commands/flags" "github.com/sacloud/autoscaler/core" "github.com/sacloud/autoscaler/defaults" "github.com/spf13/cobra" @@ -40,6 +41,6 @@ var Command = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() - return core.Start(ctx, configPath) + return core.Start(ctx, configPath, flags.NewLogger()) }, } diff --git a/core/config.go b/core/config.go index 1aca7847..d1226fe5 100644 --- a/core/config.go +++ b/core/config.go @@ -106,8 +106,8 @@ func (c *Config) APIClient() sacloud.APICaller { // TODO Validateの実装 -func (c *Config) Handlers() Handlers { - return append(BuiltinHandlers, c.CustomHandlers...) +func (c *Config) Handlers(ctx *Context) Handlers { + return append(BuiltinHandlers(ctx), c.CustomHandlers...) } // AutoScalerConfig オートスケーラー自体の動作設定 diff --git a/core/context.go b/core/context.go index b8a22c0f..81cf284b 100644 --- a/core/context.go +++ b/core/context.go @@ -17,6 +17,8 @@ package core import ( "context" "time" + + "github.com/sacloud/autoscaler/log" ) // Context 1リクエストのスコープに対応するコンテキスト、context.Contextを実装し、リクエスト情報や現在のジョブの情報を保持する @@ -24,12 +26,15 @@ type Context struct { ctx context.Context request *requestInfo job *JobStatus + logger *log.Logger } -func NewContext(parent context.Context, request *requestInfo) *Context { +func NewContext(parent context.Context, request *requestInfo, logger *log.Logger) *Context { + logger = logger.With("request", request.String()) return &Context{ ctx: parent, request: request, + logger: logger, } } @@ -43,7 +48,7 @@ func (c *Context) WithJobStatus(job *JobStatus) *Context { action: c.request.action, resourceGroupName: c.request.resourceGroupName, desiredStateName: c.request.desiredStateName, - }) + }, c.logger) ctx.job = job return ctx } @@ -59,7 +64,7 @@ func (c *Context) ForRefresh() *Context { resourceGroupName: c.request.resourceGroupName, desiredStateName: c.request.desiredStateName, refresh: true, - }) + }, c.logger) } // Request 現在のコンテキストで受けたリクエストの情報を返す @@ -67,6 +72,11 @@ func (c *Context) Request() *requestInfo { return c.request } +// Logger 現在のコンテキストのロガーを返す +func (c *Context) Logger() *log.Logger { + return c.logger +} + // JobID 現在のコンテキストでのJobのIDを返す // // まだJobの実行決定が行われていない場合でも値を返す diff --git a/core/core.go b/core/core.go index b99a5dcc..7ed52c74 100644 --- a/core/core.go +++ b/core/core.go @@ -17,12 +17,12 @@ package core import ( "context" "fmt" - "log" "net" "os" "strings" "github.com/sacloud/autoscaler/defaults" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/request" "google.golang.org/grpc" ) @@ -31,23 +31,25 @@ import ( type Core struct { config *Config jobs map[string]*JobStatus + logger *log.Logger } -func newCoreInstance(c *Config) (*Core, error) { +func newCoreInstance(c *Config, logger *log.Logger) (*Core, error) { // TODO バリデーションの実装 return &Core{ config: c, jobs: make(map[string]*JobStatus), + logger: logger.With("from", "autoscaler-core-server"), }, nil } -func Start(ctx context.Context, configPath string) error { +func Start(ctx context.Context, configPath string, logger *log.Logger) error { config, err := NewConfigFromPath(configPath) if err != nil { return err } - instance, err := newCoreInstance(config) + instance, err := newCoreInstance(config, logger) if err != nil { return err } @@ -74,13 +76,15 @@ func (c *Core) run(ctx context.Context) error { lis.Close() // ignore error if _, err := os.Stat(filename); err == nil { if err := os.RemoveAll(filename); err != nil { - log.Printf("cleanup failed: %s\n", err) + c.logger.Error("error", "cleanup failed: %s", err) // nolint } } }() go func() { - log.Printf("autoscaler started with: %s\n", lis.Addr().String()) + if err := c.logger.Info("message", "autoscaler started", "address", lis.Addr().String()); err != nil { + errCh <- err + } if err := server.Serve(lis); err != nil { errCh <- err } @@ -90,7 +94,7 @@ func (c *Core) run(ctx context.Context) error { case err := <-errCh: return fmt.Errorf("core service failed: %s", err) case <-ctx.Done(): - log.Println("shutting down with:", ctx.Err()) + c.logger.Info("message", "shutting down", "error", ctx.Err()) // nolint } return ctx.Err() } @@ -129,12 +133,12 @@ func (c *Core) handle(ctx *Context) (*JobStatus, string, error) { } // TODO バリデーションは起動時に行えるので移動すべき - if err := rg.ValidateActions(ctx.Request().action, c.config.Handlers()); err != nil { + if err := rg.ValidateActions(ctx.Request().action, c.config.Handlers(ctx)); err != nil { job.SetStatus(request.ScalingJobStatus_JOB_CANCELED) // まだ実行前のためCANCELEDを返す return job, "", err } - go rg.HandleAll(ctx, c.config.APIClient(), c.config.Handlers()) + go rg.HandleAll(ctx, c.config.APIClient(), c.config.Handlers(ctx)) job.SetStatus(request.ScalingJobStatus_JOB_ACCEPTED) return job, "", nil diff --git a/core/core_test.go b/core/core_test.go index a01833cc..c619a06a 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -17,6 +17,8 @@ package core import ( "os" + "github.com/sacloud/autoscaler/log" + "github.com/sacloud/libsacloud/v2/helper/api" ) @@ -30,4 +32,11 @@ var ( TraceHTTP: os.Getenv("SAKURACLOUD_TRACE") != "", FakeMode: true, }) + testLogger = log.NewLogger(&log.LoggerOption{ + Writer: os.Stderr, + JSON: false, + TimeStamp: true, + Caller: true, + Level: log.LevelDebug, + }) ) diff --git a/core/handler.go b/core/handler.go index 6a216492..c36408ae 100644 --- a/core/handler.go +++ b/core/handler.go @@ -16,14 +16,12 @@ package core import ( "io" - "log" "github.com/sacloud/autoscaler/handler" "github.com/sacloud/autoscaler/handlers" "github.com/sacloud/autoscaler/handlers/builtins" "github.com/sacloud/autoscaler/handlers/elb" "github.com/sacloud/autoscaler/handlers/gslb" - "github.com/sacloud/autoscaler/handlers/logging" "github.com/sacloud/autoscaler/handlers/router" "github.com/sacloud/autoscaler/handlers/server" "google.golang.org/grpc" @@ -31,51 +29,45 @@ import ( type Handlers []*Handler -var BuiltinHandlers = Handlers{ - { - Type: "logging", - Name: "logging", - BuiltinHandler: &builtins.Handler{ - Builtin: &logging.Handler{}, +func BuiltinHandlers(ctx *Context) Handlers { + return Handlers{ + { + Type: "elb-vertical-scaler", + Name: "elb-vertical-scaler", + BuiltinHandler: &builtins.Handler{ + Builtin: &elb.VerticalScaleHandler{Logger: ctx.Logger().With("handler", "elb-vertical-scaler")}, + }, }, - Disabled: true, - }, - { - Type: "elb-vertical-scaler", - Name: "elb-vertical-scaler", - BuiltinHandler: &builtins.Handler{ - Builtin: &elb.VerticalScaleHandler{}, + { + Type: "elb-servers-handler", + Name: "elb-servers-handler", + BuiltinHandler: &builtins.Handler{ + Builtin: &elb.ServersHandler{Logger: ctx.Logger().With("handler", "elb-servers-handler")}, + }, }, - }, - { - Type: "elb-servers-handler", - Name: "elb-servers-handler", - BuiltinHandler: &builtins.Handler{ - Builtin: &elb.ServersHandler{}, + { + Type: "gslb-servers-handler", + Name: "gslb-servers-handler", + BuiltinHandler: &builtins.Handler{ + Builtin: &gslb.ServersHandler{Logger: ctx.Logger().With("handler", "gslb-servers-handler")}, + }, }, - }, - { - Type: "gslb-servers-handler", - Name: "gslb-servers-handler", - BuiltinHandler: &builtins.Handler{ - Builtin: &gslb.ServersHandler{}, + { + Type: "router-vertical-scaler", + Name: "router-vertical-scaler", + BuiltinHandler: &builtins.Handler{ + Builtin: &router.VerticalScaleHandler{Logger: ctx.Logger().With("handler", "router-vertical-scaler")}, + }, }, - }, - { - Type: "router-vertical-scaler", - Name: "router-vertical-scaler", - BuiltinHandler: &builtins.Handler{ - Builtin: &router.VerticalScaleHandler{}, + { + Type: "server-vertical-scaler", + Name: "server-vertical-scaler", + BuiltinHandler: &builtins.Handler{ + Builtin: &server.VerticalScaleHandler{Logger: ctx.Logger().With("handler", "server-vertical-scaler")}, + }, }, - }, - { - Type: "server-vertical-scaler", - Name: "server-vertical-scaler", - BuiltinHandler: &builtins.Handler{ - Builtin: &server.VerticalScaleHandler{}, - }, - }, - // TODO その他ビルトインを追加 + // TODO その他ビルトインを追加 + } } // Handler カスタムハンドラーの定義 @@ -171,7 +163,7 @@ func (h *Handler) preHandleBuiltin(ctx *HandlingContext, computed Computed) erro if actualHandler, ok := h.BuiltinHandler.(handlers.PreHandler); ok { handleArg.preHandle = func(req *handler.PreHandleRequest) error { - return actualHandler.PreHandle(req, &builtinResponseSender{}) + return actualHandler.PreHandle(req, &builtinResponseSender{ctx: ctx}) } } return h.handle(ctx, computed, handleArg) @@ -182,7 +174,7 @@ func (h *Handler) handleBuiltin(ctx *HandlingContext, computed Computed) error { if actualHandler, ok := h.BuiltinHandler.(handlers.Handler); ok { handleArg.handle = func(req *handler.HandleRequest) error { - return actualHandler.Handle(req, &builtinResponseSender{}) + return actualHandler.Handle(req, &builtinResponseSender{ctx: ctx}) } } @@ -194,7 +186,7 @@ func (h *Handler) postHandleBuiltin(ctx *HandlingContext, computed Computed) err if actualHandler, ok := h.BuiltinHandler.(handlers.PostHandler); ok { handleArg.postHandle = func(req *handler.PostHandleRequest) error { - return actualHandler.PostHandle(req, &builtinResponseSender{}) + return actualHandler.PostHandle(req, &builtinResponseSender{ctx: ctx}) } } @@ -216,7 +208,7 @@ func (h *Handler) preHandleExternal(ctx *HandlingContext, computed Computed) err if err != nil { return err } - return h.handleHandlerResponse(res) + return h.handleHandlerResponse(ctx, res) }, } return h.handle(ctx, computed, handleArg) @@ -237,7 +229,7 @@ func (h *Handler) handleExternal(ctx *HandlingContext, computed Computed) error if err != nil { return err } - return h.handleHandlerResponse(res) + return h.handleHandlerResponse(ctx, res) }, } return h.handle(ctx, computed, handleArg) @@ -258,7 +250,7 @@ func (h *Handler) postHandleExternal(ctx *HandlingContext, computed Computed) er if err != nil { return err } - return h.handleHandlerResponse(res) + return h.handleHandlerResponse(ctx, res) }, } return h.handle(ctx, computed, handleArg) @@ -268,7 +260,7 @@ type handlerResponseReceiver interface { Recv() (*handler.HandleResponse, error) } -func (h *Handler) handleHandlerResponse(receiver handlerResponseReceiver) error { +func (h *Handler) handleHandlerResponse(ctx *HandlingContext, receiver handlerResponseReceiver) error { for { stat, err := receiver.Recv() if err == io.EOF { @@ -277,14 +269,17 @@ func (h *Handler) handleHandlerResponse(receiver handlerResponseReceiver) error if err != nil { return err } - log.Println("handler replied:", stat.String()) + if err := ctx.Logger().Info("message", "handler replied", "replay", stat.String()); err != nil { + return err + } } return nil } -type builtinResponseSender struct{} +type builtinResponseSender struct { + ctx *HandlingContext +} func (s *builtinResponseSender) Send(res *handler.HandleResponse) error { - log.Println("handler replied:", res.String()) - return nil + return s.ctx.Logger().Info("message", "handler replied", "reply", res.String()) } diff --git a/core/handling_context.go b/core/handling_context.go index a3f683eb..cdf648dd 100644 --- a/core/handling_context.go +++ b/core/handling_context.go @@ -31,6 +31,12 @@ func NewHandlingContext(parent *Context, computed Computed) *HandlingContext { } } +func (c *HandlingContext) WithLogger(keyvals ...interface{}) *HandlingContext { + ctx := NewHandlingContext(c.Context, c.cachedComputed) + ctx.logger = ctx.logger.With(keyvals...) + return ctx +} + // CurrentComputed 現在処理中の[]Computedを返す func (c *HandlingContext) CurrentComputed() Computed { return c.cachedComputed diff --git a/core/resource_group.go b/core/resource_group.go index cd056e13..d3b2b63a 100644 --- a/core/resource_group.go +++ b/core/resource_group.go @@ -16,13 +16,10 @@ package core import ( "fmt" - "log" + "github.com/goccy/go-yaml" "github.com/sacloud/autoscaler/defaults" - "github.com/sacloud/autoscaler/request" - - "github.com/goccy/go-yaml" "github.com/sacloud/libsacloud/v2/sacloud" ) @@ -180,13 +177,12 @@ func (rg *ResourceGroup) HandleAll(ctx *Context, apiClient sacloud.APICaller, ha handlers, err := rg.handlers(ctx.Request().action, handlerFilters) if err != nil { // 事前にValidateHandlerFiltersで検証しておくため基本的にありえないはず job.SetStatus(request.ScalingJobStatus_JOB_FAILED) - log.Printf("[FATAL] %s\n", err) - return + ctx.Logger().Error("fatal", err) // nolint } if err := rg.handleAll(ctx, apiClient, handlers); err != nil { job.SetStatus(request.ScalingJobStatus_JOB_FAILED) - log.Printf("[WARN] %s\n", err) + ctx.Logger().Warn("error", err) // nolint return } @@ -216,6 +212,7 @@ func (rg *ResourceGroup) resourceWalkFuncs(parentCtx *Context, apiClient sacloud // preHandle if err := rg.handleAllByFunc(computed, handlers, func(h *Handler, c Computed) error { + ctx = ctx.WithLogger("step", "PreHandle") return h.PreHandle(ctx, c) }); err != nil { return err @@ -223,6 +220,7 @@ func (rg *ResourceGroup) resourceWalkFuncs(parentCtx *Context, apiClient sacloud // handle if err := rg.handleAllByFunc(computed, handlers, func(h *Handler, c Computed) error { + ctx = ctx.WithLogger("step", "Handle") return h.Handle(ctx, c) }); err != nil { return err @@ -237,6 +235,7 @@ func (rg *ResourceGroup) resourceWalkFuncs(parentCtx *Context, apiClient sacloud // postHandle if err := rg.handleAllByFunc(computed, handlers, func(h *Handler, c Computed) error { + ctx = ctx.WithLogger("step", "PostHandle") return h.PostHandle(ctx, c) }); err != nil { return err diff --git a/core/resource_server_test.go b/core/resource_server_test.go index 3fb8e3e8..e228360b 100644 --- a/core/resource_server_test.go +++ b/core/resource_server_test.go @@ -48,7 +48,7 @@ func testContext() *Context { source: "default", action: "default", resourceGroupName: "web", - }) + }, testLogger) } func initTestServer(t *testing.T) func() { diff --git a/core/service.go b/core/service.go index 5f53de0c..9c3bd8f2 100644 --- a/core/service.go +++ b/core/service.go @@ -16,7 +16,6 @@ package core import ( "context" - "log" "github.com/sacloud/autoscaler/request" ) @@ -33,7 +32,9 @@ func NewScalingService(instance *Core) request.ScalingServiceServer { } func (s *ScalingService) Up(ctx context.Context, req *request.ScalingRequest) (*request.ScalingResponse, error) { - log.Println("Core.ScalingService: Up:", req) + if err := s.instance.logger.Info("message", "Core.ScalingService: Up", "request", req); err != nil { + return nil, err + } // リクエストには即時応答を返しつつバックグラウンドでジョブを実行するために引数のctxは引き継がない serviceCtx := NewContext(context.Background(), &requestInfo{ @@ -42,7 +43,7 @@ func (s *ScalingService) Up(ctx context.Context, req *request.ScalingRequest) (* action: req.Action, resourceGroupName: req.ResourceGroupName, desiredStateName: req.DesiredStateName, - }) + }, s.instance.logger) job, message, err := s.instance.Up(serviceCtx) if err != nil { return nil, err @@ -55,7 +56,9 @@ func (s *ScalingService) Up(ctx context.Context, req *request.ScalingRequest) (* } func (s *ScalingService) Down(ctx context.Context, req *request.ScalingRequest) (*request.ScalingResponse, error) { - log.Println("Core.ScalingService: Down:", req) + if err := s.instance.logger.Info("message", "Core.ScalingService: Down", "request", req.String()); err != nil { + return nil, err + } // リクエストには即時応答を返しつつバックグラウンドでジョブを実行するために引数のctxは引き継がない serviceCtx := NewContext(context.Background(), &requestInfo{ @@ -64,7 +67,7 @@ func (s *ScalingService) Down(ctx context.Context, req *request.ScalingRequest) action: req.Action, resourceGroupName: req.ResourceGroupName, desiredStateName: req.DesiredStateName, - }) + }, s.instance.logger) job, message, err := s.instance.Down(serviceCtx) if err != nil { return nil, err diff --git a/go.mod b/go.mod index 369a8b78..c7393652 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/sacloud/autoscaler go 1.16 require ( + github.com/go-kit/log v0.1.0 github.com/go-playground/validator/v10 v10.4.1 github.com/goccy/go-yaml v1.8.9 github.com/sacloud/libsacloud/v2 v2.18.1 diff --git a/go.sum b/go.sum index aeb881fe..55bb6e7f 100644 --- a/go.sum +++ b/go.sum @@ -87,8 +87,12 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -98,6 +102,7 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-yaml v1.8.9 h1:4AEXg2qx+/w29jXnXpMY6mTckmYu1TMoHteKuMf0HFg= github.com/goccy/go-yaml v1.8.9/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= diff --git a/handlers/builtins/handler.go b/handlers/builtins/handler.go index 0de075ab..ec57fa71 100644 --- a/handlers/builtins/handler.go +++ b/handlers/builtins/handler.go @@ -15,10 +15,9 @@ package builtins import ( - "log" - "github.com/sacloud/autoscaler/handler" "github.com/sacloud/autoscaler/handlers" + "github.com/sacloud/autoscaler/log" ) // Handler builtinハンドラーをラップし、リクエスト受付時のログ出力を担当するハンドラー @@ -36,32 +35,39 @@ func (h *Handler) Version() string { return h.Builtin.Version() } +func (h *Handler) GetLogger() *log.Logger { + return h.Builtin.GetLogger() +} + func (h *Handler) PreHandle(req *handler.PreHandleRequest, sender handlers.ResponseSender) error { if builtin, ok := h.Builtin.(handlers.PreHandler); ok { - log.Printf("%s: PreHandle request received: %s", handlers.HandlerFullName(h.Builtin), req.String()) + if err := h.GetLogger().Info("message", "PreHandle request received", "request", req.String()); err != nil { + return err + } return builtin.PreHandle(req, sender) } - log.Printf("%s: PreHandle request ignored: %s", handlers.HandlerFullName(h.Builtin), req.String()) - return nil + return h.GetLogger().Info("message", "PreHandle request ignored", "request", req.String()) } func (h *Handler) Handle(req *handler.HandleRequest, sender handlers.ResponseSender) error { if builtin, ok := h.Builtin.(handlers.Handler); ok { - log.Printf("%s: Handle request received: %s", handlers.HandlerFullName(h.Builtin), req.String()) + if err := h.GetLogger().Info("message", "Handle request received", "request", req.String()); err != nil { + return err + } return builtin.Handle(req, sender) } - log.Printf("%s: Handle request ignored: %s", handlers.HandlerFullName(h.Builtin), req.String()) - return nil + return h.GetLogger().Info("message", "Handle request ignored", "request", req.String()) } func (h *Handler) PostHandle(req *handler.PostHandleRequest, sender handlers.ResponseSender) error { if builtin, ok := h.Builtin.(handlers.PostHandler); ok { - log.Printf("%s: PostHandle request received: %s", handlers.HandlerFullName(h.Builtin), req.String()) + if err := h.GetLogger().Info("message", "PostHandle request received", "request", req.String()); err != nil { + return err + } return builtin.PostHandle(req, sender) } - log.Printf("%s: PostHandle request ignored: %s", handlers.HandlerFullName(h.Builtin), req.String()) - return nil + return h.GetLogger().Info("message", "PostHandle request ignored", "request", req.String()) } diff --git a/handlers/elb/servers_handler.go b/handlers/elb/servers_handler.go index 8f3dbe9a..da4e83e2 100644 --- a/handlers/elb/servers_handler.go +++ b/handlers/elb/servers_handler.go @@ -20,6 +20,7 @@ import ( "github.com/sacloud/autoscaler/handler" "github.com/sacloud/autoscaler/handlers" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/version" "github.com/sacloud/libsacloud/v2/sacloud" "github.com/sacloud/libsacloud/v2/sacloud/types" @@ -36,6 +37,7 @@ import ( // もしELBにサーバが1台しか登録されていない場合はサービス停止が発生するため注意が必要 type ServersHandler struct { handlers.SakuraCloudFlagCustomizer + Logger *log.Logger } func (h *ServersHandler) Name() string { @@ -46,6 +48,10 @@ func (h *ServersHandler) Version() string { return version.FullVersion() } +func (h *ServersHandler) GetLogger() *log.Logger { + return h.Logger +} + func (h *ServersHandler) PreHandle(req *handler.PreHandleRequest, sender handlers.ResponseSender) error { ctx := context.Background() diff --git a/handlers/elb/vertical_scale_handler.go b/handlers/elb/vertical_scale_handler.go index 75e98728..d862c1b3 100644 --- a/handlers/elb/vertical_scale_handler.go +++ b/handlers/elb/vertical_scale_handler.go @@ -20,6 +20,7 @@ import ( "github.com/sacloud/autoscaler/handler" "github.com/sacloud/autoscaler/handlers" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/version" "github.com/sacloud/libsacloud/v2/sacloud" "github.com/sacloud/libsacloud/v2/sacloud/types" @@ -27,6 +28,7 @@ import ( type VerticalScaleHandler struct { handlers.SakuraCloudFlagCustomizer + Logger *log.Logger } func (h *VerticalScaleHandler) Name() string { @@ -37,6 +39,10 @@ func (h *VerticalScaleHandler) Version() string { return version.FullVersion() } +func (h *VerticalScaleHandler) GetLogger() *log.Logger { + return h.Logger +} + func (h *VerticalScaleHandler) Handle(req *handler.HandleRequest, sender handlers.ResponseSender) error { ctx := context.TODO() diff --git a/handlers/fake/handler.go b/handlers/fake/handler.go index e1a2c51d..bf3c3173 100644 --- a/handlers/fake/handler.go +++ b/handlers/fake/handler.go @@ -19,10 +19,13 @@ import ( "github.com/sacloud/autoscaler/handler" "github.com/sacloud/autoscaler/handlers" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/version" ) -type Handler struct{} +type Handler struct { + Logger *log.Logger +} func (h *Handler) Name() string { return "fake" @@ -32,6 +35,10 @@ func (h *Handler) Version() string { return version.FullVersion() } +func (h *Handler) GetLogger() *log.Logger { + return h.Logger +} + func (h *Handler) Handle(req *handler.HandleRequest, sender handlers.ResponseSender) error { // 受付メッセージ送信 if err := sender.Send(&handler.HandleResponse{ diff --git a/handlers/gslb/servers_handler.go b/handlers/gslb/servers_handler.go index d9c27d58..1345a3a0 100644 --- a/handlers/gslb/servers_handler.go +++ b/handlers/gslb/servers_handler.go @@ -20,6 +20,7 @@ import ( "github.com/sacloud/autoscaler/handler" "github.com/sacloud/autoscaler/handlers" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/version" "github.com/sacloud/libsacloud/v2/sacloud" "github.com/sacloud/libsacloud/v2/sacloud/types" @@ -36,6 +37,7 @@ import ( // もしGSLBにサーバが1台しか登録されていない場合はサービス停止が発生するため注意が必要 type ServersHandler struct { handlers.SakuraCloudFlagCustomizer + Logger *log.Logger } func (h *ServersHandler) Name() string { @@ -46,6 +48,10 @@ func (h *ServersHandler) Version() string { return version.FullVersion() } +func (h *ServersHandler) GetLogger() *log.Logger { + return h.Logger +} + func (h *ServersHandler) PreHandle(req *handler.PreHandleRequest, sender handlers.ResponseSender) error { ctx := context.Background() diff --git a/handlers/handler.go b/handlers/handler.go index 480b31d6..1e747524 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -18,7 +18,6 @@ import ( "context" "flag" "fmt" - "log" "net" "os" "os/signal" @@ -26,12 +25,14 @@ import ( "syscall" "github.com/sacloud/autoscaler/handler" + "github.com/sacloud/autoscaler/log" "google.golang.org/grpc" ) type Server interface { Name() string Version() string + GetLogger() *log.Logger } type Handler interface { @@ -55,9 +56,10 @@ type FlagCustomizer interface { } func Serve(server Server) { - validateHandlerInterfaces(server) - handlerName := HandlerFullName(server) + logger := server.GetLogger().With("from", handlerName) + + validateHandlerInterfaces(server, logger) fs := flag.CommandLine var address string @@ -73,7 +75,7 @@ func Serve(server Server) { } if err := fs.Parse(os.Args[1:]); err != nil { - log.Fatal(err) + logger.Fatal("fatal", err) } // TODO add flag validation @@ -94,7 +96,7 @@ func Serve(server Server) { filename := strings.Replace(address, "unix:", "", -1) lis, err := net.Listen("unix", filename) if err != nil { - log.Fatal(err) + logger.Fatal("fatal", err) } grpcServer := grpc.NewServer() @@ -108,13 +110,15 @@ func Serve(server Server) { lis.Close() if _, err := os.Stat(filename); err == nil { if err := os.RemoveAll(filename); err != nil { - log.Fatal(err) + logger.Fatal("fatal", err) // nolint } } }() go func() { - log.Printf("%s started with: %s\n", handlerName, lis.Addr().String()) + if err := logger.Info("message", "started", "address", lis.Addr().String()); err != nil { + errCh <- err + } if err := grpcServer.Serve(lis); err != nil { errCh <- err } @@ -122,14 +126,14 @@ func Serve(server Server) { select { case err := <-errCh: - log.Fatalln("Fatal error: ", err) + logger.Fatal("fatal", err) case <-ctx.Done(): - log.Println("shutting down with:", ctx.Err()) + logger.Info("message", "shutting down", "error", ctx.Err()) // nolint } } } -func validateHandlerInterfaces(server Server) { +func validateHandlerInterfaces(server Server, logger *log.Logger) { if _, ok := server.(PreHandler); ok { return } @@ -139,7 +143,7 @@ func validateHandlerInterfaces(server Server) { if _, ok := server.(PostHandler); ok { return } - log.Fatalf("%s: At least one of the following must be implemented: PreHandler or Handler or PostHandler", HandlerFullName(server)) + logger.Fatal("fatal", "At least one of the following must be implemented: PreHandler or Handler or PostHandler") // nolint } func showUsage(name string, fs *flag.FlagSet) { @@ -156,34 +160,38 @@ var _ handler.HandleServiceServer = (*HandleService)(nil) type HandleService struct { handler.UnimplementedHandleServiceServer Handler Server + logger *log.Logger } func (h *HandleService) PreHandle(req *handler.PreHandleRequest, server handler.HandleService_PreHandleServer) error { if handler, ok := h.Handler.(PreHandler); ok { - log.Printf("%s: PreHandle request received: %s", HandlerFullName(h.Handler), req.String()) + if err := h.logger.Info("message", "PreHandle request received", "request", req.String()); err != nil { + return err + } return handler.PreHandle(req, server) } - log.Printf("%s: PreHandle request ignored: %s", HandlerFullName(h.Handler), req.String()) - return nil + return h.logger.Info("message", "PreHandle request ignored", "request", req.String()) } func (h *HandleService) Handle(req *handler.HandleRequest, server handler.HandleService_HandleServer) error { if handler, ok := h.Handler.(Handler); ok { - log.Printf("%s: Handle request received: %s", HandlerFullName(h.Handler), req.String()) + if err := h.logger.Info("message", "Handle request received", "request", req.String()); err != nil { + return err + } return handler.Handle(req, server) } - log.Printf("%s: Handle request ignored: %s", HandlerFullName(h.Handler), req.String()) - return nil + return h.logger.Info("message", "Handle request ignored", "request", req.String()) } func (h *HandleService) PostHandle(req *handler.PostHandleRequest, server handler.HandleService_PostHandleServer) error { if handler, ok := h.Handler.(PostHandler); ok { - log.Printf("%s: PostHandle request received: %s", HandlerFullName(h.Handler), req.String()) + if err := h.logger.Info("message", "PostHandle request received", "request", req.String()); err != nil { + return err + } return handler.PostHandle(req, server) } - log.Printf("%s: PostHandle request ignored: %s", HandlerFullName(h.Handler), req.String()) - return nil + return h.logger.Info("message", "PostHandle request ignored", "request", req.String()) } diff --git a/handlers/router/vertical_scale_handler.go b/handlers/router/vertical_scale_handler.go index 2553b5fa..04190e8b 100644 --- a/handlers/router/vertical_scale_handler.go +++ b/handlers/router/vertical_scale_handler.go @@ -20,6 +20,7 @@ import ( "github.com/sacloud/autoscaler/handler" "github.com/sacloud/autoscaler/handlers" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/version" "github.com/sacloud/libsacloud/v2/sacloud" "github.com/sacloud/libsacloud/v2/sacloud/types" @@ -27,6 +28,7 @@ import ( type VerticalScaleHandler struct { handlers.SakuraCloudFlagCustomizer + Logger *log.Logger } func (h *VerticalScaleHandler) Name() string { @@ -37,6 +39,10 @@ func (h *VerticalScaleHandler) Version() string { return version.FullVersion() } +func (h *VerticalScaleHandler) GetLogger() *log.Logger { + return h.Logger +} + func (h *VerticalScaleHandler) Handle(req *handler.HandleRequest, sender handlers.ResponseSender) error { ctx := context.TODO() diff --git a/handlers/server/vertical_scale_handler.go b/handlers/server/vertical_scale_handler.go index 26557ee1..da2c8a69 100644 --- a/handlers/server/vertical_scale_handler.go +++ b/handlers/server/vertical_scale_handler.go @@ -18,11 +18,11 @@ import ( "context" "fmt" - "github.com/sacloud/libsacloud/v2/helper/power" - "github.com/sacloud/autoscaler/handler" "github.com/sacloud/autoscaler/handlers" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/version" + "github.com/sacloud/libsacloud/v2/helper/power" "github.com/sacloud/libsacloud/v2/pkg/size" "github.com/sacloud/libsacloud/v2/sacloud" "github.com/sacloud/libsacloud/v2/sacloud/types" @@ -30,6 +30,7 @@ import ( type VerticalScaleHandler struct { handlers.SakuraCloudFlagCustomizer + Logger *log.Logger } func (h *VerticalScaleHandler) Name() string { @@ -40,6 +41,10 @@ func (h *VerticalScaleHandler) Version() string { return version.FullVersion() } +func (h *VerticalScaleHandler) GetLogger() *log.Logger { + return h.Logger +} + func (h *VerticalScaleHandler) Handle(req *handler.HandleRequest, sender handlers.ResponseSender) error { ctx := context.Background() diff --git a/handlers/stub/handler.go b/handlers/stub/handler.go index 3cf79040..31a36044 100644 --- a/handlers/stub/handler.go +++ b/handlers/stub/handler.go @@ -15,8 +15,11 @@ package stub import ( + "os" + "github.com/sacloud/autoscaler/handler" "github.com/sacloud/autoscaler/handlers" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/version" ) @@ -25,6 +28,7 @@ type Handler struct { PreHandleFunc func(*handler.PreHandleRequest, handlers.ResponseSender) error HandleFunc func(*handler.HandleRequest, handlers.ResponseSender) error PostHandleFunc func(*handler.PostHandleRequest, handlers.ResponseSender) error + Logger *log.Logger } func (h *Handler) Name() string { @@ -35,6 +39,19 @@ func (h *Handler) Version() string { return version.FullVersion() } +func (h *Handler) GetLogger() *log.Logger { + if h.Logger != nil { + return h.Logger + } + return log.NewLogger(&log.LoggerOption{ + Writer: os.Stderr, + JSON: false, + TimeStamp: true, + Caller: true, + Level: log.LevelDebug, + }) +} + func (h *Handler) PreHandle(req *handler.PreHandleRequest, sender handlers.ResponseSender) error { if h.PreHandleFunc != nil { return h.PreHandleFunc(req, sender) diff --git a/inputs/alertmanager/input.go b/inputs/alertmanager/input.go index 8843b454..c703ab8c 100644 --- a/inputs/alertmanager/input.go +++ b/inputs/alertmanager/input.go @@ -19,10 +19,23 @@ import ( "io" "net/http" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/version" ) -type Input struct{} +type Input struct { + dest string + addr string + logger *log.Logger +} + +func NewInput(dest, addr string, logger *log.Logger) *Input { + return &Input{ + dest: dest, + addr: addr, + logger: logger, + } +} func (in *Input) Name() string { return "alertmanager" @@ -32,6 +45,18 @@ func (in *Input) Version() string { return version.FullVersion() } +func (in *Input) Destination() string { + return in.dest +} + +func (in *Input) ListenAddress() string { + return in.addr +} + +func (in *Input) GetLogger() *log.Logger { + return in.logger +} + func (in *Input) ShouldAccept(req *http.Request) (bool, error) { if req.Method == http.MethodPost { reqData, err := io.ReadAll(req.Body) diff --git a/inputs/grafana/input.go b/inputs/grafana/input.go index 5a6d8999..d6c96c68 100644 --- a/inputs/grafana/input.go +++ b/inputs/grafana/input.go @@ -19,11 +19,23 @@ import ( "io" "net/http" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/version" ) -type Input struct{} +type Input struct { + dest string + addr string + logger *log.Logger +} +func NewInput(dest, addr string, logger *log.Logger) *Input { + return &Input{ + dest: dest, + addr: addr, + logger: logger, + } +} func (in *Input) Name() string { return "grafana" } @@ -32,6 +44,18 @@ func (in *Input) Version() string { return version.FullVersion() } +func (in *Input) Destination() string { + return in.dest +} + +func (in *Input) ListenAddress() string { + return in.addr +} + +func (in *Input) GetLogger() *log.Logger { + return in.logger +} + func (in *Input) ShouldAccept(req *http.Request) (bool, error) { if req.Method == http.MethodPost || req.Method == http.MethodPut { reqData, err := io.ReadAll(req.Body) diff --git a/inputs/inputs.go b/inputs/inputs.go index d7d181d8..71815be2 100644 --- a/inputs/inputs.go +++ b/inputs/inputs.go @@ -17,14 +17,12 @@ package inputs import ( "context" "fmt" - "log" "net/http" "net/http/httputil" "github.com/sacloud/autoscaler/defaults" + "github.com/sacloud/autoscaler/log" "github.com/sacloud/autoscaler/request" - "github.com/spf13/cobra" - "github.com/spf13/pflag" "google.golang.org/grpc" ) @@ -34,38 +32,21 @@ type Input interface { Name() string Version() string ShouldAccept(req *http.Request) (bool, error) // true,nilを返した場合のみCoreへのリクエストを行う -} - -type FlagCustomizer interface { - CustomizeFlags(fs *pflag.FlagSet) + Destination() string + ListenAddress() string + GetLogger() *log.Logger } func FullName(input Input) string { return fmt.Sprintf("autoscaler-inputs-%s", input.Name()) } -var ( - dest string - address string - debug bool -) - -func Init(cmd *cobra.Command, input Input) { - cmd.Flags().StringVarP(&dest, "dest", "", defaults.CoreSocketAddr, "URL of gRPC endpoint of AutoScaler Core") - cmd.Flags().StringVarP(&address, "addr", "", ":3001", "the TCP address for the server to listen on") - cmd.Flags().BoolVarP(&debug, "debug", "", false, "Show debug logs") - // 各Handlerでのカスタマイズ - if fc, ok := input.(FlagCustomizer); ok { - fc.CustomizeFlags(cmd.Flags()) - } -} - func Serve(input Input) error { server := &server{ - coreAddress: dest, - listenAddress: address, + coreAddress: input.Destination(), + listenAddress: input.ListenAddress(), input: input, - debug: debug, + logger: input.GetLogger().WithPrefix("from", FullName(input)), } return server.listenAndServe() } @@ -74,7 +55,7 @@ type server struct { coreAddress string listenAddress string input Input - debug bool + logger *log.Logger } func (s *server) listenAndServe() error { @@ -91,7 +72,10 @@ func (s *server) listenAndServe() error { w.Write([]byte("ok")) // nolint }) - log.Printf("%s: started on %s\n", FullName(s.input), s.listenAddress) + if err := s.logger.Info("message", "started", "address", s.listenAddress); err != nil { + return err + } + return http.ListenAndServe(s.listenAddress, serveMux) } @@ -112,7 +96,7 @@ func (s *server) handle(requestType string, w http.ResponseWriter, req *http.Req } if err := s.send(scalingReq); err != nil { - log.Println("[ERROR]: ", err) + s.logger.Error("error", err) // nolint w.WriteHeader(http.StatusInternalServerError) return } @@ -122,13 +106,16 @@ func (s *server) handle(requestType string, w http.ResponseWriter, req *http.Req } func (s *server) parseRequest(requestType string, req *http.Request) (*ScalingRequest, error) { - log.Println("webhook received") - if s.debug { - dump, err := httputil.DumpRequest(req, true) - if err != nil { - return nil, err - } - log.Println(string(dump)) + if err := s.logger.Info("message", "webhook received"); err != nil { + return nil, err + } + + dump, err := httputil.DumpRequest(req, true) + if err != nil { + return nil, err + } + if err := s.logger.Debug("request", string(dump)); err != nil { + return nil, err } shouldAccept, err := s.input.ShouldAccept(req) @@ -136,7 +123,9 @@ func (s *server) parseRequest(requestType string, req *http.Request) (*ScalingRe return nil, err } if !shouldAccept { - log.Println("webhook ignored") + if err := s.logger.Info("message", "webhook ignored"); err != nil { + return nil, err + } return nil, nil } @@ -204,6 +193,5 @@ func (s *server) send(scalingReq *ScalingRequest) error { if err != nil { return err } - fmt.Printf("status: %s, job-id: %s\n", res.Status, res.ScalingJobId) - return nil + return s.logger.Info("message", "webhook handled", "status", res.Status, "job-id", res.ScalingJobId) } diff --git a/inputs/request.go b/inputs/request.go index 814f064b..4ee1f03f 100644 --- a/inputs/request.go +++ b/inputs/request.go @@ -15,10 +15,7 @@ package inputs import ( - "reflect" - "strings" - - "github.com/go-playground/validator/v10" + "github.com/sacloud/autoscaler/validate" ) // ScalingRequest Inputsからのリクエストを表す @@ -31,18 +28,5 @@ type ScalingRequest struct { } func (r *ScalingRequest) Validate() error { - validate := r.newValidator() return validate.Struct(r) } - -func (r *ScalingRequest) newValidator() *validator.Validate { - validate := validator.New() - validate.RegisterTagNameFunc(func(fld reflect.StructField) string { - name := strings.SplitN(fld.Tag.Get("name"), ",", 2)[0] - if name == "-" { - return "" - } - return name - }) - return validate -} diff --git a/log/logging.go b/log/logging.go new file mode 100644 index 00000000..2a8d2805 --- /dev/null +++ b/log/logging.go @@ -0,0 +1,156 @@ +// Copyright 2021 The sacloud Authors +// +// 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. + +package log + +import ( + "io" + "os" + "time" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +// Logger ログ出力 +type Logger struct { + internal log.Logger + opt *LoggerOption +} + +// Level ログレベル +type Level string + +const ( + LevelError = Level("error") + LevelWarn = Level("warn") + LevelInfo = Level("info") + LevelDebug = Level("debug") +) + +// LoggerOption ログ出力のオプション +type LoggerOption struct { + Writer io.Writer // 出力先(デフォルトはos.Stderr) + JSON bool // JSON出力するか(falseの場合はlogfmt) + TimeStamp bool // タイムスタンプを含めるか + Caller bool // caller(呼び出し箇所)を含めるか + Level Level // 出力するログのレベル +} + +// NewLogger 指定のオプションで新しいロガーを生成して返す +// +// optは省略(nil)でも可、デフォルトでは標準エラーに出力される +func NewLogger(opt *LoggerOption) *Logger { + if opt == nil { + opt = &LoggerOption{} + } + if opt.Writer == nil { + opt.Writer = os.Stderr + } + + logger := &Logger{} + logger.initLogger(opt) + return logger +} + +func (l *Logger) initLogger(opt *LoggerOption) { + w := log.NewSyncWriter(opt.Writer) + var logger log.Logger + switch { + case opt.JSON: + logger = log.NewJSONLogger(w) + default: + logger = log.NewLogfmtLogger(w) + } + + if opt.TimeStamp { + logger = log.With(logger, "timestamp", log.TimestampFormat(time.Now, time.RFC3339)) + } + if opt.Caller { + logger = log.With(logger, "caller", log.DefaultCaller) + } + + switch opt.Level { + case LevelError: + logger = level.NewFilter(logger, level.AllowError()) + case LevelWarn: + logger = level.NewFilter(logger, level.AllowWarn()) + case LevelInfo: + logger = level.NewFilter(logger, level.AllowInfo()) + case LevelDebug: + logger = level.NewFilter(logger, level.AllowDebug()) + } + + l.internal = logger + l.opt = opt +} + +// Reset 現在のLoggerOptionを元にロガーをリセット +// +// Withxxxの影響を元に戻したい時などに利用する +func (l *Logger) Reset() { + l.initLogger(l.opt) +} + +// With 指定されたkey-valuesを持つコンテキストロガーを返す +// +// see: https://pkg.go.dev/github.com/go-kit/log +func (l *Logger) With(keyvals ...interface{}) *Logger { + logger := NewLogger(l.opt) + logger.internal = log.With(l.internal, keyvals...) + return logger +} + +// WithPrefix 指定されたkey-valuesを持つコンテキストロガーを返す +// +// see: https://pkg.go.dev/github.com/go-kit/log +func (l *Logger) WithPrefix(keyvals ...interface{}) *Logger { + logger := NewLogger(l.opt) + logger.internal = log.WithPrefix(l.internal, keyvals...) + return logger +} + +// WithSuffix 指定されたkey-valuesを持つコンテキストロガーを返す +// +// see: https://pkg.go.dev/github.com/go-kit/log +func (l *Logger) WithSuffix(keyvals ...interface{}) *Logger { + logger := NewLogger(l.opt) + logger.internal = log.WithSuffix(l.internal, keyvals...) + return logger +} + +func (l *Logger) Fatal(keyvals ...interface{}) { + l.Error(keyvals...) // nolint + os.Exit(1) +} + +// Error レベルErrorでログ出力 +func (l *Logger) Error(keyvals ...interface{}) error { + return level.Error(l.internal).Log(keyvals...) +} + +// Warn レベルWarnでログ出力 +func (l *Logger) Warn(keyvals ...interface{}) error { + return level.Warn(l.internal).Log(keyvals...) +} + +// Info レベルInfoでログ出力 +func (l *Logger) Info(keyvals ...interface{}) error { + return level.Info(l.internal).Log(keyvals...) +} + +// Debug レベルDebugでログ出力 +func (l *Logger) Debug(keyvals ...interface{}) error { + return level.Debug(l.internal).Log(keyvals...) +} diff --git a/log/logging_test.go b/log/logging_test.go new file mode 100644 index 00000000..1200057c --- /dev/null +++ b/log/logging_test.go @@ -0,0 +1,63 @@ +// Copyright 2021 The sacloud Authors +// +// 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. + +package log + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestLogger_Error(t *testing.T) { + buf := bytes.NewBufferString("") + logger := NewLogger(&LoggerOption{ + Writer: buf, + JSON: false, + TimeStamp: false, + Caller: false, + Level: LevelError, + }) + + logger.Debug("msg", "this value will never be displayed") // nolint + logger.Error("msg", "this value will be displayed") // nolint + + require.Equal(t, `level=error msg="this value will be displayed"`+"\n", buf.String()) +} + +func TestLogger_With(t *testing.T) { + buf := bytes.NewBufferString("") + + logger := NewLogger(&LoggerOption{ + Writer: buf, + JSON: false, + TimeStamp: false, + Caller: false, + Level: LevelError, + }) + + logger.Error("msg", "message without prefix") // nolint + logger = logger.With("prefix", "foobar") + logger.Error("msg", "message with prefix") // nolint + + expected := []string{ + `level=error msg="message without prefix"`, + `level=error prefix=foobar msg="message with prefix"`, + "", + } + + require.Equal(t, expected, strings.Split(buf.String(), "\n")) +} diff --git a/handlers/logging/handler.go b/validate/validate.go similarity index 56% rename from handlers/logging/handler.go rename to validate/validate.go index b8a2fbc5..31e188df 100644 --- a/handlers/logging/handler.go +++ b/validate/validate.go @@ -12,27 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -package logging +package validate import ( - "log" + "reflect" + "strings" - "github.com/sacloud/autoscaler/handler" - "github.com/sacloud/autoscaler/handlers" - "github.com/sacloud/autoscaler/version" + "github.com/go-playground/validator/v10" ) -type Handler struct{} - -func (h *Handler) Name() string { - return "logging" -} - -func (h *Handler) Version() string { - return version.FullVersion() -} - -func (h *Handler) Handle(req *handler.HandleRequest, sender handlers.ResponseSender) error { - log.Printf("autoscaler-handlers-%s: received: %s", h.Name(), req.String()) - return nil +func Struct(v interface{}) error { + validate := validator.New() + validate.RegisterTagNameFunc(func(fld reflect.StructField) string { + name := strings.SplitN(fld.Tag.Get("name"), ",", 2)[0] + if name == "-" { + return "" + } + return name + }) + // TODO エラーメッセージのカスタマイズ + return validate.Struct(v) }