Commit e2880d3fb1868efdd79b800054a2a22ad0b43d45

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

integraded golloger

gologger/http_logs.go
File was created 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 }
67
File was created 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 }
163
gologger/tracing.go
File was created 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 }
66
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 "fmt" 5 "fmt"
6 "net/http" 6 "net/http"
7 ) 7 )
8 8
9 // StatusRecorder ... 9 // StatusRecorder ...
10 type StatusRecorder struct { 10 type StatusRecorder struct {
11 writer http.ResponseWriter 11 writer http.ResponseWriter
12 status int 12 status int
13 size int 13 size int
14 data []byte
14 } 15 }
15 16
16 // NewStatusRecorder ... 17 // NewStatusRecorder ...
17 func NewStatusRecorder(w http.ResponseWriter) *StatusRecorder { 18 func NewStatusRecorder(w http.ResponseWriter) *StatusRecorder {
18 return &StatusRecorder{ 19 return &StatusRecorder{
19 writer: w, 20 writer: w,
20 status: 0, 21 status: 0,
21 size: 0, 22 size: 0,
23 data: nil,
22 } 24 }
23 } 25 }
24 26
25 // WriteHeader is a wrapper http.ResponseWriter interface 27 // WriteHeader is a wrapper http.ResponseWriter interface
26 func (r *StatusRecorder) WriteHeader(code int) { 28 func (r *StatusRecorder) WriteHeader(code int) {
27 r.status = code 29 r.status = code
28 r.writer.WriteHeader(code) 30 r.writer.WriteHeader(code)
29 } 31 }
30 32
31 // Write is a wrapper for http.ResponseWriter interface 33 // Write is a wrapper for http.ResponseWriter interface
32 func (r *StatusRecorder) Write(in []byte) (int, error) { 34 func (r *StatusRecorder) Write(in []byte) (int, error) {
33 r.size = len(in) 35 r.size = len(in)
36 if r.status >= 400 {
37 r.data = make([]byte, len(in))
38 copy(r.data, in)
39 }
34 return r.writer.Write(in) 40 return r.writer.Write(in)
35 } 41 }
36 42
37 // Header is a wrapper for http.ResponseWriter interface 43 // Header is a wrapper for http.ResponseWriter interface
38 func (r *StatusRecorder) Header() http.Header { 44 func (r *StatusRecorder) Header() http.Header {
39 return r.writer.Header() 45 return r.writer.Header()
40 } 46 }
41 47
42 // Status ... 48 // Status ...
43 func (r *StatusRecorder) Status() int { 49 func (r *StatusRecorder) Status() int {
44 return r.status 50 return r.status
45 } 51 }
46 52
47 // Size ... 53 // Size ...
48 func (r *StatusRecorder) Size() int { 54 func (r *StatusRecorder) Size() int {
49 return r.size 55 return r.size
50 } 56 }
51 57
58 // Size ...
59 func (r *StatusRecorder) Data() []byte {
60 return r.data
61 }
62
52 // NotFoundHandlerFunc writes HTTP error 404 to w. 63 // NotFoundHandlerFunc writes HTTP error 404 to w.
53 func NotFoundHandlerFunc(w http.ResponseWriter, req *http.Request) { 64 func NotFoundHandlerFunc(w http.ResponseWriter, req *http.Request) {
54 SetAccessControlHeaders(w) 65 SetAccessControlHeaders(w)
55 SetContentType(w, "application/json") 66 SetContentType(w, "application/json")
56 NotFound(w, req, fmt.Sprintf("Resource you requested was not found: %s", req.URL.String())) 67 NotFound(w, req, fmt.Sprintf("Resource you requested was not found: %s", req.URL.String()))
57 } 68 }
58 69
59 // SetContentType must be called before SetResponseStatus (w.WriteHeader) (?) 70 // SetContentType must be called before SetResponseStatus (w.WriteHeader) (?)
60 func SetContentType(w http.ResponseWriter, ctype string) { 71 func SetContentType(w http.ResponseWriter, ctype string) {
61 w.Header().Set("Content-Type", ctype) 72 w.Header().Set("Content-Type", ctype)
62 } 73 }
63 74
64 // SetResponseStatus ... 75 // SetResponseStatus ...
65 func SetResponseStatus(w http.ResponseWriter, status int) { 76 func SetResponseStatus(w http.ResponseWriter, status int) {
66 w.WriteHeader(status) 77 w.WriteHeader(status)
67 } 78 }
68 79
69 // WriteResponse ... 80 // WriteResponse ...
70 func WriteResponse(w http.ResponseWriter, content []byte) { 81 func WriteResponse(w http.ResponseWriter, content []byte) {
71 w.Write(content) 82 w.Write(content)
72 } 83 }
73 84
74 // SetAccessControlHeaders set's default headers for an HTTP response. 85 // SetAccessControlHeaders set's default headers for an HTTP response.
75 func SetAccessControlHeaders(w http.ResponseWriter) { 86 func SetAccessControlHeaders(w http.ResponseWriter) {
76 w.Header().Set("Access-Control-Allow-Origin", "*") 87 w.Header().Set("Access-Control-Allow-Origin", "*")
77 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS") 88 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
78 w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") 89 w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
79 } 90 }
80 91
81 // GetLocale ... 92 // GetLocale ...
82 func GetLocale(req *http.Request, dflt string) string { 93 func GetLocale(req *http.Request, dflt string) string {
83 loc := req.FormValue("locale") 94 loc := req.FormValue("locale")
84 if loc == "" { 95 if loc == "" {
85 return dflt 96 return dflt
86 } 97 }
87 return loc 98 return loc
88 } 99 }
89 100
90 // Success ... 101 // Success ...
91 func Success(w http.ResponseWriter, payload interface{}, code int) { 102 func Success(w http.ResponseWriter, payload interface{}, code int) {
92 w.WriteHeader(code) 103 w.WriteHeader(code)
93 if payload != nil { 104 if payload != nil {
94 json.NewEncoder(w).Encode(payload) 105 json.NewEncoder(w).Encode(payload)
95 } 106 }
96 } 107 }
97 108
98 // OK ... 109 // OK ...
99 func OK(w http.ResponseWriter, payload interface{}) { 110 func OK(w http.ResponseWriter, payload interface{}) {
100 SetContentType(w, "application/json") 111 SetContentType(w, "application/json")
101 Success(w, payload, http.StatusOK) 112 Success(w, payload, http.StatusOK)
102 } 113 }
103 114
104 // Created ... 115 // Created ...
105 func Created(w http.ResponseWriter, payload interface{}) { 116 func Created(w http.ResponseWriter, payload interface{}) {
106 SetContentType(w, "application/json") 117 SetContentType(w, "application/json")
107 Success(w, payload, http.StatusCreated) 118 Success(w, payload, http.StatusCreated)
108 } 119 }
109 120
110 type weberror struct { 121 type weberror struct {
111 Request string `json:"request"` 122 Request string `json:"request"`
112 Error string `json:"error"` 123 Error string `json:"error"`
113 //Code int64 `json:"code"` TODO 124 //Code int64 `json:"code"` TODO
114 } 125 }
115 126
116 // Error ... 127 // Error ...
117 func Error(w http.ResponseWriter, r *http.Request, code int, err string) { 128 func Error(w http.ResponseWriter, r *http.Request, code int, err string) {
118 werr := weberror{Error: err, Request: r.Method + " " + r.RequestURI} 129 werr := weberror{Error: err, Request: r.Method + " " + r.RequestURI}
119 w.WriteHeader(code) 130 w.WriteHeader(code)
120 json.NewEncoder(w).Encode(werr) 131 json.NewEncoder(w).Encode(werr)
121 } 132 }
122 133
123 // BadRequest ... 134 // BadRequest ...
124 func BadRequest(w http.ResponseWriter, r *http.Request, err string) { 135 func BadRequest(w http.ResponseWriter, r *http.Request, err string) {
125 SetContentType(w, "application/json") 136 SetContentType(w, "application/json")
126 Error(w, r, http.StatusBadRequest, err) 137 Error(w, r, http.StatusBadRequest, err)
127 } 138 }
128 139
129 // Unauthorized ... 140 // Unauthorized ...
130 func Unauthorized(w http.ResponseWriter, r *http.Request, err string) { 141 func Unauthorized(w http.ResponseWriter, r *http.Request, err string) {
131 SetContentType(w, "application/json") 142 SetContentType(w, "application/json")
132 Error(w, r, http.StatusUnauthorized, err) 143 Error(w, r, http.StatusUnauthorized, err)
133 } 144 }
134 145
135 // Forbidden ... 146 // Forbidden ...
136 func Forbidden(w http.ResponseWriter, r *http.Request, err string) { 147 func Forbidden(w http.ResponseWriter, r *http.Request, err string) {
137 SetContentType(w, "application/json") 148 SetContentType(w, "application/json")
138 Error(w, r, http.StatusForbidden, err) 149 Error(w, r, http.StatusForbidden, err)
139 } 150 }
140 151
141 // NotFound ... 152 // NotFound ...
142 func NotFound(w http.ResponseWriter, r *http.Request, err string) { 153 func NotFound(w http.ResponseWriter, r *http.Request, err string) {
143 SetContentType(w, "application/json") 154 SetContentType(w, "application/json")
144 Error(w, r, http.StatusNotFound, err) 155 Error(w, r, http.StatusNotFound, err)
145 } 156 }
146 157
147 // Conflict ... 158 // Conflict ...
148 func Conflict(w http.ResponseWriter, r *http.Request, err string) { 159 func Conflict(w http.ResponseWriter, r *http.Request, err string) {
149 SetContentType(w, "application/json") 160 SetContentType(w, "application/json")
150 Error(w, r, http.StatusConflict, err) 161 Error(w, r, http.StatusConflict, err)
151 } 162 }
152 163
153 // InternalServerError ... 164 // InternalServerError ...
154 func InternalServerError(w http.ResponseWriter, r *http.Request, err string) { 165 func InternalServerError(w http.ResponseWriter, r *http.Request, err string) {
155 SetContentType(w, "application/json") 166 SetContentType(w, "application/json")
156 Error(w, r, http.StatusInternalServerError, err) 167 Error(w, r, http.StatusInternalServerError, err)
157 } 168 }
158 169
159 func SetHeader(r *http.Request, key, val string) { 170 func SetHeader(r *http.Request, key, val string) {
160 r.Header.Set(key, val) 171 r.Header.Set(key, val)
161 } 172 }
162 173
163 func AddHeader(r *http.Request, key, val string) { 174 func AddHeader(r *http.Request, key, val string) {
164 r.Header.Add(key, val) 175 r.Header.Add(key, val)
165 } 176 }
166 177
167 func GetHeader(r *http.Request, key string) string { 178 func GetHeader(r *http.Request, key string) string {
168 return r.Header.Get(key) 179 return r.Header.Get(key)
169 } 180 }
170 181
171 func ClientUTCOffset(req *http.Request) int64 { 182 func ClientUTCOffset(req *http.Request) int64 {
172 return StringToInt64(GetHeader(req, "X-Timezone-Offset")) 183 return StringToInt64(GetHeader(req, "X-Timezone-Offset"))
173 } 184 }
174 185
middleware/middleware.go
1 package middleware 1 package middleware
2 2
3 import ( 3 import (
4 "net/http" 4 "net/http"
5 "time" 5 "time"
6 6
7 "git.to-net.rs/marko.tikvic/gologger" 7 "git.to-net.rs/marko.tikvic/gologger"
8 8
9 web "git.to-net.rs/marko.tikvic/webutility" 9 web "git.to-net.rs/marko.tikvic/webutility"
10 ) 10 )
11 11
12 var httpLogger *gologger.Logger 12 var httpLogger *gologger.Logger
13 13
14 func SetAccessControlHeaders(h http.HandlerFunc) http.HandlerFunc { 14 func SetAccessControlHeaders(h http.HandlerFunc) http.HandlerFunc {
15 return func(w http.ResponseWriter, req *http.Request) { 15 return func(w http.ResponseWriter, req *http.Request) {
16 web.SetAccessControlHeaders(w) 16 web.SetAccessControlHeaders(w)
17 17
18 h(w, req) 18 h(w, req)
19 } 19 }
20 } 20 }
21 21
22 // IgnoreOptionsRequests ... 22 // IgnoreOptionsRequests ...
23 func IgnoreOptionsRequests(h http.HandlerFunc) http.HandlerFunc { 23 func IgnoreOptionsRequests(h http.HandlerFunc) http.HandlerFunc {
24 return func(w http.ResponseWriter, req *http.Request) { 24 return func(w http.ResponseWriter, req *http.Request) {
25 if req.Method == http.MethodOptions { 25 if req.Method == http.MethodOptions {
26 return 26 return
27 } 27 }
28 28
29 h(w, req) 29 h(w, req)
30 } 30 }
31 } 31 }
32 32
33 // ParseForm ... 33 // ParseForm ...
34 func ParseForm(h http.HandlerFunc) http.HandlerFunc { 34 func ParseForm(h http.HandlerFunc) http.HandlerFunc {
35 return func(w http.ResponseWriter, req *http.Request) { 35 return func(w http.ResponseWriter, req *http.Request) {
36 err := req.ParseForm() 36 err := req.ParseForm()
37 if err != nil { 37 if err != nil {
38 web.BadRequest(w, req, err.Error()) 38 web.BadRequest(w, req, err.Error())
39 return 39 return
40 } 40 }
41 41
42 h(w, req) 42 h(w, req)
43 } 43 }
44 } 44 }
45 45
46 // ParseMultipartForm ... 46 // ParseMultipartForm ...
47 func ParseMultipartForm(h http.HandlerFunc) http.HandlerFunc { 47 func ParseMultipartForm(h http.HandlerFunc) http.HandlerFunc {
48 return func(w http.ResponseWriter, req *http.Request) { 48 return func(w http.ResponseWriter, req *http.Request) {
49 err := req.ParseMultipartForm(32 << 20) 49 err := req.ParseMultipartForm(32 << 20)
50 if err != nil { 50 if err != nil {
51 web.BadRequest(w, req, err.Error()) 51 web.BadRequest(w, req, err.Error())
52 return 52 return
53 } 53 }
54 54
55 h(w, req) 55 h(w, req)
56 } 56 }
57 } 57 }
58 58
59 // SetLogger ... 59 // SetLogger ...
60 func SetLogger(logger *gologger.Logger) { 60 func SetLogger(logger *gologger.Logger) {
61 httpLogger = logger 61 httpLogger = logger
62 } 62 }
63 63
64 func StartLogging(filename, dir string) (err error) { 64 func StartLogging(filename, dir string) (err error) {
65 if httpLogger, err = gologger.New(filename, dir, gologger.MaxLogSize1MB); err != nil { 65 if httpLogger, err = gologger.New(filename, dir, gologger.MaxLogSize1MB); err != nil {
66 return err 66 return err
67 } 67 }
68 return nil 68 return nil
69 } 69 }
70 70
71 func CloseLogger() { 71 func CloseLogger() {
72 httpLogger.Close() 72 httpLogger.Close()
73 } 73 }
74 74
75 // LogHTTP ... 75 // LogHTTP ...
76 func LogHTTP(h http.HandlerFunc) http.HandlerFunc { 76 func LogHTTP(hfunc http.HandlerFunc) http.HandlerFunc {
77 return func(w http.ResponseWriter, req *http.Request) { 77 return func(w http.ResponseWriter, req *http.Request) {
78 if httpLogger == nil { 78 if httpLogger == nil {
79 h(w, req) 79 hfunc(w, req)
80 return 80 return
81 } 81 }
82 82
83 t1 := time.Now() 83 t1 := time.Now()
84 84
85 claims, _ := web.GetTokenClaims(req) 85 claims, _ := web.GetTokenClaims(req)
86 in := httpLogger.LogHTTPRequest(req, claims.Username) 86 in := httpLogger.LogHTTPRequest(req, claims.Username)
87 87
88 rec := web.NewStatusRecorder(w) 88 rec := web.NewStatusRecorder(w)
89 89
90 h(rec, req) 90 hfunc(rec, req)
91 91
92 t2 := time.Now() 92 out := httpLogger.LogHTTPResponse(rec.Data(), rec.Status(), t1)
93 out := httpLogger.LogHTTPResponse(rec.Status(), t2.Sub(t1), rec.Size())
94 93
95 httpLogger.CombineHTTPLogs(in, out) 94 httpLogger.CombineHTTPLogs(in, out)
96 } 95 }
97 } 96 }
98 97
99 // Auth ... 98 // Auth ...
100 func Auth(roles string, h http.HandlerFunc) http.HandlerFunc { 99 func Auth(roles string, h http.HandlerFunc) http.HandlerFunc {
101 return func(w http.ResponseWriter, req *http.Request) { 100 return func(w http.ResponseWriter, req *http.Request) {
102 if _, err := web.AuthCheck(req, roles); err != nil { 101 if _, err := web.AuthCheck(req, roles); err != nil {
103 web.Unauthorized(w, req, err.Error()) 102 web.Unauthorized(w, req, err.Error())
104 return 103 return
105 } 104 }
106 105
107 h(w, req) 106 h(w, req)
108 } 107 }
109 } 108 }
110 109