mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-06 02:36:49 +00:00
feat(logging): add slog-based structured logging
Introduce slog-based structured logging throughout the booking service and server handlers. Add configurable log level via LOG_LEVEL environment variable. Replace legacy log usage with slog and propagate logger to booking service for improved observability.
This commit is contained in:
parent
2aadb421ef
commit
40d2338c0f
7 changed files with 60 additions and 7 deletions
10
.golangci.yml
Normal file
10
.golangci.yml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
version: "2"
|
||||||
|
linters:
|
||||||
|
default: none
|
||||||
|
enable:
|
||||||
|
- sloglint
|
||||||
|
settings:
|
||||||
|
sloglint:
|
||||||
|
# Enforce using attributes only.
|
||||||
|
# This will raise an error for any key-value pair arguments.
|
||||||
|
attr-only: true
|
||||||
|
|
@ -17,6 +17,8 @@ type Config struct {
|
||||||
DatabaseUrl string `env:"DATABASE_URL, required"`
|
DatabaseUrl string `env:"DATABASE_URL, required"`
|
||||||
// Debug enables debug mode when true
|
// Debug enables debug mode when true
|
||||||
Debug bool `env:"DEBUG, default=false"`
|
Debug bool `env:"DEBUG, default=false"`
|
||||||
|
// LogLevel is the logging level (e.g., debug, info, warn, error)
|
||||||
|
LogLevel string `env:"LOG_LEVEL, default=info"`
|
||||||
// Origins is the list of allowed origins
|
// Origins is the list of allowed origins
|
||||||
Origins []string `env:"ORIGINS, required"`
|
Origins []string `env:"ORIGINS, required"`
|
||||||
// Port is the HTTP server port number
|
// Port is the HTTP server port number
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ func JobMonthlyBookingReport() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
store := booking.NewPgStore(db)
|
store := booking.NewPgStore(db)
|
||||||
service, err := bookingService.NewService(store, nil, ps)
|
service, err := bookingService.NewService(nil, store, nil, ps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating booking service: %w", err)
|
return fmt.Errorf("error creating booking service: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
internal/driver/logger/log.go
Normal file
30
internal/driver/logger/log.go
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logLevel slog.LevelVar
|
||||||
|
|
||||||
|
func getLevel(levelStr string) slog.Level {
|
||||||
|
switch strings.ToLower(levelStr) {
|
||||||
|
case "debug":
|
||||||
|
return slog.LevelDebug
|
||||||
|
case "warn":
|
||||||
|
return slog.LevelWarn
|
||||||
|
case "error":
|
||||||
|
return slog.LevelError
|
||||||
|
default:
|
||||||
|
return slog.LevelInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(levelStr string) *slog.Logger {
|
||||||
|
logLevel.Set(getLevel(levelStr))
|
||||||
|
|
||||||
|
return slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||||
|
Level: &logLevel,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -31,6 +32,8 @@ func handleBookingListPage(bs *booking.Service, hc *config.Host) echo.HandlerFun
|
||||||
bookings = bs.All()
|
bookings = bs.All()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slog.Info("serving bookings", slog.Int("bookings_length", len(bookings)))
|
||||||
|
|
||||||
bvm := u.Map(bookings, func(b *booking.Line) *view.ListBookingsViewModel {
|
bvm := u.Map(bookings, func(b *booking.Line) *view.ListBookingsViewModel {
|
||||||
return &view.ListBookingsViewModel{
|
return &view.ListBookingsViewModel{
|
||||||
Id: b.InvoiceNumber(hc),
|
Id: b.InvoiceNumber(hc),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package booking
|
package booking
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rjNemo/rentease/internal/config"
|
"github.com/rjNemo/rentease/internal/config"
|
||||||
|
|
@ -46,10 +46,12 @@ type Service struct {
|
||||||
store Store
|
store Store
|
||||||
parser parserClient
|
parser parserClient
|
||||||
pdf PdfClient
|
pdf PdfClient
|
||||||
|
logger *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(store Store, parser parserClient, pdf PdfClient) (*Service, error) {
|
func NewService(logger *slog.Logger, store Store, parser parserClient, pdf PdfClient) (*Service, error) {
|
||||||
return &Service{
|
return &Service{
|
||||||
|
logger: logger.With(slog.String("component", "booking_service")),
|
||||||
store: store,
|
store: store,
|
||||||
parser: parser,
|
parser: parser,
|
||||||
pdf: pdf,
|
pdf: pdf,
|
||||||
|
|
@ -57,6 +59,7 @@ func NewService(store Store, parser parserClient, pdf PdfClient) (*Service, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs Service) All() []*Line {
|
func (bs Service) All() []*Line {
|
||||||
|
bs.logger.Info("fetching all bookings")
|
||||||
return bs.store.All()
|
return bs.store.All()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,7 +74,7 @@ func (bs Service) Create(From time.Time, To time.Time, Name, PhoneNumber, Email,
|
||||||
b := NewBooking(From, To, Name, PhoneNumber, Email, Platform, CustomerNumber, PlatformFees, externalId)
|
b := NewBooking(From, To, Name, PhoneNumber, Email, Platform, CustomerNumber, PlatformFees, externalId)
|
||||||
err := bs.store.Create(b)
|
err := bs.store.Create(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
bs.logger.Info("failed to create booking", slog.Any("err", err))
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +89,7 @@ func (bs Service) Update(id int, From time.Time, To time.Time, Name string, Phon
|
||||||
) *Booking {
|
) *Booking {
|
||||||
b := NewBooking(From, To, Name, PhoneNumber, Email, Platform, CustomerNumber, PlatformFees, externalId).WithId(id)
|
b := NewBooking(From, To, Name, PhoneNumber, Email, Platform, CustomerNumber, PlatformFees, externalId).WithId(id)
|
||||||
if err := bs.store.Update(b); err != nil {
|
if err := bs.store.Update(b); err != nil {
|
||||||
log.Println(err)
|
bs.logger.Info("failed to create booking", slog.Any("err", err))
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +97,7 @@ func (bs Service) Update(id int, From time.Time, To time.Time, Name string, Phon
|
||||||
func (bs Service) Cancel(id int) {
|
func (bs Service) Cancel(id int) {
|
||||||
err := bs.store.Cancel(id)
|
err := bs.store.Cancel(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
bs.logger.Info("failed to create booking", slog.Any("err", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
7
main.go
7
main.go
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
|
|
@ -11,6 +12,7 @@ import (
|
||||||
"github.com/rjNemo/rentease/assets"
|
"github.com/rjNemo/rentease/assets"
|
||||||
"github.com/rjNemo/rentease/internal/config"
|
"github.com/rjNemo/rentease/internal/config"
|
||||||
"github.com/rjNemo/rentease/internal/driver/database"
|
"github.com/rjNemo/rentease/internal/driver/database"
|
||||||
|
"github.com/rjNemo/rentease/internal/driver/logger"
|
||||||
"github.com/rjNemo/rentease/internal/driver/parser"
|
"github.com/rjNemo/rentease/internal/driver/parser"
|
||||||
"github.com/rjNemo/rentease/internal/driver/pdf"
|
"github.com/rjNemo/rentease/internal/driver/pdf"
|
||||||
bookingRepo "github.com/rjNemo/rentease/internal/repository/booking"
|
bookingRepo "github.com/rjNemo/rentease/internal/repository/booking"
|
||||||
|
|
@ -37,6 +39,9 @@ func run(c context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appLogger := logger.New(appConfig.LogLevel)
|
||||||
|
slog.SetDefault(appLogger)
|
||||||
|
|
||||||
// init sentry
|
// init sentry
|
||||||
if err := sentry.Init(sentry.ClientOptions{
|
if err := sentry.Init(sentry.ClientOptions{
|
||||||
Dsn: appConfig.SentryDsn,
|
Dsn: appConfig.SentryDsn,
|
||||||
|
|
@ -66,7 +71,7 @@ func run(c context.Context) error {
|
||||||
|
|
||||||
parsingClient := parser.NewBookingAgentParser()
|
parsingClient := parser.NewBookingAgentParser()
|
||||||
|
|
||||||
bookingService, err := booking.NewService(bookingStore, parsingClient, pc)
|
bookingService, err := booking.NewService(appLogger, bookingStore, parsingClient, pc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating booking service: %w", err)
|
return fmt.Errorf("error creating booking service: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue