This commit is contained in:
lbenedar
2026-03-19 13:44:50 +03:00
parent 5cc1196085
commit b8fff8f7d5
4 changed files with 112 additions and 8 deletions

View File

@@ -6,7 +6,10 @@ import (
) )
func (app *application) logError(r *http.Request, err error) { func (app *application) logError(r *http.Request, err error) {
app.logger.Print(err) app.logger.PrintError(err, map[string]string{
"request_method": r.Method,
"request_url": r.URL.String(),
})
} }
func (app *application) errorResponse(w http.ResponseWriter, r *http.Request, status int, message any) { func (app *application) errorResponse(w http.ResponseWriter, r *http.Request, status int, message any) {

View File

@@ -14,7 +14,6 @@ func (app *application) healthcheckHandler(w http.ResponseWriter, r *http.Reques
} }
err := app.writeJSON(w, http.StatusOK, env, nil) err := app.writeJSON(w, http.StatusOK, env, nil)
if err != nil { if err != nil {
app.logger.Print(err)
app.serverErrorResponse(w, r, err) app.serverErrorResponse(w, r, err)
} }
} }

View File

@@ -11,6 +11,7 @@ import (
"time" "time"
"gitea.local.lab/Lbenedar/greenlight/internal/data" "gitea.local.lab/Lbenedar/greenlight/internal/data"
"gitea.local.lab/Lbenedar/greenlight/internal/jsonlog"
_ "github.com/lib/pq" _ "github.com/lib/pq"
) )
@@ -18,7 +19,7 @@ const version = "1.0.0"
type application struct { type application struct {
config config config config
logger *log.Logger logger *jsonlog.Logger
models data.Models models data.Models
} }
@@ -70,15 +71,15 @@ func main() {
flag.StringVar(&cfg.db.maxIdleTime, "db-max-idle-time", "15m", "PostgreSQL max connection idle time") flag.StringVar(&cfg.db.maxIdleTime, "db-max-idle-time", "15m", "PostgreSQL max connection idle time")
flag.Parse() flag.Parse()
logger := log.New(os.Stdout, "", log.Ldate|log.Ltime) logger := jsonlog.New(os.Stdout, jsonlog.LevelInfo)
db, err := openDB(cfg) db, err := openDB(cfg)
if err != nil { if err != nil {
logger.Fatal(err) logger.PrintFatal(err, nil)
} }
defer db.Close() defer db.Close()
logger.Printf("database connection pool established") logger.PrintInfo("database connection pool established", nil)
app := &application{ app := &application{
config: cfg, config: cfg,
@@ -89,11 +90,15 @@ func main() {
srv := &http.Server{ srv := &http.Server{
Addr: fmt.Sprintf(":%d", app.config.port), Addr: fmt.Sprintf(":%d", app.config.port),
Handler: app.routes(), Handler: app.routes(),
ErrorLog: log.New(logger, "", 0),
IdleTimeout: time.Minute, IdleTimeout: time.Minute,
ReadTimeout: 10 * time.Second, ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second,
} }
logger.Printf("starting %s server on %s", cfg.env, srv.Addr) logger.PrintInfo("starting server", map[string]string{
"addr": srv.Addr,
"env": cfg.env,
})
err = srv.ListenAndServe() err = srv.ListenAndServe()
logger.Fatal(err) logger.PrintFatal(err, nil)
} }

View File

@@ -0,0 +1,97 @@
package jsonlog
import (
"encoding/json"
"io"
"os"
"runtime/debug"
"sync"
"time"
)
type Level int8
const (
LevelInfo Level = iota
LevelError
LevelFatal
LevelOff
)
func (l Level) String() string {
switch l {
case LevelInfo:
return "INFO"
case LevelError:
return "ERROR"
case LevelFatal:
return "FATAL"
default:
return ""
}
}
type Logger struct {
out io.Writer
minLevel Level
mu sync.Mutex
}
func New(out io.Writer, minLevel Level) *Logger {
return &Logger{
out: out,
minLevel: minLevel,
}
}
func (l *Logger) PrintInfo(message string, properties map[string]string) {
l.print(LevelInfo, message, properties)
}
func (l *Logger) PrintError(err error, properties map[string]string) {
l.print(LevelError, err.Error(), properties)
}
func (l *Logger) PrintFatal(err error, properties map[string]string) {
l.print(LevelFatal, err.Error(), properties)
os.Exit(1)
}
func (l *Logger) print(level Level, message string, properties map[string]string) (int, error) {
if level < l.minLevel {
return 0, nil
}
aux := struct {
Level string `json:"level"`
Time string `json:"time"`
Message string `json:"message"`
Properties map[string]string `json:"properties,omitempty"`
Trace string `json:"trace,omitempty"`
}{
Level: level.String(),
Time: time.Now().UTC().Format(time.RFC3339),
Message: message,
Properties: properties,
}
if level >= LevelError {
aux.Trace = string(debug.Stack())
}
var line []byte
line, err := json.Marshal(aux)
if err != nil {
line = []byte(LevelError.String() + ": unable to marshal log message: " + err.Error())
}
l.mu.Lock()
defer l.mu.Unlock()
return l.out.Write(append(line, '\n'))
}
func (l *Logger) Write(message []byte) (n int, err error) {
return l.print(LevelError, string(message), nil)
}