fork of whitequark.org/git-pages with mods for tangled

Pass the context to logging functions.

miyuko cb7802df b01e67f9

+15 -16
src/auth.go
··· 6 6 "encoding/json" 7 7 "errors" 8 8 "fmt" 9 - "log" 10 9 "net" 11 10 "net/http" 12 11 "net/url" ··· 32 31 return false 33 32 } 34 33 35 - func authorizeInsecure() *Authorization { 34 + func authorizeInsecure(r *http.Request) *Authorization { 36 35 if config.Insecure { // for testing only 37 - log.Println("auth: INSECURE mode") 36 + logc.Println(r.Context(), "auth: INSECURE mode") 38 37 return &Authorization{ 39 38 repoURLs: nil, 40 39 branch: "pages", ··· 276 275 } 277 276 278 277 if len(dnsRecords) > 0 { 279 - log.Printf("auth: %s TXT/CNAME: %q\n", host, dnsRecords) 278 + logc.Printf(r.Context(), "auth: %s TXT/CNAME: %q\n", host, dnsRecords) 280 279 } 281 280 282 281 for _, dnsRecord := range dnsRecords { ··· 324 323 func AuthorizeMetadataRetrieval(r *http.Request) (*Authorization, error) { 325 324 causes := []error{AuthError{http.StatusUnauthorized, "unauthorized"}} 326 325 327 - auth := authorizeInsecure() 326 + auth := authorizeInsecure(r) 328 327 if auth != nil { 329 328 return auth, nil 330 329 } ··· 335 334 } else if err != nil { // bad request 336 335 return nil, err 337 336 } else { 338 - log.Println("auth: DNS challenge") 337 + logc.Println(r.Context(), "auth: DNS challenge") 339 338 return auth, nil 340 339 } 341 340 ··· 346 345 } else if err != nil { // bad request 347 346 return nil, err 348 347 } else { 349 - log.Printf("auth: wildcard %s\n", pattern.GetHost()) 348 + logc.Printf(r.Context(), "auth: wildcard %s\n", pattern.GetHost()) 350 349 return auth, nil 351 350 } 352 351 } ··· 358 357 } else if err != nil { // bad request 359 358 return nil, err 360 359 } else { 361 - log.Printf("auth: codeberg %s\n", r.Host) 360 + logc.Printf(r.Context(), "auth: codeberg %s\n", r.Host) 362 361 return auth, nil 363 362 } 364 363 } ··· 376 375 return nil, err 377 376 } 378 377 379 - auth := authorizeInsecure() 378 + auth := authorizeInsecure(r) 380 379 if auth != nil { 381 380 return auth, nil 382 381 } ··· 388 387 } else if err != nil { // bad request 389 388 return nil, err 390 389 } else { 391 - log.Println("auth: DNS challenge: allow *") 390 + logc.Println(r.Context(), "auth: DNS challenge: allow *") 392 391 return auth, nil 393 392 } 394 393 ··· 400 399 } else if err != nil { // bad request 401 400 return nil, err 402 401 } else { 403 - log.Printf("auth: DNS allowlist: allow %v\n", auth.repoURLs) 402 + logc.Printf(r.Context(), "auth: DNS allowlist: allow %v\n", auth.repoURLs) 404 403 return auth, nil 405 404 } 406 405 } ··· 414 413 } else if err != nil { // bad request 415 414 return nil, err 416 415 } else { 417 - log.Printf("auth: wildcard %s: allow %v\n", pattern.GetHost(), auth.repoURLs) 416 + logc.Printf(r.Context(), "auth: wildcard %s: allow %v\n", pattern.GetHost(), auth.repoURLs) 418 417 return auth, nil 419 418 } 420 419 } ··· 426 425 } else if err != nil { // bad request 427 426 return nil, err 428 427 } else { 429 - log.Printf("auth: codeberg %s: allow %v branch %s\n", 428 + logc.Printf(r.Context(), "auth: codeberg %s: allow %v branch %s\n", 430 429 r.Host, auth.repoURLs, auth.branch) 431 430 return auth, nil 432 431 } ··· 643 642 return nil, err 644 643 } 645 644 646 - auth := authorizeInsecure() 645 + auth := authorizeInsecure(r) 647 646 if auth != nil { 648 647 return auth, nil 649 648 } ··· 655 654 } else if err != nil { // bad request 656 655 return nil, err 657 656 } else { 658 - log.Printf("auth: forge token: allow\n") 657 + logc.Printf(r.Context(), "auth: forge token: allow\n") 659 658 return auth, nil 660 659 } 661 660 ··· 669 668 } else if err != nil { // bad request 670 669 return nil, err 671 670 } else { 672 - log.Println("auth: DNS challenge") 671 + logc.Println(r.Context(), "auth: DNS challenge") 673 672 return auth, nil 674 673 } 675 674 }
+16 -17
src/backend_s3.go
··· 6 6 "crypto/sha256" 7 7 "fmt" 8 8 "io" 9 - "log" 10 9 "net/http" 11 10 "path" 12 11 "strings" ··· 170 169 if err != nil { 171 170 return nil, err 172 171 } else if !exists { 173 - log.Printf("s3: create bucket %s\n", bucket) 172 + logc.Printf(ctx, "s3: create bucket %s\n", bucket) 174 173 175 174 err = client.MakeBucket(ctx, bucket, 176 175 minio.MakeBucketOptions{Region: config.Region}) ··· 236 235 minio.StatObjectOptions{}) 237 236 if err != nil { 238 237 if errResp := minio.ToErrorResponse(err); errResp.Code == "NoSuchKey" { 239 - log.Printf("s3 feature %q: disabled", feature) 238 + logc.Printf(ctx, "s3 feature %q: disabled", feature) 240 239 return false, nil 241 240 } else { 242 241 return false, err 243 242 } 244 243 } 245 - log.Printf("s3 feature %q: enabled", feature) 244 + logc.Printf(ctx, "s3 feature %q: enabled", feature) 246 245 return true, nil 247 246 } 248 247 ··· 250 249 if err != nil { 251 250 err = fmt.Errorf("getting s3 backend feature %q: %w", feature, err) 252 251 ObserveError(err) 253 - log.Print(err) 252 + logc.Println(ctx, err) 254 253 return false 255 254 } 256 255 return isOn ··· 268 267 reader io.ReadSeeker, size uint64, mtime time.Time, err error, 269 268 ) { 270 269 loader := func(ctx context.Context, name string) (*CachedBlob, error) { 271 - log.Printf("s3: get blob %s\n", name) 270 + logc.Printf(ctx, "s3: get blob %s\n", name) 272 271 273 272 startTime := time.Now() 274 273 ··· 313 312 } 314 313 315 314 func (s3 *S3Backend) PutBlob(ctx context.Context, name string, data []byte) error { 316 - log.Printf("s3: put blob %s (%s)\n", name, datasize.ByteSize(len(data)).HumanReadable()) 315 + logc.Printf(ctx, "s3: put blob %s (%s)\n", name, datasize.ByteSize(len(data)).HumanReadable()) 317 316 318 317 _, err := s3.client.StatObject(ctx, s3.bucket, blobObjectName(name), 319 318 minio.GetObjectOptions{}) ··· 325 324 return err 326 325 } else { 327 326 ObserveData(ctx, "blob.status", "created") 328 - log.Printf("s3: put blob %s (created)\n", name) 327 + logc.Printf(ctx, "s3: put blob %s (created)\n", name) 329 328 return nil 330 329 } 331 330 } else { ··· 333 332 } 334 333 } else { 335 334 ObserveData(ctx, "blob.status", "exists") 336 - log.Printf("s3: put blob %s (exists)\n", name) 335 + logc.Printf(ctx, "s3: put blob %s (exists)\n", name) 337 336 blobsDedupedCount.Inc() 338 337 blobsDedupedBytes.Add(float64(len(data))) 339 338 return nil ··· 341 340 } 342 341 343 342 func (s3 *S3Backend) DeleteBlob(ctx context.Context, name string) error { 344 - log.Printf("s3: delete blob %s\n", name) 343 + logc.Printf(ctx, "s3: delete blob %s\n", name) 345 344 346 345 return s3.client.RemoveObject(ctx, s3.bucket, blobObjectName(name), 347 346 minio.RemoveObjectOptions{}) ··· 356 355 } 357 356 358 357 func (s3 *S3Backend) ListManifests(ctx context.Context) (manifests []string, err error) { 359 - log.Print("s3: list manifests") 358 + logc.Print(ctx, "s3: list manifests") 360 359 361 360 ctx, cancel := context.WithCancel(ctx) 362 361 defer cancel() ··· 396 395 } 397 396 398 397 func (l s3ManifestLoader) load(ctx context.Context, name string, oldManifest *CachedManifest) (*CachedManifest, error) { 399 - log.Printf("s3: get manifest %s\n", name) 398 + logc.Printf(ctx, "s3: get manifest %s\n", name) 400 399 401 400 loader := func() (*CachedManifest, error) { 402 401 opts := minio.GetObjectOptions{} ··· 475 474 476 475 func (s3 *S3Backend) StageManifest(ctx context.Context, manifest *Manifest) error { 477 476 data := EncodeManifest(manifest) 478 - log.Printf("s3: stage manifest %x\n", sha256.Sum256(data)) 477 + logc.Printf(ctx, "s3: stage manifest %x\n", sha256.Sum256(data)) 479 478 480 479 _, err := s3.client.PutObject(ctx, s3.bucket, stagedManifestObjectName(data), 481 480 bytes.NewReader(data), int64(len(data)), minio.PutObjectOptions{}) ··· 484 483 485 484 func (s3 *S3Backend) CommitManifest(ctx context.Context, name string, manifest *Manifest) error { 486 485 data := EncodeManifest(manifest) 487 - log.Printf("s3: commit manifest %x -> %s", sha256.Sum256(data), name) 486 + logc.Printf(ctx, "s3: commit manifest %x -> %s", sha256.Sum256(data), name) 488 487 489 488 // Remove staged object unconditionally (whether commit succeeded or failed), since 490 489 // the upper layer has to retry the complete operation anyway. ··· 503 502 } 504 503 505 504 func (s3 *S3Backend) DeleteManifest(ctx context.Context, name string) error { 506 - log.Printf("s3: delete manifest %s\n", name) 505 + logc.Printf(ctx, "s3: delete manifest %s\n", name) 507 506 508 507 err := s3.client.RemoveObject(ctx, s3.bucket, manifestObjectName(name), 509 508 minio.RemoveObjectOptions{}) ··· 516 515 } 517 516 518 517 func (s3 *S3Backend) CheckDomain(ctx context.Context, domain string) (exists bool, err error) { 519 - log.Printf("s3: check domain %s\n", domain) 518 + logc.Printf(ctx, "s3: check domain %s\n", domain) 520 519 521 520 _, err = s3.client.StatObject(ctx, s3.bucket, domainCheckObjectName(domain), 522 521 minio.StatObjectOptions{}) ··· 547 546 } 548 547 549 548 func (s3 *S3Backend) CreateDomain(ctx context.Context, domain string) error { 550 - log.Printf("s3: create domain %s\n", domain) 549 + logc.Printf(ctx, "s3: create domain %s\n", domain) 551 550 552 551 _, err := s3.client.PutObject(ctx, s3.bucket, domainCheckObjectName(domain), 553 552 &bytes.Reader{}, 0, minio.PutObjectOptions{})
+5 -6
src/caddy.go
··· 3 3 import ( 4 4 "crypto/tls" 5 5 "fmt" 6 - "log" 7 6 "net" 8 7 "net/http" 9 8 "net/url" ··· 22 21 // this isn't really what git-pages is designed for, and object store accesses can cost money. 23 22 // [^1]: https://letsencrypt.org/2025/07/01/issuing-our-first-ip-address-certificate 24 23 if ip := net.ParseIP(domain); ip != nil { 25 - log.Println("caddy:", domain, 404, "(bare IP)") 24 + logc.Println(r.Context(), "caddy:", domain, 404, "(bare IP)") 26 25 w.WriteHeader(http.StatusNotFound) 27 26 return 28 27 } ··· 52 51 } else { 53 52 connectHost += ":443" 54 53 } 55 - log.Printf("caddy: check TLS %s", fallbackURL) 54 + logc.Printf(r.Context(), "caddy: check TLS %s", fallbackURL) 56 55 connection, err := tls.Dial("tcp", connectHost, &tls.Config{ServerName: domain}) 57 56 if err != nil { 58 57 continue ··· 64 63 } 65 64 66 65 if found { 67 - log.Println("caddy:", domain, 200) 66 + logc.Println(r.Context(), "caddy:", domain, 200) 68 67 w.WriteHeader(http.StatusOK) 69 68 } else if err == nil { 70 - log.Println("caddy:", domain, 404) 69 + logc.Println(r.Context(), "caddy:", domain, 404) 71 70 w.WriteHeader(http.StatusNotFound) 72 71 } else { 73 - log.Println("caddy:", domain, 500) 72 + logc.Println(r.Context(), "caddy:", domain, 500) 74 73 w.WriteHeader(http.StatusInternalServerError) 75 74 fmt.Fprintln(w, err) 76 75 }
+42
src/log.go
··· 1 + package git_pages 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + "log/slog" 7 + "runtime" 8 + "time" 9 + ) 10 + 11 + var logc slogWithCtx 12 + 13 + type slogWithCtx struct{} 14 + 15 + func (l slogWithCtx) log(ctx context.Context, level slog.Level, msg string) { 16 + if ctx == nil { 17 + ctx = context.Background() 18 + } 19 + logger := slog.Default() 20 + if !logger.Enabled(ctx, level) { 21 + return 22 + } 23 + 24 + var pcs [1]uintptr 25 + // skip [runtime.Callers, this method, method calling this method] 26 + runtime.Callers(3, pcs[:]) 27 + 28 + r := slog.NewRecord(time.Now(), level, msg, pcs[0]) 29 + logger.Handler().Handle(ctx, r) 30 + } 31 + 32 + func (l slogWithCtx) Print(ctx context.Context, v ...any) { 33 + l.log(ctx, slog.LevelInfo, fmt.Sprint(v...)) 34 + } 35 + 36 + func (l slogWithCtx) Printf(ctx context.Context, format string, v ...any) { 37 + l.log(ctx, slog.LevelInfo, fmt.Sprintf(format, v...)) 38 + } 39 + 40 + func (l slogWithCtx) Println(ctx context.Context, v ...any) { 41 + l.log(ctx, slog.LevelInfo, fmt.Sprintln(v...)) 42 + }
+1 -1
src/main.go
··· 85 85 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 86 86 defer func() { 87 87 if err := recover(); err != nil { 88 - log.Printf("panic: %s %s %s: %s\n%s", 88 + logc.Printf(r.Context(), "panic: %s %s %s: %s\n%s", 89 89 r.Method, r.Host, r.URL.Path, err, string(debug.Stack())) 90 90 http.Error(w, 91 91 fmt.Sprintf("internal server error: %s", err),
+5 -6
src/manifest.go
··· 8 8 "crypto/sha256" 9 9 "errors" 10 10 "fmt" 11 - "log" 12 11 "mime" 13 12 "net/http" 14 13 "path" ··· 189 188 190 189 if originalSize != 0 { 191 190 spaceSaving := (float64(originalSize) - float64(compressedSize)) / float64(originalSize) 192 - log.Printf("compress: saved %.2f percent (%s to %s)", 191 + logc.Printf(ctx, "compress: saved %.2f percent (%s to %s)", 193 192 spaceSaving*100.0, 194 193 datasize.ByteSize(originalSize).HR(), 195 194 datasize.ByteSize(compressedSize).HR(), ··· 205 204 func PrepareManifest(ctx context.Context, manifest *Manifest) error { 206 205 // Parse Netlify-style `_redirects` 207 206 if err := ProcessRedirectsFile(manifest); err != nil { 208 - log.Printf("redirects err: %s\n", err) 207 + logc.Printf(ctx, "redirects err: %s\n", err) 209 208 } else if len(manifest.Redirects) > 0 { 210 - log.Printf("redirects ok: %d rules\n", len(manifest.Redirects)) 209 + logc.Printf(ctx, "redirects ok: %d rules\n", len(manifest.Redirects)) 211 210 } 212 211 213 212 // Parse Netlify-style `_headers` 214 213 if err := ProcessHeadersFile(manifest); err != nil { 215 - log.Printf("headers err: %s\n", err) 214 + logc.Printf(ctx, "headers err: %s\n", err) 216 215 } else if len(manifest.Headers) > 0 { 217 - log.Printf("headers ok: %d rules\n", len(manifest.Headers)) 216 + logc.Printf(ctx, "headers ok: %d rules\n", len(manifest.Headers)) 218 217 } 219 218 220 219 // Sniff content type like `http.ServeContent`
+3 -4
src/migrate.go
··· 3 3 import ( 4 4 "context" 5 5 "fmt" 6 - "log" 7 6 "slices" 8 7 "strings" 9 8 ) ··· 19 18 20 19 func createDomainMarkers(ctx context.Context) error { 21 20 if backend.HasFeature(ctx, FeatureCheckDomainMarker) { 22 - log.Print("store already has domain markers") 21 + logc.Print(ctx, "store already has domain markers") 23 22 return nil 24 23 } 25 24 ··· 36 35 } 37 36 } 38 37 for idx, domain := range domains { 39 - log.Printf("(%d / %d) creating domain %s", idx+1, len(domains), domain) 38 + logc.Printf(ctx, "(%d / %d) creating domain %s", idx+1, len(domains), domain) 40 39 if err := backend.CreateDomain(ctx, domain); err != nil { 41 40 return fmt.Errorf("creating domain %s: %w", domain, err) 42 41 } ··· 44 43 if err := backend.EnableFeature(ctx, FeatureCheckDomainMarker); err != nil { 45 44 return err 46 45 } 47 - log.Printf("created markers for %d domains", len(domains)) 46 + logc.Printf(ctx, "created markers for %d domains", len(domains)) 48 47 return nil 49 48 }
+2 -3
src/pages.go
··· 8 8 "errors" 9 9 "fmt" 10 10 "io" 11 - "log" 12 11 "maps" 13 12 "net/http" 14 13 "net/url" ··· 652 651 // any intentional deviation is an opportunity to miss an issue that will affect our 653 652 // visitors but not our health checks. 654 653 if r.Header.Get("Health-Check") == "" { 655 - log.Println("pages:", r.Method, r.Host, r.URL, r.Header.Get("Content-Type")) 654 + logc.Println(r.Context(), "pages:", r.Method, r.Host, r.URL, r.Header.Get("Content-Type")) 656 655 if region := os.Getenv("FLY_REGION"); region != "" { 657 656 machine_id := os.Getenv("FLY_MACHINE_ID") 658 657 w.Header().Add("Server", fmt.Sprintf("git-pages (fly.io; %s; %s)", region, machine_id)) ··· 695 694 http.Error(w, message, http.StatusRequestEntityTooLarge) 696 695 err = errors.New(message) 697 696 } 698 - log.Println("pages err:", err) 697 + logc.Println(r.Context(), "pages err:", err) 699 698 } 700 699 }
+9 -10
src/update.go
··· 5 5 "errors" 6 6 "fmt" 7 7 "io" 8 - "log" 9 8 "strings" 10 9 ) 11 10 ··· 71 70 status = "unchanged" 72 71 } 73 72 if newManifest.Commit != nil { 74 - log.Printf("update %s ok: %s %s", webRoot, status, *newManifest.Commit) 73 + logc.Printf(ctx, "update %s ok: %s %s", webRoot, status, *newManifest.Commit) 75 74 } else { 76 - log.Printf("update %s ok: %s", webRoot, status) 75 + logc.Printf(ctx, "update %s ok: %s", webRoot, status) 77 76 } 78 77 } else { 79 - log.Printf("update %s err: %s", webRoot, err) 78 + logc.Printf(ctx, "update %s err: %s", webRoot, err) 80 79 } 81 80 82 81 return UpdateResult{outcome, newManifest, err} ··· 91 90 span, ctx := ObserveFunction(ctx, "UpdateFromRepository", "repo.url", repoURL) 92 91 defer span.Finish() 93 92 94 - log.Printf("update %s: %s %s\n", webRoot, repoURL, branch) 93 + logc.Printf(ctx, "update %s: %s %s\n", webRoot, repoURL, branch) 95 94 96 95 manifest, err := FetchRepository(ctx, repoURL, branch) 97 96 if errors.Is(err, context.DeadlineExceeded) { ··· 119 118 120 119 switch contentType { 121 120 case "application/x-tar": 122 - log.Printf("update %s: (tar)", webRoot) 121 + logc.Printf(ctx, "update %s: (tar)", webRoot) 123 122 manifest, err = ExtractTar(reader) // yellow? 124 123 case "application/x-tar+gzip": 125 - log.Printf("update %s: (tar.gz)", webRoot) 124 + logc.Printf(ctx, "update %s: (tar.gz)", webRoot) 126 125 manifest, err = ExtractTarGzip(reader) // definitely yellow. 127 126 case "application/x-tar+zstd": 128 - log.Printf("update %s: (tar.zst)", webRoot) 127 + logc.Printf(ctx, "update %s: (tar.zst)", webRoot) 129 128 manifest, err = ExtractTarZstd(reader) 130 129 case "application/zip": 131 - log.Printf("update %s: (zip)", webRoot) 130 + logc.Printf(ctx, "update %s: (zip)", webRoot) 132 131 manifest, err = ExtractZip(reader) 133 132 default: 134 133 err = errArchiveFormat 135 134 } 136 135 137 136 if err != nil { 138 - log.Printf("update %s err: %s", webRoot, err) 137 + logc.Printf(ctx, "update %s err: %s", webRoot, err) 139 138 result = UpdateResult{UpdateError, nil, err} 140 139 } else { 141 140 result = Update(ctx, webRoot, manifest)
+1 -2
src/wildcard.go
··· 3 3 import ( 4 4 "crypto/tls" 5 5 "fmt" 6 - "log" 7 6 "net/http" 8 7 "net/http/httputil" 9 8 "net/url" ··· 95 94 96 95 for _, pattern := range wildcards { 97 96 if pattern.IsFallbackFor(host) { 98 - log.Printf("proxy: %s via %s", pattern.GetHost(), pattern.FallbackURL) 97 + logc.Printf(r.Context(), "proxy: %s via %s", pattern.GetHost(), pattern.FallbackURL) 99 98 pattern.Fallback.ServeHTTP(w, r) 100 99 return true, nil 101 100 }