Compare View

switch
from
...
to
 
Commits (2)
... ... @@ -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)
... ...
logger/http_logs.go
... ... @@ -0,0 +1,66 @@
  1 +package logger
  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 logger
  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 +// GetOutDir ...
  130 +func (l *Logger) GetOutDir() string {
  131 + return l.directory
  132 +}
  133 +
  134 +func (l *Logger) split() error {
  135 + if l.outputFile == nil {
  136 + return nil
  137 + }
  138 +
  139 + // close old file
  140 + err := l.outputFile.Close()
  141 + if err != nil {
  142 + return err
  143 + }
  144 +
  145 + // open new file
  146 + l.splitCount++
  147 + path := filepath.Join(l.directory, l.outputFileName+fmt.Sprintf("(%d)", l.splitCount)+".txt")
  148 + l.outputFile, err = os.Create(path)
  149 +
  150 + return err
  151 +}
  152 +
  153 +func (l *Logger) shouldSplit(nextEntrySize int) bool {
  154 + stats, _ := l.outputFile.Stat()
  155 + return int64(nextEntrySize) >= (l.maxFileSize - stats.Size())
  156 +}
  157 +
  158 +func printJSON(in []byte) (out []byte, err error) {
  159 + var buf bytes.Buffer
  160 + err = json.Indent(&buf, in, "", " ")
  161 + return buf.Bytes(), err
  162 +}
... ...
logger/tracing.go
... ... @@ -0,0 +1,65 @@
  1 +package logger
  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 = "logger"
  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 +}
... ...
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 }
... ...
1 1 package webutility
2 2  
3   -type QSortDirection int
  3 +import "sort"
4 4  
5   -const (
6   - QSortAscending QSortDirection = iota
7   - QSortDescending
8   -)
9   -
10   -// QuickSortable is an interface for quicksorting slices.
11   -type QuickSortable interface {
12   - Swap(i, j int)
13   - Compare(i, j int) int
14   - Len() int
15   -}
16   -
17   -// Quicksort quicksorts que.
18   -func Quicksort(que QuickSortable, low, high int, dir QSortDirection) {
  5 +// QuickSort quicksorts que.
  6 +func QuickSort(que sort.Interface, low, high int, reverse bool) {
19 7 if low >= high {
20 8 return
21 9 }
22 10  
23   - if dir != QSortAscending && dir != QSortDescending {
24   - return
25   - }
26   -
27   - index := partition(que, low, high, dir)
28   - Quicksort(que, low, index-1, dir)
29   - Quicksort(que, index+1, high, dir)
  11 + index := quickSortPartition(que, low, high, reverse)
  12 + QuickSort(que, low, index-1, reverse)
  13 + QuickSort(que, index+1, high, reverse)
30 14 }
31 15  
32   -func partition(que QuickSortable, low, high int, dir QSortDirection) int {
33   - swap := 0
34   - // -1 -> i > j
35   - // 1 -> i < j
36   - if dir == QSortDescending {
37   - swap = -1
38   - } else {
39   - swap = 1
40   - }
41   -
  16 +func quickSortPartition(que sort.Interface, low, high int, reverse bool) int {
42 17 i := low - 1
43 18 for j := low; j <= high-1; j++ {
44   - if que.Compare(j, high) == swap {
45   - i++
46   - que.Swap(i, j)
  19 + if !reverse {
  20 + if que.Less(j, high) {
  21 + i++
  22 + que.Swap(i, j)
  23 + }
  24 + } else {
  25 + if !que.Less(j, high) {
  26 + i++
  27 + que.Swap(i, j)
  28 + }
47 29 }
48   - continue
49 30 }
50 31 que.Swap(i+1, high)
51 32 return i + 1
... ...
... ... @@ -5,14 +5,14 @@ import (
5 5 "fmt"
6 6 "net/http"
7 7  
8   - "git.to-net.rs/marko.tikvic/gologger"
  8 + "git.to-net.rs/marko.tikvic/webutility/logger"
9 9 "github.com/gorilla/mux"
10 10 )
11 11  
12 12 type Server struct {
13 13 DB *sql.DB
14 14 Router *mux.Router
15   - Logger *gologger.Logger
  15 + Logger *logger.Logger
16 16 Port string
17 17 DBs map[string]*sql.DB
18 18 dsn map[string]string
... ... @@ -29,7 +29,7 @@ func NewODBCServer(dsn, port, logDir string) (s *Server, err error) {
29 29  
30 30 s.Router = mux.NewRouter()
31 31  
32   - if s.Logger, err = gologger.New("err", logDir, gologger.MaxLogSize1MB); err != nil {
  32 + if s.Logger, err = logger.New("err", logDir, logger.MaxLogSize1MB); err != nil {
33 33 return nil, fmt.Errorf("can't create logger: %s", err.Error())
34 34 }
35 35  
... ...