feat: mv internal/pkg to root [skip ci]
This commit is contained in:
43
pkg/config/config.go
Normal file
43
pkg/config/config.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_CONFIG_FILE_NAME = "weapp"
|
||||
DEFAULT_CONFIG_FILE_TYPE = "yaml"
|
||||
)
|
||||
|
||||
var defaultPaths = []string{
|
||||
".",
|
||||
"./conf",
|
||||
"./config",
|
||||
"../conf",
|
||||
"../config",
|
||||
"/conf",
|
||||
"/config",
|
||||
"${HOME}/config",
|
||||
"/",
|
||||
}
|
||||
|
||||
func AutoLoad() error {
|
||||
return LoadConfig(DEFAULT_CONFIG_FILE_NAME, DEFAULT_CONFIG_FILE_TYPE)
|
||||
}
|
||||
|
||||
func LoadConfig(fileName, filetype string, paths ...string) error {
|
||||
for _, path := range defaultPaths {
|
||||
viper.AddConfigPath(path)
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
viper.AddConfigPath(path)
|
||||
}
|
||||
viper.SetConfigName(fileName)
|
||||
viper.SetConfigType(filetype)
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
70
pkg/database/pgsqldb/gorm.go
Normal file
70
pkg/database/pgsqldb/gorm.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package pgsqldb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"git.esin.io/kimbon/weapp/internal/pkg/logging/gormlogrus"
|
||||
)
|
||||
|
||||
func DefaultDBWithGORM() *gorm.DB {
|
||||
url := NewConnectionString()
|
||||
logger := logrus.StandardLogger()
|
||||
db, err := NewGORM(url, logger)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func NewGORM(url string, logger logrus.FieldLogger) (*gorm.DB, error) {
|
||||
db, err := gorm.Open(
|
||||
postgres.Open(url),
|
||||
&gorm.Config{
|
||||
FullSaveAssociations: true,
|
||||
Logger: gormlogrus.New(logger),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
type connectionString struct {
|
||||
host string
|
||||
port int32
|
||||
user string
|
||||
password string
|
||||
database string
|
||||
applicationName string
|
||||
timezone string
|
||||
sslModel string
|
||||
}
|
||||
|
||||
func NewConnectionString() string {
|
||||
c := connectionString{
|
||||
host: viper.GetString("db.postgres.host"),
|
||||
port: viper.GetInt32("db.postgres.port"),
|
||||
database: viper.GetString("db.postgres.database"),
|
||||
user: viper.GetString("db.postgres.user"),
|
||||
password: viper.GetString("db.postgres.password"),
|
||||
applicationName: viper.GetString("db.postgres.application_name"),
|
||||
timezone: viper.GetString("db.postgres.tz"),
|
||||
sslModel: viper.GetString("db.postgres.ssl.mode"),
|
||||
}
|
||||
return c.URLString()
|
||||
}
|
||||
|
||||
func (c connectionString) URLString() string {
|
||||
return fmt.Sprintf(c.Format(),
|
||||
c.user, c.password, c.host, c.port, c.database, c.sslModel, c.applicationName, c.timezone)
|
||||
}
|
||||
|
||||
func (c connectionString) Format() string {
|
||||
return "postgres://%s:%s@%s:%d/%s?sslmode=%s&application_name=%s&timezone=%s"
|
||||
}
|
||||
22
pkg/form/conform.go
Normal file
22
pkg/form/conform.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-playground/mold/v4"
|
||||
"github.com/go-playground/mold/v4/modifiers"
|
||||
)
|
||||
|
||||
var conform = NewConform()
|
||||
|
||||
func NewConform() *mold.Transformer {
|
||||
return modifiers.New()
|
||||
}
|
||||
|
||||
func ConformStruct(v interface{}) error {
|
||||
return ConformStructWithContext(context.TODO(), v)
|
||||
}
|
||||
|
||||
func ConformStructWithContext(ctx context.Context, v interface{}) error {
|
||||
return conform.Struct(ctx, v)
|
||||
}
|
||||
28
pkg/form/encoding.go
Normal file
28
pkg/form/encoding.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/go-playground/form/v4"
|
||||
)
|
||||
|
||||
var (
|
||||
decoder = NewDecoder()
|
||||
encoder = NewEncoder()
|
||||
)
|
||||
|
||||
func NewDecoder() *form.Decoder {
|
||||
return form.NewDecoder()
|
||||
}
|
||||
|
||||
func Decode(values url.Values, v interface{}) error {
|
||||
return decoder.Decode(v, values)
|
||||
}
|
||||
|
||||
func NewEncoder() *form.Encoder {
|
||||
return form.NewEncoder()
|
||||
}
|
||||
|
||||
func Encode(v interface{}) (values url.Values, err error) {
|
||||
return encoder.Encode(v)
|
||||
}
|
||||
29
pkg/form/form.go
Normal file
29
pkg/form/form.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package form
|
||||
|
||||
import "net/url"
|
||||
|
||||
func SafeQuery(values url.Values, v interface{}) error {
|
||||
if err := Decode(values, v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ConformStruct(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ValidateStruct(v); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SafeStruct(v interface{}) error {
|
||||
if err := ConformStruct(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ValidateStruct(v); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
24
pkg/form/scrub.go
Normal file
24
pkg/form/scrub.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-playground/mold/v4"
|
||||
"github.com/go-playground/mold/v4/scrubbers"
|
||||
)
|
||||
|
||||
var (
|
||||
scrub = NewScrubbers()
|
||||
)
|
||||
|
||||
func NewScrubbers() *mold.Transformer {
|
||||
return scrubbers.New()
|
||||
}
|
||||
|
||||
func ScrubStruct(v interface{}) error {
|
||||
return ScrubStructWithContext(context.TODO(), v)
|
||||
}
|
||||
|
||||
func ScrubStructWithContext(ctx context.Context, v interface{}) error {
|
||||
return scrub.Struct(ctx, v)
|
||||
}
|
||||
121
pkg/form/validate.go
Normal file
121
pkg/form/validate.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/locales/en"
|
||||
"github.com/go-playground/locales/zh"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
zhtrans "github.com/go-playground/validator/v10/translations/zh"
|
||||
// entrans "github.com/go-playground/validator/v10/translations/en"
|
||||
)
|
||||
|
||||
var (
|
||||
translator = NewValidateTranslator()
|
||||
validate = NewValidate()
|
||||
)
|
||||
|
||||
type FieldError struct {
|
||||
Field string `json:"field"`
|
||||
Error string `json:"error"`
|
||||
Value string `json:"value"`
|
||||
Raw validator.FieldError `json:"-"`
|
||||
}
|
||||
|
||||
func (err FieldError) String() string {
|
||||
return fmt.Sprintf("%s:%s", err.Field, err.Error)
|
||||
}
|
||||
|
||||
type FieldsError []*FieldError
|
||||
|
||||
func (errs FieldsError) Error() string {
|
||||
var msg []string
|
||||
for _, err := range errs {
|
||||
msg = append(msg, err.String())
|
||||
}
|
||||
return strings.Join(msg, ";")
|
||||
}
|
||||
|
||||
func init() {
|
||||
// en := en.New() //英文翻译器
|
||||
// zh := zh.New() //中文翻译器
|
||||
|
||||
// // 第一个参数是必填,如果没有其他的语言设置,就用这第一个
|
||||
// // 后面的参数是支持多语言环境(
|
||||
// // uni := ut.New(en, en) 也是可以的
|
||||
// // uni := ut.New(en, zh, tw)
|
||||
// uni := ut.New(en, zh)
|
||||
|
||||
// translator, ok := uni.GetTranslator("zh") //获取需要的语言
|
||||
// if !ok {
|
||||
// panic("translator not found")
|
||||
// }
|
||||
|
||||
// validate := validator.New()
|
||||
// validate.RegisterTagNameFunc(func(field reflect.StructField) string {
|
||||
// label := field.Tag.Get("label")
|
||||
// if label == "" {
|
||||
// return field.Name
|
||||
// }
|
||||
// return label
|
||||
// })
|
||||
|
||||
zhtrans.RegisterDefaultTranslations(validate, translator)
|
||||
}
|
||||
|
||||
func NewValidateTranslator() ut.Translator {
|
||||
en := en.New() //英文翻译器
|
||||
zh := zh.New() //中文翻译器
|
||||
|
||||
// 第一个参数是必填,如果没有其他的语言设置,就用这第一个
|
||||
// 后面的参数是支持多语言环境(
|
||||
// uni := ut.New(en, en) 也是可以的
|
||||
// uni := ut.New(en, zh, tw)
|
||||
uni := ut.New(en, zh)
|
||||
|
||||
var ok bool
|
||||
trans, ok := uni.GetTranslator("zh") //获取需要的语言
|
||||
if !ok {
|
||||
panic("form validation translator not found")
|
||||
}
|
||||
return trans
|
||||
}
|
||||
|
||||
func NewValidate() *validator.Validate {
|
||||
v := validator.New()
|
||||
v.RegisterTagNameFunc(func(field reflect.StructField) string {
|
||||
label := field.Tag.Get("label")
|
||||
if label == "" {
|
||||
return field.Name
|
||||
}
|
||||
return label
|
||||
})
|
||||
return v
|
||||
}
|
||||
|
||||
func ValidateStruct(v interface{}) FieldsError {
|
||||
if err := validate.Struct(v); err != nil {
|
||||
validationErrs, ok := err.(validator.ValidationErrors)
|
||||
if ok {
|
||||
var fieldsErr FieldsError
|
||||
for _, ve := range validationErrs {
|
||||
fieldsErr = append(fieldsErr, NewFieldError(ve, translator))
|
||||
}
|
||||
return fieldsErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFieldError(err validator.FieldError, unt ut.Translator) *FieldError {
|
||||
return &FieldError{
|
||||
Field: err.StructField(),
|
||||
Error: err.Translate(unt),
|
||||
Raw: err,
|
||||
Value: fmt.Sprintf("%v", err.Value()),
|
||||
}
|
||||
}
|
||||
71
pkg/logging/gormlogrus/gormlogrus.go
Normal file
71
pkg/logging/gormlogrus/gormlogrus.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package gormlogrus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"gorm.io/gorm"
|
||||
gormlogger "gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/utils"
|
||||
)
|
||||
|
||||
type GormLogrusAdapter struct {
|
||||
SlowThreshold time.Duration
|
||||
SourceField string
|
||||
SkipErrRecordNotFound bool
|
||||
Logger logrus.FieldLogger
|
||||
}
|
||||
|
||||
func New(logger logrus.FieldLogger) *GormLogrusAdapter {
|
||||
return &GormLogrusAdapter{
|
||||
Logger: logger,
|
||||
SkipErrRecordNotFound: false,
|
||||
SourceField: "gorm",
|
||||
SlowThreshold: 1 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GormLogrusAdapter) LogMode(gormlogger.LogLevel) gormlogger.Interface {
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *GormLogrusAdapter) Info(ctx context.Context, s string, args ...interface{}) {
|
||||
l.Logger.Infof(s, args)
|
||||
}
|
||||
|
||||
func (l *GormLogrusAdapter) Warn(ctx context.Context, s string, args ...interface{}) {
|
||||
logrus.Warnf(s, args)
|
||||
}
|
||||
|
||||
func (l *GormLogrusAdapter) Error(ctx context.Context, s string, args ...interface{}) {
|
||||
logrus.Errorf(s, args)
|
||||
}
|
||||
|
||||
func (l *GormLogrusAdapter) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
|
||||
elapsed := time.Since(begin)
|
||||
sql, rowsAffectedNum := fc()
|
||||
fields := logrus.Fields{}
|
||||
if l.SourceField != "" {
|
||||
fields[l.SourceField] = utils.FileWithLineNum()
|
||||
}
|
||||
|
||||
// format := "%s [cost: %s] [rows: %d]"
|
||||
// args := []interface{}{sql, elapsed, rowsAffectedNum}
|
||||
msg := fmt.Sprintf("%s [cost: %s] [rows: %d]", sql, elapsed, rowsAffectedNum)
|
||||
if err != nil && !(errors.Is(err, gorm.ErrRecordNotFound) && l.SkipErrRecordNotFound) {
|
||||
fields[logrus.ErrorKey] = err
|
||||
l.Logger.WithFields(fields).Error(msg)
|
||||
return
|
||||
}
|
||||
|
||||
if l.SlowThreshold != 0 && elapsed > l.SlowThreshold {
|
||||
l.Logger.WithFields(fields).Warn(msg)
|
||||
return
|
||||
}
|
||||
|
||||
l.Logger.WithFields(fields).Debug(msg)
|
||||
}
|
||||
68
pkg/logging/hook/logrumgo/logrusmgo.go
Normal file
68
pkg/logging/hook/logrumgo/logrusmgo.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package logrusmgo
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
type LogrusMongoHook struct {
|
||||
col *mongo.Collection
|
||||
levels []logrus.Level
|
||||
}
|
||||
|
||||
func NewLogrusMongoHook(col *mongo.Collection,
|
||||
levels ...logrus.Level) *LogrusMongoHook {
|
||||
return &LogrusMongoHook{
|
||||
col: col,
|
||||
levels: levels,
|
||||
}
|
||||
}
|
||||
|
||||
var _ logrus.Hook = (*LogrusMongoHook)(nil)
|
||||
|
||||
func (hook *LogrusMongoHook) Fire(entry *logrus.Entry) error {
|
||||
doc := make(logrus.Fields)
|
||||
doc["Level"] = entry.Level.String()
|
||||
doc["Time"] = entry.Time
|
||||
doc["Message"] = entry.Message
|
||||
|
||||
if caller := entry.Caller; caller != nil {
|
||||
doc["File"] = caller.File
|
||||
doc["Line"] = caller.Line
|
||||
doc["Func"] = caller.Function
|
||||
}
|
||||
|
||||
if data := hook.FieldsData(entry.Data); len(data) > 0 {
|
||||
doc["Data"] = data
|
||||
}
|
||||
|
||||
_, err := hook.col.InsertOne(entry.Context, doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *LogrusMongoHook) SetLevels(levels ...logrus.Level) {
|
||||
hook.levels = append(hook.levels, levels...)
|
||||
}
|
||||
|
||||
func (hook *LogrusMongoHook) Levels() []logrus.Level {
|
||||
if len(hook.levels) == 0 {
|
||||
return logrus.AllLevels
|
||||
}
|
||||
return hook.levels
|
||||
}
|
||||
|
||||
func (hook *LogrusMongoHook) FieldsData(fields logrus.Fields) map[string]interface{} {
|
||||
data := make(map[string]interface{})
|
||||
for k, v := range fields {
|
||||
if errData, isError := v.(error); logrus.ErrorKey == k && v != nil && isError {
|
||||
data[k] = errData.Error()
|
||||
} else {
|
||||
data[k] = v
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
75
pkg/logging/logrus.go
Normal file
75
pkg/logging/logrus.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimestampFormat = "2006-01-02 15:04:05.000000"
|
||||
defaultLoggerLevel = logrus.DebugLevel
|
||||
)
|
||||
|
||||
var DefaultConfig = defaultConfig()
|
||||
|
||||
func defaultConfig() *Config {
|
||||
return &Config{
|
||||
Level: defaultLoggerLevel.String(),
|
||||
TimestampFormat: defaultTimestampFormat,
|
||||
ReportCaller: true,
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Level string
|
||||
TimestampFormat string `mapstructure:"timestamp_format"`
|
||||
ReportCaller bool `mapstructure:"report_caller"`
|
||||
}
|
||||
|
||||
func (cfg Config) GetLevel() logrus.Level {
|
||||
l, err := logrus.ParseLevel(cfg.Level)
|
||||
if err != nil {
|
||||
logrus.
|
||||
WithError(err).
|
||||
Errorf("parse logger level failed with: %s", cfg.Level)
|
||||
return defaultLoggerLevel
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (cfg Config) GetTimestampFormat() string {
|
||||
if cfg.TimestampFormat != "" {
|
||||
return cfg.TimestampFormat
|
||||
}
|
||||
return defaultTimestampFormat
|
||||
}
|
||||
|
||||
func Init() {
|
||||
InitLogger(DefaultConfig)
|
||||
}
|
||||
|
||||
func InitLogger(cfg *Config, hooks ...logrus.Hook) {
|
||||
logrus.SetLevel(cfg.GetLevel())
|
||||
logrus.SetReportCaller(cfg.ReportCaller)
|
||||
formatter := logrus.JSONFormatter{
|
||||
PrettyPrint: true,
|
||||
TimestampFormat: cfg.GetTimestampFormat(),
|
||||
}
|
||||
logrus.SetFormatter(&formatter)
|
||||
|
||||
for _, hook := range hooks {
|
||||
logrus.AddHook(hook)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func New() logrus.FieldLogger {
|
||||
return logrus.StandardLogger()
|
||||
}
|
||||
|
||||
func WithScope(scope string) logrus.FieldLogger {
|
||||
return logrus.WithField("scope", scope)
|
||||
}
|
||||
|
||||
func AddHook(hook logrus.Hook) {
|
||||
logrus.AddHook(hook)
|
||||
}
|
||||
50
pkg/logging/pgxlogrus/pgxlogrus.go
Normal file
50
pkg/logging/pgxlogrus/pgxlogrus.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package pgxlogrus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type pgxLogger struct {
|
||||
logger logrus.FieldLogger
|
||||
}
|
||||
|
||||
func New(logger logrus.FieldLogger) *pgxLogger {
|
||||
return &pgxLogger{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (lg pgxLogger) Log(ctx context.Context, level pgx.LogLevel, msg string, data map[string]interface{}) {
|
||||
lg.logger.WithFields(lg.fieldsData(data)).Info(msg)
|
||||
}
|
||||
|
||||
func (lg pgxLogger) fieldsData(data map[string]interface{}) logrus.Fields {
|
||||
fields := make(logrus.Fields)
|
||||
if v, ok := data["time"]; ok && v != nil {
|
||||
fields["cost"] = v
|
||||
fields["costString"] = fmt.Sprintf("%v", time.Duration(v.(time.Duration)))
|
||||
}
|
||||
|
||||
if v, ok := data["sql"]; ok {
|
||||
fields["SQL"] = v
|
||||
}
|
||||
|
||||
if v, ok := data["pid"]; ok {
|
||||
fields["PID"] = v
|
||||
}
|
||||
|
||||
if v, ok := data["args"]; ok {
|
||||
fields["args"] = v
|
||||
}
|
||||
|
||||
if v, ok := data["rowCount"]; ok {
|
||||
fields["rowCount"] = v
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
27
pkg/pubsub/pubsub.go
Normal file
27
pkg/pubsub/pubsub.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package pubsub
|
||||
|
||||
import "context"
|
||||
|
||||
type Subject string
|
||||
|
||||
func (subject Subject) String() string {
|
||||
return string(subject)
|
||||
}
|
||||
|
||||
type Message interface{}
|
||||
|
||||
type Publishing struct{}
|
||||
|
||||
type PublishOption func(*Publishing)
|
||||
|
||||
type Publisher interface {
|
||||
Publish(context.Context, Subject, Message, ...PublishOption) error
|
||||
}
|
||||
|
||||
type MessageHandler interface {
|
||||
HandleMessage(context.Context, *Message) error
|
||||
}
|
||||
|
||||
type Subscriber interface {
|
||||
Subscribe(context.Context, Subject, MessageHandler) error
|
||||
}
|
||||
16
pkg/web/middleware/header.go
Normal file
16
pkg/web/middleware/header.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package middleware
|
||||
|
||||
import "net/http"
|
||||
|
||||
func CORSHeaderMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Del("Content-Length")
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
w.Header().Set("Content-Type", "application/json;charset=utf-8")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
23
pkg/web/middleware/request.go
Normal file
23
pkg/web/middleware/request.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func LogRequestMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
at := time.Now()
|
||||
defer func() {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"RemoteAddr": r.RemoteAddr,
|
||||
"Method": r.Method,
|
||||
"URI": r.RequestURI,
|
||||
"Cost": time.Since(at).String(),
|
||||
}).Infof("[%s] %s", r.Method, r.RequestURI)
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user