diff --git a/.envrc.tepl b/.envrc.tepl index 2dc583869..41bbf6bdd 100644 --- a/.envrc.tepl +++ b/.envrc.tepl @@ -3,7 +3,7 @@ export GOOGLE_APPLICATION_CREDENTIALS="./serviceAccount.json" export PROJECT_ID="sample" # default server -export DEFAULT_ENV="LOCAL" +export DEFAULT_ENV="local" export DEFAULT_PORT="8080" export DEFAULT_MIN_LOG_SEVERITY="DEBUG" export DEFAULT_DB_HOST="tcp(localhost:3306)" @@ -16,7 +16,7 @@ export DEFAULT_GRPC_ENV="LOCAL" export DEFAULT_GRPC_PORT="50051" # push notification server -export PUSH_NOTIFICATION_ENV="LOCAL" +export PUSH_NOTIFICATION_ENV="local" export PUSH_NOTIFICATION_PORT="8081" export PUSH_NOTIFICATION_MIN_LOG_SEVERITY="DEBUG" export FCM_SERVER_KEY=hogehoge diff --git a/cmd/default/dependency.go b/cmd/default/dependency.go index d40266af8..25f74fe6a 100644 --- a/cmd/default/dependency.go +++ b/cmd/default/dependency.go @@ -7,13 +7,14 @@ import ( "github.com/abyssparanoia/rapid-go/internal/pkg/gluefirebaseauth" "github.com/abyssparanoia/rapid-go/internal/pkg/gluemysql" "github.com/abyssparanoia/rapid-go/internal/pkg/httpheader" - "github.com/abyssparanoia/rapid-go/internal/pkg/log" + "github.com/abyssparanoia/rapid-go/internal/pkg/httpmiddleware" "github.com/volatiletech/sqlboiler/boil" + "go.uber.org/zap" ) // Dependency ... dependency type Dependency struct { - Log *log.Middleware + httpMiddleware *httpmiddleware.HTTPMiddleware gluefirebaseauth *gluefirebaseauth.Middleware DummyHTTPHeader *httpheader.Middleware HTTPHeader *httpheader.Middleware @@ -21,9 +22,8 @@ type Dependency struct { } // Inject ... indect dependency -func (d *Dependency) Inject(e *environment) { +func (d *Dependency) Inject(e *environment, logger *zap.Logger) { - var lCli log.Writer var firebaseauth gluefirebaseauth.Firebaseauth authCli := gluefirebaseauth.NewClient(e.ProjectID) @@ -32,12 +32,10 @@ func (d *Dependency) Inject(e *environment) { // pkg _ = gluemysql.NewClient(e.DBHost, e.DBUser, e.DBPassword, e.DBDatabase) - if e.Envrionment == "LOCAL" { - lCli = log.NewWriterStdout() + if e.Envrionment == "local" { firebaseauth = gluefirebaseauth.NewDebug(authCli) boil.DebugMode = true } else { - lCli = log.NewWriterStackdriver(e.ProjectID) firebaseauth = gluefirebaseauth.New(authCli) } @@ -50,7 +48,8 @@ func (d *Dependency) Inject(e *environment) { uSvc := usecase.NewUser(uRepo) // Middleware - d.Log = log.NewMiddleware(lCli, e.MinLogSeverity) + d.httpMiddleware = httpmiddleware.New(logger) + d.gluefirebaseauth = gluefirebaseauth.NewMiddleware(firebaseauth) d.DummyHTTPHeader = httpheader.NewMiddleware(dhh) d.HTTPHeader = httpheader.NewMiddleware(hh) diff --git a/cmd/default/main.go b/cmd/default/main.go index c3d7d21e4..6398edbdf 100644 --- a/cmd/default/main.go +++ b/cmd/default/main.go @@ -9,6 +9,8 @@ import ( "syscall" "time" + "github.com/abyssparanoia/rapid-go/internal/pkg/log" + "github.com/caarlos0/env/v6" "github.com/go-chi/chi" ) @@ -20,9 +22,14 @@ func main() { panic(err) } + logger, err := log.New(e.Envrionment) + if err != nil { + panic(err) + } + // Dependency d := Dependency{} - d.Inject(e) + d.Inject(e, logger) // Routing r := chi.NewRouter() @@ -37,26 +44,26 @@ func main() { } // Run - fmt.Printf("[START] server. port: %s\n", addr) + logger.Sugar().Debugf("[START] server. port: %s\n", addr) go func() { if err := server.ListenAndServe(); err != http.ErrServerClosed { - fmt.Printf("[CLOSED] server closed with error: %s\n", err) + logger.Sugar().Debugf("[CLOSED] server closed with error: %s\n", err) } }() // graceful shuttdown quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGTERM, os.Interrupt) - fmt.Printf("SIGNAL %d received, so server shutting down now...\n", <-quit) + logger.Sugar().Debugf("SIGNAL %d received, so server shutting down now...\n", <-quit) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - err := server.Shutdown(ctx) + err = server.Shutdown(ctx) if err != nil { - fmt.Printf("failed to gracefully shutdown: %s\n", err) + logger.Sugar().Debugf("failed to gracefully shutdown: %s\n", err) } - fmt.Printf("server shutdown completed\n") + logger.Sugar().Debugf("server shutdown completed\n") } diff --git a/cmd/default/routing.go b/cmd/default/routing.go index bc961790c..efcdd6687 100644 --- a/cmd/default/routing.go +++ b/cmd/default/routing.go @@ -15,7 +15,7 @@ func Routing(r chi.Router, d Dependency) { r.Use(accesscontrol.Handle) // request log - r.Use(d.Log.Handle) + r.Use(d.httpMiddleware.Handle) // need to authenticate for production r.Route("/v1", func(r chi.Router) { diff --git a/go.mod b/go.mod index c30a52e9f..26c55eaf7 100644 --- a/go.mod +++ b/go.mod @@ -23,15 +23,20 @@ require ( github.com/kelseyhightower/envconfig v1.4.0 github.com/leodido/go-urn v1.2.0 // indirect github.com/lib/pq v1.4.0 + github.com/opentracing/opentracing-go v1.1.0 + github.com/pkg/errors v0.8.0 github.com/rs/xid v1.2.1 github.com/spf13/cobra v1.0.0 github.com/spf13/viper v1.6.3 + github.com/uber/jaeger-client-go v2.23.0+incompatible + github.com/uber/jaeger-lib v2.2.0+incompatible // indirect github.com/unrolled/render v1.0.3 github.com/volatiletech/inflect v0.0.0-20170731032912-e7201282ae8d // indirect github.com/volatiletech/null v8.0.0+incompatible github.com/volatiletech/sqlboiler v3.7.0+incompatible go.uber.org/zap v1.15.0 golang.org/x/text v0.3.2 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 google.golang.org/api v0.22.0 google.golang.org/grpc v1.29.1 gopkg.in/go-playground/assert.v1 v1.2.1 // indirect diff --git a/go.sum b/go.sum index 31860c86a..89177b099 100644 --- a/go.sum +++ b/go.sum @@ -197,6 +197,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= @@ -253,6 +255,10 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/uber/jaeger-client-go v2.23.0+incompatible h1:o2g11IUBdEsSZVzF3k7+bahLmxRP/dbOoW4zQ30UlKE= +github.com/uber/jaeger-client-go v2.23.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= +github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/unrolled/render v1.0.2 h1:dGS3EmChQP3yOi1YeFNO/Dx+MbWZhdvhQJTXochM5bs= github.com/unrolled/render v1.0.2/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= diff --git a/internal/default/handler/api/user.go b/internal/default/handler/api/user.go index c8f05265f..cbe19e2fe 100644 --- a/internal/default/handler/api/user.go +++ b/internal/default/handler/api/user.go @@ -32,13 +32,13 @@ func (h *UserHandler) Get(w http.ResponseWriter, r *http.Request) { v := validator.New() if err := v.Struct(param); err != nil { - renderer.HandleError(ctx, w, "validation error: ", err) + renderer.HandleError(ctx, w, err) return } user, err := h.userUsecase.Get(ctx, param.UserID) if err != nil { - renderer.HandleError(ctx, w, "h.Svc.Get", err) + renderer.HandleError(ctx, w, err) return } diff --git a/internal/default/handler/worker/admin.go b/internal/default/handler/worker/admin.go deleted file mode 100644 index 8d3c04ef8..000000000 --- a/internal/default/handler/worker/admin.go +++ /dev/null @@ -1,35 +0,0 @@ -package worker - -import ( - "net/http" - - "github.com/abyssparanoia/rapid-go/internal/pkg/log" - "github.com/abyssparanoia/rapid-go/internal/pkg/renderer" -) - -// AdminHandler ... handler for admin -type AdminHandler struct { -} - -// MigrateMasterData ... insert master data -func (h *AdminHandler) MigrateMasterData(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - log.Debugf(ctx, "call migrate master data handler") - - renderer.Success(ctx, w) -} - -// MigrateTestData ... insert master data -func (h *AdminHandler) MigrateTestData(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - log.Debugf(ctx, "call migrate test data handler") - - renderer.Success(ctx, w) -} - -// NewAdminHandler ... insert admin handler -func NewAdminHandler() *AdminHandler { - return &AdminHandler{} -} diff --git a/internal/default/handler/worker/sample.go b/internal/default/handler/worker/sample.go deleted file mode 100644 index c2d6745e6..000000000 --- a/internal/default/handler/worker/sample.go +++ /dev/null @@ -1,31 +0,0 @@ -package worker - -import ( - "net/http" - - "github.com/abyssparanoia/rapid-go/internal/pkg/log" - "github.com/abyssparanoia/rapid-go/internal/pkg/renderer" -) - -// SampleHandler ... sample handler -type SampleHandler struct { -} - -// Cron ... cron handler -func (h *SampleHandler) Cron(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - log.Debugf(ctx, "call cron handler") - renderer.Success(ctx, w) -} - -// TaskQueue ... task queue handler -func (h *SampleHandler) TaskQueue(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - log.Debugf(ctx, "call task queue handler") - renderer.Success(ctx, w) -} - -// NewSampleHandler ... get sample handler -func NewSampleHandler() *SampleHandler { - return &SampleHandler{} -} diff --git a/internal/default/infrastructure/repository/user_impl.go b/internal/default/infrastructure/repository/user_impl.go index d5ba49506..8304f5a48 100644 --- a/internal/default/infrastructure/repository/user_impl.go +++ b/internal/default/infrastructure/repository/user_impl.go @@ -3,13 +3,17 @@ package repository import ( "context" "database/sql" + "fmt" "github.com/abyssparanoia/rapid-go/internal/dbmodels/defaultdb" "github.com/abyssparanoia/rapid-go/internal/default/domain/model" "github.com/abyssparanoia/rapid-go/internal/default/domain/repository" "github.com/abyssparanoia/rapid-go/internal/default/infrastructure/entity" "github.com/abyssparanoia/rapid-go/internal/pkg/gluesqlboiler" + "github.com/abyssparanoia/rapid-go/internal/pkg/httperror" "github.com/abyssparanoia/rapid-go/internal/pkg/log" + "github.com/pkg/errors" + "go.uber.org/zap" ) type user struct { @@ -23,10 +27,11 @@ func (r *user) Get(ctx context.Context, userID string) (*model.User, error) { if err != nil { if err == sql.ErrNoRows { - log.Errorm(ctx, "dbUser.select.not.found", err) - return nil, err + msg := fmt.Sprintf("user %s not found", userID) + err = errors.Wrap(err, msg) + log.Errorf(ctx, msg, zap.Error(err)) + return nil, httperror.NotFoundError(err) } - log.Errorm(ctx, "dbUser.select", err) return nil, err } diff --git a/internal/default/usecase/error_message.go b/internal/default/usecase/error_message.go deleted file mode 100644 index 93b4e5211..000000000 --- a/internal/default/usecase/error_message.go +++ /dev/null @@ -1,12 +0,0 @@ -package usecase - -import ( - "context" - "net/http" - - "github.com/abyssparanoia/rapid-go/internal/pkg/log" -) - -func newUserNotExistError(ctx context.Context, userID string) error { - return log.Errorc(ctx, http.StatusNotFound, "user ID %s does not exist", userID) -} diff --git a/internal/default/usecase/user_impl.go b/internal/default/usecase/user_impl.go index a86b18c4f..477d04c5e 100644 --- a/internal/default/usecase/user_impl.go +++ b/internal/default/usecase/user_impl.go @@ -2,10 +2,10 @@ package usecase import ( "context" + "errors" "github.com/abyssparanoia/rapid-go/internal/default/domain/model" "github.com/abyssparanoia/rapid-go/internal/default/domain/repository" - "github.com/abyssparanoia/rapid-go/internal/pkg/log" ) type user struct { @@ -15,12 +15,11 @@ type user struct { func (s *user) Get(ctx context.Context, userID string) (*model.User, error) { user, err := s.userRepo.Get(ctx, userID) if err != nil { - log.Errorm(ctx, "s.userRepo.Get", err) return nil, err } if !user.Exist() { - return nil, newUserNotExistError(ctx, userID) + return nil, errors.New("not found user") } return user, nil diff --git a/internal/pkg/errcode/error.go b/internal/pkg/errcode/error.go deleted file mode 100644 index 67a5750ae..000000000 --- a/internal/pkg/errcode/error.go +++ /dev/null @@ -1,14 +0,0 @@ -package errcode - -// Set ... set error code -func Set(err error, code int) error { - return NewModel(code, err.Error()) -} - -// Get ... get errorcode from error -func Get(err error) (int, bool) { - if m, ok := err.(*Model); ok { - return m.Code, true - } - return 0, false -} diff --git a/internal/pkg/errcode/model.go b/internal/pkg/errcode/model.go deleted file mode 100644 index a9eda2b71..000000000 --- a/internal/pkg/errcode/model.go +++ /dev/null @@ -1,19 +0,0 @@ -package errcode - -// Model ... errorcode model -type Model struct { - Code int - Message string -} - -func (m *Model) Error() string { - return m.Message -} - -// NewModel ... get model -func NewModel(code int, message string) *Model { - return &Model{ - Code: code, - Message: message, - } -} diff --git a/internal/pkg/httperror/error.go b/internal/pkg/httperror/error.go new file mode 100644 index 000000000..84c313571 --- /dev/null +++ b/internal/pkg/httperror/error.go @@ -0,0 +1,10 @@ +package httperror + +import ( + "net/http" +) + +// NotFoundError ... not found error +func NotFoundError(err error) *HTTPError { + return NewHTTPError(err, http.StatusNotFound) +} diff --git a/internal/pkg/httperror/model.go b/internal/pkg/httperror/model.go new file mode 100644 index 000000000..625211a9a --- /dev/null +++ b/internal/pkg/httperror/model.go @@ -0,0 +1,19 @@ +package httperror + +// HTTPError ... errorcode model +type HTTPError struct { + error + Code int +} + +func (m *HTTPError) Error() string { + return m.error.Error() +} + +// NewHTTPError ... get model +func NewHTTPError(err error, code int) *HTTPError { + return &HTTPError{ + error: err, + Code: code, + } +} diff --git a/internal/pkg/httpmiddleware/log.go b/internal/pkg/httpmiddleware/log.go new file mode 100644 index 000000000..4d2f425c7 --- /dev/null +++ b/internal/pkg/httpmiddleware/log.go @@ -0,0 +1,61 @@ +package httpmiddleware + +import ( + "net/http" + "time" + + "github.com/abyssparanoia/rapid-go/internal/pkg/log" + "github.com/go-chi/chi/middleware" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// HTTPMiddleware ... http middleware +type HTTPMiddleware struct { + logger *zap.Logger +} + +// Handle ... handle http request +func (m *HTTPMiddleware) Handle(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + var requestID string + if reqID := r.Context().Value(middleware.RequestIDKey); reqID != nil { + requestID = reqID.(string) + } + + ctx := r.Context() + ctx = ctxzap.ToContext(ctx, m.logger) + + defer func() { + if rcvr := recover(); rcvr != nil { + } + }() + + sw := &statusWriter{ResponseWriter: w} + + next.ServeHTTP(sw, r.WithContext(ctx)) + + latency := time.Since(start) + + fields := []zapcore.Field{ + zap.Int("status", sw.status), + zap.Int("content-length", sw.length), + zap.Duration("took", latency), + zap.Int64("latency", latency.Nanoseconds()), + zap.String("remote", r.RemoteAddr), + zap.String("request", r.RequestURI), + zap.String("method", r.Method), + } + if requestID != "" { + fields = append(fields, zap.String("request-id", requestID)) + } + log.Infof(ctx, "request completed", fields...) + }) +} + +// New ... new http middleware +func New(logger *zap.Logger) *HTTPMiddleware { + return &HTTPMiddleware{logger} +} diff --git a/internal/pkg/httpmiddleware/staus_writer.go b/internal/pkg/httpmiddleware/staus_writer.go new file mode 100644 index 000000000..c757c3881 --- /dev/null +++ b/internal/pkg/httpmiddleware/staus_writer.go @@ -0,0 +1,23 @@ +package httpmiddleware + +import "net/http" + +type statusWriter struct { + http.ResponseWriter + status int + length int +} + +func (w *statusWriter) WriteHeader(status int) { + w.status = status + w.ResponseWriter.WriteHeader(status) +} + +func (w *statusWriter) Write(b []byte) (int, error) { + if w.status == 0 { + w.status = 200 + } + n, err := w.ResponseWriter.Write(b) + w.length += n + return n, err +} diff --git a/internal/pkg/log/config.go b/internal/pkg/log/config.go new file mode 100644 index 000000000..682f4b80f --- /dev/null +++ b/internal/pkg/log/config.go @@ -0,0 +1,64 @@ +package log + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// newTestConfig ... development config +func newTestConfig() zap.Config { + return zap.Config{ + Level: zap.NewAtomicLevelAt(zap.DebugLevel), + Development: true, + Encoding: "console", + // EncoderConfig: zap.NewDevelopmentEncoderConfig(), + EncoderConfig: encoderConfig(), + OutputPaths: []string{"stdout"}, + ErrorOutputPaths: []string{"stderr"}, + } +} + +// newDevelopmentConfig ... development config +func newDevelopmentConfig() zap.Config { + return zap.Config{ + Level: zap.NewAtomicLevelAt(zap.DebugLevel), + Development: true, + Encoding: "json", + // EncoderConfig: zap.NewDevelopmentEncoderConfig(), + EncoderConfig: encoderConfig(), + OutputPaths: []string{"stdout"}, + ErrorOutputPaths: []string{"stderr"}, + } +} + +// newProductionConfig ... production config +func newProductionConfig() zap.Config { + return zap.Config{ + Level: zap.NewAtomicLevelAt(zap.InfoLevel), + Development: false, + Sampling: &zap.SamplingConfig{ + Initial: 100, + Thereafter: 100, + }, + Encoding: "json", + EncoderConfig: encoderConfig(), + OutputPaths: []string{"stdout"}, + ErrorOutputPaths: []string{"stderr"}, + } +} + +func encoderConfig() zapcore.EncoderConfig { + return zapcore.EncoderConfig{ + TimeKey: "ts", + LevelKey: "status", + NameKey: "logger", + CallerKey: "caller", + MessageKey: "msg", + StacktraceKey: "stacktrace", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.LowercaseLevelEncoder, + EncodeTime: zapcore.EpochTimeEncoder, + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } +} diff --git a/internal/pkg/log/const.go b/internal/pkg/log/const.go deleted file mode 100644 index de0e177dd..000000000 --- a/internal/pkg/log/const.go +++ /dev/null @@ -1,88 +0,0 @@ -package log - -import ( - "encoding/json" - "fmt" - "time" -) - -// Severity ... log level -type Severity int - -const ( - // SeverityDefault ... Default - SeverityDefault Severity = 0 - // SeverityDebug ... Debug - SeverityDebug Severity = 100 - // SeverityInfo ... Info - SeverityInfo Severity = 200 - // SeverityWarning ... Warning - SeverityWarning Severity = 400 - // SeverityError ... Error - SeverityError Severity = 500 - // SeverityCritical ... Critical - SeverityCritical Severity = 600 -) - -func (c Severity) String() string { - switch c { - case SeverityDefault: - return "DEFAULT" - case SeverityDebug: - return "DEBUG" - case SeverityInfo: - return "INFO" - case SeverityWarning: - return "WARNING" - case SeverityError: - return "ERROR" - case SeverityCritical: - return "CRITICAL" - default: - panic(fmt.Sprintf("invalid log Severity: %d", c)) - } -} - -// NewSeverity ... get severity -func NewSeverity(s string) Severity { - switch s { - case "DEFAULT": - return SeverityDefault - case "DEBUG": - return SeverityDebug - case "INFO": - return SeverityInfo - case "WARNING": - return SeverityWarning - case "ERROR": - return SeverityError - case "CRITICAL": - return SeverityCritical - default: - panic(fmt.Sprintf("invalid log string: %s", s)) - } -} - -// Time ... format protocol buffer -type Time time.Time - -// MarshalJSON ... marshal to json -func (t Time) MarshalJSON() ([]byte, error) { - return json.Marshal(time.Time(t).Format(time.RFC3339Nano)) -} - -var _ json.Marshaler = Duration(0) - -// Duration ... format protocol buffer -type Duration time.Duration - -// MarshalJSON ... marshal to json -func (d Duration) MarshalJSON() ([]byte, error) { - nanos := time.Duration(d).Nanoseconds() - secs := nanos / 1e9 - nanos -= secs * 1e9 - v := make(map[string]interface{}) - v["seconds"] = int64(secs) - v["nanos"] = int32(nanos) - return json.Marshal(v) -} diff --git a/internal/pkg/log/context.go b/internal/pkg/log/context.go deleted file mode 100644 index 3bd179c0c..000000000 --- a/internal/pkg/log/context.go +++ /dev/null @@ -1,21 +0,0 @@ -package log - -import "context" - -type contextKey string - -type loggerContextKey struct{} - -// GetLogger ... get logger from context -func GetLogger(ctx context.Context) *Logger { - if itf := ctx.Value(loggerContextKey{}); itf != nil { - logger := itf.(*Logger) - return logger - } - return nil -} - -// SetLogger ... set logger to context -func SetLogger(ctx context.Context, logger *Logger) context.Context { - return context.WithValue(ctx, loggerContextKey{}, logger) -} diff --git a/internal/pkg/log/logger.go b/internal/pkg/log/logger.go index a3f0a4339..81f914632 100644 --- a/internal/pkg/log/logger.go +++ b/internal/pkg/log/logger.go @@ -2,547 +2,133 @@ package log import ( "context" - "fmt" - "net/http" - "runtime" - "strings" - "time" - "github.com/abyssparanoia/rapid-go/internal/pkg/errcode" - "github.com/abyssparanoia/rapid-go/internal/pkg/util" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" ) -// Logger ... logger model -type Logger struct { - Writer Writer - MinOutSeverity Severity - MaxOuttedSeverity Severity - TraceID string - ResponseStatus int - ApplicationLogs []*EntryChild -} - -// IsLogging ... check log level -func (l *Logger) IsLogging(severity Severity) bool { - return l.MinOutSeverity <= severity -} - -// SetOuttedSeverity ... set max log level -func (l *Logger) SetOuttedSeverity(severity Severity) { - if l.MaxOuttedSeverity < severity { - l.MaxOuttedSeverity = severity +// Must ... new logger +func Must(logger *zap.Logger, err error) *zap.Logger { + if err != nil { + panic(err) } + return logger } -// AddApplicationLog ... add application log -func (l *Logger) AddApplicationLog(severity Severity, file string, line int64, function string, msg string, at time.Time) { - dst := &EntryChild{ - Severity: severity.String(), - Message: fmt.Sprintf("%s:%d [%s] %s", file, line, function, msg), - Time: Time(at), - } - l.ApplicationLogs = append(l.ApplicationLogs, dst) -} - -// WriteRequest ... write request log -func (l *Logger) WriteRequest(r *http.Request, at time.Time, dr time.Duration) { - l.Writer.Request( - l.MaxOuttedSeverity, - l.TraceID, - l.ApplicationLogs, - r, - l.ResponseStatus, - at, - dr, - ) -} - -// SetResponseStatus ... set response status -func SetResponseStatus(ctx context.Context, status int) { - logger := GetLogger(ctx) - if logger != nil { - logger.ResponseStatus = status +// New ... new logger +func New(env string) (*zap.Logger, error) { + if env == "test" { + return newTestConfig().Build( + zap.AddStacktrace(zapcore.WarnLevel), + ) } -} - -// NewLogger ... make logger -func NewLogger(writer Writer, minSeverity Severity, traceID string) *Logger { - return &Logger{ - Writer: writer, - MinOutSeverity: minSeverity, - MaxOuttedSeverity: SeverityDebug, - TraceID: traceID, + if env == "local" { + return newDevelopmentConfig().Build( + zap.AddStacktrace(zapcore.DebugLevel), + ) } -} - -// Debugf ... output debug log -func Debugf(ctx context.Context, format string, args ...interface{}) { - severity := SeverityDebug - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf(format, args...) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) + if env == "development" { + return newDevelopmentConfig().Build( + zap.AddStacktrace(zapcore.WarnLevel), + ) } + return newProductionConfig().Build( + zap.AddStacktrace(zapcore.WarnLevel), + ) } -// Debugm ... output debug log with define message -func Debugm(ctx context.Context, method string, err error) { - severity := SeverityDebug - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf("%s: %s", method, err.Error()) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } +// Logger ... get context from context +func logger(ctx context.Context) *zap.Logger { + return ctxzap.Extract(ctx) } -// Debuge ... output debug log and make error -func Debuge(ctx context.Context, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityDebug - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := err.Error() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return err +// Debugf ... output debug log +func Debugf(ctx context.Context, msg string, fields ...zap.Field) { + withTracing( + ctx, + msg, + func(ctx context.Context, msg string, fields ...zap.Field) { + logger(ctx).WithOptions(zap.AddCallerSkip(3)).Debug(msg, fields...) + }, + fields...) } -// Debugc ... output debug log with http status code -func Debugc(ctx context.Context, code int, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityDebug - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := err.Error() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return errcode.Set(err, code) +// SugarDebugf ... output sugar debug log +func SugarDebugf(ctx context.Context, msg string, args ...interface{}) { + logger(ctx).WithOptions(zap.AddCallerSkip(3)).Sugar().Debugf(msg, args...) } // Infof ... output info log -func Infof(ctx context.Context, format string, args ...interface{}) { - severity := SeverityInfo - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf(format, args...) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } -} - -// Infom ... output info log with message -func Infom(ctx context.Context, method string, err error) { - severity := SeverityInfo - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf("%s: %s", method, err.Error()) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } -} - -// Infoe ... output info log and make error -func Infoe(ctx context.Context, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityInfo - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := err.Error() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return err -} - -// Infoc ... output info log with http status code -func Infoc(ctx context.Context, code int, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityInfo - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := err.Error() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return errcode.Set(err, code) +func Infof(ctx context.Context, msg string, fields ...zap.Field) { + withTracing( + ctx, + msg, + func(ctx context.Context, msg string, fields ...zap.Field) { + logger(ctx).WithOptions(zap.AddCallerSkip(3)).Info(msg, fields...) + }, + fields...) } // Warningf ... output warning log -func Warningf(ctx context.Context, format string, args ...interface{}) { - severity := SeverityWarning - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf(format, args...) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } -} - -// Warningm ... output warning log with message -func Warningm(ctx context.Context, method string, err error) { - severity := SeverityWarning - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf("%s: %s", method, err.Error()) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } -} - -// Warninge ... output warning log and make error -func Warninge(ctx context.Context, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityWarning - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := err.Error() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return err -} - -// Warningc ... output warning log with http status code -func Warningc(ctx context.Context, code int, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityWarning - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := err.Error() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return errcode.Set(err, code) +func Warningf(ctx context.Context, msg string, fields ...zap.Field) { + withTracing( + ctx, + msg, + func(ctx context.Context, msg string, fields ...zap.Field) { + logger(ctx).WithOptions(zap.AddCallerSkip(3)).Warn(msg, fields...) + }, + fields...) } // Errorf ... output error log -func Errorf(ctx context.Context, format string, args ...interface{}) { - severity := SeverityError - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf(format, args...) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } -} - -// Errorm ... output error log with message -func Errorm(ctx context.Context, method string, err error) { - severity := SeverityError - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf("%s: %s", method, err.Error()) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } -} - -// Errore ... output error log and make error -func Errore(ctx context.Context, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityError - logger := GetLogger(ctx) - msg := err.Error() - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return err -} - -// Errorc ... output error log with http status code -func Errorc(ctx context.Context, code int, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityError - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := err.Error() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return errcode.Set(err, code) -} - -// Criticalf ... output critical log -func Criticalf(ctx context.Context, format string, args ...interface{}) { - severity := SeverityCritical - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf(format, args...) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } -} - -// Criticalm ... output critical log with message -func Criticalm(ctx context.Context, method string, err error) { - severity := SeverityCritical - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := fmt.Sprintf("%s: %s", method, err.Error()) - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } -} - -// Criticale ... output critical log and make error -func Criticale(ctx context.Context, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityCritical - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := err.Error() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return err -} - -// Criticalc ... output critical log with http status code -func Criticalc(ctx context.Context, code int, format string, args ...interface{}) error { - err := fmt.Errorf(format, args...) - severity := SeverityCritical - logger := GetLogger(ctx) - if logger != nil && logger.IsLogging(severity) { - now := util.TimeNow() - file, line, function := getFileLine() - msg := err.Error() - logger.Writer.Application( - severity, - logger.TraceID, - msg, - file, - line, - function, - now) - logger.SetOuttedSeverity(severity) - logger.AddApplicationLog(severity, file, line, function, msg, now) - } - return errcode.Set(err, code) -} - -// Panic ... handle panic -func Panic(ctx context.Context, rcvr interface{}) string { - traces := []string{} - for depth := 0; ; depth++ { - if depth < 2 { - continue - } - _, file, line, ok := runtime.Caller(depth) - if !ok { - break +func Errorf(ctx context.Context, msg string, fields ...zap.Field) { + withTracing( + ctx, + msg, + func(ctx context.Context, msg string, fields ...zap.Field) { + logger(ctx).WithOptions(zap.AddCallerSkip(3)).Error(msg, fields...) + }, + fields...) +} + +// ErrorfIfExists ... calls Errorf only when the error exists +func ErrorfIfExists(ctx context.Context, err error, msg string, fields ...zap.Field) { + if err == nil { + return + } + withTracing( + ctx, + msg, + func(ctx context.Context, msg string, fields ...zap.Field) { + logger(ctx).WithOptions(zap.AddCallerSkip(3)).Error(msg, fields...) + }, + fields...) +} + +// LogFunc ... log func type +type LogFunc func(ctx context.Context, msg string, fields ...zap.Field) + +func withTracing( + ctx context.Context, + msg string, + f LogFunc, + fields ...zap.Field, +) { + if len(fields) == 0 { + fields = make([]zap.Field, 0) + } + if ctx != nil { + sp := opentracing.SpanFromContext(ctx) + if sp != nil { + spc := sp.Context().(jaeger.SpanContext) + + fields = append(fields, zap.String("TraceID", spc.TraceID().String())) + fields = append(fields, zap.String("ParentID", spc.ParentID().String())) + fields = append(fields, zap.String("SpanID", spc.SpanID().String())) } - trace := fmt.Sprintf("%02d: %v:%d", depth-1, file, line) - traces = append(traces, trace) - } - msg := fmt.Sprintf("panic!! %v\n%s", rcvr, strings.Join(traces, "\n")) - Criticalf(ctx, msg) - return msg -} - -func getFileLine() (string, int64, string) { - if pt, file, line, ok := runtime.Caller(2); ok { - parts := strings.Split(file, "/") - length := len(parts) - file := fmt.Sprintf("%s/%s", parts[length-2], parts[length-1]) - - fParts := strings.Split(runtime.FuncForPC(pt).Name(), ".") - fLength := len(fParts) - return file, int64(line), fParts[fLength-1] } - return "", 0, "" + f(ctx, msg, fields...) } diff --git a/internal/pkg/log/middleware.go b/internal/pkg/log/middleware.go deleted file mode 100644 index 5725e2c2d..000000000 --- a/internal/pkg/log/middleware.go +++ /dev/null @@ -1,54 +0,0 @@ -package log - -import ( - "net/http" - - "github.com/abyssparanoia/rapid-go/internal/pkg/util" -) - -// Middleware ... middleware logger -type Middleware struct { - Writer Writer - MinOutSeverity Severity -} - -// Handle ... initialize logger -func (m *Middleware) Handle(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - startAt := util.TimeNow() - - traceID := util.StrUniqueID() - logger := NewLogger(m.Writer, m.MinOutSeverity, traceID) - ctx := r.Context() - ctx = SetLogger(ctx, logger) - - defer func() { - if rcvr := recover(); rcvr != nil { - msg := Panic(ctx, rcvr) - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(msg)) - - endAt := util.TimeNow() - dr := endAt.Sub(startAt) - - logger.WriteRequest(r, endAt, dr) - } - }() - - next.ServeHTTP(w, r.WithContext(ctx)) - - endAt := util.TimeNow() - dr := endAt.Sub(startAt) - - logger.WriteRequest(r, endAt, dr) - }) -} - -// NewMiddleware ... get middleware -func NewMiddleware(writer Writer, minOutSeverity string) *Middleware { - mos := NewSeverity(minOutSeverity) - return &Middleware{ - Writer: writer, - MinOutSeverity: mos, - } -} diff --git a/internal/pkg/log/model.go b/internal/pkg/log/model.go deleted file mode 100644 index 2b2f7ba84..000000000 --- a/internal/pkg/log/model.go +++ /dev/null @@ -1,46 +0,0 @@ -package log - -// Entry ... definition log structure -// https://cloud.google.com/logging/docs/agent/configuration#special-fields -type Entry struct { - Severity string `json:"severity"` - HTTPRequest *EntryHTTPRequest `json:"httpRequest,omitempty"` - Time Time `json:"time"` - Trace string `json:"logging.googleapis.com/trace"` - TraceID string `json:"traceId"` - Childs []*EntryChild `json:"childs"` - Message string `json:"message,omitempty"` -} - -// EntryHTTPRequest ... definition http request structure -// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#httprequest -type EntryHTTPRequest struct { - RequestMethod string `json:"requestMethod"` - RequestURL string `json:"requestUrl"` - RequestSize int64 `json:"requestSize,string,omitempty"` - Status int `json:"status"` - ResponseSize int64 `json:"responseSize,string,omitempty"` - UserAgent string `json:"userAgent,omitempty"` - Referer string `json:"referer,omitempty"` - Latency Duration `json:"latency,omitempty"` - CacheLookup *bool `json:"cacheLookup,omitempty"` - CacheHit *bool `json:"cacheHit,omitempty"` - CacheValidatedWithOriginServer *bool `json:"cacheValidatedWithOriginServer,omitempty"` - CacheFillBytes *int64 `json:"cacheFillBytes,string,omitempty"` - Protocol string `json:"protocol"` -} - -// EntryChild ... definition child log structure -type EntryChild struct { - Severity string `json:"severity"` - Message string `json:"message"` - Time Time `json:"time"` -} - -// EntrySourceLocation ... definition source location -// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logentrysourcelocation -type EntrySourceLocation struct { - File string `json:"file,omitempty"` - Line int64 `json:"line,string,omitempty"` - Function string `json:"function,omitempty"` -} diff --git a/internal/pkg/log/writer.go b/internal/pkg/log/writer.go deleted file mode 100644 index b5fd32fdc..000000000 --- a/internal/pkg/log/writer.go +++ /dev/null @@ -1,26 +0,0 @@ -package log - -import ( - "net/http" - "time" -) - -// Writer ... log writer model -type Writer interface { - Request( - severity Severity, - traceID string, - applicationLogs []*EntryChild, - r *http.Request, - status int, - at time.Time, - dr time.Duration) - Application( - severity Severity, - traceID string, - msg string, - file string, - line int64, - function string, - at time.Time) -} diff --git a/internal/pkg/log/writer_stackdriver.go b/internal/pkg/log/writer_stackdriver.go deleted file mode 100644 index fc34c8458..000000000 --- a/internal/pkg/log/writer_stackdriver.go +++ /dev/null @@ -1,86 +0,0 @@ -package log - -import ( - "encoding/json" - "fmt" - "net/http" - "os" - "time" -) - -type writerStackdriver struct { - ProjectID string -} - -func (w *writerStackdriver) Request(severity Severity, - traceID string, - applicationLogs []*EntryChild, - r *http.Request, - status int, - at time.Time, - dr time.Duration) { - u := *r.URL - u.Fragment = "" - - falseV := false - - e := &Entry{ - Severity: severity.String(), - Time: Time(at), - Trace: fmt.Sprintf("projects/%s/traces/%s", w.ProjectID, traceID), - TraceID: traceID, - Childs: applicationLogs, - Message: "", - HTTPRequest: &EntryHTTPRequest{ - RequestMethod: r.Method, - RequestURL: u.RequestURI(), - RequestSize: r.ContentLength, - Status: status, - UserAgent: r.UserAgent(), - Referer: r.Referer(), - Latency: Duration(dr), - CacheLookup: &falseV, - CacheHit: &falseV, - CacheValidatedWithOriginServer: &falseV, - CacheFillBytes: nil, - Protocol: r.Proto, - }, - } - - b, err := json.Marshal(e) - if err != nil { - panic(err) - } - - fmt.Fprintf(os.Stderr, string(b)+"\n") - -} - -func (w *writerStackdriver) Application( - severity Severity, - traceID string, - msg string, - file string, - line int64, - function string, - at time.Time, -) { - e := &Entry{ - Severity: severity.String(), - Time: Time(at), - Trace: fmt.Sprintf("projects/%s/traces/%s", w.ProjectID, traceID), - Message: fmt.Sprintf("%s:%d [%s] %s", file, line, function, msg), - } - b, err := json.Marshal(e) - if err != nil { - panic(err) - } - fmt.Fprintf(os.Stdout, string(b)+"\n") -} - -// NewWriterStackdriver ... get writer for stackdriver -func NewWriterStackdriver(projectID string) Writer { - return &writerStackdriver{ - ProjectID: projectID, - } -} diff --git a/internal/pkg/log/writer_stdout.go b/internal/pkg/log/writer_stdout.go deleted file mode 100644 index b7dca7a94..000000000 --- a/internal/pkg/log/writer_stdout.go +++ /dev/null @@ -1,44 +0,0 @@ -package log - -import ( - "fmt" - "net/http" - "time" -) - -type writerStdout struct { - TimeFormat string -} - -func (w *writerStdout) Request( - severity Severity, - traceID string, - applicationLogs []*EntryChild, - r *http.Request, - status int, - at time.Time, - dr time.Duration) { - u := *r.URL - u.Fragment = "" - date := at.Format(w.TimeFormat) - fmt.Printf("%s \"%s %s\" %d %dms\n", date, r.Method, u.RequestURI(), status, dr/1000000) -} - -func (w *writerStdout) Application( - severity Severity, - traceID string, - msg string, - file string, - line int64, - function string, - at time.Time) { - date := at.Format(w.TimeFormat) - fmt.Printf("%s [%s] %s:%d [%s] %s\n", date, severity.String(), file, line, function, msg) -} - -// NewWriterStdout ... get writer for stdout -func NewWriterStdout() Writer { - return &writerStdout{ - TimeFormat: "2006-01-02 15:04:05.000", - } -} diff --git a/internal/pkg/renderer/handler.go b/internal/pkg/renderer/handler.go index 83f692a6a..c6070ae0d 100644 --- a/internal/pkg/renderer/handler.go +++ b/internal/pkg/renderer/handler.go @@ -3,10 +3,13 @@ package renderer import ( "context" "encoding/csv" + "errors" "fmt" "net/http" - "github.com/abyssparanoia/rapid-go/internal/pkg/errcode" + "github.com/abyssparanoia/rapid-go/internal/pkg/httperror" + "go.uber.org/zap" + "github.com/abyssparanoia/rapid-go/internal/pkg/log" "github.com/unrolled/render" "golang.org/x/text/encoding/japanese" @@ -14,32 +17,23 @@ import ( ) // HandleError ... handle http error -func HandleError(ctx context.Context, w http.ResponseWriter, msg string, err error) { - code, ok := errcode.Get(err) +func HandleError(ctx context.Context, w http.ResponseWriter, err error) { + var httpError *httperror.HTTPError + ok := errors.As(err, &httpError) if !ok { + log.Errorf(ctx, "internal error", zap.Error(err)) Error(ctx, w, http.StatusInternalServerError, err.Error()) return } - switch code { - case http.StatusBadRequest: - msg := fmt.Sprintf("%d StatusBadRequest: %s, %s", code, msg, err.Error()) - log.Warningf(ctx, msg) - case http.StatusUnauthorized: - msg := fmt.Sprintf("%d Unauthorized: %s, %s", code, msg, err.Error()) - log.Warningf(ctx, msg) - case http.StatusForbidden: - msg := fmt.Sprintf("%d Forbidden: %s, %s", code, msg, err.Error()) - log.Warningf(ctx, msg) - case http.StatusNotFound: - msg := fmt.Sprintf("%d NotFound: %s, %s", code, msg, err.Error()) - log.Warningf(ctx, msg) + switch httpError.Code { + case http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound: + log.Warningf(ctx, httpError.Error(), zap.Error(httpError)) default: - msg := fmt.Sprintf("%d: %s, %s", code, msg, err.Error()) - log.Errorf(ctx, msg) + log.Errorf(ctx, httpError.Error(), zap.Error(httpError)) } - Error(ctx, w, code, err.Error()) + Error(ctx, w, httpError.Code, httpError.Error()) } // Success ... render success response