Files
honeyDueAPI/pkg/utils/logger.go
Trey t 6dac34e373 Migrate from Gin to Echo framework and add comprehensive integration tests
Major changes:
- Migrate all handlers from Gin to Echo framework
- Add new apperrors, echohelpers, and validator packages
- Update middleware for Echo compatibility
- Add ArchivedHandler to task categorization chain (archived tasks go to cancelled_tasks column)
- Add 6 new integration tests:
  - RecurringTaskLifecycle: NextDueDate advancement for weekly/monthly tasks
  - MultiUserSharing: Complex sharing with user removal
  - TaskStateTransitions: All state transitions and kanban column changes
  - DateBoundaryEdgeCases: Threshold boundary testing
  - CascadeOperations: Residence deletion cascade effects
  - MultiUserOperations: Shared residence collaboration
- Add single-purpose repository functions for kanban columns (GetOverdueTasks, GetDueSoonTasks, etc.)
- Fix RemoveUser route param mismatch (userId -> user_id)
- Fix determineExpectedColumn helper to correctly prioritize in_progress over overdue

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 13:52:08 -06:00

132 lines
2.9 KiB
Go

package utils
import (
"io"
"os"
"time"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
// InitLogger initializes the zerolog logger
func InitLogger(debug bool) {
InitLoggerWithWriter(debug, nil)
}
// InitLoggerWithWriter initializes the zerolog logger with an optional additional writer
// The additional writer receives JSON formatted logs (useful for monitoring)
func InitLoggerWithWriter(debug bool, additionalWriter io.Writer) {
zerolog.TimeFieldFormat = time.RFC3339
if debug {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
} else {
zerolog.SetGlobalLevel(zerolog.InfoLevel)
}
// Build the output writer(s)
var output io.Writer
if additionalWriter != nil {
// Always write JSON to additional writer for monitoring
// The additional writer parses JSON to extract log entries
if debug {
// In debug mode: pretty console to stdout + JSON to additional writer
consoleOutput := zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: "15:04:05",
}
output = io.MultiWriter(consoleOutput, additionalWriter)
} else {
// In production: JSON to both stdout and additional writer
output = io.MultiWriter(os.Stdout, additionalWriter)
}
} else {
if debug {
output = zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: "15:04:05",
}
} else {
output = os.Stdout
}
}
log.Logger = zerolog.New(output).With().Timestamp().Caller().Logger()
}
// EchoLogger returns an Echo middleware for request logging
func EchoLogger() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
req := c.Request()
path := req.URL.Path
raw := req.URL.RawQuery
// Process request
err := next(c)
// Log after request
end := time.Now()
latency := end.Sub(start)
if raw != "" {
path = path + "?" + raw
}
res := c.Response()
statusCode := res.Status
msg := "Request"
if err != nil {
msg = err.Error()
}
event := log.Info()
if statusCode >= 400 && statusCode < 500 {
event = log.Warn()
} else if statusCode >= 500 {
event = log.Error()
}
event.
Str("method", req.Method).
Str("path", path).
Int("status", statusCode).
Str("ip", c.RealIP()).
Dur("latency", latency).
Str("user-agent", req.UserAgent()).
Msg(msg)
return err
}
}
}
// EchoRecovery returns an Echo middleware for panic recovery
func EchoRecovery() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
defer func() {
if err := recover(); err != nil {
log.Error().
Interface("error", err).
Str("path", c.Request().URL.Path).
Str("method", c.Request().Method).
Msg("Panic recovered")
c.JSON(500, map[string]interface{}{
"error": "Internal server error",
})
}
}()
return next(c)
}
}
}