Commit 28a6cc60c98e9c7f1e88ab4b376fb993c39b1d82

Authored by Marko Tikvić
1 parent e2880d3fb1
Exists in master

Integrated logger and improved QuickSort

gologger/http_logs.go
... ... @@ -1,66 +0,0 @@
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   -}
gologger/main.go
... ... @@ -1,162 +0,0 @@
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
... ... @@ -1,65 +0,0 @@
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   -}
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 +}
... ...
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  
... ...