logging library up to top level
continuous-integration/drone/push Build is passing Details

This commit is contained in:
tjpcc 2023-01-23 22:15:16 -07:00
parent df57a12539
commit 0480e066a3
11 changed files with 186 additions and 49 deletions

View File

@ -110,7 +110,12 @@ func RunCGI(
}
basename := pathSegments[len(pathSegments)-1]
scriptName := req.Path[:len(req.Path)-len(pathInfo)]
infoLen := len(pathInfo)
if pathInfo == "/" {
infoLen -= 1
}
scriptName := req.Path[:len(req.Path)-infoLen]
if strings.HasSuffix(scriptName, "/") {
scriptName = scriptName[:len(scriptName)-1]
}

View File

@ -1,35 +0,0 @@
package log
import (
"context"
"io"
"time"
kitlog "github.com/go-kit/log"
"tildegit.org/tjp/gus"
)
func Requests(out io.Writer, logger kitlog.Logger) gus.Middleware {
if logger == nil {
logger = kitlog.NewLogfmtLogger(kitlog.NewSyncWriter(out))
}
return func(next gus.Handler) gus.Handler {
return func(ctx context.Context, r *gus.Request) (resp *gus.Response) {
start := time.Now()
defer func() {
end := time.Now()
logger.Log(
"msg", "request",
"ts", end,
"dur", end.Sub(start),
"url", r.URL,
"status", resp.Status,
)
}()
return next(ctx, r)
}
}
}

View File

