Blame view

main.go 4.75 KB
a99c98307   Marko Tikvić   first commit
1
2
3
  package gologger
  
  import (
a7081e581   Marko Tikvić   pretty print json
4
5
  	"bytes"
  	"encoding/json"
a99c98307   Marko Tikvić   first commit
6
  	"fmt"
df74f4a7e   Marko Tikvić   new filename format
7
  	"net/http"
cccb4b47c   Marko Tikvić   log http response
8
  	"net/http/httputil"
a99c98307   Marko Tikvić   first commit
9
  	"os"
e025a8738   Marko Tikvić   platform agnostic...
10
  	"path/filepath"
98708cdaf   Marko Tikvić   new version, does...
11
  	"runtime"
aa60a45ee   Marko Tikvić   changed log file ...
12
  	"strings"
3ba7b5695   Marko Tikvić   added mutex locks...
13
  	"sync"
baa4468b7   Marko Tikvić   Comments go befor...
14
  	"time"
a99c98307   Marko Tikvić   first commit
15
  )
b45f88e17   Marko Tikvić   more readable dat...
16
  const dateTimeFormat = "2006-01-02 15:04:05"
92485819d   Marko Tikvić   lint
17
  // Block ...
434e7da25   Marko Tikvić   changed initializ...
18
19
20
21
22
23
24
  const (
  	MaxLogSize5MB   int64 = 5 * 1024 * 1024
  	MaxLogSize1MB   int64 = 1 * 1024 * 1024
  	MaxLogSize500KB int64 = 500 * 1024
  	MaxLogSize100KB int64 = 100 * 1024
  	MaxLogSize512B  int64 = 512
  )
a99c98307   Marko Tikvić   first commit
25

92485819d   Marko Tikvić   lint
26
  // Logger ...
5eaa49d2a   Marko Tikvić   new design; allow...
27
  type Logger struct {
98cf2ceeb   Marko Tikvić   new interaface; a...
28
29
  	mu         *sync.Mutex
  	outputFile *os.File
23ce66f0f   Marko Tikvić   optional init par...
30

98cf2ceeb   Marko Tikvić   new interaface; a...
31
  	outputFileName string
3e936a3c2   Marko Tikvić   output file name ...
32
  	fullName       string
98cf2ceeb   Marko Tikvić   new interaface; a...
33
  	maxFileSize    int64
df74f4a7e   Marko Tikvić   new filename format
34
35
  
  	splitCount int
c138614d9   Marko Tikvić   output directory ...
36
37
  
  	directory string
a99c98307   Marko Tikvić   first commit
38
  }
92485819d   Marko Tikvić   lint
39
  // New ...
c138614d9   Marko Tikvić   output directory ...
40
  func New(name, dir string, maxFileSize int64) (logger *Logger, err error) {
5eaa49d2a   Marko Tikvić   new design; allow...
41
  	logger = &Logger{}
23ce66f0f   Marko Tikvić   optional init par...
42

b45f88e17   Marko Tikvić   more readable dat...
43
  	logger.outputFileName = name
98cf2ceeb   Marko Tikvić   new interaface; a...
44
45
  	logger.mu = &sync.Mutex{}
  	logger.maxFileSize = maxFileSize
c138614d9   Marko Tikvić   output directory ...
46
  	logger.directory = dir
a99c98307   Marko Tikvić   first commit
47

c138614d9   Marko Tikvić   output directory ...
48
  	err = os.Mkdir(dir, os.ModePerm)
98cf2ceeb   Marko Tikvić   new interaface; a...
49
50
  	if err != nil {
  		if !os.IsExist(err) {
5eaa49d2a   Marko Tikvić   new design; allow...
51
  			return nil, err
a99c98307   Marko Tikvić   first commit
52
53
  		}
  	}
b45f88e17   Marko Tikvić   more readable dat...
54
55
  	date := strings.Replace(time.Now().Format(dateTimeFormat), ":", ".", -1)
  	logger.outputFileName += " " + date
3e936a3c2   Marko Tikvić   output file name ...
56
  	logger.fullName = logger.outputFileName + ".txt"
c138614d9   Marko Tikvić   output directory ...
57
58
  	path := filepath.Join(dir, logger.fullName)
  	if logger.outputFile, err = os.Create(path); err != nil {
98cf2ceeb   Marko Tikvić   new interaface; a...
59
  		return nil, err
e025a8738   Marko Tikvić   platform agnostic...
60
  	}
5eaa49d2a   Marko Tikvić   new design; allow...
61
  	return logger, nil
98708cdaf   Marko Tikvić   new version, does...
62
  }
92485819d   Marko Tikvić   lint
63
  // Log ...
