Log slow API requests with server spans and client timing

This commit is contained in:
Ian Gulliver
2026-10-21 14:45:19 -07:00
parent 671165ec71
commit b4c1dbab37
31 changed files with 458 additions and 185 deletions
+70 -68
View File
@@ -14,6 +14,7 @@ import (
"time"
"g.fc.run/ai/byre/internal/procgroup"
"g.fc.run/ai/byre/internal/trace"
)
type RepoInfo struct {
@@ -22,13 +23,13 @@ type RepoInfo struct {
Remote string
}
func Inspect(path string) (RepoInfo, error) {
func Inspect(ctx context.Context, path string) (RepoInfo, error) {
abs, err := filepath.Abs(path)
if err != nil {
return RepoInfo{}, err
}
root, err := run(abs, "rev-parse", "--show-toplevel")
root, err := run(ctx, abs, "rev-parse", "--show-toplevel")
if err != nil {
return RepoInfo{}, err
}
@@ -47,12 +48,12 @@ func Inspect(path string) (RepoInfo, error) {
return RepoInfo{}, fmt.Errorf("%s is inside the repo at %s; register the repo root", abs, root)
}
branch, err := run(root, "symbolic-ref", "--short", "HEAD")
branch, err := run(ctx, root, "symbolic-ref", "--short", "HEAD")
if err != nil {
return RepoInfo{}, err
}
remote, err := run(root, "remote", "get-url", "origin")
remote, err := run(ctx, root, "remote", "get-url", "origin")
if err != nil {
return RepoInfo{}, err
}
@@ -60,13 +61,13 @@ func Inspect(path string) (RepoInfo, error) {
return RepoInfo{Root: root, DefaultBranch: branch, Remote: remote}, nil
}
func AddWorktree(repo, path, branch, base string) (string, error) {
sha, err := run(repo, "rev-parse", "--verify", base+"^{commit}")
func AddWorktree(ctx context.Context, repo, path, branch, base string) (string, error) {
sha, err := run(ctx, repo, "rev-parse", "--verify", base+"^{commit}")
if err != nil {
return "", err
}
_, err = run(repo, "worktree", "add", "-b", branch, path, sha)
_, err = run(ctx, repo, "worktree", "add", "-b", branch, path, sha)
if err != nil {
return "", err
}
@@ -74,38 +75,38 @@ func AddWorktree(repo, path, branch, base string) (string, error) {
return sha, nil
}
func AddDetachedWorktree(repo, path, commit string) error {
_, err := run(repo, "worktree", "add", "--detach", path, commit)
func AddDetachedWorktree(ctx context.Context, repo, path, commit string) error {
_, err := run(ctx, repo, "worktree", "add", "--detach", path, commit)
return err
}
func RemoveDetachedWorktree(repo, path string) error {
func RemoveDetachedWorktree(ctx context.Context, repo, path string) error {
_, err := os.Stat(path)
if errors.Is(err, os.ErrNotExist) {
return nil
}
_, err = run(repo, "worktree", "remove", "--force", path)
_, err = run(ctx, repo, "worktree", "remove", "--force", path)
return err
}
func RemoveWorktree(repo, path, branch string) error {
list, err := run(repo, "worktree", "list", "--porcelain")
func RemoveWorktree(ctx context.Context, repo, path, branch string) error {
list, err := run(ctx, repo, "worktree", "list", "--porcelain")
if err != nil {
return err
}
_, statErr := os.Stat(path)
if statErr == nil || slices.Contains(strings.Split(list, "\n"), "worktree "+path) {
_, err = run(repo, "worktree", "remove", "--force", path)
_, err = run(ctx, repo, "worktree", "remove", "--force", path)
if err != nil {
return err
}
}
_, _, code, err := runCode(repo, nil, "rev-parse", "--verify", "--quiet", "refs/heads/"+branch)
_, _, code, err := runCode(ctx, repo, nil, "rev-parse", "--verify", "--quiet", "refs/heads/"+branch)
if err != nil || code != 0 {
return err
}
_, err = run(repo, "branch", "-D", branch)
_, err = run(ctx, repo, "branch", "-D", branch)
return err
}
@@ -117,24 +118,24 @@ func (e *ConflictError) Error() string {
return "merge conflicts in " + strings.Join(e.Files, ", ")
}
func Ahead(worktree, target string) (int, error) {
count, err := run(worktree, "rev-list", "--count", target+"..HEAD")
func Ahead(ctx context.Context, worktree, target string) (int, error) {
count, err := run(ctx, worktree, "rev-list", "--count", target+"..HEAD")
if err != nil {
return 0, err
}
return strconv.Atoi(count)
}
func Behind(worktree, target string) (int, error) {
count, err := run(worktree, "rev-list", "--count", "HEAD.."+target)
func Behind(ctx context.Context, worktree, target string) (int, error) {
count, err := run(ctx, worktree, "rev-list", "--count", "HEAD.."+target)
if err != nil {
return 0, err
}
return strconv.Atoi(count)
}
func SquashCommit(repo, worktree, target, message string, env []string, sign func(payload []byte) ([]byte, error)) (string, error) {
checkedOut, err := run(repo, "symbolic-ref", "--short", "HEAD")
func SquashCommit(ctx context.Context, repo, worktree, target, message string, env []string, sign func(payload []byte) ([]byte, error)) (string, error) {
checkedOut, err := run(ctx, repo, "symbolic-ref", "--short", "HEAD")
if err != nil {
return "", err
}
@@ -142,20 +143,20 @@ func SquashCommit(repo, worktree, target, message string, env []string, sign fun
return "", fmt.Errorf("%s has %s checked out, not %s", repo, checkedOut, target)
}
tree, err := Snapshot(worktree)
tree, err := Snapshot(ctx, worktree)
if err != nil {
return "", err
}
base, err := run(worktree, "merge-base", target, "HEAD")
base, err := run(ctx, worktree, "merge-base", target, "HEAD")
if err != nil {
return "", err
}
head, err := run(repo, "rev-parse", target)
head, err := run(ctx, repo, "rev-parse", target)
if err != nil {
return "", err
}
out, stderr, code, err := runCode(repo, nil, "merge-tree", "--write-tree", "--name-only", "--merge-base="+base, head, tree)
out, stderr, code, err := runCode(ctx, repo, nil, "merge-tree", "--write-tree", "--name-only", "--merge-base="+base, head, tree)
if err != nil {
return "", err
}
@@ -174,7 +175,7 @@ func SquashCommit(repo, worktree, target, message string, env []string, sign fun
return "", fmt.Errorf("git merge-tree exited %d: %s", code, stderr)
}
headTree, err := run(repo, "rev-parse", head+"^{tree}")
headTree, err := run(ctx, repo, "rev-parse", head+"^{tree}")
if err != nil {
return "", err
}
@@ -182,11 +183,11 @@ func SquashCommit(repo, worktree, target, message string, env []string, sign fun
return "", nil
}
commit, err := runEnv(repo, env, "commit-tree", lines[0], "-p", head, "-m", message)
commit, err := runEnv(ctx, repo, env, "commit-tree", lines[0], "-p", head, "-m", message)
if err != nil {
return "", err
}
signing, stderr, code, err := runCode(repo, nil, "config", "--type=bool", "--get", "commit.gpgsign")
signing, stderr, code, err := runCode(ctx, repo, nil, "config", "--type=bool", "--get", "commit.gpgsign")
if err != nil {
return "", err
}
@@ -196,11 +197,11 @@ func SquashCommit(repo, worktree, target, message string, env []string, sign fun
if signing != "true" {
return commit, nil
}
return signCommit(repo, commit, sign)
return signCommit(ctx, repo, commit, sign)
}
func signCommit(repo, commit string, sign func(payload []byte) ([]byte, error)) (string, error) {
payload, err := runRaw(repo, nil, "", "cat-file", "commit", commit)
func signCommit(ctx context.Context, repo, commit string, sign func(payload []byte) ([]byte, error)) (string, error) {
payload, err := runRaw(ctx, repo, nil, "", "cat-file", "commit", commit)
if err != nil {
return "", err
}
@@ -214,7 +215,7 @@ func signCommit(repo, commit string, sign func(payload []byte) ([]byte, error))
}
header := "gpgsig " + strings.ReplaceAll(strings.TrimSuffix(string(sig), "\n"), "\n", "\n ") + "\n"
signed := payload[:eoh+1] + header + payload[eoh+1:]
return runInput(repo, nil, signed, "hash-object", "-t", "commit", "-w", "--stdin")
return runInput(ctx, repo, nil, signed, "hash-object", "-t", "commit", "-w", "--stdin")
}
const Remote = "origin"
@@ -244,8 +245,8 @@ func Push(ctx context.Context, repo, commit, branch, notes string) error {
return fmt.Errorf("pushing %s to %s: exit status %d: %s", branch, Remote, code, strings.TrimSpace(out+"\n"+stderr))
}
func FastForward(repo, rev string) error {
_, err := run(repo, "merge", "--ff-only", rev)
func FastForward(ctx context.Context, repo, rev string) error {
_, err := run(ctx, repo, "merge", "--ff-only", rev)
return err
}
@@ -265,19 +266,19 @@ func FetchFastForward(ctx context.Context, repo, branch string) error {
if err != nil {
return err
}
return FastForward(repo, Remote+"/"+branch)
return FastForward(ctx, repo, Remote+"/"+branch)
}
func Head(dir string) (string, error) {
return run(dir, "rev-parse", "HEAD")
func Head(ctx context.Context, dir string) (string, error) {
return run(ctx, dir, "rev-parse", "HEAD")
}
func BranchHead(repo, branch string) (string, error) {
return run(repo, "rev-parse", "--verify", "refs/heads/"+branch+"^{commit}")
func BranchHead(ctx context.Context, repo, branch string) (string, error) {
return run(ctx, repo, "rev-parse", "--verify", "refs/heads/"+branch+"^{commit}")
}
func IsAncestor(dir, ancestor, descendant string) (bool, error) {
_, stderr, code, err := runCode(dir, nil, "merge-base", "--is-ancestor", ancestor, descendant)
func IsAncestor(ctx context.Context, dir, ancestor, descendant string) (bool, error) {
_, stderr, code, err := runCode(ctx, dir, nil, "merge-base", "--is-ancestor", ancestor, descendant)
if err != nil {
return false, err
}
@@ -290,38 +291,38 @@ func IsAncestor(dir, ancestor, descendant string) (bool, error) {
return false, fmt.Errorf("git merge-base --is-ancestor %s %s: exit status %d: %s", ancestor, descendant, code, stderr)
}
func MergeInProgress(dir string) (bool, error) {
_, _, code, err := runCode(dir, nil, "rev-parse", "--quiet", "--verify", "MERGE_HEAD")
func MergeInProgress(ctx context.Context, dir string) (bool, error) {
_, _, code, err := runCode(ctx, dir, nil, "rev-parse", "--quiet", "--verify", "MERGE_HEAD")
return err == nil && code == 0, err
}
func AbortMerge(dir string) error {
merging, err := MergeInProgress(dir)
func AbortMerge(ctx context.Context, dir string) error {
merging, err := MergeInProgress(ctx, dir)
if err != nil || !merging {
return err
}
_, err = run(dir, "merge", "--abort")
_, err = run(ctx, dir, "merge", "--abort")
return err
}
func ReadFile(repo, rev, path string) (string, bool, error) {
_, _, code, err := runCode(repo, nil, "cat-file", "-e", rev+":"+path)
func ReadFile(ctx context.Context, repo, rev, path string) (string, bool, error) {
_, _, code, err := runCode(ctx, repo, nil, "cat-file", "-e", rev+":"+path)
if err != nil {
return "", false, err
}
if code != 0 {
return "", false, nil
}
out, err := run(repo, "cat-file", "blob", rev+":"+path)
out, err := run(ctx, repo, "cat-file", "blob", rev+":"+path)
return out, true, err
}
func CommitAll(dir, message, name, email string) (string, error) {
status, err := run(dir, "status", "--porcelain", "--untracked-files=all")
func CommitAll(ctx context.Context, dir, message, name, email string) (string, error) {
status, err := run(ctx, dir, "status", "--porcelain", "--untracked-files=all")
if err != nil {
return "", err
}
merging, err := MergeInProgress(dir)
merging, err := MergeInProgress(ctx, dir)
if err != nil {
return "", err
}
@@ -330,38 +331,38 @@ func CommitAll(dir, message, name, email string) (string, error) {
}
env := IdentityEnv(name, email)
_, err = runEnv(dir, env, "add", "--all")
_, err = runEnv(ctx, dir, env, "add", "--all")
if err != nil {
return "", err
}
_, err = runEnv(dir, env, "commit", "--quiet", "--message", message)
_, err = runEnv(ctx, dir, env, "commit", "--quiet", "--message", message)
if err != nil {
return "", err
}
return Head(dir)
return Head(ctx, dir)
}
func Merge(dir, rev, message, name, email string) error {
func Merge(ctx context.Context, dir, rev, message, name, email string) error {
env := IdentityEnv(name, email)
_, err := runEnv(dir, env, "merge", "--no-ff", "--quiet", "--message", message, rev)
_, err := runEnv(ctx, dir, env, "merge", "--no-ff", "--quiet", "--message", message, rev)
return err
}
func run(dir string, args ...string) (string, error) {
return runEnv(dir, nil, args...)
func run(ctx context.Context, dir string, args ...string) (string, error) {
return runEnv(ctx, dir, nil, args...)
}
func runEnv(dir string, env []string, args ...string) (string, error) {
return runInput(dir, env, "", args...)
func runEnv(ctx context.Context, dir string, env []string, args ...string) (string, error) {
return runInput(ctx, dir, env, "", args...)
}
func runInput(dir string, env []string, input string, args ...string) (string, error) {
out, err := runRaw(dir, env, input, args...)
func runInput(ctx context.Context, dir string, env []string, input string, args ...string) (string, error) {
out, err := runRaw(ctx, dir, env, input, args...)
return strings.TrimSpace(out), err
}
func runRaw(dir string, env []string, input string, args ...string) (string, error) {
out, stderr, code, err := runCodeInput(context.Background(), dir, env, strings.NewReader(input), args...)
func runRaw(ctx context.Context, dir string, env []string, input string, args ...string) (string, error) {
out, stderr, code, err := runCodeInput(context.WithoutCancel(ctx), dir, env, strings.NewReader(input), args...)
if err != nil {
return "", err
}
@@ -371,8 +372,8 @@ func runRaw(dir string, env []string, input string, args ...string) (string, err
return out, nil
}
func runCode(dir string, env []string, args ...string) (string, string, int, error) {
out, stderr, code, err := runCodeInput(context.Background(), dir, env, nil, args...)
func runCode(ctx context.Context, dir string, env []string, args ...string) (string, string, int, error) {
out, stderr, code, err := runCodeInput(context.WithoutCancel(ctx), dir, env, nil, args...)
return strings.TrimSpace(out), stderr, code, err
}
@@ -384,6 +385,7 @@ func runNetwork(ctx context.Context, dir string, args ...string) (string, string
}
func runCodeInput(ctx context.Context, dir string, env []string, stdin io.Reader, args ...string) (string, string, int, error) {
defer trace.Start(ctx, "git "+args[0])()
argv := args
if args[0] != "config" {
argv = append([]string{"-c", "commit.gpgsign=false"}, args...)