@ -137,6 +137,7 @@ func setup(
server, err := gemini.NewServer(
context.Background(),
nil,
serverTLS,
"tcp",
"127.0.0.1:0",

View File

@ -8,8 +8,8 @@ import (
"syscall"
"tildegit.org/tjp/gus/contrib/cgi"
guslog "tildegit.org/tjp/gus/contrib/log"
"tildegit.org/tjp/gus/gemini"
"tildegit.org/tjp/gus/logging"
)
func main() {
@ -25,15 +25,17 @@ func main() {
// make use of a CGI request handler
cgiHandler := cgi.CGIDirectory("/cgi-bin", "./cgi-bin")
_, infoLog, _, errLog := logging.DefaultLoggers()
// add stdout logging to the request handler
handler := guslog.Requests(os.Stdout, nil)(cgiHandler)
handler := logging.LogRequests(infoLog)(cgiHandler)
// set up signals to trigger graceful shutdown
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGHUP)
defer stop()
// run the server
server, err := gemini.NewServer(ctx, tlsconf, "tcp4", ":1965", handler)
server, err := gemini.NewServer(ctx, errLog, tlsconf, "tcp4", ":1965", handler)
if err != nil {
log.Fatal(err)
}

View File

@ -9,8 +9,8 @@ import (
"os/exec"
"tildegit.org/tjp/gus"
guslog "tildegit.org/tjp/gus/contrib/log"
"tildegit.org/tjp/gus/gemini"
"tildegit.org/tjp/gus/logging"
)
func main() {
@ -23,11 +23,13 @@ func main() {
log.Fatal(err)
}
_, infoLog, _, errLog := logging.DefaultLoggers()
// add request logging to the request handler
handler := guslog.Requests(os.Stdout, nil)(cowsayHandler)
handler := logging.LogRequests(infoLog)(cowsayHandler)
// run the server
server, err := gemini.NewServer(context.Background(), tlsconf, "tcp4", ":1965", handler)
server, err := gemini.NewServer(context.Background(), errLog, tlsconf, "tcp4", ":1965", handler)
if err != nil {
log.Fatal(err)
}

View File

@ -7,8 +7,8 @@ import (
"tildegit.org/tjp/gus"
"tildegit.org/tjp/gus/contrib/fs"
guslog "tildegit.org/tjp/gus/contrib/log"
"tildegit.org/tjp/gus/gemini"
"tildegit.org/tjp/gus/logging"
)
func main() {
@ -32,11 +32,14 @@ func main() {
// finally, try to find a file at the request path and respond with that
fs.FileHandler(fileSystem),
)
_, infoLog, _, errLog := logging.DefaultLoggers()
// add request logging to stdout
handler = guslog.Requests(os.Stdout, nil)(handler)
handler = logging.LogRequests(infoLog)(handler)
// run the server
server, err := gemini.NewServer(context.Background(), tlsconf, "tcp4", ":1965", handler)
server, err := gemini.NewServer(context.Background(), errLog, tlsconf, "tcp4", ":1965", handler)
if err != nil {
log.Fatal(err)
}

View File

@ -13,8 +13,8 @@ import (
"strings"
"tildegit.org/tjp/gus"
guslog "tildegit.org/tjp/gus/contrib/log"
"tildegit.org/tjp/gus/gemini"
"tildegit.org/tjp/gus/logging"
)
func main() {
@ -27,11 +27,13 @@ func main() {
log.Fatal(err)
}
_, infoLog, _, errLog := logging.DefaultLoggers()
// add stdout logging to the request handler
handler := guslog.Requests(os.Stdout, nil)(inspectHandler)
handler := logging.LogRequests(infoLog)(inspectHandler)
// run the server
server, err := gemini.NewServer(context.Background(), tlsconf, "tcp4", ":1965", handler)
server, err := gemini.NewServer(context.Background(), errLog, tlsconf, "tcp4", ":1965", handler)
if err != nil {
log.Fatal(err)
}

View File

@ -23,7 +23,7 @@ func TestRoundTrip(t *testing.T) {
return gemini.Success("text/gemini", bytes.NewBufferString("you've found my page"))
}
server, err := gemini.NewServer(context.Background(), tlsConf, "tcp", "127.0.0.1:0", handler)
server, err := gemini.NewServer(context.Background(), nil, tlsConf, "tcp", "127.0.0.1:0", handler)
if err != nil {
t.Fatalf("NewServer(): %s", err.Error())
}

View File

@ -8,10 +8,12 @@ import (
"sync"
"tildegit.org/tjp/gus"
"tildegit.org/tjp/gus/logging"
)
type server struct {
ctx context.Context
errorLog logging.Logger
network string
address string
cancel context.CancelFunc
@ -23,6 +25,7 @@ type server struct {
// NewServer builds a gemini server.
func NewServer(
ctx context.Context,
errorLog logging.Logger,
tlsConfig *tls.Config,
network string,
address string,
@ -37,6 +40,7 @@ func NewServer(
s := &server{
ctx: ctx,
errorLog: errorLog,
network: addr.Network(),
address: addr.String(),
wg: &sync.WaitGroup{},
@ -69,7 +73,10 @@ func (s *server) Serve() error {
if err != nil {
if s.Closed() {
err = nil
} else {
s.errorLog.Log("msg", "accept_error", "error", err)
}
return err
}

43
logging/logger.go Normal file
View File

@ -0,0 +1,43 @@
package logging
import (
"os"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/go-kit/log/term"
)
// Logger records log lines to an output.
type Logger interface {
Log(keyvals ...any) error
}
// DefaultLoggers produces helpful base loggers for each level.
//
// They write logfmt to standard out, annotated with ANSI colors depending on the level.
func DefaultLoggers() (debug, info, warn, error Logger) {
base := term.NewLogger(os.Stdout, log.NewLogfmtLogger, func(keyvals ...any) term.FgBgColor {
for i := 0; i < len(keyvals)-1; i += 2 {
if keyvals[i] != "level" {
continue
}
switch keyvals[i+1] {
case level.DebugValue():
return term.FgBgColor{Fg: term.DarkGray}
case level.InfoValue():
return term.FgBgColor{Fg: term.Green}
case level.WarnValue():
return term.FgBgColor{Fg: term.Yellow}
case level.ErrorValue():
return term.FgBgColor{Fg: term.Red}
}
}
return term.FgBgColor{}
})
base = log.NewSyncLogger(base)
return level.Debug(base), level.Info(base), level.Warn(base), level.Error(base)
}

107
logging/middleware.go Normal file
View File

@ -0,0 +1,107 @@
package logging
import (
"context"
"errors"
"io"
"time"
"tildegit.org/tjp/gus"
)
func LogRequests(logger Logger) gus.Middleware {
return func(inner gus.Handler) gus.Handler {
return func(ctx context.Context, request *gus.Request) *gus.Response {
start := time.Now()
response := inner(ctx, request)
if response != nil {
body := loggedResponseBody{
request: request,
response: response,
body: response.Body,
start: start,
logger: logger,
}
response.Body = &body
}
return response
}
}
}
type loggedResponseBody struct {
request *gus.Request
response *gus.Response
body io.Reader
start time.Time
written int
logger Logger
}
func loggingBody(logger Logger, request *gus.Request, response *gus.Response) io.Reader {
body := &loggedResponseBody{
request: request,
response: response,
body: response.Body,
start: time.Now(),
written: 0,
logger: logger,
}
if _, ok := response.Body.(io.WriterTo); ok {
return loggedWriteToResponseBody{body}
}
return body
}
func (lr *loggedResponseBody) log() {
end := time.Now()
_ = lr.logger.Log(
"msg", "request",
"ts", end,
"dur", end.Sub(lr.start),
"url", lr.request.URL,
"status", lr.response.Status,
"bodylen", lr.written,
)
}
func (lr *loggedResponseBody) Read(b []byte) (int, error) {
if lr.body == nil {
return 0, io.EOF
}
wr, err := lr.body.Read(b)
lr.written += wr
if errors.Is(err, io.EOF) {
lr.log()
}
return wr, err
}
func (lr *loggedResponseBody) Close() error {
if cl, ok := lr.body.(io.Closer); ok {
return cl.Close()
}
return nil
}
type loggedWriteToResponseBody struct {
*loggedResponseBody
}
func (lwtr loggedWriteToResponseBody) WriteTo(dst io.Writer) (int64, error) {
n, err := lwtr.body.(io.WriterTo).WriteTo(dst)
if err == nil {
lwtr.written += int(n)
lwtr.log()
}
return n, err
}