df74f4a7e   Marko Tikvić   new filename format
64
  func (l *Logger) Log(format string, v ...interface{}) {
372beaf62   Marko Tikvić   refactored if sta...
65
66
67
68
69
70
71
72
  	if l.outputFile == nil {
  		return
  	}
  
  	l.mu.Lock()
  	defer l.mu.Unlock()
  
  	msg := fmt.Sprintf(format, v...)
b45f88e17   Marko Tikvić   more readable dat...
73
74
  	s := time.Now().Format(dateTimeFormat) + ": " + msg + "
  "
372beaf62   Marko Tikvić   refactored if sta...
75
76
  	if l.shouldSplit(len(s)) {
  		l.split()
23ce66f0f   Marko Tikvić   optional init par...
77
  	}
372beaf62   Marko Tikvić   refactored if sta...
78
  	l.outputFile.WriteString(s)
98708cdaf   Marko Tikvić   new version, does...
79
  }
92485819d   Marko Tikvić   lint
80
81
82
  // Print ...
  func (l *Logger) Print(format string, v ...interface{}) {
  	msg := fmt.Sprintf(format, v...)
b45f88e17   Marko Tikvić   more readable dat...
83
84
  	fmt.Printf("%s: %s
  ", time.Now().Format(dateTimeFormat), msg)
92485819d   Marko Tikvić   lint
85
  }
7dab6e005   Marko Tikvić   call stack for er...
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  const MaxStackDepth = 10000
  
  // CallStack
  func CallStack() (stack string) {
  	callers := make([]uintptr, MaxStackDepth) // min 1
  	stackDepth := runtime.Callers(2, callers)
  	frames := runtime.CallersFrames(callers)
  
  	for i := 0; i < stackDepth; i++ {
  		if i >= 2 { // skip runtime and startup
  			frame, next := frames.Next()
  			stack += fmt.Sprintf("[%s %d]
  ", frame.File, frame.Line)
  			if !next {
  				break
  			}
  		}
  	}
  
  	return
9dd4c982b   Marko Tikvić   add CallerFilenam...
106
  }
92485819d   Marko Tikvić   lint
107
108
  // PrintTrace ...
  func (l *Logger) PrintTrace(format string, v ...interface{}) {
7dab6e005   Marko Tikvić   call stack for er...
109
  	stack := CallStack()
92485819d   Marko Tikvić   lint
110
  	msg := fmt.Sprintf(format, v...)
7dab6e005   Marko Tikvić   call stack for er...
111
112
113
114
  	fmt.Printf("%s:
  %s
  %s
  ", time.Now().Format(dateTimeFormat), stack, msg)
92485819d   Marko Tikvić   lint
115
116
117
118
119
120
121
122
123
124
  }
  
  // Trace ...
  func (l *Logger) Trace(format string, v ...interface{}) {
  	if l.outputFile == nil {
  		return
  	}
  
  	l.mu.Lock()
  	defer l.mu.Unlock()
7dab6e005   Marko Tikvić   call stack for er...
125
  	stack := CallStack()
9dd4c982b   Marko Tikvić   add CallerFilenam...
126

92485819d   Marko Tikvić   lint
127
  	msg := fmt.Sprintf(format, v...)
03121daf9   Marko Tikvić   extra padding on ...
128
129
130
131
132
  	s := fmt.Sprintf("%s:
  %s
  %s
  
  ", time.Now().Format(dateTimeFormat), stack, msg)
92485819d   Marko Tikvić   lint
133
134
135
136
137
138
139
140
141
  
  	if l.shouldSplit(len(s)) {
  		l.split()
  	}
  	l.outputFile.WriteString(s)
  }
  
  // PrintAndTrace ...
  func (l *Logger) PrintAndTrace(format string, v ...interface{}) {
d9c022688   Marko Tikvić   fixed PrintAndTra...
142
143
  	l.Print(format, v...)
  	l.Trace(format, v...)
92485819d   Marko Tikvić   lint
144
145
146
  }
  
  // LogHTTPRequest ...
093191eb2   Marko Tikvić   much cleaner, muc...
147
  func (l *Logger) LogHTTPRequest(req *http.Request, userID string) string {
1f7fbc601   Marko Tikvić   logging user ID
148
149
  	if userID == "" {
  		userID = "-"
372beaf62   Marko Tikvić   refactored if sta...
150
  	}
df74f4a7e   Marko Tikvić   new filename format
151

372beaf62   Marko Tikvić   refactored if sta...
152
  	var b strings.Builder
093191eb2   Marko Tikvić   much cleaner, muc...
153

372beaf62   Marko Tikvić   refactored if sta...
154
155
156
  	b.WriteString("Request:
  ")
  	// CLF-like header
b45f88e17   Marko Tikvić   more readable dat...
157
158
  	fmt.Fprintf(&b, "%s %s %s
  ", req.RemoteAddr, userID, time.Now().Format(dateTimeFormat))
cccb4b47c   Marko Tikvić   log http response
159

093191eb2   Marko Tikvić   much cleaner, muc...
160
161
162
163
  	body, err := httputil.DumpRequest(req, true)
  	if err != nil {
  		fmt.Fprintf(os.Stderr, "%v
  ", err)
cccb4b47c   Marko Tikvić   log http response
164
  	}
a7081e581   Marko Tikvić   pretty print json
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  
  	const sepStr = "\r
  \r
  "
  	sepIndex := bytes.Index(body, []byte(sepStr))
  	if sepIndex == -1 {
  		b.WriteString(string(body) + "
  
  ")
  	} else {
  		sepIndex += len(sepStr)
  		payload, _ := printJSON(body[sepIndex:])
  		b.WriteString(string(body[:sepIndex]) + string(payload) + "
  
  ")
  	}
372beaf62   Marko Tikvić   refactored if sta...
181

093191eb2   Marko Tikvić   much cleaner, muc...
182
183
  	return b.String()
  }
372beaf62   Marko Tikvić   refactored if sta...
184

feba66094   Marko Tikvić   split line constant
185
  const splitLine = "=============================================================="
92485819d   Marko Tikvić   lint
186
  // LogHTTPResponse ...
093191eb2   Marko Tikvić   much cleaner, muc...
187
  func (l *Logger) LogHTTPResponse(status int, duration time.Duration, size int) string {
feba66094   Marko Tikvić   split line constant
188
189
190
191
  	return fmt.Sprintf("Response:
  %d %v %dB
  %s
  ", status, duration, size, splitLine)
cccb4b47c   Marko Tikvić   log http response
192
  }
92485819d   Marko Tikvić   lint
193
  // CombineHTTPLogs ...
093191eb2   Marko Tikvić   much cleaner, muc...
194
  func (l *Logger) CombineHTTPLogs(in string, out string) {
372beaf62   Marko Tikvić   refactored if sta...
195
196
197
  	if l.outputFile == nil {
  		return
  	}
df74f4a7e   Marko Tikvić   new filename format
198

372beaf62   Marko Tikvić   refactored if sta...
199
200
  	l.mu.Lock()
  	defer l.mu.Unlock()
093191eb2   Marko Tikvić   much cleaner, muc...
201
  	msg := in + out
372beaf62   Marko Tikvić   refactored if sta...
202
203
  	if l.shouldSplit(len(msg)) {
  		l.split()
98708cdaf   Marko Tikvić   new version, does...
204
  	}
372beaf62   Marko Tikvić   refactored if sta...
205
  	l.outputFile.WriteString(msg)
98708cdaf   Marko Tikvić   new version, does...
206
  }
92485819d   Marko Tikvić   lint
207
  // Close ...
c138614d9   Marko Tikvić   output directory ...
208
  func (l *Logger) Close() error {
372beaf62   Marko Tikvić   refactored if sta...
209
  	if l.outputFile == nil {
c138614d9   Marko Tikvić   output directory ...
210
  		return nil
372beaf62   Marko Tikvić   refactored if sta...
211
  	}
c138614d9   Marko Tikvić   output directory ...
212
  	return l.outputFile.Close()
a99c98307   Marko Tikvić   first commit
213
  }
c138614d9   Marko Tikvić   output directory ...
214
  func (l *Logger) split() error {
372beaf62   Marko Tikvić   refactored if sta...
215
  	if l.outputFile == nil {
c138614d9   Marko Tikvić   output directory ...
216
  		return nil
372beaf62   Marko Tikvić   refactored if sta...
217
  	}
98cf2ceeb   Marko Tikvić   new interaface; a...
218
219
220
  	// close old file
  	err := l.outputFile.Close()
  	if err != nil {
c138614d9   Marko Tikvić   output directory ...
221
  		return err
98cf2ceeb   Marko Tikvić   new interaface; a...
222
  	}
df74f4a7e   Marko Tikvić   new filename format
223

98cf2ceeb   Marko Tikvić   new interaface; a...
224
  	// open new file
c138614d9   Marko Tikvić   output directory ...
225
226
227
  	l.splitCount++
  	path := filepath.Join(l.directory, l.outputFileName+fmt.Sprintf("(%d)", l.splitCount)+".txt")
  	l.outputFile, err = os.Create(path)
a99c98307   Marko Tikvić   first commit
228

c138614d9   Marko Tikvić   output directory ...
229
  	return err
a99c98307   Marko Tikvić   first commit
230
  }
98cf2ceeb   Marko Tikvić   new interaface; a...
231
232
233
  func (l *Logger) shouldSplit(nextEntrySize int) bool {
  	stats, _ := l.outputFile.Stat()
  	return int64(nextEntrySize) >= (l.maxFileSize - stats.Size())
a99c98307   Marko Tikvić   first commit
234
  }
a7081e581   Marko Tikvić   pretty print json
235
236
237
238
239
240
  
  func printJSON(in []byte) (out []byte, err error) {
  	var buf bytes.Buffer
  	err = json.Indent(&buf, in, "", "    ")
  	return buf.Bytes(), err
  }