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
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/tigorlazuardi/redmage/pkg/log"
|
"github.com/tigorlazuardi/redmage/pkg/log"
|
||||||
|
"github.com/tigorlazuardi/redmage/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
var serveCmd = &cobra.Command{
|
var serveCmd = &cobra.Command{
|
||||||
Use: "serve",
|
Use: "serve",
|
||||||
Short: "Starts the HTTP Server",
|
Short: "Starts the HTTP Server",
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
hostPort := cfg.String("http.host") + ":" + cfg.String("http.port")
|
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.port": "8080",
|
||||||
"http.host": "0.0.0.0",
|
"http.host": "0.0.0.0",
|
||||||
|
"http.shutdown_timeout": "5s",
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,17 @@ func (ca Caller) File() string {
|
||||||
return ca.Frame.File
|
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 {
|
func (ca Caller) Line() int {
|
||||||
return ca.Frame.Line
|
return ca.Frame.Line
|
||||||
}
|
}
|
||||||
|
@ -35,7 +46,7 @@ func (ca Caller) LogValue() slog.Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
return slog.GroupValue(
|
return slog.GroupValue(
|
||||||
slog.String("file", ca.File()),
|
slog.String("file", ca.ShortFile()),
|
||||||
slog.Int("line", ca.Line()),
|
slog.Int("line", ca.Line()),
|
||||||
slog.String("function", ca.ShortFunction()),
|
slog.String("function", ca.ShortFunction()),
|
||||||
)
|
)
|
||||||
|
|
|
@ -139,11 +139,18 @@ func (er *Err) GetDetails() []any {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (er *Err) Log(ctx context.Context) Error {
|
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
|
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{
|
return &Err{
|
||||||
origin: err,
|
origin: err,
|
||||||
details: details,
|
details: details,
|
||||||
|
|
|
@ -49,6 +49,7 @@ type Entry struct {
|
||||||
handler slog.Handler
|
handler slog.Handler
|
||||||
caller caller.Caller
|
caller caller.Caller
|
||||||
time time.Time
|
time time.Time
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log prepares a new entry to write logs.
|
// Log prepares a new entry to write logs.
|
||||||
|
@ -65,10 +66,18 @@ func (entry *Entry) Caller(caller caller.Caller) *Entry {
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Err(err error) *Entry {
|
||||||
|
entry.err = err
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
func (entry *Entry) Info(message string, fields ...any) {
|
func (entry *Entry) Info(message string, fields ...any) {
|
||||||
record := slog.NewRecord(entry.time, slog.LevelInfo, message, entry.getCaller().PC)
|
record := slog.NewRecord(entry.time, slog.LevelInfo, message, entry.getCaller().PC)
|
||||||
record.AddAttrs(entry.getExtra()...)
|
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)
|
_ = entry.handler.Handle(entry.ctx, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,13 +85,19 @@ func (entry *Entry) Infof(format string, args ...any) {
|
||||||
message := fmt.Sprintf(format, args...)
|
message := fmt.Sprintf(format, args...)
|
||||||
record := slog.NewRecord(entry.time, slog.LevelInfo, message, entry.getCaller().PC)
|
record := slog.NewRecord(entry.time, slog.LevelInfo, message, entry.getCaller().PC)
|
||||||
record.AddAttrs(entry.getExtra()...)
|
record.AddAttrs(entry.getExtra()...)
|
||||||
|
if entry.err != nil {
|
||||||
|
record.AddAttrs(slog.Any("error", entry.err))
|
||||||
|
}
|
||||||
_ = entry.handler.Handle(entry.ctx, record)
|
_ = entry.handler.Handle(entry.ctx, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) Error(message string, fields ...any) {
|
func (entry *Entry) Error(message string, fields ...any) {
|
||||||
record := slog.NewRecord(entry.time, slog.LevelError, message, entry.getCaller().PC)
|
record := slog.NewRecord(entry.time, slog.LevelError, message, entry.getCaller().PC)
|
||||||
record.AddAttrs(entry.getExtra()...)
|
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)
|
_ = entry.handler.Handle(entry.ctx, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,13 +105,19 @@ func (entry *Entry) Errorf(format string, args ...any) {
|
||||||
message := fmt.Sprintf(format, args...)
|
message := fmt.Sprintf(format, args...)
|
||||||
record := slog.NewRecord(entry.time, slog.LevelError, message, entry.getCaller().PC)
|
record := slog.NewRecord(entry.time, slog.LevelError, message, entry.getCaller().PC)
|
||||||
record.AddAttrs(entry.getExtra()...)
|
record.AddAttrs(entry.getExtra()...)
|
||||||
|
if entry.err != nil {
|
||||||
|
record.AddAttrs(slog.Any("details", entry.err))
|
||||||
|
}
|
||||||
_ = entry.handler.Handle(entry.ctx, record)
|
_ = entry.handler.Handle(entry.ctx, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) Debug(message string, fields ...any) {
|
func (entry *Entry) Debug(message string, fields ...any) {
|
||||||
record := slog.NewRecord(entry.time, slog.LevelDebug, message, entry.getCaller().PC)
|
record := slog.NewRecord(entry.time, slog.LevelDebug, message, entry.getCaller().PC)
|
||||||
record.AddAttrs(entry.getExtra()...)
|
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)
|
_ = entry.handler.Handle(entry.ctx, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,13 +125,19 @@ func (entry *Entry) Debugf(format string, args ...any) {
|
||||||
message := fmt.Sprintf(format, args...)
|
message := fmt.Sprintf(format, args...)
|
||||||
record := slog.NewRecord(entry.time, slog.LevelDebug, message, entry.getCaller().PC)
|
record := slog.NewRecord(entry.time, slog.LevelDebug, message, entry.getCaller().PC)
|
||||||
record.AddAttrs(entry.getExtra()...)
|
record.AddAttrs(entry.getExtra()...)
|
||||||
|
if entry.err != nil {
|
||||||
|
record.AddAttrs(slog.Any("error", entry.err))
|
||||||
|
}
|
||||||
_ = entry.handler.Handle(entry.ctx, record)
|
_ = entry.handler.Handle(entry.ctx, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (entry *Entry) Warn(message string, fields ...any) {
|
func (entry *Entry) Warn(message string, fields ...any) {
|
||||||
record := slog.NewRecord(entry.time, slog.LevelWarn, message, entry.getCaller().PC)
|
record := slog.NewRecord(entry.time, slog.LevelWarn, message, entry.getCaller().PC)
|
||||||
record.AddAttrs(entry.getExtra()...)
|
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)
|
_ = entry.handler.Handle(entry.ctx, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +145,9 @@ func (entry *Entry) Warnf(format string, args ...any) {
|
||||||
message := fmt.Sprintf(format, args...)
|
message := fmt.Sprintf(format, args...)
|
||||||
record := slog.NewRecord(entry.time, slog.LevelWarn, message, entry.getCaller().PC)
|
record := slog.NewRecord(entry.time, slog.LevelWarn, message, entry.getCaller().PC)
|
||||||
record.AddAttrs(entry.getExtra()...)
|
record.AddAttrs(entry.getExtra()...)
|
||||||
|
if entry.err != nil {
|
||||||
|
record.AddAttrs(slog.Any("error", entry.err))
|
||||||
|
}
|
||||||
_ = entry.handler.Handle(entry.ctx, record)
|
_ = 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)
|
_ = serializer.Handle(ctx, record)
|
||||||
if jsonBuf.Len() > 3 { // Ignore empty json like "{}\n"
|
if jsonBuf.Len() > 3 { // Ignore empty json like "{}\n"
|
||||||
_ = json.Indent(buf, jsonBuf.Bytes(), "", " ")
|
_ = 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()
|
pr.mu.Lock()
|
||||||
defer pr.mu.Unlock()
|
defer pr.mu.Unlock()
|
||||||
|
|
|
@ -1,19 +1,44 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/tigorlazuardi/redmage/config"
|
"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/api"
|
||||||
"github.com/tigorlazuardi/redmage/server/routes/htmx"
|
"github.com/tigorlazuardi/redmage/server/routes/htmx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
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 {
|
func New(cfg *config.Config) *Server {
|
||||||
|
@ -23,7 +48,10 @@ func New(cfg *config.Config) *Server {
|
||||||
|
|
||||||
router.Route("/htmx", htmx.Register)
|
router.Route("/htmx", htmx.Register)
|
||||||
|
|
||||||
return &Server{
|
server := &http.Server{
|
||||||
handler: router,
|
Handler: router,
|
||||||
|
Addr: cfg.String("http.host") + ":" + cfg.String("http.port"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &Server{server: server, config: cfg}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue