Commit e2880d3fb1868efdd79b800054a2a22ad0b43d45

Authored by Marko Tikvić
1 parent 62a0d7a8a7
Exists in master

integraded golloger

gologger/http_logs.go
... ... @@ -0,0 +1,66 @@
  1 +package gologger
  2 +
  3 +import (
  4 + "bytes"
  5 + "fmt"
  6 + "net/http"
  7 + "net/http/httputil"
  8 + "os"
  9 + "strings"
  10 + "time"
  11 +)
  12 +
  13 +const splitLine = "=============================================================="
  14 +
  15 +// LogHTTPRequest ...
  16 +func (l *Logger) LogHTTPRequest(req *http.Request, userID string) string {
  17 + if userID == "" {
  18 + userID = "-"
  19 + }
  20 +
  21 + var b strings.Builder
  22 +
  23 + b.WriteString("Request:\n")
  24 + // CLF-like header
  25 + fmt.Fprintf(&b, "%s %s %s\n", req.RemoteAddr, userID, time.Now().Format(dateTimeFormat))
  26 +
  27 + body, err := httputil.DumpRequest(req, true)
  28 + if err != nil {
  29 + fmt.Fprintf(os.Stderr, "%v\n", err)
  30 + }
  31 +
  32 + const sepStr = "\r\n\r\n"
  33 + sepIndex := bytes.Index(body, []byte(sepStr))
  34 + if sepIndex == -1 {
  35 + b.WriteString(string(body) + "\n\n")
  36 + } else {
  37 + sepIndex += len(sepStr)
  38 + payload, _ := printJSON(body[sepIndex:])
  39 + b.WriteString(string(body[:sepIndex]) + string(payload) + "\n\n")
  40 + }
  41 +
  42 + return b.String()
  43 +}
  44 +
  45 +// LogHTTPResponse ...
  46 +func (l *Logger) LogHTTPResponse(data []byte, status int, startTime time.Time) string {
  47 + duration := time.Now().Sub(startTime)
  48 + jsonData, _ := printJSON(data)
  49 + return fmt.Sprintf("Response:\n%d %v %dB\n%s\n%s\n\n", status, duration, len(data), jsonData, splitLine)
  50 +}
  51 +
  52 +// CombineHTTPLogs ...
  53 +func (l *Logger) CombineHTTPLogs(in string, out string) {
  54 + if l.outputFile == nil {
  55 + return
  56 + }
  57 +
  58 + l.mu.Lock()
  59 + defer l.mu.Unlock()
  60 +
  61 + msg := in + out
  62 + if l.shouldSplit(len(msg)) {
  63 + l.split()
  64 + }
  65 + l.outputFile.WriteString(msg)
  66 +}
... ...
... ... @@ -0,0 +1,162 @@
  1 +package gologger
  2 +
  3 +import (
  4 + "bytes"
  5 + "encoding/json"
  6 + "fmt"
  7 + "os"
  8 + "path/filepath"
  9 + "strings"
  10 + "sync"
  11 + "time"
  12 +)
  13 +
  14 +const dateTimeFormat = "2006-01-02 15:04:05"
  15 +
  16 +// Block ...
  17 +const (
  18 + MaxLogSize5MB int64 = 5 * 1024 * 1024
  19 + MaxLogSize1MB int64 = 1 * 1024 * 1024
  20 + MaxLogSize500KB int64 = 500 * 1024
  21 + MaxLogSize100KB int64 = 100 * 1024
  22 + MaxLogSize512B int64 = 512
  23 +)
  24 +
  25 +// Logger ...
  26 +type Logger struct {
  27 + mu *sync.Mutex
  28 + outputFile *os.File
  29 +
  30 + outputFileName string
  31 + fullName string
  32 + maxFileSize int64
  33 +
  34 + splitCount int
  35 +
  36 + directory string
  37 +}
  38 +
  39 +// New ...
  40 +func New(name, dir string, maxFileSize int64) (logger *Logger, err error) {
  41 + logger = &Logger{}
  42 +
  43 + logger.outputFileName = name
  44 + logger.mu = &sync.Mutex{}
  45 + logger.maxFileSize = maxFileSize
  46 + logger.directory = dir
  47 +
  48 + err = os.Mkdir(dir, os.ModePerm)
  49 + if err != nil {
  50 + if !os.IsExist(err) {
  51 + return nil, err
  52 + }
  53 + }
  54 +
  55 + date := strings.Replace(time.Now().Format(dateTimeFormat), ":", ".", -1)
  56 + logger.outputFileName += " " + date
  57 + logger.fullName = logger.outputFileName + ".txt"
  58 + path := filepath.Join(dir, logger.fullName)
  59 + if logger.outputFile, err = os.Create(path); err != nil {
  60 + return nil, err
  61 + }
  62 +
  63 + return logger, nil
  64 +}
  65 +
  66 +// Log ...
  67 +func (l *Logger) Log(format string, v ...interface{}) {
  68 + if l.outputFile == nil {
  69 + return
  70 + }
  71 +
  72 + l.mu.Lock()
  73 + defer l.mu.Unlock()
  74 +
  75 + msg := fmt.Sprintf(format, v...)
  76 + s := time.Now().Format(dateTimeFormat) + ": " + msg + "\n"
  77 + if l.shouldSplit(len(s)) {
  78 + l.split()
  79 + }
  80 + l.outputFile.WriteString(s)
  81 +}
  82 +
  83 +// Print ...
  84 +func (l *Logger) Print(format string, v ...interface{}) {
  85 + msg := fmt.Sprintf(format, v...)
  86 + fmt.Printf("%s: %s\n", time.Now().Format(dateTimeFormat), msg)
  87 +}
  88 +
  89 +// Trace ...
  90 +func (l *Logger) Trace(format string, v ...interface{}) string {
  91 + if l.outputFile == nil {
  92 + return ""
  93 + }
  94 +
  95 + l.mu.Lock()
  96 + defer l.mu.Unlock()
  97 +
  98 + s := getTrace(format, v...) + "\n"
  99 +
  100 + if l.shouldSplit(len(s)) {
  101 + l.split()
  102 + }
  103 + l.outputFile.WriteString(s)
  104 +
  105 + return s
  106 +}
  107 +
  108 +// PrintTrace ...
  109 +func (l *Logger) PrintTrace(format string, v ...interface{}) {
  110 + s := getTrace(format, v...)
  111 + fmt.Printf("%s\n", s)
  112 +}
  113 +
  114 +// PrintAndTrace ...
  115 +func (l *Logger) PrintAndTrace(format string, v ...interface{}) {
  116 + t := l.Trace(format, v...)
  117 + fmt.Println(t)
  118 +}
  119 +
  120 +// Close ...
  121 +func (l *Logger) Close() error {
  122 + if l.outputFile == nil {
  123 + return nil
  124 + }
  125 +
  126 + return l.outputFile.Close()
  127 +}
  128 +
  129 +func (l *Logger) split() error {
  130 + if l.outputFile == nil {
  131 + return nil
  132 + }
  133 +
  134 + // close old file
  135 + err := l.outputFile.Close()
  136 + if err != nil {
  137 + return err
  138 + }
  139 +
  140 + // open new file
  141 + l.splitCount++
  142 + path := filepath.Join(l.directory, l.outputFileName+fmt.Sprintf("(%d)", l.splitCount)+".txt")
  143 + l.outputFile, err = os.Create(path)
  144 +
  145 + return err
  146 +}
  147 +
  148 +func (l *Logger) shouldSplit(nextEntrySize int) bool {
  149 + stats, _ := l.outputFile.Stat()
  150 + return int64(nextEntrySize) >= (l.maxFileSize - stats.Size())
  151 +}
  152 +
  153 +func printJSON(in []byte) (out []byte, err error) {
  154 + var buf bytes.Buffer
  155 + err = json.Indent(&buf, in, "", " ")
  156 + return buf.Bytes(), err
  157 +}
  158 +
  159 +// GetOutDir ...
  160 +func (l *Logger) GetOutDir() string {
  161 + return l.directory
  162 +}
... ...
gologger/tracing.go
... ... @@ -0,0 +1,65 @@
  1 +package gologger
  2 +
  3 +import (
  4 + "fmt"
  5 + "math"
  6 + "runtime"
  7 + "strings"
  8 + "time"
  9 +)
  10 +
  11 +// getCallStack
  12 +func getCallStack() (stack []string) {
  13 + const (
  14 + maxStackDepth = 10000
  15 + skipRuntime = 2 // skip runtime and startup
  16 + skipCallers = 2
  17 + thisPackage = "gologger"
  18 + )
  19 +
  20 + callers := make([]uintptr, maxStackDepth) // min 1
  21 +
  22 + stackDepth := runtime.Callers(skipCallers, callers)
  23 + frames := runtime.CallersFrames(callers)
  24 +
  25 + for i := 0; i < skipRuntime; i++ {
  26 + frames.Next()
  27 + }
  28 +
  29 + for i := 0; i < stackDepth-skipRuntime-skipCallers; i++ {
  30 + frame, next := frames.Next()
  31 + if !strings.Contains(frame.File, thisPackage) {
  32 + stack = append(stack, fmt.Sprintf("[%s %d]", frame.File, frame.Line))
  33 + }
  34 + if !next {
  35 + break
  36 + }
  37 + }
  38 +
  39 + reverseStack(stack)
  40 +
  41 + return stack
  42 +}
  43 +
  44 +// in place
  45 +func reverseStack(stack []string) {
  46 + middle := int(math.Floor(float64(len(stack)) / 2.0))
  47 +
  48 + lastIndex := len(stack) - 1
  49 + for i := 0; i < middle; i++ {
  50 + stack[i], stack[lastIndex] = stack[lastIndex], stack[i]
  51 + lastIndex--
  52 + }
  53 +}
  54 +
  55 +func getTrace(format string, v ...interface{}) (t string) {
  56 + stack := getCallStack()
  57 +
  58 + t = time.Now().Format(dateTimeFormat) + ":\n"
  59 + for _, s := range stack {
  60 + t += s + "\n"
  61 + }
  62 + t += fmt.Sprintf(format, v...) + "\n"
  63 +
  64 + return t
  65 +}
... ...
... ... @@ -11,6 +11,7 @@ type StatusRecorder struct {
11 11 writer http.ResponseWriter
12 12 status int
13 13 size int
  14 + data []byte
14 15 }
15 16  
16 17 // NewStatusRecorder ...
... ... @@ -19,6 +20,7 @@ func NewStatusRecorder(w http.ResponseWriter) *StatusRecorder {
19 20 writer: w,
20 21 status: 0,
21 22 size: 0,
  23 + data: nil,
22 24 }
23 25 }
24 26  
... ... @@ -31,6 +33,10 @@ func (r *StatusRecorder) WriteHeader(code int) {
31 33 // Write is a wrapper for http.ResponseWriter interface
32 34 func (r *StatusRecorder) Write(in []byte) (int, error) {
33 35 r.size = len(in)
  36 + if r.status >= 400 {
  37 + r.data = make([]byte, len(in))
  38 + copy(r.data, in)
  39 + }
34 40 return r.writer.Write(in)
35 41 }
36 42  
... ... @@ -49,6 +55,11 @@ func (r *StatusRecorder) Size() int {
49 55 return r.size
50 56 }
51 57  
  58 +// Size ...
  59 +func (r *StatusRecorder) Data() []byte {
  60 + return r.data
  61 +}
  62 +
52 63 // NotFoundHandlerFunc writes HTTP error 404 to w.
53 64 func NotFoundHandlerFunc(w http.ResponseWriter, req *http.Request) {
54 65 SetAccessControlHeaders(w)
... ...
middleware/middleware.go
... ... @@ -73,10 +73,10 @@ func CloseLogger() {
73 73 }
74 74  
75 75 // LogHTTP ...
76   -func LogHTTP(h http.HandlerFunc) http.HandlerFunc {
  76 +func LogHTTP(hfunc http.HandlerFunc) http.HandlerFunc {
77 77 return func(w http.ResponseWriter, req *http.Request) {
78 78 if httpLogger == nil {
79   - h(w, req)
  79 + hfunc(w, req)
80 80 return
81 81 }
82 82  
... ... @@ -87,10 +87,9 @@ func LogHTTP(h http.HandlerFunc) http.HandlerFunc {
87 87  
88 88 rec := web.NewStatusRecorder(w)
89 89  
90   - h(rec, req)
  90 + hfunc(rec, req)
91 91  
92   - t2 := time.Now()
93   - out := httpLogger.LogHTTPResponse(rec.Status(), t2.Sub(t1), rec.Size())
  92 + out := httpLogger.LogHTTPResponse(rec.Data(), rec.Status(), t1)
94 93  
95 94 httpLogger.CombineHTTPLogs(in, out)
96 95 }
... ...