Commit 3faa19c

mo khan <mo@mokhan.ca>
2025-03-27 17:47:58
feat: start to add structured logging
1 parent b08580f
cmd/gtwy/main.go
@@ -1,12 +1,11 @@
 package main
 
 import (
-	"log"
-
 	"github.com/xlgmokha/x/pkg/env"
 	"gitlab.com/mokhax/spike/pkg/app"
+	xlog "gitlab.com/mokhax/spike/pkg/log"
 )
 
 func main() {
-	log.Fatal(app.Start(env.Fetch("BIND_ADDR", ":8080")))
+	xlog.Default.Fatal(app.Start(env.Fetch("BIND_ADDR", ":8080")))
 }
pkg/app/app.go
@@ -3,7 +3,6 @@ package app
 import (
 	"encoding/json"
 	"fmt"
-	"log"
 	"net"
 	"net/http"
 	"os"
@@ -14,6 +13,7 @@ import (
 	"github.com/xlgmokha/x/pkg/x"
 	"gitlab.com/mokhax/spike/pkg/authz"
 	"gitlab.com/mokhax/spike/pkg/cfg"
+	xlog "gitlab.com/mokhax/spike/pkg/log"
 	"gitlab.com/mokhax/spike/pkg/srv"
 )
 
@@ -23,6 +23,7 @@ func WithCasbin() authz.Authorizer {
 	return authz.AuthorizerFunc(func(r *http.Request) bool {
 		host, _, err := net.SplitHostPort(r.Host)
 		if err != nil {
+			xlog.WithFields(r, xlog.Fields{"error": err})
 			return false
 		}
 
@@ -32,11 +33,18 @@ func WithCasbin() authz.Authorizer {
 		}
 		ok, err := enforcer.Enforce(subject, host, r.Method, r.URL.Path)
 		if err != nil {
-			fmt.Printf("%v\n", err)
+			xlog.WithFields(r, xlog.Fields{"error": err})
 			return false
 		}
 
 		fmt.Printf("%v: %v -> %v %v%v\n", ok, subject, r.Method, host, r.URL.Path)
+		xlog.WithFields(r, xlog.Fields{
+			"ok":      ok,
+			"subject": subject,
+			"action":  r.Method,
+			"domain":  host,
+			"object":  r.URL.Path,
+		})
 		return ok
 	})
 }
@@ -50,8 +58,10 @@ func WithCedar() authz.Authorizer {
 
 	var entities cedar.EntityMap
 	if err := json.Unmarshal(x.Must(os.ReadFile("cedar.json")), &entities); err != nil {
-		log.Fatal(err)
+		xlog.Logger.Error("Error", "error", err)
+		return nil
 	}
+
 	return authz.AuthorizerFunc(func(r *http.Request) bool {
 		host, _, err := net.SplitHostPort(r.Host)
 		if err != nil {
@@ -79,9 +89,9 @@ func WithCedar() authz.Authorizer {
 }
 
 func Start(bindAddr string) error {
+	mux := authz.HTTP(WithCasbin(), Routes())
 	return srv.Run(cfg.New(
 		bindAddr,
-		// cfg.WithMux(authz.HTTP(WithCedar(), Routes())),
-		cfg.WithMux(authz.HTTP(WithCasbin(), Routes())),
+		cfg.WithMux(mux),
 	))
 }
pkg/authz/token.go
@@ -1,11 +1,11 @@
 package authz
 
 import (
-	"fmt"
 	"net/http"
 	"strings"
 
 	"github.com/lestrrat-go/jwx/v3/jwt"
+	xlog "gitlab.com/mokhax/spike/pkg/log"
 )
 
 func TokenFrom(r *http.Request) jwt.Token {
@@ -22,7 +22,7 @@ func TokenFrom(r *http.Request) jwt.Token {
 	)
 
 	if err != nil {
-		fmt.Printf("error: %v\n", err)
+		xlog.WithFields(r, xlog.Fields{"error": err})
 		return jwt.New()
 	}
 
pkg/cfg/mux.go
@@ -1,9 +1,17 @@
 package cfg
 
-import "net/http"
+import (
+	"net/http"
+
+	sloghttp "github.com/samber/slog-http"
+	xlog "gitlab.com/mokhax/spike/pkg/log"
+)
 
 func WithMux(mux http.Handler) Option {
 	return func(config *Config) {
-		config.Mux = mux
+		config.Mux = sloghttp.NewWithConfig(xlog.Logger, sloghttp.Config{
+			WithSpanID:  true,
+			WithTraceID: true,
+		})(sloghttp.Recovery(mux))
 	}
 }
pkg/log/init.go
@@ -0,0 +1,29 @@
+package log
+
+import (
+	"log"
+	"log/slog"
+	"net/http"
+	"os"
+
+	sloghttp "github.com/samber/slog-http"
+	"github.com/xlgmokha/x/pkg/env"
+)
+
+var Logger *slog.Logger
+var Default *log.Logger
+var Handler slog.Handler
+
+func init() {
+	Handler = slog.NewJSONHandler(os.Stdout, nil)
+	Logger = slog.New(Handler).With("env", env.Fetch("APP_ENV", "development"))
+	Default = slog.NewLogLogger(Handler, slog.LevelInfo)
+}
+
+type Fields map[string]interface{}
+
+func WithFields(r *http.Request, fields Fields) {
+	for key, value := range fields {
+		sloghttp.AddCustomAttributes(r, slog.Any(key, value))
+	}
+}
pkg/prxy/prxy.go
@@ -2,13 +2,13 @@ package prxy
 
 import (
 	"fmt"
-	"log"
 	"net"
 	"net/http"
 	"net/http/httputil"
 	"net/url"
 
 	"github.com/xlgmokha/x/pkg/x"
+	xlog "gitlab.com/mokhax/spike/pkg/log"
 )
 
 func New(routes map[string]string) http.Handler {
@@ -21,7 +21,7 @@ func New(routes map[string]string) http.Handler {
 		Rewrite: func(r *httputil.ProxyRequest) {
 			host, _, err := net.SplitHostPort(r.In.Host)
 			if err != nil {
-				log.Println(err)
+				xlog.WithFields(r.In, xlog.Fields{"error": err})
 				return
 			}
 
@@ -37,7 +37,7 @@ func New(routes map[string]string) http.Handler {
 			return nil
 		},
 		ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
-			log.Println(err)
+			xlog.WithFields(r, xlog.Fields{"error": err})
 		},
 	}
 }
pkg/srv/srv.go
@@ -1,11 +1,11 @@
 package srv
 
 import (
-	"log"
 	"net/http"
 	"time"
 
 	"gitlab.com/mokhax/spike/pkg/cfg"
+	xlog "gitlab.com/mokhax/spike/pkg/log"
 )
 
 func New(c *cfg.Config) *http.Server {
@@ -15,9 +15,9 @@ func New(c *cfg.Config) *http.Server {
 		TLSConfig:         c.TLS,
 		ReadHeaderTimeout: 10 * time.Second,
 		ReadTimeout:       30 * time.Second,
-		WriteTimeout:      2 * time.Minute,
-		IdleTimeout:       5 * time.Minute,
-		ErrorLog:          log.Default(),
+		WriteTimeout:      30 * time.Second,
+		IdleTimeout:       30 * time.Second,
+		ErrorLog:          xlog.Default,
 	}
 }
 
go.mod
@@ -8,6 +8,7 @@ require (
 	github.com/lestrrat-go/jwx/v3 v3.0.0-alpha3
 	github.com/magefile/mage v1.15.0
 	github.com/playwright-community/playwright-go v0.5001.0
+	github.com/samber/slog-http v1.6.0
 	github.com/stretchr/testify v1.10.0
 	github.com/xlgmokha/x v0.0.0-20240605230110-5cbcac4d8ff8
 	golang.org/x/oauth2 v0.28.0
@@ -24,6 +25,7 @@ require (
 	github.com/goccy/go-json v0.10.3 // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/jsonapi v1.0.0 // indirect
+	github.com/google/uuid v1.6.0 // indirect
 	github.com/kr/text v0.2.0 // indirect
 	github.com/lestrrat-go/blackmagic v1.0.2 // indirect
 	github.com/lestrrat-go/httpcc v1.0.1 // indirect
@@ -32,6 +34,8 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/segmentio/asm v1.2.0 // indirect
+	go.opentelemetry.io/otel v1.29.0 // indirect
+	go.opentelemetry.io/otel/trace v1.29.0 // indirect
 	golang.org/x/crypto v0.36.0 // indirect
 	golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
 	golang.org/x/sys v0.31.0 // indirect
go.sum
@@ -24,10 +24,13 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
 github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/jsonapi v1.0.0 h1:qIGgO5Smu3yJmSs+QlvhQnrscdZfFhiV6S8ryJAglqU=
 github.com/google/jsonapi v1.0.0/go.mod h1:YYHiRPJT8ARXGER8In9VuLv4qvLfDmA9ULQqptbLE4s=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -54,6 +57,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
 github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
+github.com/samber/slog-http v1.6.0 h1:+rD5QtOWGTcFT7jq8Yf0EgGy87krv0pcgh9jtWkrqjQ=
+github.com/samber/slog-http v1.6.0/go.mod h1:PAcQQrYFo5KM7Qbk50gNNwKEAMGCyfsw6GN5dI0iv9g=
 github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
 github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -65,6 +70,10 @@ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf
 github.com/xlgmokha/x v0.0.0-20240605230110-5cbcac4d8ff8 h1:Hmyf8pgNUs3l8TW0YdUarBVAU+hWX87efBukspg4nWc=
 github.com/xlgmokha/x v0.0.0-20240605230110-5cbcac4d8ff8/go.mod h1:C9MUZ3A7PTPbrLNTvu2lKhpM0dFpPHt5yH8YGuYzmKQ=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
+go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
+go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
+go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=