Commit c138614d93069506b195c717ad830679af51b209

Authored by Marko Tikvić
1 parent a7081e5813
Exists in master

output directory controlor; better error handling

Showing 1 changed file with 17 additions and 25 deletions   Show diff stats
1 package gologger 1 package gologger
2 2
3 import ( 3 import (
4 "bytes" 4 "bytes"
5 "encoding/json" 5 "encoding/json"
6 "fmt" 6 "fmt"
7 "net/http" 7 "net/http"
8 "net/http/httputil" 8 "net/http/httputil"
9 "os" 9 "os"
10 "path/filepath" 10 "path/filepath"
11 "runtime" 11 "runtime"
12 "strings" 12 "strings"
13 "sync" 13 "sync"
14 "time" 14 "time"
15 ) 15 )
16 16
17 const dateTimeFormat = "2006-01-02 15:04:05" 17 const dateTimeFormat = "2006-01-02 15:04:05"
18 18
19 // Block ... 19 // Block ...
20 const ( 20 const (
21 MaxLogSize5MB int64 = 5 * 1024 * 1024 21 MaxLogSize5MB int64 = 5 * 1024 * 1024
22 MaxLogSize1MB int64 = 1 * 1024 * 1024 22 MaxLogSize1MB int64 = 1 * 1024 * 1024
23 MaxLogSize500KB int64 = 500 * 1024 23 MaxLogSize500KB int64 = 500 * 1024
24 MaxLogSize100KB int64 = 100 * 1024 24 MaxLogSize100KB int64 = 100 * 1024
25 MaxLogSize512B int64 = 512 25 MaxLogSize512B int64 = 512
26 logDirName = "log"
27 ) 26 )
28 27
29 // Logger ... 28 // Logger ...
30 type Logger struct { 29 type Logger struct {
31 mu *sync.Mutex 30 mu *sync.Mutex
32 outputFile *os.File 31 outputFile *os.File
33 32
34 outputFileName string 33 outputFileName string
35 fullName string 34 fullName string
36 maxFileSize int64 35 maxFileSize int64
37 36
38 splitCount int 37 splitCount int
38
39 directory string
39 } 40 }
40 41
41 // New ... 42 // New ...
42 func New(name string, maxFileSize int64) (logger *Logger, err error) { 43 func New(name, dir string, maxFileSize int64) (logger *Logger, err error) {
43 logger = &Logger{} 44 logger = &Logger{}
44 45
45 logger.outputFileName = name 46 logger.outputFileName = name
46 logger.mu = &sync.Mutex{} 47 logger.mu = &sync.Mutex{}
47 logger.maxFileSize = maxFileSize 48 logger.maxFileSize = maxFileSize
49 logger.directory = dir
48 50
49 err = os.Mkdir(logDirName, os.ModePerm) 51 err = os.Mkdir(dir, os.ModePerm)
50 if err != nil { 52 if err != nil {
51 if !os.IsExist(err) { 53 if !os.IsExist(err) {
52 fmt.Fprintf(os.Stderr, "gologger: couldn't create event log directory: %s\n", err.Error())
53 return nil, err 54 return nil, err
54 } 55 }
55 } 56 }
56 57
57 date := strings.Replace(time.Now().Format(dateTimeFormat), ":", ".", -1) 58 date := strings.Replace(time.Now().Format(dateTimeFormat), ":", ".", -1)
58 logger.outputFileName += " " + date 59 logger.outputFileName += " " + date
59 logger.fullName = logger.outputFileName + ".txt" 60 logger.fullName = logger.outputFileName + ".txt"
60 path := filepath.Join(logDirName, logger.fullName) 61 path := filepath.Join(dir, logger.fullName)
61 logger.outputFile, err = os.Create(path) 62 if logger.outputFile, err = os.Create(path); err != nil {
62 if err != nil {
63 fmt.Fprintf(os.Stderr, "gologger: couldn't create event log file: %s\n", err.Error())
64 return nil, err 63 return nil, err
65 } 64 }
66 65
67 return logger, nil 66 return logger, nil
68 } 67 }
69 68
70 // Log ... 69 // Log ...
71 func (l *Logger) Log(format string, v ...interface{}) { 70 func (l *Logger) Log(format string, v ...interface{}) {
72 if l.outputFile == nil { 71 if l.outputFile == nil {
73 return 72 return
74 } 73 }
75 74
76 l.mu.Lock() 75 l.mu.Lock()
77 defer l.mu.Unlock() 76 defer l.mu.Unlock()
78 77
79 msg := fmt.Sprintf(format, v...) 78 msg := fmt.Sprintf(format, v...)
80 s := time.Now().Format(dateTimeFormat) + ": " + msg + "\n" 79 s := time.Now().Format(dateTimeFormat) + ": " + msg + "\n"
81 if l.shouldSplit(len(s)) { 80 if l.shouldSplit(len(s)) {
82 l.split() 81 l.split()
83 } 82 }
84 l.outputFile.WriteString(s) 83 l.outputFile.WriteString(s)
85 } 84 }
86 85
87 // Print ... 86 // Print ...
88 func (l *Logger) Print(format string, v ...interface{}) { 87 func (l *Logger) Print(format string, v ...interface{}) {
89 msg := fmt.Sprintf(format, v...) 88 msg := fmt.Sprintf(format, v...)
90 fmt.Printf("%s: %s\n", time.Now().Format(dateTimeFormat), msg) 89 fmt.Printf("%s: %s\n", time.Now().Format(dateTimeFormat), msg)
91 } 90 }
92 91
93 // PrintTrace ... 92 // PrintTrace ...
94 func (l *Logger) PrintTrace(format string, v ...interface{}) { 93 func (l *Logger) PrintTrace(format string, v ...interface{}) {
95 _, file, line, _ := runtime.Caller(1) 94 _, file, line, _ := runtime.Caller(1)
96 95
97 msg := fmt.Sprintf(format, v...) 96 msg := fmt.Sprintf(format, v...)
98 fmt.Printf("%s: %s %d: %s\n", time.Now().Format(dateTimeFormat), file, line, msg) 97 fmt.Printf("%s: %s %d: %s\n", time.Now().Format(dateTimeFormat), file, line, msg)
99 } 98 }
100 99
101 // Trace ... 100 // Trace ...
102 func (l *Logger) Trace(format string, v ...interface{}) { 101 func (l *Logger) Trace(format string, v ...interface{}) {
103 if l.outputFile == nil { 102 if l.outputFile == nil {
104 return 103 return
105 } 104 }
106 105
107 l.mu.Lock() 106 l.mu.Lock()
108 defer l.mu.Unlock() 107 defer l.mu.Unlock()
109 108
110 _, file, line, _ := runtime.Caller(1) 109 _, file, line, _ := runtime.Caller(1)
111 msg := fmt.Sprintf(format, v...) 110 msg := fmt.Sprintf(format, v...)
112 s := fmt.Sprintf("%s: %s %d: %s\n", time.Now().Format(dateTimeFormat), file, line, msg) 111 s := fmt.Sprintf("%s: %s %d: %s\n", time.Now().Format(dateTimeFormat), file, line, msg)
113 112
114 if l.shouldSplit(len(s)) { 113 if l.shouldSplit(len(s)) {
115 l.split() 114 l.split()
116 } 115 }
117 l.outputFile.WriteString(s) 116 l.outputFile.WriteString(s)
118 } 117 }
119 118
120 // PrintAndTrace ... 119 // PrintAndTrace ...
121 func (l *Logger) PrintAndTrace(format string, v ...interface{}) { 120 func (l *Logger) PrintAndTrace(format string, v ...interface{}) {
122 l.Print(format, v) 121 l.Print(format, v)
123 l.Trace(format, v) 122 l.Trace(format, v)
124 } 123 }
125 124
126 // LogHTTPRequest ... 125 // LogHTTPRequest ...
127 func (l *Logger) LogHTTPRequest(req *http.Request, userID string) string { 126 func (l *Logger) LogHTTPRequest(req *http.Request, userID string) string {
128 if userID == "" { 127 if userID == "" {
129 userID = "-" 128 userID = "-"
130 } 129 }
131 130
132 var b strings.Builder 131 var b strings.Builder
133 132
134 b.WriteString("Request:\n") 133 b.WriteString("Request:\n")
135 // CLF-like header 134 // CLF-like header
136 fmt.Fprintf(&b, "%s %s %s\n", req.RemoteAddr, userID, time.Now().Format(dateTimeFormat)) 135 fmt.Fprintf(&b, "%s %s %s\n", req.RemoteAddr, userID, time.Now().Format(dateTimeFormat))
137 136
138 body, err := httputil.DumpRequest(req, true) 137 body, err := httputil.DumpRequest(req, true)
139 if err != nil { 138 if err != nil {
140 fmt.Fprintf(os.Stderr, "%v\n", err) 139 fmt.Fprintf(os.Stderr, "%v\n", err)
141 } 140 }
142 141
143 const sepStr = "\r\n\r\n" 142 const sepStr = "\r\n\r\n"
144 sepIndex := bytes.Index(body, []byte(sepStr)) 143 sepIndex := bytes.Index(body, []byte(sepStr))
145 if sepIndex == -1 { 144 if sepIndex == -1 {
146 b.WriteString(string(body) + "\n\n") 145 b.WriteString(string(body) + "\n\n")
147 } else { 146 } else {
148 sepIndex += len(sepStr) 147 sepIndex += len(sepStr)
149 payload, _ := printJSON(body[sepIndex:]) 148 payload, _ := printJSON(body[sepIndex:])
150 b.WriteString(string(body[:sepIndex]) + string(payload) + "\n\n") 149 b.WriteString(string(body[:sepIndex]) + string(payload) + "\n\n")
151 } 150 }
152 151
153 return b.String() 152 return b.String()
154 } 153 }
155 154
156 const splitLine = "==============================================================" 155 const splitLine = "=============================================================="
157 156
158 // LogHTTPResponse ... 157 // LogHTTPResponse ...
159 func (l *Logger) LogHTTPResponse(status int, duration time.Duration, size int) string { 158 func (l *Logger) LogHTTPResponse(status int, duration time.Duration, size int) string {
160 return fmt.Sprintf("Response:\n%d %v %dB\n%s\n", status, duration, size, splitLine) 159 return fmt.Sprintf("Response:\n%d %v %dB\n%s\n", status, duration, size, splitLine)
161 } 160 }
162 161
163 // CombineHTTPLogs ... 162 // CombineHTTPLogs ...
164 func (l *Logger) CombineHTTPLogs(in string, out string) { 163 func (l *Logger) CombineHTTPLogs(in string, out string) {
165 if l.outputFile == nil { 164 if l.outputFile == nil {
166 return 165 return
167 } 166 }
168 167
169 l.mu.Lock() 168 l.mu.Lock()
170 defer l.mu.Unlock() 169 defer l.mu.Unlock()
171 170
172 msg := in + out 171 msg := in + out
173 if l.shouldSplit(len(msg)) { 172 if l.shouldSplit(len(msg)) {
174 l.split() 173 l.split()
175 } 174 }
176 l.outputFile.WriteString(msg) 175 l.outputFile.WriteString(msg)
177 } 176 }
178 177
179 // Close ... 178 // Close ...
180 func (l *Logger) Close() { 179 func (l *Logger) Close() error {
181 if l.outputFile == nil { 180 if l.outputFile == nil {
182 return 181 return nil
183 } 182 }
184 183
185 err := l.outputFile.Close() 184 return l.outputFile.Close()
186 if err != nil {
187 fmt.Fprintf(os.Stderr, "gologger: couldn't close event log file: %s\n", err.Error())
188 }
189 } 185 }
190 186
191 func (l *Logger) split() { 187 func (l *Logger) split() error {
192 if l.outputFile == nil { 188 if l.outputFile == nil {
193 return 189 return nil
194 } 190 }
195 191
196 // close old file 192 // close old file
197 err := l.outputFile.Close() 193 err := l.outputFile.Close()
198 if err != nil { 194 if err != nil {
199 fmt.Fprintf(os.Stderr, "gologger: split: couldn't close event file\n") 195 return err
200 return
201 } 196 }
202 197
203 l.splitCount++
204 // open new file 198 // open new file
205 var errnew error 199 l.splitCount++
206 path := filepath.Join(logDirName, l.outputFileName+fmt.Sprintf("(%d)", l.splitCount)+".txt") 200 path := filepath.Join(l.directory, l.outputFileName+fmt.Sprintf("(%d)", l.splitCount)+".txt")
207 l.outputFile, errnew = os.Create(path) 201 l.outputFile, err = os.Create(path)
208 202
209 if errnew != nil { 203 return err
210 fmt.Fprintf(os.Stderr, "gologger: couldn't create new event log file: %s\n", err.Error())
211 }
212 } 204 }
213 205
214 func (l *Logger) shouldSplit(nextEntrySize int) bool { 206 func (l *Logger) shouldSplit(nextEntrySize int) bool {
215 stats, _ := l.outputFile.Stat() 207 stats, _ := l.outputFile.Stat()
216 return int64(nextEntrySize) >= (l.maxFileSize - stats.Size()) 208 return int64(nextEntrySize) >= (l.maxFileSize - stats.Size())
217 } 209 }
218 210
219 func printJSON(in []byte) (out []byte, err error) { 211 func printJSON(in []byte) (out []byte, err error) {
220 var buf bytes.Buffer 212 var buf bytes.Buffer
221 err = json.Indent(&buf, in, "", " ") 213 err = json.Indent(&buf, in, "", " ")