moira

Форк
0
/
logger.go 
134 строки · 3.9 Кб
1
package middleware
2

3
import (
4
	"bytes"
5
	"context"
6
	"encoding/json"
7
	"fmt"
8
	"net/http"
9
	"runtime/debug"
10
	"time"
11

12
	"github.com/go-chi/chi/middleware"
13
	"github.com/go-chi/render"
14

15
	"go.avito.ru/DO/moira"
16
	"go.avito.ru/DO/moira/api"
17
)
18

19
type apiLoggerEntry struct {
20
	logger  moira.Logger
21
	request *http.Request
22
	buf     *bytes.Buffer
23
}
24

25
// GetLoggerEntry gets logger entry with configured logger
26
func GetLoggerEntry(request *http.Request) moira.Logger {
27
	return request.Context().Value(middleware.LogEntryCtxKey).(*apiLoggerEntry).logger
28
}
29

30
// WithLogEntry sets to context configured logger entry
31
func WithLogEntry(r *http.Request, entry *apiLoggerEntry) *http.Request {
32
	return r.WithContext(context.WithValue(r.Context(), middleware.LogEntryCtxKey, entry))
33
}
34

35
// RequestLogger is overload method of go-chi.middleware RequestLogger with custom response logging
36
func RequestLogger(logger moira.Logger) func(next http.Handler) http.Handler {
37
	return func(next http.Handler) http.Handler {
38
		fn := func(writer http.ResponseWriter, request *http.Request) {
39
			entry := newLogEntry(logger, request)
40
			wrapWriter := middleware.NewWrapResponseWriter(&responseWriterWithBody{ResponseWriter: writer}, request.ProtoMajor)
41

42
			t1 := time.Now()
43
			defer func() {
44
				if rvr := recover(); rvr != nil {
45
					render.Render(wrapWriter, request, api.ErrorInternalServer(fmt.Errorf("Internal Server Error")))
46
					entry.writePanic(wrapWriter.Status(), wrapWriter.BytesWritten(), time.Since(t1), rvr, debug.Stack())
47
				} else {
48
					entry.write(wrapWriter.Status(), wrapWriter.BytesWritten(), time.Since(t1), wrapWriter.Unwrap())
49
				}
50
			}()
51

52
			next.ServeHTTP(wrapWriter, WithLogEntry(request, entry))
53
		}
54
		return http.HandlerFunc(fn)
55
	}
56
}
57

58
func getErrorResponseIfItHas(writer http.ResponseWriter) *api.ErrorResponse {
59
	writerWithBody := writer.(*responseWriterWithBody)
60
	var errResp = &api.ErrorResponse{}
61
	json.NewDecoder(&writerWithBody.body).Decode(errResp)
62
	return errResp
63
}
64

65
func newLogEntry(logger moira.Logger, request *http.Request) *apiLoggerEntry {
66
	entry := &apiLoggerEntry{
67
		logger:  logger,
68
		request: request,
69
		buf:     &bytes.Buffer{},
70
	}
71

72
	entry.buf.WriteString("\"")
73
	entry.buf.WriteString(fmt.Sprintf("%s ", request.Method))
74
	scheme := "http"
75
	if request.TLS != nil {
76
		scheme = "https"
77
	}
78
	userName := GetLogin(request)
79
	if userName == "" {
80
		userName = "anonymous"
81
	}
82
	entry.buf.WriteString(fmt.Sprintf("%s:// %s%s %s\"", scheme, request.Host, request.RequestURI, request.Proto))
83
	entry.buf.WriteString(" from ")
84
	entry.buf.WriteString(request.RemoteAddr)
85
	entry.buf.WriteString(" by ")
86
	entry.buf.WriteString(userName)
87
	entry.buf.WriteString(" - ")
88
	return entry
89
}
90

91
func (entry *apiLoggerEntry) write(status, bytes int, elapsed time.Duration, response http.ResponseWriter) {
92
	if status == 0 {
93
		status = 200
94
	}
95
	entry.buf.WriteString(fmt.Sprintf("%03d", status))
96
	entry.buf.WriteString(fmt.Sprintf(" %dB", bytes))
97
	entry.buf.WriteString(" in ")
98
	entry.buf.WriteString(fmt.Sprintf("%s", elapsed))
99
	if status >= 500 {
100
		errorResponse := getErrorResponseIfItHas(response)
101
		if errorResponse != nil {
102
			entry.buf.WriteString(fmt.Sprintf(" - Error : %s", errorResponse.ErrorText))
103
		}
104
		entry.logger.Error(entry.buf.String())
105
	} else {
106
		entry.logger.Info(entry.buf.String())
107
	}
108
}
109

110
func (entry *apiLoggerEntry) writePanic(status, bytes int, elapsed time.Duration, v interface{}, stack []byte) {
111
	entry.buf.WriteString(fmt.Sprintf("%03d", status))
112
	entry.buf.WriteString(fmt.Sprintf(" %dB", bytes))
113
	entry.buf.WriteString(" in ")
114
	entry.buf.WriteString(fmt.Sprintf("%s", elapsed))
115
	entry.buf.WriteString(fmt.Sprintf(" - Panic: %+v", v))
116
	entry.buf.WriteString("\n")
117
	entry.buf.WriteString(string(stack))
118

119
	entry.logger.Error(entry.buf.String())
120
}
121

122
type responseWriterWithBody struct {
123
	http.ResponseWriter
124
	body bytes.Buffer
125
}
126

127
func (w *responseWriterWithBody) Write(buf []byte) (int, error) {
128
	n, err := w.ResponseWriter.Write(buf)
129
	_, err2 := w.body.Write(buf[:n])
130
	if err == nil {
131
		err = err2
132
	}
133
	return n, err
134
}
135

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.