Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: impl http logger #135

Merged
merged 5 commits into from
May 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cmd/default/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +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/httpmiddleware"
"github.com/abyssparanoia/rapid-go/internal/pkg/middleware/requestlog"
"github.com/volatiletech/sqlboiler/boil"
"go.uber.org/zap"
)

// Dependency ... dependency
type Dependency struct {
httpMiddleware *httpmiddleware.HTTPMiddleware
httpMiddleware *requestlog.HTTPMiddleware
gluefirebaseauth *gluefirebaseauth.Middleware
DummyHTTPHeader *httpheader.Middleware
HTTPHeader *httpheader.Middleware
Expand Down Expand Up @@ -48,7 +48,7 @@ func (d *Dependency) Inject(e *environment, logger *zap.Logger) {
uSvc := usecase.NewUser(uRepo)

// Middleware
d.httpMiddleware = httpmiddleware.New(logger)
d.httpMiddleware = requestlog.New(logger)

d.gluefirebaseauth = gluefirebaseauth.NewMiddleware(firebaseauth)
d.DummyHTTPHeader = httpheader.NewMiddleware(dhh)
Expand Down
9 changes: 1 addition & 8 deletions internal/default/infrastructure/repository/user_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package repository
import (
"context"
"database/sql"
"fmt"

"github.com/volatiletech/sqlboiler/boil"

Expand All @@ -13,9 +12,6 @@ import (
"github.com/abyssparanoia/rapid-go/internal/default/infrastructure/entity"
"github.com/abyssparanoia/rapid-go/internal/pkg/error/httperror"
"github.com/abyssparanoia/rapid-go/internal/pkg/gluesqlboiler"
"github.com/abyssparanoia/rapid-go/internal/pkg/log"
"github.com/pkg/errors"
"go.uber.org/zap"
)

type user struct {
Expand All @@ -29,10 +25,7 @@ func (r *user) Get(ctx context.Context, userID string) (*model.User, error) {

if err != nil {
if err == sql.ErrNoRows {
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)
return nil, httperror.UserNotFoundErr.New()
}
return nil, err
}
Expand Down
5 changes: 0 additions & 5 deletions internal/default/usecase/user_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package usecase

import (
"context"
"errors"

"github.com/abyssparanoia/rapid-go/internal/default/domain/model"
"github.com/abyssparanoia/rapid-go/internal/default/domain/repository"
Expand All @@ -18,10 +17,6 @@ func (s *user) Get(ctx context.Context, userID string) (*model.User, error) {
return nil, err
}

if !user.Exist() {
return nil, errors.New("not found user")
}

return user, nil
}

Expand Down
29 changes: 29 additions & 0 deletions internal/pkg/error/httperror/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package httperror

import (
"errors"
"net/http"

"go.uber.org/zap/zapcore"
)

// CodeToLevel ... convert grpc code to zapcore level
func CodeToLevel(code int) zapcore.Level {
switch code {
case http.StatusNotFound, http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden:
return zapcore.WarnLevel
case http.StatusInternalServerError, http.StatusTooManyRequests, http.StatusServiceUnavailable:
return zapcore.ErrorLevel
default:
return zapcore.InfoLevel
}
}

// ErrToCode ... convert err to code
func ErrToCode(err error) int {
var notFoundError *NotFoundError
if ok := errors.As(err, &notFoundError); ok {
return http.StatusNotFound
}
return http.StatusInternalServerError
}
10 changes: 3 additions & 7 deletions internal/pkg/error/httperror/error.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package httperror

import (
"net/http"
var (
// UserNotFoundErr ... user not found error
UserNotFoundErr NotFoundError = "UserNotFoundErr"
)

// NotFoundError ... not found error
func NotFoundError(err error) *HTTPError {
return NewHTTPError(err, http.StatusNotFound)
}
55 changes: 44 additions & 11 deletions internal/pkg/error/httperror/model.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
package httperror

// HTTPError ... errorcode model
type HTTPError struct {
error
Code int
import (
"fmt"

"github.com/pkg/errors"
)

// HTTPError ... base error
type HTTPError string

func (e HTTPError) Error() string {
return string(e)
}

// New ... new error
func (e HTTPError) New() error {
return errors.Wrap(e, "")
}

// Errorf ... errorf
func (e HTTPError) Errorf(format string, args ...interface{}) error {
return errors.Wrapf(e, format, args...)
}

// Wrap ... wrap error
func (e HTTPError) Wrap(err error) error {
if err == nil {
return e.New()
}
return errors.Wrap(e, err.Error())
}

func (m *HTTPError) Error() string {
return m.error.Error()
// Wrapf ... wrapf
func (e HTTPError) Wrapf(err error, format string, args ...interface{}) error {
if err == nil {
return e.Errorf(format, args...)
}
msg := fmt.Sprintf(format, args...)
return errors.Wrapf(e, "err: %s; %s", err, msg)
}

// NewHTTPError ... get model
func NewHTTPError(err error, code int) *HTTPError {
return &HTTPError{
error: err,
Code: code,
// NotFoundError ... not found error
type NotFoundError = HTTPError

// As ... as method
func (ne NotFoundError) As(target interface{}) bool {
if _, ok := target.(**NotFoundError); ok {
return true
}
return false
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package httpmiddleware
package requestlog

import (
"net/http"
"time"

"github.com/abyssparanoia/rapid-go/internal/pkg/log"
"github.com/go-chi/chi/middleware"
"github.com/abyssparanoia/rapid-go/internal/pkg/error/httperror"

"github.com/blendle/zapdriver"
"github.com/google/uuid"
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

const producerID = "rapid-go"

// HTTPMiddleware ... http middleware
type HTTPMiddleware struct {
logger *zap.Logger
Expand All @@ -20,13 +23,15 @@ type HTTPMiddleware struct {
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)
}

operationID := uuid.New()

m.logger.Info("call start", zapdriver.OperationStart(operationID.String(), producerID))

ctx := r.Context()
ctx = ctxzap.ToContext(ctx, m.logger)
ctx = ctxzap.ToContext(ctx, m.logger.With(
zapdriver.OperationCont(operationID.String(), producerID),
))

defer func() {
if rcvr := recover(); rcvr != nil {
Expand All @@ -38,20 +43,16 @@ func (m *HTTPMiddleware) Handle(next http.Handler) http.Handler {
next.ServeHTTP(sw, r.WithContext(ctx))

latency := time.Since(start)

fields := []zapcore.Field{
zapcoreLevel := httperror.CodeToLevel(sw.status)
ctxzap.Extract(ctx).Check(zapcoreLevel, "call end").Write(
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...)
zapdriver.OperationEnd(operationID.String(), producerID))
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package httpmiddleware
package requestlog

import "net/http"

Expand Down
22 changes: 4 additions & 18 deletions internal/pkg/renderer/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,23 @@ package renderer
import (
"context"
"encoding/csv"
"errors"
"fmt"
"net/http"

"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
"go.uber.org/zap"

"github.com/abyssparanoia/rapid-go/internal/pkg/error/httperror"
"github.com/abyssparanoia/rapid-go/internal/pkg/log"
"github.com/unrolled/render"
"golang.org/x/text/encoding/japanese"
"golang.org/x/text/transform"
)

// HandleError ... handle http error
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 httpError.Code {
case http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound:
log.Warningf(ctx, httpError.Error(), zap.Error(httpError))
default:
log.Errorf(ctx, httpError.Error(), zap.Error(httpError))
}

Error(ctx, w, httpError.Code, httpError.Error())
statusCode := httperror.ErrToCode(err)
ctxzap.AddFields(ctx, zap.Error(err))
Error(ctx, w, statusCode, err.Error())
}

// Success ... render success response
Expand Down