add better logs
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"acc-server-manager/local/controller"
|
"acc-server-manager/local/controller"
|
||||||
"acc-server-manager/local/utl/common"
|
"acc-server-manager/local/utl/common"
|
||||||
"acc-server-manager/local/utl/configs"
|
"acc-server-manager/local/utl/configs"
|
||||||
|
"acc-server-manager/local/utl/logging"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@@ -40,13 +41,13 @@ func Init(di *dig.Container, app *fiber.App) {
|
|||||||
return routeGroups
|
return routeGroups
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("unable to bind routes")
|
logging.Panic("unable to bind routes")
|
||||||
}
|
}
|
||||||
err = di.Provide(func() *dig.Container {
|
err = di.Provide(func() *dig.Container {
|
||||||
return di
|
return di
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("unable to bind dig")
|
logging.Panic("unable to bind dig")
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.InitializeControllers(di)
|
controller.InitializeControllers(di)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// BaseFilter contains common filter fields that can be embedded in other filters
|
// BaseFilter contains common filter fields that can be embedded in other filters
|
||||||
type BaseFilter struct {
|
type BaseFilter struct {
|
||||||
@@ -21,15 +23,6 @@ type ServerBasedFilter struct {
|
|||||||
ServerID int `param:"id"`
|
ServerID int `param:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerFilter defines filtering options for Server queries
|
|
||||||
type ServerFilter struct {
|
|
||||||
BaseFilter
|
|
||||||
ServerBasedFilter
|
|
||||||
Name string `query:"name"`
|
|
||||||
ServiceName string `query:"service_name"`
|
|
||||||
Status string `query:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigFilter defines filtering options for Config queries
|
// ConfigFilter defines filtering options for Config queries
|
||||||
type ConfigFilter struct {
|
type ConfigFilter struct {
|
||||||
BaseFilter
|
BaseFilter
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package model
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server represents an ACC server instance
|
// Server represents an ACC server instance
|
||||||
@@ -49,4 +51,23 @@ type ServerState struct {
|
|||||||
SessionDurationMinutes int `json:"sessionDurationMinutes"`
|
SessionDurationMinutes int `json:"sessionDurationMinutes"`
|
||||||
// Players map[int]*PlayerState
|
// Players map[int]*PlayerState
|
||||||
// etc.
|
// etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerFilter defines filtering options for Server queries
|
||||||
|
type ServerFilter struct {
|
||||||
|
BaseFilter
|
||||||
|
ServerBasedFilter
|
||||||
|
Name string `query:"name"`
|
||||||
|
ServiceName string `query:"service_name"`
|
||||||
|
Status string `query:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyFilter implements the Filterable interface
|
||||||
|
func (f *ServerFilter) ApplyFilter(query *gorm.DB) *gorm.DB {
|
||||||
|
// Apply server filter
|
||||||
|
if f.ServerID != 0 {
|
||||||
|
query = query.Where("id = ?", f.ServerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
// StateHistoryFilter combines common filter capabilities
|
// StateHistoryFilter combines common filter capabilities
|
||||||
type StateHistoryFilter struct {
|
type StateHistoryFilter struct {
|
||||||
BaseFilter // Adds pagination and sorting
|
|
||||||
ServerBasedFilter // Adds server ID from path parameter
|
ServerBasedFilter // Adds server ID from path parameter
|
||||||
DateRangeFilter // Adds date range filtering
|
DateRangeFilter // Adds date range filtering
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"acc-server-manager/local/model"
|
"acc-server-manager/local/model"
|
||||||
"acc-server-manager/local/repository"
|
"acc-server-manager/local/repository"
|
||||||
|
"acc-server-manager/local/utl/logging"
|
||||||
"acc-server-manager/local/utl/tracking"
|
"acc-server-manager/local/utl/tracking"
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
@@ -58,7 +59,7 @@ func NewServerService(repository *repository.ServerRepository, stateHistoryRepo
|
|||||||
// Initialize instances for all servers
|
// Initialize instances for all servers
|
||||||
servers, err := repository.GetAll(context.Background(), &model.ServerFilter{})
|
servers, err := repository.GetAll(context.Background(), &model.ServerFilter{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err.Error())
|
logging.Error("Failed to get servers: %v", err)
|
||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +107,7 @@ func (s *ServerService) updateSessionDuration(server *model.Server, sessionType
|
|||||||
// Try to load sessions from config
|
// Try to load sessions from config
|
||||||
event, err := DecodeFileName(EventJson)(server.ConfigPath)
|
event, err := DecodeFileName(EventJson)(server.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to load event config for server %d: %v", server.ID, err)
|
logging.Error("Failed to load event config for server %d: %v", server.ID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
evt := event.(model.EventConfig)
|
evt := event.(model.EventConfig)
|
||||||
@@ -115,7 +116,7 @@ func (s *ServerService) updateSessionDuration(server *model.Server, sessionType
|
|||||||
}
|
}
|
||||||
|
|
||||||
sessions := sessionsInterface.([]model.Session)
|
sessions := sessionsInterface.([]model.Session)
|
||||||
if (sessionType == "" && len(sessions) > 0) {
|
if sessionType == "" && len(sessions) > 0 {
|
||||||
sessionType = sessions[0].SessionType
|
sessionType = sessions[0].SessionType
|
||||||
}
|
}
|
||||||
for _, session := range sessions {
|
for _, session := range sessions {
|
||||||
@@ -200,24 +201,25 @@ func (s *ServerService) StartAccServerRuntime(server *model.Server) {
|
|||||||
// context.Context: Application context
|
// context.Context: Application context
|
||||||
// Returns:
|
// Returns:
|
||||||
// string: Application version
|
// string: Application version
|
||||||
func (as ServerService) GetAll(ctx *fiber.Ctx, filter *model.ServerFilter) (*[]model.Server, error) {
|
func (s ServerService) GetAll(ctx *fiber.Ctx, filter *model.ServerFilter) (*[]model.Server, error) {
|
||||||
servers, err := as.repository.GetAll(ctx.UserContext(), filter)
|
servers, err := s.repository.GetAll(ctx.UserContext(), filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logging.Error("Failed to get servers: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, server := range *servers {
|
for i, server := range *servers {
|
||||||
status, err := as.apiService.StatusServer(server.ServiceName)
|
status, err := s.apiService.StatusServer(server.ServiceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err.Error())
|
logging.Error("Failed to get status for server %s: %v", server.ServiceName, err)
|
||||||
}
|
}
|
||||||
(*servers)[i].Status = model.ParseServiceStatus(status)
|
(*servers)[i].Status = model.ParseServiceStatus(status)
|
||||||
instance, ok := as.instances.Load(server.ID)
|
instance, ok := s.instances.Load(server.ID)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Print("Unable to retrieve instance for server of ID: ", server.ID)
|
logging.Warn("No instance found for server ID: %d", server.ID)
|
||||||
} else {
|
} else {
|
||||||
serverInstance := instance.(*tracking.AccServerInstance)
|
serverInstance := instance.(*tracking.AccServerInstance)
|
||||||
if (serverInstance.State != nil) {
|
if serverInstance.State != nil {
|
||||||
(*servers)[i].State = *serverInstance.State
|
(*servers)[i].State = *serverInstance.State
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,12 +155,18 @@ func parsePathParam(c *fiber.Ctx, field reflect.Value, paramName string) error {
|
|||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
val, err := c.ParamsInt(paramName)
|
val, err := c.ParamsInt(paramName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "strconv.Atoi: parsing \"\": invalid syntax") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
field.SetInt(int64(val))
|
field.SetInt(int64(val))
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
val, err := c.ParamsInt(paramName)
|
val, err := c.ParamsInt(paramName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "strconv.Atoi: parsing \"\": invalid syntax") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
field.SetUint(uint64(val))
|
field.SetUint(uint64(val))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"acc-server-manager/local/model"
|
"acc-server-manager/local/model"
|
||||||
|
"acc-server-manager/local/utl/logging"
|
||||||
|
|
||||||
"go.uber.org/dig"
|
"go.uber.org/dig"
|
||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
@@ -11,13 +12,13 @@ import (
|
|||||||
func Start(di *dig.Container) {
|
func Start(di *dig.Container) {
|
||||||
db, err := gorm.Open(sqlite.Open("acc.db"), &gorm.Config{})
|
db, err := gorm.Open(sqlite.Open("acc.db"), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to connect database")
|
logging.Panic("failed to connect database")
|
||||||
}
|
}
|
||||||
err = di.Provide(func() *gorm.DB {
|
err = di.Provide(func() *gorm.DB {
|
||||||
return db
|
return db
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to bind database")
|
logging.Panic("failed to bind database")
|
||||||
}
|
}
|
||||||
Migrate(db)
|
Migrate(db)
|
||||||
}
|
}
|
||||||
@@ -25,39 +26,39 @@ func Start(di *dig.Container) {
|
|||||||
func Migrate(db *gorm.DB) {
|
func Migrate(db *gorm.DB) {
|
||||||
err := db.AutoMigrate(&model.ApiModel{})
|
err := db.AutoMigrate(&model.ApiModel{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.ApiModel")
|
logging.Panic("failed to migrate model.ApiModel")
|
||||||
}
|
}
|
||||||
err = db.AutoMigrate(&model.Server{})
|
err = db.AutoMigrate(&model.Server{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.Server")
|
logging.Panic("failed to migrate model.Server")
|
||||||
}
|
}
|
||||||
err = db.AutoMigrate(&model.Config{})
|
err = db.AutoMigrate(&model.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.Config")
|
logging.Panic("failed to migrate model.Config")
|
||||||
}
|
}
|
||||||
err = db.AutoMigrate(&model.Track{})
|
err = db.AutoMigrate(&model.Track{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.Track")
|
logging.Panic("failed to migrate model.Track")
|
||||||
}
|
}
|
||||||
err = db.AutoMigrate(&model.CarModel{})
|
err = db.AutoMigrate(&model.CarModel{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.CarModel")
|
logging.Panic("failed to migrate model.CarModel")
|
||||||
}
|
}
|
||||||
err = db.AutoMigrate(&model.CupCategory{})
|
err = db.AutoMigrate(&model.CupCategory{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.CupCategory")
|
logging.Panic("failed to migrate model.CupCategory")
|
||||||
}
|
}
|
||||||
err = db.AutoMigrate(&model.DriverCategory{})
|
err = db.AutoMigrate(&model.DriverCategory{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.DriverCategory")
|
logging.Panic("failed to migrate model.DriverCategory")
|
||||||
}
|
}
|
||||||
err = db.AutoMigrate(&model.SessionType{})
|
err = db.AutoMigrate(&model.SessionType{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.SessionType")
|
logging.Panic("failed to migrate model.SessionType")
|
||||||
}
|
}
|
||||||
err = db.AutoMigrate(&model.StateHistory{})
|
err = db.AutoMigrate(&model.StateHistory{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.StateHistory")
|
logging.Panic("failed to migrate model.StateHistory")
|
||||||
}
|
}
|
||||||
db.FirstOrCreate(&model.ApiModel{Api: "Works"})
|
db.FirstOrCreate(&model.ApiModel{Api: "Works"})
|
||||||
|
|
||||||
|
|||||||
149
local/utl/logging/logger.go
Normal file
149
local/utl/logging/logger.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logger *Logger
|
||||||
|
once sync.Once
|
||||||
|
timeFormat = "2006-01-02 15:04:05.000"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
file *os.File
|
||||||
|
logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize creates or gets the singleton logger instance
|
||||||
|
func Initialize() (*Logger, error) {
|
||||||
|
var err error
|
||||||
|
once.Do(func() {
|
||||||
|
logger, err = newLogger()
|
||||||
|
})
|
||||||
|
return logger, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLogger() (*Logger, error) {
|
||||||
|
// Ensure logs directory exists
|
||||||
|
if err := os.MkdirAll("logs", 0755); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create logs directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open log file with date in name
|
||||||
|
logPath := filepath.Join("logs", fmt.Sprintf("acc-server-%s.log", time.Now().Format("2006-01-02")))
|
||||||
|
file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open log file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create multi-writer for both file and console
|
||||||
|
multiWriter := io.MultiWriter(file, os.Stdout)
|
||||||
|
|
||||||
|
// Create logger with custom prefix
|
||||||
|
logger := &Logger{
|
||||||
|
file: file,
|
||||||
|
logger: log.New(multiWriter, "", 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
return logger, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Close() error {
|
||||||
|
if l.file != nil {
|
||||||
|
return l.file.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) log(level, format string, v ...interface{}) {
|
||||||
|
// Get caller info
|
||||||
|
_, file, line, _ := runtime.Caller(2)
|
||||||
|
file = filepath.Base(file)
|
||||||
|
|
||||||
|
// Format message
|
||||||
|
msg := fmt.Sprintf(format, v...)
|
||||||
|
|
||||||
|
// Format final log line
|
||||||
|
logLine := fmt.Sprintf("[%s] [%s] [%s:%d] %s",
|
||||||
|
time.Now().Format(timeFormat),
|
||||||
|
level,
|
||||||
|
file,
|
||||||
|
line,
|
||||||
|
msg,
|
||||||
|
)
|
||||||
|
|
||||||
|
l.logger.Println(logLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Info(format string, v ...interface{}) {
|
||||||
|
l.log("INFO", format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Error(format string, v ...interface{}) {
|
||||||
|
l.log("ERROR", format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Warn(format string, v ...interface{}) {
|
||||||
|
l.log("WARN", format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Debug(format string, v ...interface{}) {
|
||||||
|
l.log("DEBUG", format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Panic(format string) {
|
||||||
|
l.Panic("PANIC " + format)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global convenience functions
|
||||||
|
func Info(format string, v ...interface{}) {
|
||||||
|
if logger != nil {
|
||||||
|
logger.Info(format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(format string, v ...interface{}) {
|
||||||
|
if logger != nil {
|
||||||
|
logger.Error(format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warn(format string, v ...interface{}) {
|
||||||
|
if logger != nil {
|
||||||
|
logger.Warn(format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debug(format string, v ...interface{}) {
|
||||||
|
if logger != nil {
|
||||||
|
logger.Debug(format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Panic(format string) {
|
||||||
|
if logger != nil {
|
||||||
|
logger.Panic(format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecoverAndLog recovers from panics and logs them
|
||||||
|
func RecoverAndLog() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if logger != nil {
|
||||||
|
// Get stack trace
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
n := runtime.Stack(buf, false)
|
||||||
|
stackTrace := string(buf[:n])
|
||||||
|
|
||||||
|
logger.log("PANIC", "Recovered from panic: %v\nStack Trace:\n%s", r, stackTrace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,8 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"acc-server-manager/local/api"
|
"acc-server-manager/local/api"
|
||||||
"acc-server-manager/local/utl/common"
|
"acc-server-manager/local/utl/logging"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
@@ -14,6 +13,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Start(di *dig.Container) *fiber.App {
|
func Start(di *dig.Container) *fiber.App {
|
||||||
|
// Initialize logger
|
||||||
|
logger, err := logging.Initialize()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to initialize logger: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer logger.Close()
|
||||||
|
|
||||||
|
// Set up panic recovery
|
||||||
|
defer logging.RecoverAndLog()
|
||||||
|
|
||||||
app := fiber.New(fiber.Config{
|
app := fiber.New(fiber.Config{
|
||||||
EnablePrintRoutes: true,
|
EnablePrintRoutes: true,
|
||||||
})
|
})
|
||||||
@@ -22,22 +32,22 @@ func Start(di *dig.Container) *fiber.App {
|
|||||||
|
|
||||||
app.Get("/swagger/*", swagger.HandlerDefault)
|
app.Get("/swagger/*", swagger.HandlerDefault)
|
||||||
|
|
||||||
file, err := os.OpenFile("logs.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Cannot open file logs.log")
|
|
||||||
}
|
|
||||||
log.SetOutput(file)
|
|
||||||
|
|
||||||
api.Init(di, app)
|
api.Init(di, app)
|
||||||
|
|
||||||
app.Get("/ping", func(c *fiber.Ctx) error {
|
app.Get("/ping", func(c *fiber.Ctx) error {
|
||||||
return c.SendString("pong")
|
return c.SendString("pong")
|
||||||
})
|
})
|
||||||
|
|
||||||
port := os.Getenv("PORT")
|
port := os.Getenv("PORT")
|
||||||
err = app.Listen(":" + port)
|
if port == "" {
|
||||||
if err != nil {
|
port = "3000" // Default port
|
||||||
msg := fmt.Sprintf("Running on %s:%s", common.GetIP(), port)
|
|
||||||
println(msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logging.Info("Starting server on port %s", port)
|
||||||
|
if err := app.Listen(":" + port); err != nil {
|
||||||
|
logging.Error("Failed to start server: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user