server: server now starts properly
This commit is contained in:
parent
94e5a3b127
commit
7de31e87d5
22
cli/serve.go
22
cli/serve.go
|
@ -1,20 +1,34 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tigorlazuardi/redmage/pkg/log"
|
||||
"github.com/tigorlazuardi/redmage/server"
|
||||
)
|
||||
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Starts the HTTP Server",
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
hostPort := cfg.String("http.host") + ":" + cfg.String("http.port")
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
server := server.New(cfg)
|
||||
|
||||
log.Log(cmd.Context()).Info("starting http server", "host", hostPort)
|
||||
exit := make(chan struct{}, 1)
|
||||
|
||||
return nil
|
||||
go func() {
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
<-sig
|
||||
exit <- struct{}{}
|
||||
}()
|
||||
|
||||
if err := server.Start(exit); err != nil {
|
||||
log.Log(cmd.Context()).Err(err).Error("failed to start server")
|
||||
os.Exit(1)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -13,4 +13,5 @@ var DefaultConfig = map[string]any{
|
|||
|
||||
"http.port": "8080",
|
||||
"http.host": "0.0.0.0",
|
||||
"http.shutdown_timeout": "5s",
|
||||
}
|
||||
|
|
|
@ -16,6 +16,17 @@ func (ca Caller) File() string {
|
|||
return ca.Frame.File
|
||||
}
|
||||
|
||||
func (ca Caller) ShortFile() string {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return ca.Frame.File
|
||||
}
|
||||
if after, found := strings.CutPrefix(ca.Frame.File, wd); found {
|
||||
return strings.TrimPrefix(after, string(os.PathSeparator))
|
||||
}
|
||||
return ca.Frame.File
|
||||
}
|
||||
|
||||
func (ca Caller) Line() int {
|
||||
return ca.Frame.Line
|
||||
}
|
||||
|
@ -35,7 +46,7 @@ func (ca Caller) LogValue() slog.Value {
|
|||
}
|
||||
|
||||
return slog.GroupValue(
|
||||
slog.String("file", ca.File()),
|
||||
slog.String("file", ca.ShortFile()),
|
||||
slog.Int("line", ca.Line()),
|
||||
slog.String("function", ca.ShortFunction()),
|
||||
)
|
||||
|
|
|
@ -139,11 +139,18 @@ func (er *Err) GetDetails() []any {
|
|||
}
|
||||
|
||||
func (er *Err) Log(ctx context.Context) Error {
|
||||
log.Log(ctx).Caller(er.caller).Error(er.message, "error", er)
|
||||
log.Log(ctx).Caller(er.caller).Err(er).Error(er.message)
|
||||
return er
|
||||
}
|
||||
|
||||
func Wrap(err error, message string, details ...any) Error {
|
||||
func Wrap(err error) Error {
|
||||
return &Err{
|
||||
origin: err,
|
||||
caller: caller.New(3),
|
||||
}
|
||||
}
|
||||
|
||||
func Wrapw(err error, message string, details ...any) Error {
|
||||
return &Err{
|
||||
origin: err,
|
||||
details: details,
|
||||
|
|
|
@ -49,6 +49,7 @@ type Entry struct {
|
|||
handler slog.Handler
|
||||
caller caller.Caller
|
||||
time time.Time
|
||||
err error
|
||||
}
|
||||
|
||||
// Log prepares a new entry to write logs.
|
||||
|
@ -65,10 +66,18 @@ func (entry *Entry) Caller(caller caller.Caller) *Entry {
|
|||
return entry
|
||||
}
|
||||
|
||||
func (entry *Entry) Err(err error) *Entry {
|
||||
entry.err = err
|
||||
return entry
|
||||
}
|
||||
|
||||
func (entry *Entry) Info(message string, fields ...any) {
|
||||
record := slog.NewRecord(entry.time, slog.LevelInfo, message, entry.getCaller().PC)
|
||||
record.AddAttrs(entry.getExtra()...)
|
||||
record.AddAttrs(slog.Group("context", fields...))
|
||||
record.AddAttrs(slog.Group("details", fields...))
|
||||
if entry.err != nil {
|
||||
record.AddAttrs(slog.Any("error", entry.err))
|
||||
}
|
||||
_ = entry.handler.Handle(entry.ctx, record)
|
||||
}
|
||||
|
||||
|
@ -76,13 +85,19 @@ func (entry *Entry) Infof(format string, args ...any) {
|
|||
message := fmt.Sprintf(format, args...)
|
||||
record := slog.NewRecord(entry.time, slog.LevelInfo, message, entry.getCaller().PC)
|
||||
record.AddAttrs(entry.getExtra()...)
|
||||
if entry.err != nil {
|
||||
record.AddAttrs(slog.Any("error", entry.err))
|
||||
}
|
||||
_ = entry.handler.Handle(entry.ctx, record)
|
||||
}
|
||||
|
||||
func (entry *Entry) Error(message string, fields ...any) {
|
||||
record := slog.NewRecord(entry.time, slog.LevelError, message, entry.getCaller().PC)
|
||||
record.AddAttrs(entry.getExtra()...)
|
||||
record.AddAttrs(slog.Group("context", fields...))
|
||||
record.AddAttrs(slog.Group("details", fields...))
|
||||
if entry.err != nil {
|
||||
record.AddAttrs(slog.Any("error", entry.err))
|
||||
}
|
||||
_ = entry.handler.Handle(entry.ctx, record)
|
||||
}
|
||||
|
||||
|
@ -90,13 +105,19 @@ func (entry *Entry) Errorf(format string, args ...any) {
|
|||
message := fmt.Sprintf(format, args...)
|
||||
record := slog.NewRecord(entry.time, slog.LevelError, message, entry.getCaller().PC)
|
||||
record.AddAttrs(entry.getExtra()...)
|
||||
if entry.err != nil {
|
||||
record.AddAttrs(slog.Any("details", entry.err))
|
||||
}
|
||||
_ = entry.handler.Handle(entry.ctx, record)
|
||||
}
|
||||
|
||||
func (entry *Entry) Debug(message string, fields ...any) {
|
||||
record := slog.NewRecord(entry.time, slog.LevelDebug, message, entry.getCaller().PC)
|
||||
record.AddAttrs(entry.getExtra()...)
|
||||
record.AddAttrs(slog.Group("context", fields...))
|
||||
record.AddAttrs(slog.Group("details", fields...))
|
||||
if entry.err != nil {
|
||||
record.AddAttrs(slog.Any("error", entry.err))
|
||||
}
|
||||
_ = entry.handler.Handle(entry.ctx, record)
|
||||
}
|
||||
|
||||
|
@ -104,13 +125,19 @@ func (entry *Entry) Debugf(format string, args ...any) {
|
|||
message := fmt.Sprintf(format, args...)
|
||||
record := slog.NewRecord(entry.time, slog.LevelDebug, message, entry.getCaller().PC)
|
||||
record.AddAttrs(entry.getExtra()...)
|
||||
if entry.err != nil {
|
||||
record.AddAttrs(slog.Any("error", entry.err))
|
||||
}
|
||||
_ = entry.handler.Handle(entry.ctx, record)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warn(message string, fields ...any) {
|
||||
record := slog.NewRecord(entry.time, slog.LevelWarn, message, entry.getCaller().PC)
|
||||
record.AddAttrs(entry.getExtra()...)
|
||||
record.AddAttrs(slog.Group("context", fields...))
|
||||
record.AddAttrs(slog.Group("details", fields...))
|
||||
if entry.err != nil {
|
||||
record.AddAttrs(slog.Any("error", entry.err))
|
||||
}
|
||||
_ = entry.handler.Handle(entry.ctx, record)
|
||||
}
|
||||
|
||||
|
@ -118,6 +145,9 @@ func (entry *Entry) Warnf(format string, args ...any) {
|
|||
message := fmt.Sprintf(format, args...)
|
||||
record := slog.NewRecord(entry.time, slog.LevelWarn, message, entry.getCaller().PC)
|
||||
record.AddAttrs(entry.getExtra()...)
|
||||
if entry.err != nil {
|
||||
record.AddAttrs(slog.Any("error", entry.err))
|
||||
}
|
||||
_ = entry.handler.Handle(entry.ctx, record)
|
||||
}
|
||||
|
||||
|
|
|
@ -118,10 +118,8 @@ func (pr *PrettyHandler) Handle(ctx context.Context, record slog.Record) error {
|
|||
_ = serializer.Handle(ctx, record)
|
||||
if jsonBuf.Len() > 3 { // Ignore empty json like "{}\n"
|
||||
_ = json.Indent(buf, jsonBuf.Bytes(), "", " ")
|
||||
// json indent includes new line, no need to add extra new line.
|
||||
} else {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
|
||||
pr.mu.Lock()
|
||||
defer pr.mu.Unlock()
|
||||
|
|
|
@ -1,19 +1,44 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/tigorlazuardi/redmage/config"
|
||||
"github.com/tigorlazuardi/redmage/pkg/caller"
|
||||
"github.com/tigorlazuardi/redmage/pkg/errs"
|
||||
"github.com/tigorlazuardi/redmage/pkg/log"
|
||||
"github.com/tigorlazuardi/redmage/server/routes/api"
|
||||
"github.com/tigorlazuardi/redmage/server/routes/htmx"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
handler http.Handler
|
||||
server *http.Server
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
func (svr *Server) Serve() {
|
||||
func (srv *Server) Start(exit <-chan struct{}) error {
|
||||
errch := make(chan error, 1)
|
||||
caller := caller.New(3)
|
||||
go func() {
|
||||
log.Log(context.Background()).Caller(caller).Info("starting http server", "address", "http://"+srv.server.Addr)
|
||||
errch <- srv.server.ListenAndServe()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-exit:
|
||||
log.Log(context.Background()).Caller(caller).Info("received exit signal. shutting down server")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), srv.config.Duration("http.shutdown_timeout"))
|
||||
defer cancel()
|
||||
return srv.server.Shutdown(ctx)
|
||||
case err := <-errch:
|
||||
if errors.Is(err, http.ErrServerClosed) {
|
||||
return nil
|
||||
}
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
func New(cfg *config.Config) *Server {
|
||||
|
@ -23,7 +48,10 @@ func New(cfg *config.Config) *Server {
|
|||
|
||||
router.Route("/htmx", htmx.Register)
|
||||
|
||||
return &Server{
|
||||
handler: router,
|
||||
server := &http.Server{
|
||||
Handler: router,
|
||||
Addr: cfg.String("http.host") + ":" + cfg.String("http.port"),
|
||||
}
|
||||
|
||||
return &Server{server: server, config: cfg}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue