Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions _examples/debug/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package main

import (
"fmt"
"net/http"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)

func main() {
r := chi.NewRouter()

// Standard middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)

// Routes
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Welcome"))
})

// User routes
r.Route("/users", func(r chi.Router) {
r.Get("/", listUsers)
r.Post("/", createUser)

r.Route("/{userID}", func(r chi.Router) {
r.Get("/", getUser)
r.Put("/", updateUser)
r.Delete("/", deleteUser)
})
})

// Article routes
r.Route("/articles", func(r chi.Router) {
r.Get("/", listArticles)
r.Get("/{articleID}", getArticle)
r.Post("/", createArticle)
})

// Admin routes
adminRouter := chi.NewRouter()
adminRouter.Get("/", adminDashboard)
adminRouter.Get("/users", adminListUsers)
r.Mount("/admin", adminRouter)

// Print all registered routes
fmt.Println("Registered routes:")
fmt.Println("==================")
r.PrintRoutes()
fmt.Println("==================")

// Start server
fmt.Println("\nServer starting on :3000")
http.ListenAndServe(":3000", r)
}

func listUsers(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("List users"))
}

func createUser(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Create user"))
}

func getUser(w http.ResponseWriter, r *http.Request) {
userID := chi.URLParam(r, "userID")
w.Write([]byte(fmt.Sprintf("Get user %s", userID)))
}

func updateUser(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Update user"))
}

func deleteUser(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Delete user"))
}

func listArticles(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("List articles"))
}

func getArticle(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Get article"))
}

func createArticle(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Create article"))
}

func adminDashboard(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Admin dashboard"))
}

func adminListUsers(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Admin list users"))
}
84 changes: 84 additions & 0 deletions _examples/logging-integration/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package main

import (
"fmt"
"log"
"net/http"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)

func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)

// Register some routes
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Home"))
})

r.Route("/api", func(r chi.Router) {
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
r.Post("/users", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Create user"))
})
r.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Get user"))
})
})

fmt.Println("\n=== Example 1: Using PrintRoutes (simple) ===")
r.PrintRoutes()

fmt.Println("\n=== Example 2: Using PrintRoutesFunc with standard log ===")
r.PrintRoutesFunc(func(s string) {
log.Println(s)
})

fmt.Println("\n=== Example 3: Using PrintRoutesFunc with custom logger ===")
// Simulate a custom logger (like logrus, zap, slog, etc.)
customLogger := &CustomLogger{prefix: "[ROUTES]"}
r.PrintRoutesFunc(customLogger.Info)

fmt.Println("\n=== Example 4: Using PrintRoutesFunc with filtering ===")
// Only log GET routes
r.PrintRoutesFunc(func(s string) {
if contains(s, "GET") {
fmt.Printf("GET route: %s\n", s)
}
})

fmt.Println("\nServer starting on :3000")
http.ListenAndServe(":3000", r)
}

// CustomLogger simulates a structured logging library
type CustomLogger struct {
prefix string
}

func (l *CustomLogger) Info(msg string) {
fmt.Printf("%s [INFO] %s\n", l.prefix, msg)
}

func (l *CustomLogger) Debug(msg string) {
fmt.Printf("%s [DEBUG] %s\n", l.prefix, msg)
}

func contains(s, substr string) bool {
return len(s) >= len(substr) && s[:len(substr)] == substr ||
len(s) > len(substr) && findSubstring(s, substr)
}

func findSubstring(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}
77 changes: 77 additions & 0 deletions mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package chi
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strings"
"sync"
)
Expand Down Expand Up @@ -526,3 +528,78 @@ func methodNotAllowedHandler(methodsAllowed ...methodTyp) func(w http.ResponseWr
w.Write(nil)
}
}

// PrintRoutes prints all registered routes to stdout in a readable format.
// This is useful for debugging and development purposes.
//
// Example output:
//
// [GET] /
// [POST] /users
// [GET] /users/{id}
func (mx *Mux) PrintRoutes() {
mx.PrintRoutesWithWriter(os.Stdout)
}

// PrintRoutesWithWriter prints all registered routes to the specified writer.
// This allows flexibility for testing and custom output destinations.
func (mx *Mux) PrintRoutesWithWriter(w io.Writer) {
// Track routes we've already printed to avoid duplicates
printed := make(map[string]bool)

err := Walk(mx, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
// Create unique key for this route
key := fmt.Sprintf("%s %s", method, route)

// Skip if already printed (can happen with route groups)
if printed[key] {
return nil
}
printed[key] = true

// Print in aligned format
fmt.Fprintf(w, "[%-7s] %s\n", method, route)
return nil
})
if err != nil {
fmt.Fprintf(w, "Error walking routes: %v\n", err)
}
}

// PrintRoutesFunc prints all registered routes using a custom logging function.
// This is useful when you want to integrate with existing logging infrastructure.
//
// Example with standard log:
//
// r.PrintRoutesFunc(func(s string) { log.Println(s) })
//
// Example with logrus:
//
// r.PrintRoutesFunc(logger.Info)
//
// Example with slog:
//
// r.PrintRoutesFunc(slog.Info)
func (mx *Mux) PrintRoutesFunc(logFunc func(string)) {
// Track routes we've already printed to avoid duplicates
printed := make(map[string]bool)

// Use chi.Walk to traverse all routes
err := Walk(mx, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
// Create unique key for this route
key := fmt.Sprintf("%s %s", method, route)

// Skip if already printed (can happen with route groups)
if printed[key] {
return nil
}
printed[key] = true

// Format and pass to custom logging function
logFunc(fmt.Sprintf("[%-7s] %s", method, route))
return nil
})
if err != nil {
logFunc(fmt.Sprintf("Error walking routes: %v", err))
}
}
Loading