package elinps import ( "context" "log/slog" "net" "net/http" "os" "time" "github.com/go-chi/chi/v5/middleware" ) // LoggerPadrao cria um logger simples (stdout) adequado para Docker. // // Regras do projeto: // - logs relevantes (rotas, tempo de execução, erros) // - NUNCA logar segredos (Authorization/cookies/senha/DSN) func LoggerPadrao() *slog.Logger { return slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) } type responseWriterComStatus struct { http.ResponseWriter status int bytes int } func (w *responseWriterComStatus) WriteHeader(status int) { w.status = status w.ResponseWriter.WriteHeader(status) } func (w *responseWriterComStatus) Write(b []byte) (int, error) { if w.status == 0 { w.status = http.StatusOK } n, err := w.ResponseWriter.Write(b) w.bytes += n return n, err } // MiddlewareLogRequisicao registra uma linha por requisição com: // - método, path, status, duração // - request_id (se existir) // - ip_real (após chi/middleware.RealIP) func MiddlewareLogRequisicao(logger *slog.Logger) func(http.Handler) http.Handler { if logger == nil { logger = slog.Default() } return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ini := time.Now() ww := &responseWriterComStatus{ResponseWriter: w} next.ServeHTTP(ww, r) dur := time.Since(ini) attrs := []any{ "metodo", r.Method, "path", r.URL.Path, "status", ww.status, "dur_ms", dur.Milliseconds(), "bytes", ww.bytes, "ip_real", ipSomenteHost(r.RemoteAddr), } if reqID := requestIDFromContext(r.Context()); reqID != "" { attrs = append(attrs, "request_id", reqID) } logger.Info("http_request", attrs...) }) } } // ipSomenteHost retorna apenas o IP (sem porta). Se o valor não parecer um IP, // devolve string vazia. func ipSomenteHost(remoteAddr string) string { ip := remoteAddr if host, _, err := net.SplitHostPort(remoteAddr); err == nil { ip = host } if net.ParseIP(ip) == nil { return "" } return ip } func requestIDFromContext(ctx context.Context) string { // O chi/middleware.RequestID coloca o ID no context. return middleware.GetReqID(ctx) }