···88 "time"991010 "github.com/gorilla/websocket"1111+ "tangled.org/core/log"1112)12131314var upgrader = websocket.Upgrader{···1716}18171918func (h *Knot) Events(w http.ResponseWriter, r *http.Request) {2020- l := h.l.With("handler", "OpLog")1919+ l := log.SubLogger(h.l, "eventstream")2120 l.Debug("received new connection")22212322 conn, err := upgrader.Upgrade(w, r, nil)···7675 }7776 case <-time.After(30 * time.Second):7877 // send a keep-alive7979- l.Debug("sent keepalive")8078 if err = conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second)); err != nil {8179 l.Error("failed to write control", "err", err)8280 }···8989 h.l.Error("failed to fetch events from db", "err", err, "cursor", cursor)9090 return err9191 }9292- h.l.Debug("ops", "ops", events)93929493 for _, event := range events {9594 // first extract the inner json into a map
+18-18
knotserver/git.go
···1313 "tangled.org/core/knotserver/git/service"1414)15151616-func (d *Knot) InfoRefs(w http.ResponseWriter, r *http.Request) {1616+func (h *Knot) InfoRefs(w http.ResponseWriter, r *http.Request) {1717 did := chi.URLParam(r, "did")1818 name := chi.URLParam(r, "name")1919 repoName, err := securejoin.SecureJoin(did, name)2020 if err != nil {2121 gitError(w, "repository not found", http.StatusNotFound)2222- d.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err)2222+ h.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err)2323 return2424 }25252626- repoPath, err := securejoin.SecureJoin(d.c.Repo.ScanPath, repoName)2626+ repoPath, err := securejoin.SecureJoin(h.c.Repo.ScanPath, repoName)2727 if err != nil {2828 gitError(w, "repository not found", http.StatusNotFound)2929- d.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err)2929+ h.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err)3030 return3131 }3232···46464747 if err := cmd.InfoRefs(); err != nil {4848 gitError(w, err.Error(), http.StatusInternalServerError)4949- d.l.Error("git: process failed", "handler", "InfoRefs", "service", serviceName, "error", err)4949+ h.l.Error("git: process failed", "handler", "InfoRefs", "service", serviceName, "error", err)5050 return5151 }5252 case "git-receive-pack":5353- d.RejectPush(w, r, name)5353+ h.RejectPush(w, r, name)5454 default:5555 gitError(w, fmt.Sprintf("service unsupported: '%s'", serviceName), http.StatusForbidden)5656 }5757}58585959-func (d *Knot) UploadPack(w http.ResponseWriter, r *http.Request) {5959+func (h *Knot) UploadPack(w http.ResponseWriter, r *http.Request) {6060 did := chi.URLParam(r, "did")6161 name := chi.URLParam(r, "name")6262- repo, err := securejoin.SecureJoin(d.c.Repo.ScanPath, filepath.Join(did, name))6262+ repo, err := securejoin.SecureJoin(h.c.Repo.ScanPath, filepath.Join(did, name))6363 if err != nil {6464 gitError(w, err.Error(), http.StatusInternalServerError)6565- d.l.Error("git: failed to secure join repo path", "handler", "UploadPack", "error", err)6565+ h.l.Error("git: failed to secure join repo path", "handler", "UploadPack", "error", err)6666 return6767 }6868···7777 gzipReader, err := gzip.NewReader(r.Body)7878 if err != nil {7979 gitError(w, err.Error(), http.StatusInternalServerError)8080- d.l.Error("git: failed to create gzip reader", "handler", "UploadPack", "error", err)8080+ h.l.Error("git: failed to create gzip reader", "handler", "UploadPack", "error", err)8181 return8282 }8383 defer gzipReader.Close()···8888 w.Header().Set("Connection", "Keep-Alive")8989 w.Header().Set("Cache-Control", "no-cache, max-age=0, must-revalidate")90909191- d.l.Info("git: executing git-upload-pack", "handler", "UploadPack", "repo", repo)9191+ h.l.Info("git: executing git-upload-pack", "handler", "UploadPack", "repo", repo)92929393 cmd := service.ServiceCommand{9494 GitProtocol: r.Header.Get("Git-Protocol"),···100100 w.WriteHeader(http.StatusOK)101101102102 if err := cmd.UploadPack(); err != nil {103103- d.l.Error("git: failed to execute git-upload-pack", "handler", "UploadPack", "error", err)103103+ h.l.Error("git: failed to execute git-upload-pack", "handler", "UploadPack", "error", err)104104 return105105 }106106}107107108108-func (d *Knot) ReceivePack(w http.ResponseWriter, r *http.Request) {108108+func (h *Knot) ReceivePack(w http.ResponseWriter, r *http.Request) {109109 did := chi.URLParam(r, "did")110110 name := chi.URLParam(r, "name")111111- _, err := securejoin.SecureJoin(d.c.Repo.ScanPath, filepath.Join(did, name))111111+ _, err := securejoin.SecureJoin(h.c.Repo.ScanPath, filepath.Join(did, name))112112 if err != nil {113113 gitError(w, err.Error(), http.StatusForbidden)114114- d.l.Error("git: failed to secure join repo path", "handler", "ReceivePack", "error", err)114114+ h.l.Error("git: failed to secure join repo path", "handler", "ReceivePack", "error", err)115115 return116116 }117117118118- d.RejectPush(w, r, name)118118+ h.RejectPush(w, r, name)119119}120120121121-func (d *Knot) RejectPush(w http.ResponseWriter, r *http.Request, unqualifiedRepoName string) {121121+func (h *Knot) RejectPush(w http.ResponseWriter, r *http.Request, unqualifiedRepoName string) {122122 // A text/plain response will cause git to print each line of the body123123 // prefixed with "remote: ".124124 w.Header().Set("content-type", "text/plain; charset=UTF-8")···131131 ownerHandle := r.Header.Get("x-tangled-repo-owner-handle")132132 ownerHandle = strings.TrimPrefix(ownerHandle, "@")133133 if ownerHandle != "" && !strings.ContainsAny(ownerHandle, ":") {134134- hostname := d.c.Server.Hostname134134+ hostname := h.c.Server.Hostname135135 if strings.Contains(hostname, ":") {136136 hostname = strings.Split(hostname, ":")[0]137137 }
+4-1
knotserver/internal.go
···2020 "tangled.org/core/knotserver/config"2121 "tangled.org/core/knotserver/db"2222 "tangled.org/core/knotserver/git"2323+ "tangled.org/core/log"2324 "tangled.org/core/notifier"2425 "tangled.org/core/rbac"2526 "tangled.org/core/workflow"···315314 return h.db.InsertEvent(event, h.n)316315}317316318318-func Internal(ctx context.Context, c *config.Config, db *db.DB, e *rbac.Enforcer, l *slog.Logger, n *notifier.Notifier) http.Handler {317317+func Internal(ctx context.Context, c *config.Config, db *db.DB, e *rbac.Enforcer, n *notifier.Notifier) http.Handler {319318 r := chi.NewRouter()319319+ l := log.FromContext(ctx)320320+ l = log.SubLogger(l, "internal")320321321322 h := InternalHandle{322323 db,
···44 "context"55 "log/slog"66 "os"77+88+ "github.com/charmbracelet/log"79)81099-// NewHandler sets up a new slog.Handler with the service name1010-// as an attribute1111func NewHandler(name string) slog.Handler {1212- handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{1313- Level: slog.LevelDebug,1212+ return log.NewWithOptions(os.Stderr, log.Options{1313+ ReportTimestamp: true,1414+ Prefix: name,1515+ Level: log.DebugLevel,1416 })1515-1616- var attrs []slog.Attr1717- attrs = append(attrs, slog.Attr{Key: "service", Value: slog.StringValue(name)})1818- handler.WithAttrs(attrs)1919- return handler2017}21182219func New(name string) *slog.Logger {···4548 }46494750 return slog.Default()5151+}5252+5353+// sublogger derives a new logger from an existing one by appending a suffix to its prefix.5454+func SubLogger(base *slog.Logger, suffix string) *slog.Logger {5555+ // try to get the underlying charmbracelet logger5656+ if cl, ok := base.Handler().(*log.Logger); ok {5757+ prefix := cl.GetPrefix()5858+ if prefix != "" {5959+ prefix = prefix + "/" + suffix6060+ } else {6161+ prefix = suffix6262+ }6363+ return slog.New(NewHandler(prefix))6464+ }6565+6666+ // Fallback: no known handler type6767+ return slog.New(NewHandler(suffix))4868}