mirror of https://github.com/chubin/wttr.in
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
2.3 KiB
125 lines
2.3 KiB
package logging
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/chubin/wttr.in/internal/util"
|
|
)
|
|
|
|
// Logging request.
|
|
//
|
|
|
|
// RequestLogger logs all incoming HTTP requests.
|
|
type RequestLogger struct {
|
|
buf map[logEntry]int
|
|
filename string
|
|
m sync.Mutex
|
|
|
|
period time.Duration
|
|
lastFlush time.Time
|
|
}
|
|
|
|
type logEntry struct {
|
|
Proto string
|
|
IP string
|
|
URI string
|
|
UserAgent string
|
|
}
|
|
|
|
// NewRequestLogger returns a new RequestLogger for the specified log file.
|
|
// Flush logging entries after period of time.
|
|
//
|
|
// If filename is empty, no log will be written, and all logging entries
|
|
// will be silently dropped.
|
|
func NewRequestLogger(filename string, period time.Duration) *RequestLogger {
|
|
return &RequestLogger{
|
|
buf: map[logEntry]int{},
|
|
filename: filename,
|
|
m: sync.Mutex{},
|
|
period: period,
|
|
}
|
|
}
|
|
|
|
// Log logs information about a HTTP request.
|
|
func (rl *RequestLogger) Log(r *http.Request) error {
|
|
le := logEntry{
|
|
Proto: "http",
|
|
IP: util.ReadUserIP(r),
|
|
URI: r.RequestURI,
|
|
UserAgent: r.Header.Get("User-Agent"),
|
|
}
|
|
if r.TLS != nil {
|
|
le.Proto = "https"
|
|
}
|
|
|
|
// Do not log 127.0.0.1 connections
|
|
if le.IP == "127.0.0.1" {
|
|
return nil
|
|
}
|
|
|
|
rl.m.Lock()
|
|
rl.buf[le]++
|
|
rl.m.Unlock()
|
|
|
|
if time.Since(rl.lastFlush) > rl.period {
|
|
return rl.flush()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// flush stores log data to disk, and flushes the buffer.
|
|
func (rl *RequestLogger) flush() error {
|
|
rl.m.Lock()
|
|
defer rl.m.Unlock()
|
|
|
|
// It is possible, that while waiting the mutex,
|
|
// the buffer was already flushed.
|
|
if time.Since(rl.lastFlush) <= rl.period {
|
|
return nil
|
|
}
|
|
|
|
if rl.filename != "" {
|
|
// Generate log output.
|
|
output := ""
|
|
for k, hitsNumber := range rl.buf {
|
|
output += fmt.Sprintf("%s %3d %s\n", time.Now().Format(time.RFC3339), hitsNumber, k.String())
|
|
}
|
|
|
|
// Open log file.
|
|
//nolint:nosnakecase
|
|
f, err := os.OpenFile(rl.filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
// Save output to log file.
|
|
_, err = f.Write([]byte(output))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Flush buffer.
|
|
rl.buf = map[logEntry]int{}
|
|
rl.lastFlush = time.Now()
|
|
|
|
return nil
|
|
}
|
|
|
|
// String returns string representation of logEntry.
|
|
func (e *logEntry) String() string {
|
|
return fmt.Sprintf(
|
|
"%s %s %s %s",
|
|
e.Proto,
|
|
e.IP,
|
|
e.URI,
|
|
e.UserAgent,
|
|
)
|
|
}
|