From 2a89076d1e5d7c1b1ca01a6d88601247d5b2b677 Mon Sep 17 00:00:00 2001 From: amrshaban2005 Date: Wed, 15 Jul 2026 22:45:03 +0400 Subject: [PATCH 1/4] Start slog logger migration --- backend/backend.go | 36 +++++++++---------- backend/user.go | 5 +-- cache/cache.go | 31 ++++++++-------- cache/cache_test.go | 10 +++--- cache/dev.go | 22 ++++++------ cache/observer/observer.go | 10 +++--- cmd/main.go | 4 +-- database/mongo/base.go | 17 ++++----- database/mongo/mongo.go | 5 +-- database/postgresql/base.go | 9 ++--- database/postgresql/postgresql.go | 6 ++-- database/postgresql/sb.go | 3 +- database/sqlite/base.go | 9 ++--- database/sqlite/sqlite.go | 15 ++------ function/runtime.go | 5 ++- function/runtime_search_test.go | 4 +-- function/scheduler.go | 44 +++++++++++------------ function/scheduler_test.go | 6 ++-- function/subscriber.go | 27 +++++++------- logger/logger.go | 60 ++++++++++++++++--------------- middleware/telemetry.go | 8 ++--- realtime/broker.go | 21 +++++------ server.go | 9 +++-- 23 files changed, 172 insertions(+), 194 deletions(-) diff --git a/backend/backend.go b/backend/backend.go index 6969112..2440571 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -118,6 +118,7 @@ import ( "database/sql" "errors" "fmt" + "log/slog" "os" "strings" "sync" @@ -158,8 +159,6 @@ var ( // Cache initialized Volatilizer for cache and pub/sub Cache cache.Volatilizer Search *search.Search - // Log initialized Logger for all logging - Log *logger.Logger // Membership exposes Account and User functionalities like register, login, etc // account and user functionalities. @@ -181,21 +180,20 @@ var ( // Setup initializes the core services based on the configuration received. func Setup(cfg config.AppConfig) { + logger.Setup(cfg) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - if err := Close(ctx); err != nil && Log != nil { - Log.Error().Err(err).Msg("error closing existing backend services") + if err := Close(ctx); err != nil { + slog.Error("error closing existing backend services", "error", err) } cancel() Config = cfg resetLifecycle() - Log = logger.Get(cfg) - if strings.EqualFold(cfg.DatabaseURL, "mem") || strings.EqualFold(cfg.RedisHost, "mem") { - Cache = cache.NewDevCache(Log) + Cache = cache.NewDevCache() } else { - Cache = cache.NewCache(Log) + Cache = cache.NewCache() } persister := config.Current.DataStore @@ -204,23 +202,23 @@ func Setup(cfg config.AppConfig) { } else if strings.EqualFold(persister, "mongo") { cl, err := openMongoDatabase(cfg.DatabaseURL) if err != nil { - Log.Fatal().Err(err).Msg("failed to create connection with mongodb") + logger.FatalError("failed to create connection with mongodb", err) } - DB = mongo.New(cl, Cache.PublishDocument, Log) + DB = mongo.New(cl, Cache.PublishDocument) } else if strings.EqualFold(persister, "sqlite") { cl, err := openSQLite(cfg.DatabaseURL) if err != nil { - Log.Fatal().Err(err).Msg("failed to create connection with SQLite") + logger.FatalError("failed to create connection with SQLite", err) } - DB = sqlite.New(cl, Cache.PublishDocument, Log) + DB = sqlite.New(cl, Cache.PublishDocument) } else { cl, err := openPGDatabase(cfg.DatabaseURL) if err != nil { - Log.Fatal().Err(err).Msg("failed to create connection with postgres") + logger.FatalError("failed to create connection with postgres", err) } - DB = postgresql.New(cl, Cache.PublishDocument, Log) + DB = postgresql.New(cl, Cache.PublishDocument) } mp := cfg.MailProvider @@ -246,14 +244,14 @@ func Setup(cfg config.AppConfig) { } src, err := search.New(ftsFilename, Cache) if err != nil { - Log.Fatal().Err(err).Msg("unable to start full-text search") + logger.FatalError("unable to start full-text search", err) return } Search = src } - sub := &function.Subscriber{Log: Log} + sub := &function.Subscriber{} sub.PubSub = Cache sub.GetExecEnv = func(msg model.Command) (*function.ExecutionEnvironment, error) { exe := &function.ExecutionEnvironment{ @@ -263,7 +261,6 @@ func Setup(cfg config.AppConfig) { Volatile: Cache, Search: Search, Email: Emailer, - Log: Log, } return exe, nil @@ -274,7 +271,7 @@ func Setup(cfg config.AppConfig) { // if no value is provided, like on GH action for tests, we assume primary isPrimary = true } else if hostname, err := os.Hostname(); err != nil { - Log.Warn().Err(err).Msg("cannot determine if it's primary instance") + slog.Warn("cannot determine if it's primary instance", "error", err) } else if strings.EqualFold(hostname, cfg.PrimaryInstanceHostname) { isPrimary = true } @@ -300,12 +297,11 @@ func Setup(cfg config.AppConfig) { DataStore: DB, Search: Search, Email: Emailer, - Log: Log, } Scheduler = runner go runner.Start() - Log.Info().Msg("job scheduler / runner started on primary instance") + slog.Info("job scheduler / runner started on primary instance") } Membership = newUser diff --git a/backend/user.go b/backend/user.go index 93e3ae4..d546d0b 100644 --- a/backend/user.go +++ b/backend/user.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "math/rand" "strings" "time" @@ -249,7 +250,7 @@ func (u User) publishAccountCreated(accountID, email string, tok model.User) { } b, err := json.Marshal(data) if err != nil { - Log.Error().Err(err).Msg("error marshaling system account event") + slog.Error("error marshaling system account event", "error", err) return } @@ -260,7 +261,7 @@ func (u User) publishAccountCreated(accountID, email string, tok model.User) { Auth: auth, Base: u.conf.Name, }); err != nil { - Log.Error().Err(err).Msg("error publishing system account event") + slog.Error("error publishing system account event", "error", err) } } diff --git a/cache/cache.go b/cache/cache.go index 509fcb5..21242c7 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "time" "github.com/staticbackendhq/core/config" @@ -26,18 +27,17 @@ const ( type Cache struct { Rdb *redis.Client Ctx context.Context - log *logger.Logger } // NewCache returns an initiated Redis client -func NewCache(log *logger.Logger) *Cache { +func NewCache() *Cache { var err error var opt *redis.Options if uri := config.Current.RedisURL; len(uri) > 0 { opt, err = redis.ParseURL(uri) if err != nil { - log.Fatal().Err(err).Msg("invalid REDIS_URL value") + logger.FatalError("invalid REDIS_URL value", err) } } else { opt = &redis.Options{ @@ -51,7 +51,6 @@ func NewCache(log *logger.Logger) *Cache { return &Cache{ Rdb: rdb, Ctx: context.Background(), - log: log, } } @@ -113,12 +112,12 @@ func (c *Cache) Subscribe(send chan model.Command, token, channel string, close pubsub := c.Rdb.Subscribe(c.Ctx, channel) if _, err := pubsub.Receive(c.Ctx); err != nil { - c.log.Error().Err(err).Msg("error establishing PubSub subscription") + slog.Error("error establishing PubSub subscription", "error", err) return } defer func() { if err := pubsub.Close(); err != nil && !errors.Is(err, redis.ErrClosed) { - c.log.Warn().Err(err).Msg("error closing PubSub subscription") + slog.Warn("error closing PubSub subscription", "error", err) } }() @@ -132,13 +131,13 @@ func (c *Cache) Subscribe(send chan model.Command, token, channel string, close select { case m, ok := <-ch: if !ok { - c.log.Warn().Msgf("PubSub channel closed: %s", channel) + slog.Warn("PubSub channel closed", "channel", channel) return } var msg model.Command if err := json.Unmarshal([]byte(m.Payload), &msg); err != nil { - c.log.Error().Err(err).Msg("error parsing JSON message") + slog.Error("error parsing JSON message", "error", err) return } @@ -169,7 +168,7 @@ func (c *Cache) sendMessage(send chan model.Command, close chan bool, msg model. case <-close: return false case <-timer.C: - c.log.Warn().Msgf("dropping PubSub message after blocked receiver timeout: %s", msg.Channel) + slog.Warn("dropping PubSub message after blocked receiver timeout", "channel", msg.Channel) return true } } @@ -192,27 +191,27 @@ func (c *Cache) Publish(msg model.Command) error { sysmsg.IsSystemEvent = true b, err := json.Marshal(sysmsg) if err != nil { - c.log.Error().Err(err).Msg("error marshaling the system msg") + slog.Error("error marshaling the system msg", "error", err) return } sysctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() if err := c.Rdb.Publish(sysctx, "sbsys", string(b)).Err(); err != nil { - c.log.Error().Err(err).Msg("error publishing to system channel") + slog.Error("error publishing to system channel", "error", err) } }(msg) } subs, err := c.Rdb.PubSubNumSub(c.Ctx, msg.Channel).Result() if err != nil { - c.log.Error().Err(err).Msgf("error getting db subscribers for %s", msg.Channel) + slog.Error("error getting db subscribers for", "channel", msg.Channel) return err } count, ok := subs[msg.Channel] if !ok { - c.log.Warn().Msgf("cannot find channel in subs: %s", msg.Channel) + slog.Warn("cannot find channel in subs", "channel", msg.Channel) return nil } else if count == 0 { return nil @@ -226,7 +225,7 @@ func (c *Cache) Publish(msg model.Command) error { func (c *Cache) PublishDocument(auth model.Auth, dbName, channel, typ string, v interface{}) { b, err := json.Marshal(v) if err != nil { - c.log.Error().Err(err).Msg("error publishing db doc") + slog.Error("error publishing db doc", "error", err) return } @@ -239,7 +238,7 @@ func (c *Cache) PublishDocument(auth model.Auth, dbName, channel, typ string, v } if err := c.Publish(msg); err != nil { - c.log.Error().Err(err).Msg("unable to publish db doc events") + slog.Error("unable to publish db doc events", "error", err) } } @@ -258,7 +257,7 @@ func (c *Cache) HasPermission(token, repo, payload string) bool { docs := make(map[string]interface{}) if err := json.Unmarshal([]byte(payload), &docs); err != nil { - c.log.Error().Err(err).Msg("error decoding docs for permissions check") + slog.Error("error decoding docs for permissions check", "error", err) return false } diff --git a/cache/cache_test.go b/cache/cache_test.go index b3dce25..fd0cd57 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -26,9 +26,9 @@ type suite struct { func TestMain(m *testing.M) { config.Current = config.LoadConfig() - logz := logger.Get(config.Current) - redisCache = NewCache(logz) - devCache = NewDevCache(logz) + logger.Setup(config.Current) + redisCache = NewCache() + devCache = NewDevCache() adminAuth = model.Auth{ AccountID: "047cfe5b-b91d-4ec6-9bc2-8f68309d8532", @@ -219,7 +219,7 @@ func TestCachePublishDocument(t *testing.T) { } func TestDevCacheDequeueWorkMissingQueue(t *testing.T) { - cache := NewDevCache(logger.Get(config.Current)) + cache := NewDevCache() val, err := cache.DequeueWork("missing_queue") if err != nil { @@ -231,7 +231,7 @@ func TestDevCacheDequeueWorkMissingQueue(t *testing.T) { } func TestDevCacheDequeueWorkEmptyQueue(t *testing.T) { - cache := NewDevCache(logger.Get(config.Current)) + cache := NewDevCache() if err := cache.QueueWork("queue", "work"); err != nil { t.Fatal(err) diff --git a/cache/dev.go b/cache/dev.go index 0725e85..b3c7a3c 100644 --- a/cache/dev.go +++ b/cache/dev.go @@ -4,11 +4,11 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "sync" "github.com/staticbackendhq/core/cache/observer" "github.com/staticbackendhq/core/internal" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" ) @@ -18,17 +18,15 @@ var ErrKeyNotFound = errors.New("key not found in cache") // CacheDev used in local dev mode and is memory-based type CacheDev struct { data map[string]string - log *logger.Logger observer observer.Observer m *sync.RWMutex } // NewDevCache returns a memory-based Volatilizer -func NewDevCache(log *logger.Logger) *CacheDev { +func NewDevCache() *CacheDev { return &CacheDev{ data: make(map[string]string), - observer: observer.NewObserver(log), - log: log, + observer: observer.NewObserver(), m: &sync.RWMutex{}, } } @@ -115,7 +113,7 @@ func (d *CacheDev) Subscribe(send chan model.Command, token, channel string, clo case m := <-ch: var msg model.Command if err := json.Unmarshal([]byte(m.(string)), &msg); err != nil { - d.log.Error().Err(err).Msg("error parsing JSON message") + slog.Error("error parsing JSON message", "error", err) _ = pubsub.Close() _ = d.observer.Unsubscribe(channel, pubsub) return @@ -153,11 +151,11 @@ func (d *CacheDev) Publish(msg model.Command) error { sysmsg.IsSystemEvent = true b, err := json.Marshal(sysmsg) if err != nil { - d.log.Error().Err(err).Msg("error marshaling the system msg") + slog.Error("error marshaling the system msg", "error", err) return } if err := d.observer.Publish("sbsys", string(b)); err != nil { - d.log.Error().Err(err).Msg("error occurred during publishing to 'sbsys' channel") + slog.Error("error occurred during publishing to 'sbsys' channel", "error", err) } }(msg) } @@ -166,7 +164,7 @@ func (d *CacheDev) Publish(msg model.Command) error { count, ok := subs[msg.Channel] if !ok { - d.log.Warn().Msgf("cannot find channel in subs: %s", msg.Channel) + slog.Warn("cannot find channel in subs", "channel", msg.Channel) return nil } else if count == 0 { return nil @@ -179,7 +177,7 @@ func (d *CacheDev) Publish(msg model.Command) error { func (d *CacheDev) PublishDocument(auth model.Auth, dbName, channel, typ string, v any) { b, err := json.Marshal(v) if err != nil { - d.log.Error().Err(err).Msg("error publishing db doc") + slog.Error("error publishing db doc", "error", err) return } @@ -192,7 +190,7 @@ func (d *CacheDev) PublishDocument(auth model.Auth, dbName, channel, typ string, } if err := d.Publish(msg); err != nil { - d.log.Error().Err(err).Msg("unable to publish db doc events") + slog.Error("unable to publish db doc events", "error", err) } } @@ -209,7 +207,7 @@ func (d *CacheDev) HasPermission(token, repo, payload string) bool { docs := make(map[string]interface{}) if err := json.Unmarshal([]byte(payload), &docs); err != nil { - d.log.Error().Err(err).Msg("error decoding docs for permissions check") + slog.Error("error decoding docs for permissions check", "error", err) return false } diff --git a/cache/observer/observer.go b/cache/observer/observer.go index e57193d..e84705e 100644 --- a/cache/observer/observer.go +++ b/cache/observer/observer.go @@ -2,10 +2,9 @@ package observer import ( "errors" + "log/slog" "sync" "time" - - "github.com/staticbackendhq/core/logger" ) type Observer interface { @@ -47,12 +46,11 @@ func (ps *memSubscriber) Close() error { type memObserver struct { Subscriptions map[string][]*memSubscriber mx sync.Mutex - log *logger.Logger } -func NewObserver(log *logger.Logger) Observer { +func NewObserver() Observer { subs := make(map[string][]*memSubscriber) - return &memObserver{Subscriptions: subs, mx: sync.Mutex{}, log: log} + return &memObserver{Subscriptions: subs, mx: sync.Mutex{}} } func (o *memObserver) Subscribe(channel string) Subscriber { @@ -85,7 +83,7 @@ func (o *memObserver) Publish(channel string, msg interface{}) error { <-timer.C } case <-timer.C: - o.log.Error().Msg("the previous message is not read; dropping this message") + slog.Error("the previous message is not read; dropping this message") timer.Stop() } }(sub, msg) diff --git a/cmd/main.go b/cmd/main.go index 17457f0..950fc28 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,13 +7,11 @@ import ( backend "github.com/staticbackendhq/core" "github.com/staticbackendhq/core/config" - "github.com/staticbackendhq/core/logger" ) func main() { c := config.LoadConfig() - log := logger.Get(c) var v bool flag.BoolVar(&v, "v", false, "Display the version and build info") @@ -31,5 +29,5 @@ func main() { c.Port = "8099" } - backend.Start(c, log) + backend.Start(c) } diff --git a/database/mongo/base.go b/database/mongo/base.go index 0e58aa5..ad3d434 100644 --- a/database/mongo/base.go +++ b/database/mongo/base.go @@ -2,6 +2,7 @@ package mongo import ( "fmt" + "log/slog" "strings" "sync" "time" @@ -72,7 +73,7 @@ func (mg *Mongo) ensureIndex(dbName, col string) { cur, err := dbCol.Indexes().List(mg.Ctx) if err != nil { - mg.log.Warn().Err(err).Msg("error getting col indexes") + slog.Warn("error getting col indexes", "error", err) return } @@ -81,13 +82,13 @@ func (mg *Mongo) ensureIndex(dbName, col string) { for cur.Next(mg.Ctx) { var v bson.M if err := cur.Decode(&v); err != nil { - mg.log.Warn().Err(err).Msg("cannot cast to IndexModel") + slog.Warn("cannot cast to IndexModel", "error", err) return } keys, ok := v["key"].(bson.M) if !ok { - mg.log.Warn().Msg("unable to cast IndexModel Key to map") + slog.Warn("unable to cast IndexModel Key to map") return } @@ -113,7 +114,7 @@ func (mg *Mongo) ensureIndex(dbName, col string) { } if err := mg.CreateIndex(dbName, col, FieldAccountID); err != nil { - mg.log.Warn().Err(err).Msg("error creating accountId idx") + slog.Warn("error creating accountId idx", "error", err) } } @@ -430,7 +431,7 @@ func (mg *Mongo) UpdateDocuments(auth model.Auth, dbName, col string, filters ma for cur.Next(mg.Ctx) { var v map[string]interface{} if err := cur.Decode(&v); err != nil { - mg.log.Error().Err(err).Msg("") + slog.Error("error decoding document id for bulk update", "error", err) } id, ok := v[FieldID].(primitive.ObjectID) if ok { @@ -457,7 +458,7 @@ func (mg *Mongo) UpdateDocuments(auth model.Auth, dbName, col string, filters ma go func() { docs, err := mg.GetDocumentsByIDs(auth, dbName, col, ids) if err != nil { - mg.log.Error().Err(err).Msgf("the documents with ids=%s are not received for publishDocument event", ids) + slog.Error("the documents are not received for publishDocument event", "ids", ids, "error", err) } for _, doc := range docs { mg.PublishDocument(auth, dbName, "db-"+col, model.MsgTypeDBUpdated, doc) @@ -547,13 +548,13 @@ func (mg *Mongo) DeleteDocuments(auth model.Auth, dbName, col string, filters ma findOpts := options.Find().SetProjection(bson.M{FieldID: 1}) cur, err := db.Collection(model.CleanCollectionName(col)).Find(mg.Ctx, filters, findOpts) if err != nil { - mg.log.Error().Err(err).Msg("trying to get list of ids for bulk delete") + slog.Error("trying to get list of ids for bulk delete", "error", err) return } for cur.Next(mg.Ctx) { var v map[string]interface{} if err := cur.Decode(&v); err != nil { - mg.log.Error().Err(err).Msg("") + slog.Error("error decoding document id for bulk delete", "error", err) } id, ok := v[FieldID].(primitive.ObjectID) if ok { diff --git a/database/mongo/mongo.go b/database/mongo/mongo.go index cc89a5a..708b7fe 100644 --- a/database/mongo/mongo.go +++ b/database/mongo/mongo.go @@ -7,7 +7,6 @@ import ( "github.com/staticbackendhq/core/cache" "github.com/staticbackendhq/core/database" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" @@ -18,15 +17,13 @@ type Mongo struct { Client *mongo.Client Ctx context.Context PublishDocument cache.PublishDocumentEvent - log *logger.Logger } -func New(client *mongo.Client, pubdoc cache.PublishDocumentEvent, log *logger.Logger) database.Persister { +func New(client *mongo.Client, pubdoc cache.PublishDocumentEvent) database.Persister { return &Mongo{ Client: client, Ctx: context.Background(), PublishDocument: pubdoc, - log: log, } } diff --git a/database/postgresql/base.go b/database/postgresql/base.go index ff327e3..315f67e 100644 --- a/database/postgresql/base.go +++ b/database/postgresql/base.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "strings" "time" @@ -145,7 +146,7 @@ func (pg *PostgreSQL) ListDocuments(auth model.Auth, dbName, col string, params rows, err := pg.DB.Query(qry, auth.AccountID, auth.UserID) if err != nil { - pg.log.Error().Err(err).Msg("error in select") + slog.Error("error in select", "error", err) return } defer func() { _ = rows.Close() }() @@ -307,7 +308,7 @@ func (pg *PostgreSQL) UpdateDocuments(auth model.Auth, dbName, col string, filte for rows.Next() { var id string if err := rows.Scan(&id); err != nil { - pg.log.Error().Err(err).Msg("error occurred during scanning id for UpdateDocument event") + slog.Error("error occurred during scanning id for UpdateDocument event", "error", err) continue } ids = append(ids, id) @@ -339,7 +340,7 @@ func (pg *PostgreSQL) UpdateDocuments(auth model.Auth, dbName, col string, filte go func() { docs, err := pg.GetDocumentsByIDs(auth, dbName, col, ids) if err != nil { - pg.log.Error().Err(err).Msgf("the documents with ids=%s are not received for publishDocument event", ids) + slog.Error("the documents are not received for publishDocument event", "ids", ids, "error", err) } for _, doc := range docs { pg.PublishDocument(auth, dbName, "db-"+col, model.MsgTypeDBUpdated, doc) @@ -409,7 +410,7 @@ func (pg *PostgreSQL) DeleteDocuments(auth model.Auth, dbName, col string, filte for rows.Next() { var id string if err := rows.Scan(&id); err != nil { - pg.log.Error().Err(err).Msg("error occurred during scanning id for DeleteDocuments event") + slog.Error("error occurred during scanning id for DeleteDocuments event", "error", err) continue } diff --git a/database/postgresql/postgresql.go b/database/postgresql/postgresql.go index ee1b623..52523be 100644 --- a/database/postgresql/postgresql.go +++ b/database/postgresql/postgresql.go @@ -11,20 +11,18 @@ import ( "github.com/staticbackendhq/core/cache" "github.com/staticbackendhq/core/database" sbquery "github.com/staticbackendhq/core/internal/query" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" ) type PostgreSQL struct { DB *sql.DB PublishDocument cache.PublishDocumentEvent - log *logger.Logger } //go:embed sql var migrationFS embed.FS -func New(db *sql.DB, pubdoc cache.PublishDocumentEvent, log *logger.Logger) database.Persister { +func New(db *sql.DB, pubdoc cache.PublishDocumentEvent) database.Persister { // run migrations if err := migrate(db); err != nil { fmt.Println("=== MIGRATION FAILED ===") @@ -33,7 +31,7 @@ func New(db *sql.DB, pubdoc cache.PublishDocumentEvent, log *logger.Logger) data os.Exit(1) } - return &PostgreSQL{DB: db, PublishDocument: pubdoc, log: log} + return &PostgreSQL{DB: db, PublishDocument: pubdoc} } func (pg *PostgreSQL) Ping() error { diff --git a/database/postgresql/sb.go b/database/postgresql/sb.go index 06b9faa..de158ea 100644 --- a/database/postgresql/sb.go +++ b/database/postgresql/sb.go @@ -2,6 +2,7 @@ package postgresql import ( "fmt" + "log/slog" "strings" "github.com/lib/pq" @@ -287,7 +288,7 @@ func (pg *PostgreSQL) EnableExternalLogin(tenantID string, config map[string]mod func (pg *PostgreSQL) NewID() string { var id string if err := pg.DB.QueryRow(`SELECT uuid_generate_v4 ()`).Scan(&id); err != nil { - pg.log.Error().Err(err).Msg("error in postgresql.NewID") + slog.Error("error in postgresql.NewID", "error", err) return "" } return id diff --git a/database/sqlite/base.go b/database/sqlite/base.go index a03bde4..05aa70b 100644 --- a/database/sqlite/base.go +++ b/database/sqlite/base.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "reflect" "strings" "sync" @@ -165,7 +166,7 @@ func (sl *SQLite) ListDocuments(auth model.Auth, dbName, col string, params mode rows, err := sl.DB.Query(qry, auth.AccountID, auth.UserID) if err != nil { - sl.log.Error().Err(err).Msg("error in select") + slog.Error("error in select", "error", err) return } defer func() { _ = rows.Close() }() @@ -339,7 +340,7 @@ func (sl *SQLite) UpdateDocuments(auth model.Auth, dbName, col string, filters m for rows.Next() { var id string if err := rows.Scan(&id); err != nil { - sl.log.Error().Err(err).Msg("error occurred during scanning id for UpdateDocument event") + slog.Error("error occurred during scanning id for UpdateDocument event", "error", err) continue } ids = append(ids, id) @@ -371,7 +372,7 @@ func (sl *SQLite) UpdateDocuments(auth model.Auth, dbName, col string, filters m go func() { docs, err := sl.GetDocumentsByIDs(auth, dbName, col, ids) if err != nil { - sl.log.Error().Err(err).Msgf("the documents with ids=%s are not received for publishDocument event", ids) + slog.Error("the documents are not received for publishDocument event", "ids", ids, "error", err) } for _, doc := range docs { sl.PublishDocument(auth, dbName, "db-"+col, model.MsgTypeDBUpdated, doc) @@ -449,7 +450,7 @@ func (sl *SQLite) DeleteDocuments(auth model.Auth, dbName, col string, filters m for rows.Next() { var id string if err := rows.Scan(&id); err != nil { - sl.log.Error().Err(err).Msg("error occurred during scanning id for DeleteDocuments event") + slog.Error("error occurred during scanning id for DeleteDocuments event", "error", err) continue } diff --git a/database/sqlite/sqlite.go b/database/sqlite/sqlite.go index ca60955..3898124 100644 --- a/database/sqlite/sqlite.go +++ b/database/sqlite/sqlite.go @@ -4,7 +4,6 @@ import ( "database/sql" "embed" "fmt" - "os" "github.com/staticbackendhq/core/cache" "github.com/staticbackendhq/core/database" @@ -17,32 +16,24 @@ var migrationFS embed.FS type SQLite struct { DB *sql.DB PublishDocument cache.PublishDocumentEvent - log *logger.Logger collections map[string]bool } -func New(db *sql.DB, pubdoc cache.PublishDocumentEvent, log *logger.Logger) database.Persister { +func New(db *sql.DB, pubdoc cache.PublishDocumentEvent) database.Persister { if _, err := db.Exec(`PRAGMA foreign_keys = ON;`); err != nil { - fmt.Println("=== SQLITE PRAGMA FAILED ===") - fmt.Println(err) - fmt.Println("=== /SQLITE PRAGMA FAILED ===") - os.Exit(1) + logger.FatalError("SQLITE PRAGMA FAILED", err) } // run migrations if err := migrate(db); err != nil { - fmt.Println("=== MIGRATION FAILED ===") - fmt.Println(err) - fmt.Println("=== /MIGRATION FAILED ===") - os.Exit(1) + logger.FatalError("MIGRATION FAILED", err) } return &SQLite{ DB: db, PublishDocument: pubdoc, collections: make(map[string]bool), - log: log, } } diff --git a/function/runtime.go b/function/runtime.go index 83f81da..3842e14 100644 --- a/function/runtime.go +++ b/function/runtime.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "log/slog" "mime" "net/http" "strings" @@ -13,7 +14,6 @@ import ( "github.com/staticbackendhq/core/cache" "github.com/staticbackendhq/core/database" "github.com/staticbackendhq/core/email" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" "github.com/staticbackendhq/core/search" @@ -30,7 +30,6 @@ type ExecutionEnvironment struct { Data model.ExecData CurrentRun model.ExecHistory - Log *logger.Logger } type Result struct { @@ -1202,6 +1201,6 @@ func (env *ExecutionEnvironment) complete(err error) { //TODO: this needs to be regrouped and ran un batch if err := env.DataStore.RanFunction(env.BaseName, env.Data.ID, env.CurrentRun); err != nil { - env.Log.Error().Err(err).Msg("error logging function complete") + slog.Error("error logging function complete", "error", err) } } diff --git a/function/runtime_search_test.go b/function/runtime_search_test.go index dcb84cd..be923b9 100644 --- a/function/runtime_search_test.go +++ b/function/runtime_search_test.go @@ -424,8 +424,8 @@ func newRuntimeTestContext(t *testing.T, dbName, code string) runtimeTestContext t.Helper() cfg := config.AppConfig{} - log := logger.Get(cfg) - pubsub := cache.NewDevCache(log) + logger.Setup(cfg) + pubsub := cache.NewDevCache() datastore := memory.New(pubsub.PublishDocument).(*memory.Memory) src, err := search.New(filepath.Join(t.TempDir(), "test.fts"), pubsub) diff --git a/function/scheduler.go b/function/scheduler.go index 157b1c5..48bdf8f 100644 --- a/function/scheduler.go +++ b/function/scheduler.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "net/url" "strings" @@ -14,7 +15,6 @@ import ( "github.com/staticbackendhq/core/cache" "github.com/staticbackendhq/core/database" "github.com/staticbackendhq/core/email" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" "github.com/staticbackendhq/core/search" @@ -26,7 +26,6 @@ type TaskScheduler struct { DataStore database.Persister Search *search.Search Email email.Mailer - Log *logger.Logger Scheduler gocron.Scheduler @@ -84,7 +83,7 @@ func (ts *TaskScheduler) Start() { tasks, err := ts.DataStore.ListTasks() if err != nil { - ts.Log.Error().Err(err).Msg("error loading tasks") + slog.Error("error loading tasks", "error", err) return } @@ -144,7 +143,7 @@ func (ts *TaskScheduler) AddOnTheFly(task model.Task) { func (ts *TaskScheduler) addTask(task model.Task) { if ts.Scheduler == nil { - ts.Log.Error().Msgf("scheduler is not initialized; cannot schedule task: %s", task.ID) + slog.Error("scheduler is not initialized; cannot schedule task", "task_id", task.ID) return } @@ -154,7 +153,7 @@ func (ts *TaskScheduler) addTask(task model.Task) { gocron.WithTags(task.ID), ) if err != nil { - ts.Log.Error().Err(err).Msgf("error scheduling this task: %s", task.ID) + slog.Error("error scheduling task", "task_id", task.ID, "error", err) } } @@ -181,7 +180,7 @@ func (ts *TaskScheduler) ensureScheduler() { scheduler, err := gocron.NewScheduler(gocron.WithLocation(time.UTC)) if err != nil { - ts.Log.Error().Err(err).Msg("error creating task scheduler") + slog.Error("error creating task scheduler", "error", err) return } @@ -211,7 +210,7 @@ func (ts *TaskScheduler) run(task model.Task) { } else { tok, err := ts.DataStore.GetRootForBase(task.BaseName) if err != nil { - ts.Log.Error().Err(err).Msgf("error finding root token for base %s", task.BaseName) + slog.Error("error finding root token for base", "base", task.BaseName, "error", err) return } @@ -231,7 +230,7 @@ func (ts *TaskScheduler) run(task model.Task) { Role: auth.Role, Token: auth.Token, }); err != nil { - ts.Log.Error().Err(err).Msg("error setting auth inside TaskScheduler.run") + slog.Error("error setting auth inside TaskScheduler.run", "error", err) return } } @@ -249,20 +248,20 @@ func (ts *TaskScheduler) run(task model.Task) { func (ts *TaskScheduler) markTaskRan(task model.Task) { stored, err := ts.DataStore.GetTask(task.BaseName, task.ID) if err != nil { - ts.Log.Error().Err(err).Msgf("error loading task before updating last run: %s", task.ID) + slog.Error("error loading task before updating last run", "task_id", task.ID, "error", err) return } stored.LastRun = time.Now().UTC() if err := ts.DataStore.UpdateTask(task.BaseName, stored); err != nil { - ts.Log.Error().Err(err).Msgf("error updating last run for task: %s", task.ID) + slog.Error("error updating last run for task", "task_id", task.ID, "error", err) } } func (ts *TaskScheduler) execFunction(auth model.Auth, task model.Task) { fn, err := ts.DataStore.GetFunctionForExecution(task.BaseName, task.Value) if err != nil { - ts.Log.Error().Err(err).Msgf("cannot find function %s on task %s", task.Value, task.ID) + slog.Error("cannot find function on task", "function", task.Value, "task_id", task.ID, "error", err) return } @@ -274,14 +273,13 @@ func (ts *TaskScheduler) execFunction(auth model.Auth, task model.Task) { Search: ts.Search, Email: ts.Email, Data: fn, - Log: ts.Log, } var meta model.MetaMessage if len(task.Meta) > 0 { if err := json.Unmarshal([]byte(task.Meta), &meta); err != nil { - ts.Log.Warn().Msgf("unable to get meta data for type MetaMessage for task: %s", task.ID) + slog.Warn("unable to get meta data for type MetaMessage", "task_id", task.ID, "error", err) return } } @@ -296,7 +294,7 @@ func (ts *TaskScheduler) execFunction(auth model.Auth, task model.Task) { } if err := exe.Execute(msg); err != nil { - ts.Log.Error().Err(err).Msgf("error executing function %s", task.Value) + slog.Error("error executing function", "function", task.Value, "error", err) } } @@ -307,7 +305,7 @@ func (ts *TaskScheduler) sendMessage(auth model.Auth, task model.Task) { if len(task.Meta) > 0 { if err := json.Unmarshal([]byte(task.Meta), &meta); err != nil { - ts.Log.Warn().Msgf("unable to get meta data for type MetaMessage for task: %s", task.ID) + slog.Warn("unable to get meta data for type MetaMessage", "task_id", task.ID, "error", err) return } } @@ -323,7 +321,7 @@ func (ts *TaskScheduler) sendMessage(auth model.Auth, task model.Task) { } if err := ts.Volatile.Publish(msg); err != nil { - ts.Log.Error().Err(err).Msgf("error publishing message from task: %s", task.ID) + slog.Error("error publishing message from task", "task_id", task.ID, "error", err) } } @@ -335,12 +333,12 @@ func (ts *TaskScheduler) httpRequest(auth model.Auth, task model.Task) { if len(task.Meta) > 0 { if err := json.Unmarshal([]byte(task.Meta), &meta); err != nil { - ts.Log.Warn().Msgf("unable to get meta data for type MetaMessage for task: %s", task.ID) + slog.Warn("unable to get meta data for type MetaMessage", "task_id", task.ID, "error", err) return } if err := json.Unmarshal([]byte(meta.HTTPHeaders), &headers); err != nil { - ts.Log.Err(err).Msg("unable to parse HTTP headers from meta data") + slog.Error("unable to parse HTTP headers from meta data", "error", err) return } } @@ -359,7 +357,7 @@ func (ts *TaskScheduler) httpRequest(auth model.Auth, task model.Task) { } else { var v map[string]any if err := json.Unmarshal([]byte(meta.Data), &v); err != nil { - ts.Log.Warn().Err(err).Msg("unable to parse meta data") + slog.Warn("unable to parse meta data", "error", err) return } @@ -373,7 +371,7 @@ func (ts *TaskScheduler) httpRequest(auth model.Auth, task model.Task) { req, err := http.NewRequest(meta.HTTPMethod, task.Value, strings.NewReader(body)) if err != nil { - ts.Log.Err(err).Msg("unable to construct the HTTP request") + slog.Error("unable to construct the HTTP request", "error", err) return } @@ -385,14 +383,14 @@ func (ts *TaskScheduler) httpRequest(auth model.Auth, task model.Task) { resp, err := http.DefaultClient.Do(req) if err != nil { - ts.Log.Err(err).Msg("error executing HTTP request") + slog.Error("error executing HTTP request", "error", err) return } defer func() { _ = resp.Body.Close() }() b, err := io.ReadAll(resp.Body) if err != nil { - ts.Log.Err(err).Msg("unable to read HTTP response body") + slog.Error("unable to read HTTP response body", "error", err) return } @@ -407,6 +405,6 @@ func (ts *TaskScheduler) httpRequest(auth model.Auth, task model.Task) { } if err := ts.Volatile.Publish(msg); err != nil { - ts.Log.Error().Err(err).Msgf("error publishing message from task: %s", task.ID) + slog.Error("error publishing message from task", "task_id", task.ID, "error", err) } } diff --git a/function/scheduler_test.go b/function/scheduler_test.go index 22e52ca..4fb02d7 100644 --- a/function/scheduler_test.go +++ b/function/scheduler_test.go @@ -17,7 +17,7 @@ import ( func TestTaskSchedulerUsesRootAuthForFunctionTask(t *testing.T) { baseName := fmt.Sprintf("sched_%d", time.Now().UnixNano()) ds, rootAuth := newSchedulerTestStore(t, baseName) - vol := cache.NewDevCache(logger.Get(config.LoadConfig())) + vol := cache.NewDevCache() fn := model.ExecData{ FunctionName: "scheduled-create", @@ -39,7 +39,6 @@ func TestTaskSchedulerUsesRootAuthForFunctionTask(t *testing.T) { ts := &TaskScheduler{ Volatile: vol, DataStore: ds, - Log: logger.Get(config.LoadConfig()), } task := model.Task{ ID: "task-root-auth", @@ -107,7 +106,7 @@ func TestTaskSchedulerUsesRootAuthForFunctionTask(t *testing.T) { func TestTaskSchedulerFunctionTaskWithoutMetaUsesEmptyData(t *testing.T) { baseName := fmt.Sprintf("sched_empty_meta_%d", time.Now().UnixNano()) ds, rootAuth := newSchedulerTestStore(t, baseName) - vol := cache.NewDevCache(logger.Get(config.LoadConfig())) + vol := cache.NewDevCache() fn := model.ExecData{ FunctionName: "empty-meta", @@ -150,7 +149,6 @@ func TestTaskSchedulerFunctionTaskWithoutMetaUsesEmptyData(t *testing.T) { ts := &TaskScheduler{ Volatile: vol, DataStore: ds, - Log: logger.Get(config.LoadConfig()), } ts.run(task) diff --git a/function/subscriber.go b/function/subscriber.go index ce327e4..2605175 100644 --- a/function/subscriber.go +++ b/function/subscriber.go @@ -3,18 +3,17 @@ package function import ( "context" "fmt" + "log/slog" "sync" "time" "github.com/staticbackendhq/core/cache" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" ) type Subscriber struct { PubSub cache.Volatilizer GetExecEnv func(msg model.Command) (*ExecutionEnvironment, error) - Log *logger.Logger IsPrimaryInstance bool relax sync.Map @@ -48,7 +47,7 @@ func (sub *Subscriber) StartContext(ctx context.Context) { }() } case <-closeSub: - sub.Log.Info().Msg("system event channel closed?!?") + slog.Info("system event channel closed") case <-ctx.Done(): close(closeSub) wg.Wait() @@ -82,10 +81,10 @@ func (sub *Subscriber) process(msg model.Command) { n, ok := v.(int64) if !ok { - sub.Log.Warn().Msgf("subscriber.process(): unable to cast %v into int64", v) + slog.Warn("subscriber.process(): unable to cast value into int64", "value", v) return } else if n >= 5 { - sub.Log.Warn().Msgf("user exeeded amount of allowed message in 60 second: %d", n) + slog.Warn("user exceeded amount of allowed message in 60 seconds", "count", n) //TODO: This silently returns, should this app owner get some // notification about their users flooding the system? return @@ -101,7 +100,7 @@ func (sub *Subscriber) process(msg model.Command) { func (sub *Subscriber) handleRealtimeEvents(msg model.Command, wg *sync.WaitGroup) { exe, err := sub.GetExecEnv(msg) if err != nil { - sub.Log.Error().Err(err).Msgf("cannot retrieve base from token: %s", msg.Token) + slog.Error("cannot retrieve base from token", "token", msg.Token, "error", err) return } @@ -109,7 +108,7 @@ func (sub *Subscriber) handleRealtimeEvents(msg model.Command, wg *sync.WaitGrou // for msg type error, we do nothing if msg.Type == model.MsgTypeError { - sub.Log.Err(err).Msg("receiving msg of type error") + slog.Error("receiving msg of type error", "error", err) return } @@ -117,14 +116,14 @@ func (sub *Subscriber) handleRealtimeEvents(msg model.Command, wg *sync.WaitGrou if err := sub.PubSub.GetTyped(key, &ids); err != nil { funcs, err := exe.DataStore.ListFunctionsByTrigger(exe.BaseName, msg.Channel) if err != nil { - sub.Log.Error().Err(err).Msg("error getting functions by trigger") - sub.Log.Debug().Msg("channgel: " + msg.Channel + " type: " + msg.Type) + slog.Error("error getting functions by trigger", "error", err) + slog.Debug("channel and message type", "channel", msg.Channel, "type", msg.Type) return } for _, fn := range funcs { if err := sub.PubSub.SetTyped("fn_"+fn.ID, fn); err != nil { - sub.Log.Error().Err(err).Msg("error adding function to cache") + slog.Error("error adding function to cache", "error", err) return } @@ -132,19 +131,19 @@ func (sub *Subscriber) handleRealtimeEvents(msg model.Command, wg *sync.WaitGrou } if err := sub.PubSub.SetTyped(key, ids); err != nil { - sub.Log.Error().Err(err).Msg("unable to publish message") + slog.Error("unable to publish message", "error", err) } } for _, id := range ids { var fn model.ExecData if err := sub.PubSub.GetTyped("fn_"+id, &fn); err != nil { - sub.Log.Error().Err(err).Msg("error getting function out of cache") + slog.Error("error getting function out of cache", "error", err) return } fn, err = exe.DataStore.GetFunctionForExecution(exe.BaseName, fn.FunctionName) if err != nil { - sub.Log.Error().Err(err).Msg("error getting function for execution") + slog.Error("error getting function for execution", "error", err) return } @@ -153,7 +152,7 @@ func (sub *Subscriber) handleRealtimeEvents(msg model.Command, wg *sync.WaitGrou go func(ex *ExecutionEnvironment) { defer wg.Done() if err := ex.Execute(msg); err != nil { - sub.Log.Error().Err(err).Msgf(`executing "%s" function failed"`, ex.Data.FunctionName) + slog.Error("executing function failed", "function", ex.Data.FunctionName, "error", err) } }(exe) } diff --git a/logger/logger.go b/logger/logger.go index 9ed42ff..fb9d07b 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -2,23 +2,16 @@ package logger import ( "io" + "log/slog" "os" + "strings" "sync" - "time" - "github.com/rs/zerolog" "github.com/staticbackendhq/core/config" "gopkg.in/natefinch/lumberjack.v2" ) -type Logger struct { - *zerolog.Logger -} - -var ( - logger Logger - once sync.Once -) +var once sync.Once func newFileWriter(filename string) io.Writer { return &lumberjack.Logger{ @@ -27,34 +20,45 @@ func newFileWriter(filename string) io.Writer { } } -func Get(cfg config.AppConfig) *Logger { +func Setup(cfg config.AppConfig) { once.Do(func() { + level := parseLevel(cfg.LogConsoleLevel) + if cfg.AppEnv == "dev" { + level = slog.LevelDebug + } // By default create console writer - writers := []io.Writer{zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.Stamp}} + writers := []io.Writer{os.Stdout} if cfg.LogFilename != "" { writers = append(writers, newFileWriter(cfg.LogFilename)) } - if cfg.LogConsoleLevel != "" { - level, err := zerolog.ParseLevel(cfg.LogConsoleLevel) - if err != nil { - panic(err) - } - - zerolog.SetGlobalLevel(level) - } - - if cfg.AppEnv == "dev" { - zerolog.SetGlobalLevel(zerolog.TraceLevel) - } - multiWriters := io.MultiWriter(writers...) - zeroLogger := zerolog.New(multiWriters).With().Timestamp().Logger() + handler := slog.NewTextHandler(multiWriters, &slog.HandlerOptions{Level: level}) - logger = Logger{&zeroLogger} + slog.SetDefault(slog.New(handler)) }) +} - return &logger +// FatalError logs a structured error message and exits the process. +func FatalError(msg string, err error, args ...any) { + if err != nil { + args = append(args, "error", err) + } + slog.Error(msg, args...) + os.Exit(1) +} + +func parseLevel(level string) slog.Level { + switch strings.ToLower(level) { + case "debug", "trace": + return slog.LevelDebug + case "warn", "warning": + return slog.LevelWarn + case "error": + return slog.LevelError + default: + return slog.LevelInfo + } } diff --git a/middleware/telemetry.go b/middleware/telemetry.go index 540ed5a..f94acce 100644 --- a/middleware/telemetry.go +++ b/middleware/telemetry.go @@ -2,12 +2,12 @@ package middleware import ( "encoding/json" + "log/slog" "net/http" "slices" "time" "github.com/staticbackendhq/core/cache" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" ) @@ -48,7 +48,7 @@ func (w *telemetryResponseWriter) Write(b []byte) (int, error) { // LongRequestTelemetry publishes a tenant event for requests exceeding // SlowRequestThreshold. Publishing is best effort and does not affect responses. -func LongRequestTelemetry(volatile cache.Volatilizer, log *logger.Logger) Middleware { +func LongRequestTelemetry(volatile cache.Volatilizer) Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { started := time.Now() @@ -91,7 +91,7 @@ func LongRequestTelemetry(volatile cache.Volatilizer, log *logger.Logger) Middle b, err := json.Marshal(data) if err != nil { - log.Error().Err(err).Msg("error marshaling slow request telemetry") + slog.Error("error marshaling slow request telemetry", "error", err) return } @@ -105,7 +105,7 @@ func LongRequestTelemetry(volatile cache.Volatilizer, log *logger.Logger) Middle Base: conf.Name, } if err := volatile.Publish(msg); err != nil { - log.Error().Err(err).Msg("error publishing slow request telemetry") + slog.Error("error publishing slow request telemetry", "error", err) } }() diff --git a/realtime/broker.go b/realtime/broker.go index 4dd1f4d..4bdf8c5 100644 --- a/realtime/broker.go +++ b/realtime/broker.go @@ -4,13 +4,13 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "strings" "sync" "time" "github.com/staticbackendhq/core/cache" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" "github.com/google/uuid" @@ -38,15 +38,13 @@ type Broker struct { pubsub cache.Volatilizer - log *logger.Logger - shutdown chan struct{} done chan struct{} once sync.Once } // NewBroker returns a ready to use Broker for accepting web socket connections -func NewBroker(v Validator, pubsub cache.Volatilizer, log *logger.Logger) *Broker { +func NewBroker(v Validator, pubsub cache.Volatilizer) *Broker { b := &Broker{ Broadcast: make(chan model.Command, 1), newConnections: make(chan ConnectionData), @@ -57,7 +55,6 @@ func NewBroker(v Validator, pubsub cache.Volatilizer, log *logger.Logger) *Broke subscriptions: make(map[string][]chan bool), validateAuth: v, pubsub: pubsub, - log: log, shutdown: make(chan struct{}), done: make(chan struct{}), } @@ -75,7 +72,7 @@ func (b *Broker) start() { case data := <-b.newConnections: id, err := uuid.NewUUID() if err != nil { - b.log.Error().Err(err) + slog.Error("error creating connection id", "error", err) } b.clients[data.messages] = id.String() @@ -128,7 +125,7 @@ func (b *Broker) unsub(c chan model.Command) { id, ok := b.clients[c] if !ok { - b.log.Info().Msg("cannot find connection id") + slog.Info("cannot find connection id") } subs, ok := b.subscriptions[id] @@ -193,13 +190,13 @@ func (b *Broker) Accept(w http.ResponseWriter, r *http.Request) { // write Server Sent Event data bytes, err := json.Marshal(msg) if err != nil { - b.log.Warn().Err(err).Msg("error converting to JSON") + slog.Warn("error converting to JSON", "error", err) continue } if _, err := fmt.Fprintf(w, "data: %s\n\n", bytes); err != nil { - b.log.Warn().Err(err).Msg("error writing server sent event") + slog.Warn("error writing server sent event", "error", err) continue } @@ -221,7 +218,7 @@ func (b *Broker) getTargets(msg model.Command) (sockets []chan model.Command, pa if msg.SID != model.SystemID { s, ok := b.ids[msg.SID] if !ok { - b.log.Info().Msgf("cannot find sender socket: %s", msg.SID) + slog.Info("cannot find sender socket", "sid", msg.SID) return } sender = s @@ -267,7 +264,7 @@ func (b *Broker) getTargets(msg model.Command) (sockets []chan model.Command, pa go func(m model.Command) { time.Sleep(250 * time.Millisecond) if err := b.pubsub.Publish(joinedMsg); err != nil { - b.log.Error().Err(err) + slog.Error("error publishing joined message", "error", err) } }(joinedMsg) @@ -294,7 +291,7 @@ func (b *Broker) getTargets(msg model.Command) (sockets []chan model.Command, pa go func() { if err := b.pubsub.Publish(msg); err != nil { - b.log.Error().Err(err) + slog.Error("error publishing channel message", "error", err) } }() diff --git a/server.go b/server.go index e104a0e..31797e1 100644 --- a/server.go +++ b/server.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "net/http" "os" "os/signal" @@ -13,6 +14,7 @@ import ( "syscall" "time" + "github.com/rs/zerolog/log" "github.com/staticbackendhq/core/backend" "github.com/staticbackendhq/core/config" "github.com/staticbackendhq/core/internal" @@ -36,8 +38,9 @@ const ( var content embed.FS // Start starts the web server and all dependencies services -func Start(c config.AppConfig, log *logger.Logger) { - log.Info().Str("Addr", c.AppURL).Msg("server started") +func Start(c config.AppConfig) { + logger.Setup(c) + slog.Info("server started", "Addr", c.AppURL) config.Current = c @@ -46,7 +49,7 @@ func Start(c config.AppConfig, log *logger.Logger) { if err := loadTemplates(); err != nil { // if we're running from the CLI, no need to load templates if len(config.Current.FromCLI) == 0 { - log.Fatal().Err(err).Msg("error loading templates") + logger.FatalError("error loading templates", err) } } From 379dbb65eae5dc9ff6be6d7e2e756510ef63ffaf Mon Sep 17 00:00:00 2001 From: amrshaban2005 Date: Thu, 16 Jul 2026 17:28:06 +0400 Subject: [PATCH 2/4] Complete slog logger migration --- account.go | 23 +++-- backend/backend.go | 4 +- backend/backend_test.go | 11 ++- database/mongo/mongo_test.go | 3 - db.go | 13 ++- extras.go | 7 +- extras_test.go | 3 - function/runtime_search_test.go | 1 - function/scheduler_test.go | 12 +-- function/subscriber_test.go | 25 ++---- functions.go | 1 - json.go | 5 +- main_test.go | 6 +- membership.go | 5 +- middleware/telemetry_test.go | 6 +- oauth.go | 4 +- realtime/broker_test.go | 6 +- render.go | 17 ++-- search/search_test.go | 6 +- server.go | 21 +++-- stripe.go | 38 ++++---- ui.go | 154 ++++++++++++++++---------------- 22 files changed, 172 insertions(+), 199 deletions(-) diff --git a/account.go b/account.go index 9bd3c1c..07e38bc 100644 --- a/account.go +++ b/account.go @@ -2,6 +2,7 @@ package staticbackend import ( "fmt" + "log/slog" "net/http" "strconv" "strings" @@ -11,7 +12,6 @@ import ( "github.com/staticbackendhq/core/config" emailFuncs "github.com/staticbackendhq/core/email" "github.com/staticbackendhq/core/internal" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/middleware" "github.com/staticbackendhq/core/model" @@ -23,7 +23,6 @@ import ( ) type accounts struct { - log *logger.Logger } func (a *accounts) create(w http.ResponseWriter, r *http.Request) { @@ -192,7 +191,7 @@ func (a *accounts) create(w http.ResponseWriter, r *http.Request) { // "safe-to-use-in-dev-root-token" as root token instead of // the changing one across CLI start/stop if err := backend.Cache.Set("dev-root-token", rootToken); err != nil { - backend.Log.Error().Err(err) + slog.Error(err.Error()) } } @@ -221,7 +220,7 @@ Refer to the documentation at https://staticbackend.dev/docs } else if !bypassStripe { err = backend.Emailer.Send(ed) if err != nil { - a.log.Error().Err(err).Msg("error sending email") + slog.Error("error sending email", "error", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -250,7 +249,7 @@ Refer to the documentation at https://staticbackend.dev/docs return } - render(w, r, "login.html", nil, &Flash{Type: "sucess", Message: "We've emailed you all the information you need to get started."}, a.log) + render(w, r, "login.html", nil, &Flash{Type: "sucess", Message: "We've emailed you all the information you need to get started."}) } func (a *accounts) addDatabase(w http.ResponseWriter, r *http.Request) { @@ -280,7 +279,12 @@ func (a *accounts) addDatabase(w http.ResponseWriter, r *http.Request) { if len(config.Current.StripeKey) > 0 && len(cust.SubscriptionID) > 0 { curSub, err := subscription.Get(cust.SubscriptionID, nil) if err != nil { - a.log.Err(err).Msgf("trying to get stripe cust %s sub %s", cust.StripeID, cust.SubscriptionID) + slog.Error( + "trying to get stripe customer subscription", + "stripe_customer_id", cust.StripeID, + "subscription_id", cust.SubscriptionID, + "error", err, + ) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -302,7 +306,12 @@ func (a *accounts) addDatabase(w http.ResponseWriter, r *http.Request) { } if _, err := subscription.Update(cust.SubscriptionID, params); err != nil { - a.log.Err(err).Msgf("unable to update stripe cust %s sub %s quantity", cust.ID, cust.SubscriptionID) + slog.Error( + "unable to update stripe customer subscription quantity", + "tenant_id", cust.ID, + "subscription_id", cust.SubscriptionID, + "error", err, + ) http.Error(w, err.Error(), http.StatusInternalServerError) return } diff --git a/backend/backend.go b/backend/backend.go index 2440571..2a8a228 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -182,10 +182,12 @@ var ( func Setup(cfg config.AppConfig) { logger.Setup(cfg) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if err := Close(ctx); err != nil { slog.Error("error closing existing backend services", "error", err) } - cancel() + Config = cfg resetLifecycle() diff --git a/backend/backend_test.go b/backend/backend_test.go index a8d4c0a..7c34612 100644 --- a/backend/backend_test.go +++ b/backend/backend_test.go @@ -40,19 +40,22 @@ func TestMain(t *testing.M) { // initializes all core services basesd on config backend.Setup(config.Current) - setup() + if err := setup(); err != nil { + panic(err) + } os.Exit(t.Run()) } -func setup() { +func setup() error { if err := createTenantAndDatabase(); err != nil { - backend.Log.Fatal().Err(err) + return err } if err := createUser(); err != nil { - backend.Log.Fatal().Err(err) + return err } + return nil } func createTenantAndDatabase() error { diff --git a/database/mongo/mongo_test.go b/database/mongo/mongo_test.go index 53e89b4..24de952 100644 --- a/database/mongo/mongo_test.go +++ b/database/mongo/mongo_test.go @@ -8,8 +8,6 @@ import ( "testing" "time" - "github.com/staticbackendhq/core/logger" - "github.com/staticbackendhq/core/config" "github.com/staticbackendhq/core/model" "go.mongodb.org/mongo-driver/mongo" @@ -63,7 +61,6 @@ func TestMain(m *testing.M) { Client: cl, Ctx: context.Background(), PublishDocument: fakePubDocEvent, - log: logger.Get(config.Current), } if err := datastore.Ping(); err != nil { diff --git a/db.go b/db.go index 551a0a9..c3e5643 100644 --- a/db.go +++ b/db.go @@ -3,6 +3,7 @@ package staticbackend import ( "encoding/base64" "encoding/json" + "log/slog" "net/http" "net/url" "strconv" @@ -11,14 +12,12 @@ import ( "github.com/staticbackendhq/core/backend" "github.com/staticbackendhq/core/cache" dbpkg "github.com/staticbackendhq/core/database" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/middleware" "github.com/staticbackendhq/core/model" ) type Database struct { cache cache.Volatilizer - log *logger.Logger } func (database *Database) dbreq(w http.ResponseWriter, r *http.Request) { @@ -145,18 +144,18 @@ func (database *Database) count(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&clauses); err != nil { // Here we don't return an error because filters are optional - database.log.Error().Err(err).Msg("error parsing body") + slog.Error("error parsing body", "error", err) } filter, err := backend.DB.ParseQuery(clauses) if err != nil { // Here we don't return an error because filters are optional - database.log.Error().Err(err).Msg("error parsing query") + slog.Error("error parsing query", "error", err) } conf, auth, err := middleware.Extract(r, true) if err != nil { - database.log.Error().Err(err).Msg("error extracting conf and auth") + slog.Error("error extracting conf and auth", "error", err) http.Error(w, err.Error(), http.StatusBadRequest) return @@ -196,7 +195,7 @@ func (database *Database) get(w http.ResponseWriter, r *http.Request) { func (database *Database) query(w http.ResponseWriter, r *http.Request) { var clauses [][]interface{} if err := json.NewDecoder(r.Body).Decode(&clauses); err != nil { - database.log.Error().Err(err).Msg("error parsing body") + slog.Error("error parsing body", "error", err) http.Error(w, err.Error(), http.StatusBadRequest) return @@ -221,7 +220,7 @@ func (database *Database) query(w http.ResponseWriter, r *http.Request) { conf, auth, err := middleware.Extract(r, true) if err != nil { - database.log.Error().Err(err).Msg("error extracting conf and auth") + slog.Error("error extracting conf and auth", "error", err) http.Error(w, err.Error(), http.StatusBadRequest) return } diff --git a/extras.go b/extras.go index 0068bcb..c39cd54 100644 --- a/extras.go +++ b/extras.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "path" "path/filepath" @@ -16,19 +17,17 @@ import ( "github.com/staticbackendhq/core/config" "github.com/staticbackendhq/core/extra" "github.com/staticbackendhq/core/internal" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/middleware" "github.com/staticbackendhq/core/model" "github.com/staticbackendhq/core/sms" ) type extras struct { - log *logger.Logger } func (ex *extras) resizeImage(w http.ResponseWriter, r *http.Request) { if err := r.ParseMultipartForm(32 << 20); err != nil { - ex.log.Error().Err(err).Msg("cannot parse form") + slog.Error("cannot parse form", "error", err) http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -84,7 +83,7 @@ func (ex *extras) resizeImage(w http.ResponseWriter, r *http.Request) { resizedBytes := buf.Bytes() - ex.log.Info().Msgf("resized bytes: %d", len(resizedBytes)) + slog.Info("resized bytes", "length", len(resizedBytes)) upData := model.UploadFileData{FileKey: fileKey, File: bytes.NewReader(resizedBytes)} url, err := backend.Filestore.Save(upData) if err != nil { diff --git a/extras_test.go b/extras_test.go index 7733f6b..2ee5aec 100644 --- a/extras_test.go +++ b/extras_test.go @@ -12,7 +12,6 @@ import ( "github.com/staticbackendhq/core/backend" "github.com/staticbackendhq/core/config" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/middleware" "github.com/staticbackendhq/core/sms" ) @@ -64,8 +63,6 @@ func TestUploadAndResizeImage(t *testing.T) { middleware.RequireAuth(backend.DB, backend.Cache), } - extexec.log = logger.Get(config.Current) - h := middleware.Chain(http.HandlerFunc(extexec.resizeImage), stdAuth...) h.ServeHTTP(resp, req) diff --git a/function/runtime_search_test.go b/function/runtime_search_test.go index be923b9..641116f 100644 --- a/function/runtime_search_test.go +++ b/function/runtime_search_test.go @@ -458,7 +458,6 @@ func newRuntimeTestContext(t *testing.T, dbName, code string) runtimeTestContext Volatile: pubsub, Search: src, Data: fn, - Log: log, } return runtimeTestContext{ diff --git a/function/scheduler_test.go b/function/scheduler_test.go index 4fb02d7..0974cfc 100644 --- a/function/scheduler_test.go +++ b/function/scheduler_test.go @@ -7,10 +7,8 @@ import ( "time" "github.com/staticbackendhq/core/cache" - "github.com/staticbackendhq/core/config" "github.com/staticbackendhq/core/database" "github.com/staticbackendhq/core/database/memory" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" ) @@ -172,7 +170,7 @@ func TestTaskSchedulerFunctionTaskWithoutMetaUsesEmptyData(t *testing.T) { } func TestTaskSchedulerAddAndCancelOnTheFly(t *testing.T) { - ts := &TaskScheduler{Log: logger.Get(config.LoadConfig())} + ts := &TaskScheduler{} task := model.Task{ ID: "task-on-the-fly", Name: "on-the-fly", @@ -201,9 +199,8 @@ func TestTaskSchedulerStopUnblocksStart(t *testing.T) { baseName := fmt.Sprintf("sched_stop_%d", time.Now().UnixNano()) ds, _ := newSchedulerTestStore(t, baseName) ts := &TaskScheduler{ - Volatile: cache.NewDevCache(logger.Get(config.LoadConfig())), + Volatile: cache.NewDevCache(), DataStore: ds, - Log: logger.Get(config.LoadConfig()), } done := make(chan struct{}) @@ -237,7 +234,7 @@ func TestTaskSchedulerStopUnblocksStart(t *testing.T) { } func TestTaskSchedulerStopBeforeStart(t *testing.T) { - ts := &TaskScheduler{Log: logger.Get(config.LoadConfig())} + ts := &TaskScheduler{} ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -291,9 +288,8 @@ func TestTaskSchedulerDoesNotRunCronTaskOnStart(t *testing.T) { task.ID = taskID ts := &TaskScheduler{ - Volatile: cache.NewDevCache(logger.Get(config.LoadConfig())), + Volatile: cache.NewDevCache(), DataStore: ds, - Log: logger.Get(config.LoadConfig()), } ts.AddOnTheFly(task) ts.Scheduler.Start() diff --git a/function/subscriber_test.go b/function/subscriber_test.go index 35c8ee2..4009f32 100644 --- a/function/subscriber_test.go +++ b/function/subscriber_test.go @@ -8,16 +8,14 @@ import ( "time" "github.com/staticbackendhq/core/cache" - "github.com/staticbackendhq/core/config" "github.com/staticbackendhq/core/database/memory" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" ) func TestSubscriberDBTriggerRecordsHistoryAndLastRun(t *testing.T) { baseName := "subscriber_db_trigger" - log := logger.Get(config.LoadConfig()) - vol := cache.NewDevCache(log) + + vol := cache.NewDevCache() ds := memory.New(vol.PublishDocument) fnID, err := ds.AddFunction(baseName, model.ExecData{ @@ -35,14 +33,12 @@ func TestSubscriberDBTriggerRecordsHistoryAndLastRun(t *testing.T) { sub := &Subscriber{ PubSub: vol, - Log: log, GetExecEnv: func(msg model.Command) (*ExecutionEnvironment, error) { return &ExecutionEnvironment{ Auth: msg.Auth, BaseName: msg.Base, DataStore: ds, Volatile: vol, - Log: log, }, nil }, } @@ -90,14 +86,13 @@ func TestSubscriberDBTriggerRecordsHistoryAndLastRun(t *testing.T) { func TestSubscriberTelemetryBypassesUserMessageThrottle(t *testing.T) { baseName := "subscriber_telemetry_trigger" - log := logger.Get(config.LoadConfig()) - vol := cache.NewDevCache(log) + + vol := cache.NewDevCache() ds := memory.New(vol.PublishDocument) called := false sub := &Subscriber{ PubSub: vol, - Log: log, GetExecEnv: func(msg model.Command) (*ExecutionEnvironment, error) { called = true return &ExecutionEnvironment{ @@ -105,7 +100,6 @@ func TestSubscriberTelemetryBypassesUserMessageThrottle(t *testing.T) { BaseName: msg.Base, DataStore: ds, Volatile: vol, - Log: log, }, nil }, } @@ -130,8 +124,8 @@ func TestSubscriberTelemetryBypassesUserMessageThrottle(t *testing.T) { func TestSubscriberTelemetryTriggerRecordsHistory(t *testing.T) { baseName := "subscriber_telemetry_history" - log := logger.Get(config.LoadConfig()) - vol := cache.NewDevCache(log) + + vol := cache.NewDevCache() ds := memory.New(vol.PublishDocument) fnID, err := ds.AddFunction(baseName, model.ExecData{ @@ -149,14 +143,12 @@ func TestSubscriberTelemetryTriggerRecordsHistory(t *testing.T) { sub := &Subscriber{ PubSub: vol, - Log: log, GetExecEnv: func(msg model.Command) (*ExecutionEnvironment, error) { return &ExecutionEnvironment{ Auth: msg.Auth, BaseName: msg.Base, DataStore: ds, Volatile: vol, - Log: log, }, nil }, } @@ -200,13 +192,12 @@ func TestSubscriberTelemetryTriggerRecordsHistory(t *testing.T) { } func TestSubscriberStartContextStopsOnCancel(t *testing.T) { - log := logger.Get(config.LoadConfig()) + ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) sub := &Subscriber{ - PubSub: cache.NewDevCache(log), - Log: log, + PubSub: cache.NewDevCache(), GetExecEnv: func(msg model.Command) (*ExecutionEnvironment, error) { t.Fatal("unexpected message processing") return nil, nil diff --git a/functions.go b/functions.go index 53adbd7..27f55d8 100644 --- a/functions.go +++ b/functions.go @@ -191,7 +191,6 @@ func (f *functions) exec(w http.ResponseWriter, r *http.Request) { Volatile: backend.Cache, Data: fn, Email: backend.Emailer, - Log: backend.Log, } if err := env.Execute(r); err != nil { diff --git a/json.go b/json.go index e65dbcb..d5d3de5 100644 --- a/json.go +++ b/json.go @@ -3,9 +3,8 @@ package staticbackend import ( "encoding/json" "io" + "log/slog" "net/http" - - "github.com/staticbackendhq/core/backend" ) func respond(w http.ResponseWriter, code int, v interface{}) { @@ -18,7 +17,7 @@ func respond(w http.ResponseWriter, code int, v interface{}) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) if _, err := w.Write(b); err != nil { - backend.Log.Error().Err(err) + slog.Error(err.Error()) } } diff --git a/main_test.go b/main_test.go index e3bf7ba..9ab3957 100644 --- a/main_test.go +++ b/main_test.go @@ -43,11 +43,11 @@ func TestMain(m *testing.M) { backend.Setup(config.Current) - db = &Database{cache: backend.Cache, log: backend.Log} + db = &Database{cache: backend.Cache} - acct = &accounts{log: backend.Log} + acct = &accounts{} - mship = &membership{log: backend.Log} + mship = &membership{} deleteAndSetupTestAccount() diff --git a/membership.go b/membership.go index 00ed600..5db1350 100644 --- a/membership.go +++ b/membership.go @@ -4,20 +4,19 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "net/http" "strings" "github.com/staticbackendhq/core/backend" "github.com/staticbackendhq/core/internal" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/middleware" "github.com/staticbackendhq/core/model" ) type membership struct { //volatile internal.Volatilizer - log *logger.Logger } func (m *membership) emailExists(w http.ResponseWriter, r *http.Request) { @@ -70,7 +69,7 @@ func (m *membership) register(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { http.Error(w, "invalid StaticBackend key", http.StatusUnauthorized) - m.log.Error().Err(err).Msg("invalid StaticBackend key") + slog.Error("invalid StaticBackend key", "error", err) return } diff --git a/middleware/telemetry_test.go b/middleware/telemetry_test.go index 5d35501..37fdd82 100644 --- a/middleware/telemetry_test.go +++ b/middleware/telemetry_test.go @@ -8,8 +8,6 @@ import ( "testing" "time" - "github.com/staticbackendhq/core/config" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" ) @@ -35,7 +33,7 @@ func (c *telemetryCache) DequeueWork(key string) (string, error) func TestLongRequestTelemetryDoesNotPublishBelowThreshold(t *testing.T) { vol := &telemetryCache{} - h := LongRequestTelemetry(vol, logger.Get(config.LoadConfig()))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h := LongRequestTelemetry(vol)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) })) @@ -51,7 +49,7 @@ func TestLongRequestTelemetryDoesNotPublishBelowThreshold(t *testing.T) { func TestLongRequestTelemetryPublishesTenantMetadata(t *testing.T) { vol := &telemetryCache{} - h := LongRequestTelemetry(vol, logger.Get(config.LoadConfig()))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + h := LongRequestTelemetry(vol)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(SlowRequestThreshold + 10*time.Millisecond) http.Error(w, "nope", http.StatusTeapot) })) diff --git a/oauth.go b/oauth.go index 4c29a4a..bd0f2dd 100644 --- a/oauth.go +++ b/oauth.go @@ -8,7 +8,6 @@ import ( "github.com/staticbackendhq/core/backend" "github.com/staticbackendhq/core/config" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/middleware" "github.com/staticbackendhq/core/model" @@ -25,7 +24,6 @@ const ( ) type ExternalLogins struct { - log *logger.Logger } type ExternalUser struct { @@ -190,7 +188,7 @@ func (el *ExternalLogins) callback() http.Handler { return } - render(w, r, "oauth.html", nil, nil, el.log) + render(w, r, "oauth.html", nil, nil) }) next.ServeHTTP(w, r) diff --git a/realtime/broker_test.go b/realtime/broker_test.go index 68023e3..6957b4a 100644 --- a/realtime/broker_test.go +++ b/realtime/broker_test.go @@ -6,15 +6,13 @@ import ( "time" "github.com/staticbackendhq/core/cache" - "github.com/staticbackendhq/core/config" - "github.com/staticbackendhq/core/logger" ) func TestBrokerCloseIsIdempotent(t *testing.T) { - log := logger.Get(config.LoadConfig()) + b := NewBroker(func(context.Context, string) (string, error) { return "", nil - }, cache.NewDevCache(log), log) + }, cache.NewDevCache()) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() diff --git a/render.go b/render.go index 5d5d7e0..e3fc603 100644 --- a/render.go +++ b/render.go @@ -3,13 +3,12 @@ package staticbackend import ( "fmt" "html/template" + "log/slog" "net/http" "path/filepath" "strconv" "strings" "time" - - "github.com/staticbackendhq/core/logger" ) var ( @@ -68,11 +67,11 @@ type ViewData struct { Data interface{} } -func render(w http.ResponseWriter, r *http.Request, view string, data interface{}, flash *Flash, log *logger.Logger) { - renderWithMenu(w, r, view, data, flash, "", log) +func render(w http.ResponseWriter, r *http.Request, view string, data interface{}, flash *Flash) { + renderWithMenu(w, r, view, data, flash, "") } -func renderWithMenu(w http.ResponseWriter, r *http.Request, view string, data interface{}, flash *Flash, menu string, log *logger.Logger) { +func renderWithMenu(w http.ResponseWriter, r *http.Request, view string, data interface{}, flash *Flash, menu string) { vd := ViewData{ ActiveMenu: menu, Data: data, @@ -86,18 +85,18 @@ func renderWithMenu(w http.ResponseWriter, r *http.Request, view string, data in } if err := tmpl.Execute(w, vd); err != nil { - log.Error().Err(err).Msgf(`error executing template "%s"`, view) + slog.Error("error executing template", "template", view, "error", err) http.Error(w, err.Error(), http.StatusInternalServerError) } } -func renderErr(w http.ResponseWriter, r *http.Request, err error, log *logger.Logger) { +func renderErr(w http.ResponseWriter, r *http.Request, err error) { if err != nil { - log.Error().Err(err).Stack().Msg("err in ui") + slog.Error("err in ui", "error", err) } - render(w, r, "err.html", nil, nil, log) + render(w, r, "err.html", nil, nil) } func customFuncs() template.FuncMap { diff --git a/search/search_test.go b/search/search_test.go index f3431bb..311ebb5 100644 --- a/search/search_test.go +++ b/search/search_test.go @@ -5,8 +5,6 @@ import ( "testing" "github.com/staticbackendhq/core/cache" - "github.com/staticbackendhq/core/config" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/search" ) @@ -124,9 +122,7 @@ func TestSearchDelete(t *testing.T) { func newSearch(t *testing.T) *search.Search { t.Helper() - c := config.AppConfig{} - l := logger.Get(c) - pubsub := cache.NewDevCache(l) + pubsub := cache.NewDevCache() s, err := search.New(filepath.Join(t.TempDir(), "test.fts"), pubsub) if err != nil { diff --git a/server.go b/server.go index 31797e1..cff9a4e 100644 --- a/server.go +++ b/server.go @@ -105,11 +105,10 @@ func Start(c config.AppConfig) { } return key, nil - }, backend.Cache, log) + }, backend.Cache) database := &Database{ cache: backend.Cache, - log: log, } stdPub := []middleware.Middleware{ @@ -119,26 +118,26 @@ func Start(c config.AppConfig) { pubWithDB := []middleware.Middleware{ middleware.Cors(), middleware.WithDB(backend.DB, backend.Cache, getStripePortalURL), - middleware.LongRequestTelemetry(backend.Cache, log), + middleware.LongRequestTelemetry(backend.Cache), } stdAuth := []middleware.Middleware{ middleware.Cors(), middleware.WithDB(backend.DB, backend.Cache, getStripePortalURL), middleware.RequireAuth(backend.DB, backend.Cache), - middleware.LongRequestTelemetry(backend.Cache, log), + middleware.LongRequestTelemetry(backend.Cache), } stdRoot := []middleware.Middleware{ middleware.WithDB(backend.DB, backend.Cache, getStripePortalURL), middleware.RequireRoot(backend.DB, backend.Cache), - middleware.LongRequestTelemetry(backend.Cache, log), + middleware.LongRequestTelemetry(backend.Cache), } // static assets http.Handle("/static/", http.StripPrefix("/", http.FileServer(http.FS(content)))) - m := &membership{log: log} + m := &membership{} http.Handle("/login/magic", middleware.Chain(http.HandlerFunc(m.magicLink), pubWithDB...)) http.Handle("/login", middleware.Chain(http.HandlerFunc(m.login), pubWithDB...)) @@ -152,7 +151,7 @@ func Start(c config.AppConfig) { http.Handle("/account", middleware.Chain(http.HandlerFunc(m.deleteAccount), stdAuth...)) // oauth handlers - el := &ExternalLogins{log: log} + el := &ExternalLogins{} http.Handle("/oauth/login", middleware.Chain(el.login(), pubWithDB...)) http.Handle("/oauth/callback/", middleware.Chain(el.callback(), stdPub...)) http.Handle("/oauth/get-user", middleware.Chain(http.HandlerFunc(el.getUser), pubWithDB...)) @@ -188,7 +187,7 @@ func Start(c config.AppConfig) { http.Handle("/sudo/cache", middleware.Chain(http.HandlerFunc(sudoCache), stdRoot...)) // account - acct := &accounts{log: log} + acct := &accounts{} http.Handle("/account/init", middleware.Chain(http.HandlerFunc(acct.create), stdPub...)) http.Handle("/account/auth", middleware.Chain(http.HandlerFunc(acct.auth), stdRoot...)) http.Handle("/account/portal", middleware.Chain(http.HandlerFunc(acct.portal), stdRoot...)) @@ -200,7 +199,7 @@ func Start(c config.AppConfig) { http.Handle("/account/user-accounts", middleware.Chain(http.HandlerFunc(acct.getUserAccounts), stdRoot...)) // stripe webhooks - swh := stripeWebhook{log: log} + swh := stripeWebhook{} http.HandleFunc("/stripe", swh.process) http.HandleFunc("/ping", ping) @@ -244,7 +243,7 @@ func Start(c config.AppConfig) { http.Handle("/publish-message", middleware.Chain(http.HandlerFunc(publishMessage), stdRoot...)) // extras routes - ex := &extras{log: log} + ex := &extras{} http.Handle("/extra/resizeimg", middleware.Chain(http.HandlerFunc(ex.resizeImage), stdAuth...)) http.Handle("/extra/sms", middleware.Chain(http.HandlerFunc(ex.sudoSendSMS), stdRoot...)) http.Handle("/extra/htmltox", middleware.Chain(http.HandlerFunc(ex.htmlToX), stdAuth...)) @@ -258,7 +257,7 @@ func Start(c config.AppConfig) { } // ui routes - webUI := ui{log: log} + webUI := ui{} http.HandleFunc("/ui/login", webUI.auth) http.Handle("/ui/accounts", middleware.Chain(http.HandlerFunc(webUI.accounts), stdRoot...)) http.Handle("/ui/users/", middleware.Chain(http.HandlerFunc(webUI.users), stdRoot...)) diff --git a/stripe.go b/stripe.go index b52016c..7282ea1 100644 --- a/stripe.go +++ b/stripe.go @@ -3,19 +3,17 @@ package staticbackend import ( "encoding/json" "io" - "log" + "log/slog" "net/http" "github.com/staticbackendhq/core/backend" "github.com/staticbackendhq/core/config" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/model" "github.com/stripe/stripe-go/v84" "github.com/stripe/stripe-go/v84/webhook" ) type stripeWebhook struct { - log *logger.Logger } func (wh *stripeWebhook) process(w http.ResponseWriter, r *http.Request) { @@ -23,7 +21,7 @@ func (wh *stripeWebhook) process(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, MaxBodyBytes) body, err := io.ReadAll(r.Body) if err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (read body)") + slog.Error("STRIPE ERROR (read body)", "error", err) w.WriteHeader(http.StatusServiceUnavailable) return @@ -35,7 +33,7 @@ func (wh *stripeWebhook) process(w http.ResponseWriter, r *http.Request) { // See https://stripe.com/docs/webhooks/signatures for more information. event, err := webhook.ConstructEvent(body, r.Header.Get("Stripe-Signature"), endpointSecret) if err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (verify secret)") + slog.Error("STRIPE ERROR (verify secret)", "error", err) w.WriteHeader(http.StatusBadRequest) // Return a 400 error on a bad signature. return @@ -46,7 +44,7 @@ func (wh *stripeWebhook) process(w http.ResponseWriter, r *http.Request) { var sub stripe.Subscription err := json.Unmarshal(event.Data.Raw, &sub) if err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (sub update json))") + slog.Error("STRIPE ERROR (sub update json)", "error", err) w.WriteHeader(http.StatusBadRequest) return @@ -56,7 +54,7 @@ func (wh *stripeWebhook) process(w http.ResponseWriter, r *http.Request) { var sub stripe.Subscription err := json.Unmarshal(event.Data.Raw, &sub) if err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (sub del json))") + slog.Error("STRIPE ERROR (sub del json)", "error", err) w.WriteHeader(http.StatusBadRequest) return @@ -65,7 +63,7 @@ func (wh *stripeWebhook) process(w http.ResponseWriter, r *http.Request) { case "checkout.session.completed": var cs stripe.CheckoutSession if err := json.Unmarshal(event.Data.Raw, &cs); err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (checkout session completed JSON))") + slog.Error("STRIPE ERROR (checkout session completed JSON)", "error", err) w.WriteHeader(http.StatusBadRequest) return @@ -73,7 +71,7 @@ func (wh *stripeWebhook) process(w http.ResponseWriter, r *http.Request) { wh.handleCheckoutSessionCompleted(cs) default: - log.Printf("received unhandled Stripe webhook: %s\n", event.Type) + slog.Info("received unhandled Stripe webhook", "type", event.Type) } w.WriteHeader(http.StatusOK) @@ -86,25 +84,25 @@ func (wh *stripeWebhook) handleSubChanged(sub stripe.Subscription) { stripeID := sub.Customer.ID - wh.log.Info().Msgf("[Sub Changed]: for StripeID: %s", stripeID) + slog.Info("[Sub Changed]: for StripeID", "stripe_id", stripeID) // find the customer cus, err := backend.DB.GetTenantByStripeID(stripeID) if err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (find cus by stripe id)") + slog.Error("STRIPE ERROR (find cus by stripe id)", "stripe_id", stripeID, "error", err) return } - wh.log.Info().Msgf("[Sub Changed]: found account: %s", cus.Email) + slog.Info("[Sub Changed]: found account", "email", cus.Email) if sub.Items != nil && len(sub.Items.Data) > 0 { - wh.log.Info().Msg("[Sub Changed]: there's at least 1 sub") + slog.Info("[Sub Changed]: there's at least 1 sub") priceID := sub.Items.Data[0].Price.ID newLevel := wh.priceToLevel(priceID) if err := backend.DB.ChangeTenantPlan(cus.ID, newLevel); err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (update cus plan)") + slog.Error("STRIPE ERROR (update cus plan)", "error", err) return } } @@ -119,20 +117,20 @@ func (wh *stripeWebhook) handleSubCancelled(sub stripe.Subscription) { cus, err := backend.DB.GetTenantByStripeID(stripeID) if err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (find cus by id)") + slog.Error("STRIPE ERROR (find cus by id)", "stripe_id", stripeID, "error", err) return } if err := backend.DB.ActivateTenant(cus.ID, false); err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (sub canceled)") + slog.Error("STRIPE ERROR (sub canceled)", "tenant_id", cus.ID, "error", err) } } func (wh *stripeWebhook) handleCheckoutSessionCompleted(cs stripe.CheckoutSession) { if !wh.isSBCustomer(cs.Customer.Metadata) { - wh.log.Warn().Msg("STRIPE: checkout completed, not a sb customer") + slog.Warn("STRIPE: checkout completed, not a sb customer") for k, v := range cs.Customer.Metadata { - wh.log.Warn().Msgf("-> %s: %s", k, v) + slog.Warn("Stripe customer metadata", "key", k, "value", v) } return } @@ -141,7 +139,7 @@ func (wh *stripeWebhook) handleCheckoutSessionCompleted(cs stripe.CheckoutSessio cus, err := backend.DB.GetTenantByStripeID(stripeID) if err != nil { - wh.log.Error().Err(err).Msg("STRIPE ERROR (get cus by stripe id)") + slog.Error("STRIPE ERROR (get cus by stripe id)", "stripe_id", stripeID, "error", err) return } @@ -150,7 +148,7 @@ func (wh *stripeWebhook) handleCheckoutSessionCompleted(cs stripe.CheckoutSessio } if err := backend.DB.ActivateTenant(cus.ID, true); err != nil { - wh.log.Error().Err(err).Msgf("STRIPE ERROR (activate cus): %s", stripeID) + slog.Error("STRIPE ERROR (activate cus)", "stripe_id", stripeID, "tenant_id", cus.ID, "error", err) } } diff --git a/ui.go b/ui.go index a03b043..2a20dbc 100644 --- a/ui.go +++ b/ui.go @@ -9,24 +9,22 @@ import ( "time" "github.com/staticbackendhq/core/backend" - "github.com/staticbackendhq/core/logger" "github.com/staticbackendhq/core/middleware" "github.com/staticbackendhq/core/model" ) type ui struct { - log *logger.Logger } func (x *ui) login(w http.ResponseWriter, r *http.Request) { - render(w, r, "login.html", nil, nil, nil) + render(w, r, "login.html", nil, nil) } /* TODO: this function is not used ?!? func (x *ui) createApp(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -51,7 +49,7 @@ func (x *ui) createApp(w http.ResponseWriter, r *http.Request) { func (x ui) auth(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -60,12 +58,12 @@ func (x ui) auth(w http.ResponseWriter, r *http.Request) { conf, err := backend.DB.FindDatabase(pk) if err != nil { - render(w, r, "login.html", nil, &Flash{Type: "danger", Message: "This app does not exists"}, x.log) + render(w, r, "login.html", nil, &Flash{Type: "danger", Message: "This app does not exists"}) return } if _, err := middleware.ValidateRootToken(backend.DB, conf.Name, token); err != nil { - render(w, r, "login.html", nil, &Flash{Type: "danger", Message: "invalid public key / token"}, x.log) + render(w, r, "login.html", nil, &Flash{Type: "danger", Message: "invalid public key / token"}) return } @@ -93,40 +91,40 @@ func (x ui) auth(w http.ResponseWriter, r *http.Request) { func (x ui) logins(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } cus, err := backend.DB.FindTenant(conf.TenantID) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } logins, err := cus.GetExternalLogins() if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } - render(w, r, "logins.html", logins, nil, x.log) + render(w, r, "logins.html", logins, nil) } func (x ui) enableExternalLogin(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } cus, err := backend.DB.FindTenant(conf.TenantID) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } if err := r.ParseForm(); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -136,7 +134,7 @@ func (x ui) enableExternalLogin(w http.ResponseWriter, r *http.Request) { logins, err := cus.GetExternalLogins() if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -151,18 +149,18 @@ func (x ui) enableExternalLogin(w http.ResponseWriter, r *http.Request) { logins[provider] = keys if err := backend.DB.EnableExternalLogin(cus.ID, logins); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } flash := &Flash{Type: "success", Message: "OAuth provider successfully added"} - render(w, r, "logins.html", logins, flash, x.log) + render(w, r, "logins.html", logins, flash) } func (x *ui) dbCols(w http.ResponseWriter, r *http.Request) { conf, auth, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -179,7 +177,7 @@ func (x *ui) dbCols(w http.ResponseWriter, r *http.Request) { allNames, err := backend.DB.ListCollections(conf.Name) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -194,7 +192,7 @@ func (x *ui) dbCols(w http.ResponseWriter, r *http.Request) { } if len(names) == 0 { - render(w, r, "db_cols.html", data, nil, x.log) + render(w, r, "db_cols.html", data, nil) return } @@ -212,7 +210,7 @@ func (x *ui) dbCols(w http.ResponseWriter, r *http.Request) { // handle post if r.Method == http.MethodPost { if err := r.ParseForm(); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -226,13 +224,13 @@ func (x *ui) dbCols(w http.ResponseWriter, r *http.Request) { var clauses [][]interface{} if err := json.Unmarshal([]byte(query), &clauses); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } filter, err = backend.DB.ParseQuery(clauses) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } } @@ -243,13 +241,13 @@ func (x *ui) dbCols(w http.ResponseWriter, r *http.Request) { if len(filter) == 0 { list, err = backend.DB.ListDocuments(auth, conf.Name, col, params) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } } else { list, err = backend.DB.QueryDocuments(auth, conf.Name, col, filter, params) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } } @@ -268,13 +266,13 @@ func (x *ui) dbCols(w http.ResponseWriter, r *http.Request) { data.SortDescending = "0" } - render(w, r, "db_cols.html", data, nil, x.log) + render(w, r, "db_cols.html", data, nil) } func (x ui) dbDoc(w http.ResponseWriter, r *http.Request) { conf, auth, err := middleware.Extract(r, true) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -283,7 +281,7 @@ func (x ui) dbDoc(w http.ResponseWriter, r *http.Request) { doc, err := backend.DB.GetDocumentByID(auth, conf.Name, col, id) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -300,18 +298,18 @@ func (x ui) dbDoc(w http.ResponseWriter, r *http.Request) { data.Columns = x.readColumnNames(docs) data.Doc = doc - render(w, r, "db_doc.html", data, nil, x.log) + render(w, r, "db_doc.html", data, nil) } func (x ui) dbSave(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } conf, auth, err := middleware.Extract(r, true) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -327,7 +325,7 @@ func (x ui) dbSave(w http.ResponseWriter, r *http.Request) { case "int": i, err := strconv.ParseInt(value, 10, 64) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -335,7 +333,7 @@ func (x ui) dbSave(w http.ResponseWriter, r *http.Request) { case "float": f, err := strconv.ParseFloat(value, 64) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -347,7 +345,7 @@ func (x ui) dbSave(w http.ResponseWriter, r *http.Request) { } if _, err := backend.DB.UpdateDocument(auth, conf.Name, col, id, update); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -358,7 +356,7 @@ func (x ui) dbSave(w http.ResponseWriter, r *http.Request) { func (x ui) dbDel(w http.ResponseWriter, r *http.Request) { conf, auth, err := middleware.Extract(r, true) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -366,7 +364,7 @@ func (x ui) dbDel(w http.ResponseWriter, r *http.Request) { id := getURLPart(r.URL.Path, 4) if _, err := backend.DB.DeleteDocument(auth, conf.Name, col, id); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -398,7 +396,7 @@ func (ui) readColumnNames(docs []map[string]interface{}) []string { func (x ui) forms(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -406,13 +404,13 @@ func (x ui) forms(w http.ResponseWriter, r *http.Request) { forms, err := backend.DB.GetForms(conf.Name) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } entries, err := backend.DB.ListFormSubmissions(conf.Name, formName) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -426,20 +424,20 @@ func (x ui) forms(w http.ResponseWriter, r *http.Request) { data.Forms = forms data.Entries = entries - render(w, r, "forms.html", data, nil, x.log) + render(w, r, "forms.html", data, nil) } func (x ui) formDel(w http.ResponseWriter, r *http.Request) { conf, auth, err := middleware.Extract(r, true) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } id := getURLPart(r.URL.Path, 4) if _, err := backend.DB.DeleteDocument(auth, conf.Name, "sb_forms", id); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -449,28 +447,28 @@ func (x ui) formDel(w http.ResponseWriter, r *http.Request) { func (x ui) fnList(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } results, err := backend.DB.ListFunctions(conf.Name) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } - render(w, r, "fn_list.html", results, nil, x.log) + render(w, r, "fn_list.html", results, nil) } func (x *ui) fnNew(w http.ResponseWriter, r *http.Request) { fn := model.ExecData{} - render(w, r, "fn_edit.html", fn, nil, x.log) + render(w, r, "fn_edit.html", fn, nil) } func (x *ui) fnEdit(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -478,22 +476,22 @@ func (x *ui) fnEdit(w http.ResponseWriter, r *http.Request) { fn, err := backend.DB.GetFunctionByID(conf.Name, id) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } - render(w, r, "fn_edit.html", fn, nil, x.log) + render(w, r, "fn_edit.html", fn, nil) } func (x *ui) fnSave(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } if err := r.ParseForm(); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) } id := r.Form.Get("id") @@ -509,7 +507,7 @@ func (x *ui) fnSave(w http.ResponseWriter, r *http.Request) { } newID, err := backend.DB.AddFunction(conf.Name, fn) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -522,7 +520,7 @@ func (x *ui) fnSave(w http.ResponseWriter, r *http.Request) { Code: code, TriggerTopic: trigger, }); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -532,12 +530,12 @@ func (x *ui) fnSave(w http.ResponseWriter, r *http.Request) { func (x *ui) fnDel(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } name := getURLPart(r.URL.Path, 4) if err := backend.DB.DeleteFunction(conf.Name, name); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -547,7 +545,7 @@ func (x *ui) fnDel(w http.ResponseWriter, r *http.Request) { func (x *ui) fsList(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -555,24 +553,24 @@ func (x *ui) fsList(w http.ResponseWriter, r *http.Request) { results, err := backend.DB.ListAllFiles(conf.Name, accountID) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } - render(w, r, "fs_list.html", results, nil, x.log) + render(w, r, "fs_list.html", results, nil) } func (x *ui) fsDel(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } fileID := getURLPart(r.URL.Path, 4) if err := backend.DB.DeleteFile(conf.Name, fileID); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -582,23 +580,23 @@ func (x *ui) fsDel(w http.ResponseWriter, r *http.Request) { func (x ui) accounts(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } accounts, err := backend.DB.ListAccounts(conf.Name) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } - render(w, r, "accounts_list.html", accounts, nil, nil) + render(w, r, "accounts_list.html", accounts, nil) } func (x ui) users(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -606,39 +604,39 @@ func (x ui) users(w http.ResponseWriter, r *http.Request) { users, err := backend.DB.ListUsers(conf.Name, id) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } - render(w, r, "users_list.html", users, nil, nil) + render(w, r, "users_list.html", users, nil) } func (x ui) tasks(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } allTasks, err := backend.DB.ListTasksByBase(conf.Name) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } - render(w, r, "tasks_list.html", allTasks, nil, x.log) + render(w, r, "tasks_list.html", allTasks, nil) } func (x ui) taskNew(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodPost { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } if err := r.ParseForm(); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -653,7 +651,7 @@ func (x ui) taskNew(w http.ResponseWriter, r *http.Request) { taskID, err := backend.DB.AddTask(conf.Name, task) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -664,25 +662,25 @@ func (x ui) taskNew(w http.ResponseWriter, r *http.Request) { return } - render(w, r, "tasks_new.html", nil, nil, nil) + render(w, r, "tasks_new.html", nil, nil) } func (x ui) myAccount(w http.ResponseWriter, r *http.Request) { conf, _, err := middleware.Extract(r, false) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } if r.Method == http.MethodPost { if err := r.ParseForm(); err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } url, err := getStripePortalURL(conf.TenantID) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } @@ -692,9 +690,9 @@ func (x ui) myAccount(w http.ResponseWriter, r *http.Request) { tenant, err := backend.DB.FindTenant(conf.TenantID) if err != nil { - renderErr(w, r, err, x.log) + renderErr(w, r, err) return } - render(w, r, "customer.html", tenant, nil, x.log) + render(w, r, "customer.html", tenant, nil) } From fe77ed858915b71875e53a315c1bc21c2bb6ffd3 Mon Sep 17 00:00:00 2001 From: amrshaban2005 Date: Thu, 16 Jul 2026 17:43:18 +0400 Subject: [PATCH 3/4] Remove zerolog after slog migration --- backend/backend.go | 3 +- cmd/main.go | 1 - go.mod | 2 - go.sum | 10 ---- server.go | 3 +- sms/sms.go | 116 ++++++++++++++++++++++----------------------- 6 files changed, 60 insertions(+), 75 deletions(-) diff --git a/backend/backend.go b/backend/backend.go index 2a8a228..d5a150d 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -183,11 +183,10 @@ func Setup(cfg config.AppConfig) { logger.Setup(cfg) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - + if err := Close(ctx); err != nil { slog.Error("error closing existing backend services", "error", err) } - Config = cfg resetLifecycle() diff --git a/cmd/main.go b/cmd/main.go index 950fc28..e830f02 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,7 +12,6 @@ import ( func main() { c := config.LoadConfig() - var v bool flag.BoolVar(&v, "v", false, "Display the version and build info") flag.Parse() diff --git a/go.mod b/go.mod index 78c105d..4af5426 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( github.com/markbates/goth v1.73.0 github.com/minio/minio-go/v7 v7.0.70 github.com/redis/go-redis/v9 v9.19.0 - github.com/rs/zerolog v1.27.0 github.com/stripe/stripe-go/v84 v84.2.0 go.mongodb.org/mongo-driver v1.17.7 golang.org/x/crypto v0.48.0 @@ -78,7 +77,6 @@ require ( github.com/klauspost/compress v1.17.6 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/magefile/mage v1.9.0 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/montanaflynn/stats v0.7.1 // indirect diff --git a/go.sum b/go.sum index 3ec6c23..425a3ed 100644 --- a/go.sum +++ b/go.sum @@ -131,7 +131,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -168,7 +167,6 @@ github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakr github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -279,9 +277,6 @@ github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQ github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA= github.com/markbates/goth v1.73.0 h1:X5QUUHLP5puJ4dhoPKkV3PhDIvvQEzsfVxsUmDNSJ28= github.com/markbates/goth v1.73.0/go.mod h1:X6xdNgpapSENS0O35iTBBcMHoJDQDfI9bJl+APCkYMc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= @@ -309,11 +304,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= -github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -490,8 +482,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/server.go b/server.go index cff9a4e..6499ec2 100644 --- a/server.go +++ b/server.go @@ -14,7 +14,6 @@ import ( "syscall" "time" - "github.com/rs/zerolog/log" "github.com/staticbackendhq/core/backend" "github.com/staticbackendhq/core/config" "github.com/staticbackendhq/core/internal" @@ -317,7 +316,7 @@ func Start(c config.AppConfig) { }) if err := g.Wait(); err != nil { - log.Error().Err(err).Msg("exit reason") + slog.Error("exit reason", "error", err) } } diff --git a/sms/sms.go b/sms/sms.go index a5c92f4..6597e55 100644 --- a/sms/sms.go +++ b/sms/sms.go @@ -1,58 +1,58 @@ -package sms - -import ( - "fmt" - "io" - "net/http" - "net/url" - "strings" -) - -type SMSData struct { - AccountSID string `json:"accountSID"` - AuthToken string `json:"authToken"` - ToNumber string `json:"toNumber"` - FromNumber string `json:"fromNumber"` - Body string `json:"body"` -} - -// Send sends a text-message using Twilio -func Send(data SMSData) error { - apiURL := "https://api.twilio.com/2010-04-01/Accounts/" + data.AccountSID + "/Messages.json" - - // Build out the data for the message - v := url.Values{} - v.Set("To", data.ToNumber) - v.Set("From", data.FromNumber) - v.Set("Body", data.Body) - rb := strings.NewReader(v.Encode()) - - client := &http.Client{} - - req, err := http.NewRequest("POST", apiURL, rb) - if err != nil { - return err - } - - req.SetBasicAuth(data.AccountSID, data.AuthToken) - - req.Header.Add("Accept", "application/json") - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - - // Make request - resp, err := client.Do(req) - if err != nil { - return err - } - defer func() { _ = resp.Body.Close() }() - - if resp.StatusCode > 299 { - b, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - - return fmt.Errorf("error returned by Twilio: %s", string(b)) - } - return nil -} +package sms + +import ( + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +type SMSData struct { + AccountSID string `json:"accountSID"` + AuthToken string `json:"authToken"` + ToNumber string `json:"toNumber"` + FromNumber string `json:"fromNumber"` + Body string `json:"body"` +} + +// Send sends a text-message using Twilio +func Send(data SMSData) error { + apiURL := "https://api.twilio.com/2010-04-01/Accounts/" + data.AccountSID + "/Messages.json" + + // Build out the data for the message + v := url.Values{} + v.Set("To", data.ToNumber) + v.Set("From", data.FromNumber) + v.Set("Body", data.Body) + rb := strings.NewReader(v.Encode()) + + client := &http.Client{} + + req, err := http.NewRequest("POST", apiURL, rb) + if err != nil { + return err + } + + req.SetBasicAuth(data.AccountSID, data.AuthToken) + + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + // Make request + resp, err := client.Do(req) + if err != nil { + return err + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode > 299 { + b, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + return fmt.Errorf("error returned by Twilio: %s", string(b)) + } + return nil +} From 673ba17b33656654437ba579eae3c1f1342b58d1 Mon Sep 17 00:00:00 2001 From: amrshaban2005 Date: Thu, 16 Jul 2026 18:02:57 +0400 Subject: [PATCH 4/4] Clean up slog migration follow-ups --- backend/backend.go | 1 - cache/cache.go | 2 +- function/subscriber.go | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/backend.go b/backend/backend.go index d5a150d..bf50526 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -34,7 +34,6 @@ // - [Filestore]: raw blob storage // - [Emailer]: to send emails // - [Config]: the config that was passed to [Setup] -// - [Log]: logger // // You may see those services as raw building blocks that give you the most // flexibility to build on top. diff --git a/cache/cache.go b/cache/cache.go index 21242c7..eb002b1 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -205,7 +205,7 @@ func (c *Cache) Publish(msg model.Command) error { subs, err := c.Rdb.PubSubNumSub(c.Ctx, msg.Channel).Result() if err != nil { - slog.Error("error getting db subscribers for", "channel", msg.Channel) + slog.Error("error getting db subscribers for", "channel", msg.Channel, "error", err) return err } diff --git a/function/subscriber.go b/function/subscriber.go index 2605175..e5ed4ab 100644 --- a/function/subscriber.go +++ b/function/subscriber.go @@ -108,7 +108,7 @@ func (sub *Subscriber) handleRealtimeEvents(msg model.Command, wg *sync.WaitGrou // for msg type error, we do nothing if msg.Type == model.MsgTypeError { - slog.Error("receiving msg of type error", "error", err) + slog.Error("receiving msg of type error") return }