This repository has been archived by the owner on Feb 21, 2024. It is now read-only.
forked from mdigger/rest
/
logger.go
115 lines (109 loc) · 3.82 KB
/
logger.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package rest
import (
"bytes"
"fmt"
"io"
"net"
"os"
"runtime"
"time"
)
var (
isTTY bool // флаг, что поддерживается вывод в цвете
accessLog io.Writer // вывод в лог
)
func init() {
// инициализируем вывод в лог
SetLogger(os.Stderr)
}
// SetLogger позволяет определить вывод лога обработки запросов и ошибок.
// Если установлен флаг Debug, то в лог так же пишутся все запросы, которые
// вызвали ошибку и дамп вызовов функций, приведших к panic.
func SetLogger(out io.Writer) {
if out, ok := out.(*os.File); ok {
fi, err := out.Stat()
if err == nil {
m := os.ModeDevice | os.ModeCharDevice
isTTY = fi.Mode()&m == m
}
} else {
isTTY = false
}
accessLog = out
}
// log выводит информацию в лог, если он определен.
func (c *Context) log() {
if accessLog == nil {
return // не выводим в лог, если он не определен
}
stop := time.Now() // время окончания обработки запроса
buf := buffers.Get().(*bytes.Buffer) // формируем буфер для генерации лога
buf.Reset()
// время окончания обработки и вывода в лог
buf.WriteString(stop.Format("2006/01/02 15:04:05"))
// адрес пользователя
remoteAddr := c.Request.Header.Get("X-Real-IP")
// Если IP-адрес путой, то смотрим на адрес proxy
if remoteAddr == "" {
remoteAddr = c.Request.Header.Get("X-Forwarded-For")
}
// Если и этот адрес не указан, то читаем адрес socket
if remoteAddr == "" {
remoteAddr, _, _ = net.SplitHostPort(c.Request.RemoteAddr)
}
// адрес пользователя, продолжительность обработки и размер переданных данных
fmt.Fprintf(buf, " %-15s %12v %9.3fKb ",
remoteAddr, stop.Sub(c.started), float32(c.size)/1024)
// Определяем цвет для вывода статуса, в зависимости от кода.
if isTTY {
buf.WriteString("\x1b[3")
switch {
case c.status < 200:
buf.WriteRune('4') // blue
case c.status < 300:
buf.WriteRune('2') // green
case c.status < 400:
buf.WriteRune('3') // yellow
case c.status < 500:
buf.WriteRune('5') // magenta
default:
buf.WriteRune('1') // red
}
buf.WriteString(";2m")
}
fmt.Fprintf(buf, "%3d", c.status) // код ответа сервера
if isTTY {
buf.WriteString("\x1b[0m")
}
fmt.Fprintf(buf, " %7s ", c.Request.Method) // метод HTTP-запроса
buf.WriteString(c.URL.RequestURI() + "\n") // URL
buf.WriteTo(accessLog)
buffers.Put(buf)
}
// errorLog выводит в лог информацию об ошибке
func (c *Context) errorLog(err interface{}, callLevel int) {
if accessLog == nil {
return // не выводим в лог, если он не определен
}
buf := buffers.Get().(*bytes.Buffer) // формируем буфер для генерации лога
buf.Reset()
// время окончания обработки и вывода в лог
buf.WriteString(time.Now().Format("2006/01/02 15:04:05"))
// публикуем саму ошибку
if isTTY {
buf.WriteString(" \x1b[31mError:\x1b[0m ")
} else {
buf.WriteString(" Error: ")
}
fmt.Fprint(buf, err)
// добавляем информацию о файле и строке, где произошла ошибка
if Debug {
_, file, line, ok := runtime.Caller(callLevel + 1)
if ok && file != "<autogenerated>" {
fmt.Fprintf(buf, " (%s:%d)", file, line)
}
}
buf.WriteRune('\n')
buf.WriteTo(accessLog)
buffers.Put(buf)
}