From a34734366d8ba0fd0819af10fc82834cc778516a Mon Sep 17 00:00:00 2001 From: ScuroNeko Date: Mon, 27 Apr 2026 10:23:54 +0300 Subject: [PATCH] (new): sneklog v2 --- CHANGELOG.md | 3 ++ bot.go | 81 +++++++++++++++++++++++++++++++++---------- bot_config.go | 10 +++--- bot_register.go | 8 ++--- bot_test.go | 70 +++++++++++++++++++++++++------------ bot_utils.go | 41 ++++++++++++---------- bot_webhook_test.go | 12 +++---- cmd_generator_test.go | 4 +-- drafts_test.go | 4 +-- go.mod | 11 ++---- go.sum | 16 +++------ handler_test.go | 38 ++++++++++---------- methods.go | 4 +-- msg_context.go | 4 +-- msg_context_test.go | 18 +++++----- plugins.go | 6 ++-- policy_test.go | 26 +++++++------- runners_test.go | 8 ++--- scene_test.go | 34 +++++++++--------- tgapi/api.go | 6 ++-- tgapi/uploader_api.go | 6 ++-- utils/utils.go | 14 ++++---- utils/utils_test.go | 6 ++-- 23 files changed, 245 insertions(+), 185 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a735ad..afae59b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,14 @@ ## v1.0.0-rc.16 ### Changed +- Updated `slog` to `v2`. - Bot loggers now apply the configured token replacer consistently across the main bot logger, request logger, internal API and uploader loggers, webhook logger, and auto-managed plugin loggers, so bot tokens stay masked in both stdout and file-backed logs. - JSON `BotOpts` files now write `version`, reject newer unsupported config versions, keep older unversioned files loadable, and preserve the loaded file version in `BotOpts.FileConfigVersion`. - Active scenes now support scene-local callback payload handlers through `Scene.OnPayload(...)`, including observer lifecycle events for scene payload execution. ### Tests +- Added regression coverage proving polling startup preserves an enabled request logger. +- Updated file logger regression coverage for the current `sneklog` text prefix format. - Added regression coverage proving token masking still applies after `initLoggers(...)` switches loggers to file-backed writers and that auto-managed plugin loggers inherit token masking as well. - Added regression coverage for JSON config version handling and scene-local payload routing, including observer lifecycle events and callback fallthrough behavior. diff --git a/bot.go b/bot.go index 34b67cc..7d2083b 100644 --- a/bot.go +++ b/bot.go @@ -10,7 +10,7 @@ import ( "git.scuroneko.dev/scuroneko/extypes" "git.scuroneko.dev/scuroneko/laniakea/tgapi" "git.scuroneko.dev/scuroneko/laniakea/utils" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) // AppData is the generic shared application data type injected into bots, @@ -38,11 +38,11 @@ type AppData any // Use Bot[NoData] to indicate no shared dependency injection is required. type NoData struct{ AppData } -// AppDataLogger builds a slog.LoggerWriter from injected application data. +// AppDataLogger builds a sneklog.LoggerWriter from injected application data. // // Use it when shared application data exposes a log sink or adapter that should // receive framework logs. -type AppDataLogger[T AppData] func(data T) slog.LoggerWriter +type AppDataLogger[T AppData] func(data T) sneklog.LoggerWriter // BotPayloadType defines the serialization format for callback data payloads. type BotPayloadType string @@ -90,10 +90,11 @@ type Bot[T AppData] struct { strictPayloadType bool maxWorkers int - logger *slog.Logger // Main bot logger (JSON stdout + optional file) - RequestLogger *slog.Logger // Optional request-level API logging - webHookLogger *slog.Logger // Webhook logger. Available only after Bot.RunWebHookWithContext. - extraLoggers extypes.Slice[*slog.Logger] // API, Uploader, and custom loggers + logger *sneklog.Logger // Main bot logger (JSON stdout + optional file) + requestLogger *sneklog.Logger // Optional request-level API logging + useReqLogger bool + webHookLogger *sneklog.Logger // Webhook logger. Available only after Bot.RunWebHookWithContext. + extraLoggers extypes.Slice[*sneklog.Logger] // API, Uploader, and custom loggers plugins []Plugin[T] // Command/event handlers middlewares []Middleware[T] // Pre-processing filters (sorted by order) @@ -187,12 +188,14 @@ func NewBot[T any](opts *BotOpts) (*Bot[T], error) { debug: opts.Debug, prefixes: prefixes, token: opts.Token, - plugins: make([]Plugin[T], 0), - updateTypes: append([]tgapi.UpdateType{}, opts.UpdateTypes...), - runners: make([]Runner[T], 0), - extraLoggers: make([]*slog.Logger, 0), - l10n: &L10n{}, - draftProvider: NewRandomDraftProvider(api), + useReqLogger: opts.UseRequestLogger, + + plugins: make([]Plugin[T], 0), + updateTypes: append([]tgapi.UpdateType{}, opts.UpdateTypes...), + runners: make([]Runner[T], 0), + extraLoggers: make([]*sneklog.Logger, 0), + l10n: &L10n{}, + draftProvider: NewRandomDraftProvider(api), sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -235,6 +238,24 @@ func NewBot[T any](opts *BotOpts) (*Bot[T], error) { return bot, nil } +// SetLogger replaces the main bot logger. +func (b *Bot[T]) SetLogger(l *sneklog.Logger) *Bot[T] { + b.logger = l + return b +} + +// SetRequestLogger replaces the request-level logger. +func (b *Bot[T]) SetRequestLogger(l *sneklog.Logger) *Bot[T] { + b.requestLogger = l + return b +} + +// SetWebHookLogger replaces the webhook logger. +func (b *Bot[T]) SetWebHookLogger(l *sneklog.Logger) *Bot[T] { + b.webHookLogger = l + return b +} + // Close gracefully shuts down bot-owned resources. // // Close shuts down, in order: @@ -283,8 +304,8 @@ func (bot *Bot[T]) Close() error { logCloseErr(err) } } - if bot.RequestLogger != nil { - if err := bot.RequestLogger.Close(); err != nil { + if bot.requestLogger != nil { + if err := bot.requestLogger.Close(); err != nil { logCloseErr(err) } } @@ -322,14 +343,20 @@ func (bot *Bot[T]) SetUpdateOffset(offset int) { } // GetLogger returns the main bot logger. -func (bot *Bot[T]) GetLogger() *slog.Logger { return bot.logger } +func (bot *Bot[T]) GetLogger() *sneklog.Logger { return bot.logger } + +// GetRequestLogger returns the request-level logger, if configured. +func (bot *Bot[T]) GetRequestLogger() *sneklog.Logger { return bot.requestLogger } + +// GetWebHookLogger returns the webhook logger, if configured. +func (bot *Bot[T]) GetWebHookLogger() *sneklog.Logger { return bot.webHookLogger } // GetLoggerLevel returns the effective log level derived from the bot's debug // flag. -func (bot *Bot[T]) GetLoggerLevel() slog.LogLevel { - level := slog.FATAL +func (bot *Bot[T]) GetLoggerLevel() sneklog.LogLevel { + level := sneklog.FATAL if bot.debug { - level = slog.DEBUG + level = sneklog.DEBUG } return level } @@ -373,6 +400,22 @@ func (bot *Bot[T]) RunWithContext(ctx context.Context) error { return err } defer bot.finishRun() + if !bot.useReqLogger && bot.requestLogger != nil { + bot.logger.Warnln("Opts#UseRequestLogger is false, but Bot#requestLogger present. Remove Bot#SetRequestLogger or set Opts#UseRequestLogger to true!") + err := bot.requestLogger.Close() + if err != nil { + bot.logger.Errorln(err) + } + bot.requestLogger = nil + } + if bot.webHookLogger != nil { + bot.logger.Warnln("Bot#webHookLogger present. You shouldn't set this, if ran in Long Polling mode!") + err := bot.webHookLogger.Close() + if err != nil { + bot.logger.Errorln(err) + } + bot.webHookLogger = nil + } bot.ExecRunners(ctx) diff --git a/bot_config.go b/bot_config.go index 851915c..ad179b5 100644 --- a/bot_config.go +++ b/bot_config.go @@ -5,7 +5,7 @@ import ( "slices" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) // AddPrefixes adds one or more command prefixes (e.g., "/", "!"). @@ -187,14 +187,14 @@ func (bot *Bot[T]) SetErrorTemplate(s string) *Bot[T] { // SetDebug enables or disables debug logging. func (bot *Bot[T]) SetDebug(debug bool) *Bot[T] { bot.debug = debug - level := slog.FATAL + level := sneklog.FATAL if debug { - level = slog.DEBUG + level = sneklog.DEBUG } bot.logger.Level(level) - if bot.RequestLogger != nil { - bot.RequestLogger.Level(level) + if bot.requestLogger != nil { + bot.requestLogger.Level(level) } for _, p := range bot.plugins { if p.logger == nil { diff --git a/bot_register.go b/bot_register.go index 8d046ab..c71fbb1 100644 --- a/bot_register.go +++ b/bot_register.go @@ -129,7 +129,7 @@ func (bot *Bot[T]) AddRunner(runner Runner[T]) *Bot[T] { // // Example: // -// bot.AddAppDataLoggerWriter(func(data *MyAppData) slog.LoggerWriter { +// bot.AddAppDataLoggerWriter(func(data *MyAppData) sneklog.LoggerWriter { // return data.QueryLogger() // }) func (bot *Bot[T]) AddAppDataLoggerWriter(writer AppDataLogger[T]) *Bot[T] { @@ -143,8 +143,8 @@ func (bot *Bot[T]) AddAppDataLoggerWriter(writer AppDataLogger[T]) *Bot[T] { } w := writer(bot.appData) bot.logger.AddWriter(w) - if bot.RequestLogger != nil { - bot.RequestLogger.AddWriter(w) + if bot.requestLogger != nil { + bot.requestLogger.AddWriter(w) } for _, l := range bot.managedExtraLoggers() { l.AddWriter(w) @@ -154,7 +154,7 @@ func (bot *Bot[T]) AddAppDataLoggerWriter(writer AppDataLogger[T]) *Bot[T] { p.logger.AddWriter(w) } } - bot.addTokenReplacer(bot.logger, bot.RequestLogger) + bot.addTokenReplacer(bot.logger, bot.requestLogger) bot.addTokenReplacer(bot.managedExtraLoggers()...) return bot } diff --git a/bot_test.go b/bot_test.go index 711bf9f..1551a8e 100644 --- a/bot_test.go +++ b/bot_test.go @@ -13,7 +13,7 @@ import ( "time" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) type pollingRoundTripFunc func(*http.Request) (*http.Response, error) @@ -59,7 +59,7 @@ func TestGetUpdateTypesReturnsCopy(t *testing.T) { } func TestAddPluginsSnapshotsConfiguration(t *testing.T) { - bot := &Bot[NoData]{logger: slog.CreateLogger()} + bot := &Bot[NoData]{logger: sneklog.CreateLogger()} plugin := NewPlugin[NoData]("demo") cmd := plugin.NewCommand(func(ctx *MsgContext, db NoData) error { return nil }, "start") @@ -100,7 +100,7 @@ func TestBotPayloadTypeConfiguration(t *testing.T) { } func TestAddPluginsSkipsNilPlugin(t *testing.T) { - bot := &Bot[NoData]{logger: slog.CreateLogger()} + bot := &Bot[NoData]{logger: sneklog.CreateLogger()} plugin := NewPlugin[NoData]("demo") bot.AddPlugins(nil, plugin) @@ -126,10 +126,10 @@ func TestInitLoggersFallsBackToStdoutLoggerOnFileError(t *testing.T) { if bot.logger == nil { t.Fatal("expected main logger fallback") } - if bot.RequestLogger == nil { + if bot.requestLogger == nil { t.Fatal("expected request logger fallback") } - if err := bot.RequestLogger.Close(); err != nil { + if err := bot.requestLogger.Close(); err != nil { t.Fatalf("failed to close request logger: %v", err) } if err := bot.logger.Close(); err != nil { @@ -177,11 +177,11 @@ func TestInitLoggersAppliesTokenReplacerToFileLoggers(t *testing.T) { bot.uploader.GetLogger().AddWriter(bot.uploader.GetLogger().CreateTextWriter(uploaderFile)) bot.logger.Infoln("main secret-token") - bot.RequestLogger.Infoln("request secret-token") + bot.requestLogger.Infoln("request secret-token") bot.api.GetLogger().Infoln("api secret-token") bot.uploader.GetLogger().Infoln("uploader secret-token") - if err := bot.RequestLogger.Close(); err != nil { + if err := bot.requestLogger.Close(); err != nil { t.Fatalf("failed to close request logger: %v", err) } if err := bot.logger.Close(); err != nil { @@ -226,7 +226,7 @@ func TestInitLoggersAppliesTokenReplacerToFileLoggers(t *testing.T) { func TestAddPluginsAppliesTokenReplacerToPluginLogger(t *testing.T) { bot := &Bot[NoData]{ token: "secret-token", - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), } defer func() { _ = bot.logger.Close() }() @@ -276,10 +276,10 @@ func TestNextPollRetryDelay(t *testing.T) { } func TestAddDatabaseLoggerWriterSkipsWhenAppDataIsUnset(t *testing.T) { - bot := &Bot[NoData]{logger: slog.CreateLogger()} + bot := &Bot[NoData]{logger: sneklog.CreateLogger()} called := false - bot.AddAppDataLoggerWriter(func(db NoData) slog.LoggerWriter { + bot.AddAppDataLoggerWriter(func(db NoData) sneklog.LoggerWriter { called = true return nil }) @@ -292,12 +292,12 @@ func TestAddDatabaseLoggerWriterSkipsWhenAppDataIsUnset(t *testing.T) { func TestAddDatabaseLoggerWriterSkipsWhenAppDataIsNil(t *testing.T) { type testDB struct{} - bot := &Bot[*testDB]{logger: slog.CreateLogger()} + bot := &Bot[*testDB]{logger: sneklog.CreateLogger()} var db *testDB bot.SetAppData(db) called := false - bot.AddAppDataLoggerWriter(func(db *testDB) slog.LoggerWriter { + bot.AddAppDataLoggerWriter(func(db *testDB) sneklog.LoggerWriter { called = true return nil }) @@ -336,13 +336,13 @@ func TestShouldWarnOnValueAppData(t *testing.T) { func TestSetAppDataMarksValueWarningOnce(t *testing.T) { type testDB struct{} - bot := &Bot[testDB]{logger: slog.CreateLogger()} + bot := &Bot[testDB]{logger: sneklog.CreateLogger()} bot.SetAppData(testDB{}) if !bot.warnedValueData { t.Fatal("expected value-typed app data to mark warning state") } - ptrBot := &Bot[*testDB]{logger: slog.CreateLogger()} + ptrBot := &Bot[*testDB]{logger: sneklog.CreateLogger()} ptrBot.SetAppData(&testDB{}) if ptrBot.warnedValueData { t.Fatal("did not expect pointer-typed app data to mark warning state") @@ -350,7 +350,7 @@ func TestSetAppDataMarksValueWarningOnce(t *testing.T) { } func TestSetObserverAndGetObserver(t *testing.T) { - bot := &Bot[NoData]{logger: slog.CreateLogger()} + bot := &Bot[NoData]{logger: sneklog.CreateLogger()} observer := testObserver{} if got := bot.GetObserver(); got != nil { @@ -364,7 +364,7 @@ func TestSetObserverAndGetObserver(t *testing.T) { } func TestSetObserverNilClearsObserver(t *testing.T) { - bot := &Bot[NoData]{logger: slog.CreateLogger()} + bot := &Bot[NoData]{logger: sneklog.CreateLogger()} bot.SetObserver(testObserver{}) if bot.GetObserver() == nil { @@ -382,7 +382,7 @@ func TestRunWithContextRejectsSecondRun(t *testing.T) { cancel() bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, plugins: []Plugin[NoData]{{name: "demo"}}, updateQueue: make(chan *tgapi.Update, 1), @@ -397,6 +397,32 @@ func TestRunWithContextRejectsSecondRun(t *testing.T) { } } +func TestRunWithContextKeepsEnabledRequestLogger(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + requestLogger := sneklog.CreateLogger() + bot := &Bot[NoData]{ + logger: sneklog.CreateLogger(), + requestLogger: requestLogger, + useReqLogger: true, + prefixes: []string{"/"}, + plugins: []Plugin[NoData]{{name: "demo"}}, + updateQueue: make(chan *tgapi.Update, 1), + maxWorkers: 1, + } + t.Cleanup(func() { + _ = bot.Close() + }) + + if err := bot.RunWithContext(ctx); err != nil { + t.Fatalf("RunWithContext returned error: %v", err) + } + if got := bot.GetRequestLogger(); got != requestLogger { + t.Fatalf("expected enabled request logger to be preserved, got %#v", got) + } +} + func TestCloseDoesNotDeleteWebhook(t *testing.T) { requests := 0 client := &http.Client{ @@ -418,8 +444,8 @@ func TestCloseDoesNotDeleteWebhook(t *testing.T) { uploader := tgapi.NewUploader(api) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), - webHookLogger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), + webHookLogger: sneklog.CreateLogger(), api: api, uploader: uploader, } @@ -457,7 +483,7 @@ func TestRunWithContextEmitsPollingRetryAndErrorEvents(t *testing.T) { }() bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), api: api, prefixes: []string{"/"}, plugins: []Plugin[NoData]{{name: "demo"}}, @@ -489,7 +515,7 @@ func TestBotConfigurationFreezesAfterRunStarts(t *testing.T) { makeBot := func() *Bot[*testDB] { return &Bot[*testDB]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, updateTypes: []tgapi.UpdateType{tgapi.UpdateTypeMessage}, payloadType: BotPayloadBase64, @@ -681,7 +707,7 @@ func TestBotConfigurationFreezesAfterRunStarts(t *testing.T) { func TestAddPluginsAndRuntimeRegistrationsNoOpAfterRunStarts(t *testing.T) { bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, middlewares: []Middleware[NoData]{NewMiddleware("base", func(ctx *MsgContext, db NoData) bool { return true })}, runners: []Runner[NoData]{NewRunner("base", func(bot *Bot[NoData]) error { return nil })}, diff --git a/bot_utils.go b/bot_utils.go index 29fc3b2..340db54 100644 --- a/bot_utils.go +++ b/bot_utils.go @@ -12,11 +12,12 @@ import ( "git.scuroneko.dev/scuroneko/extypes" "git.scuroneko.dev/scuroneko/laniakea/tgapi" "git.scuroneko.dev/scuroneko/laniakea/utils" - "git.scuroneko.dev/scuroneko/slog" + + "git.scuroneko.dev/scuroneko/sneklog/v2" "github.com/alitto/pond/v2" ) -func (bot *Bot[T]) addTokenReplacer(loggers ...*slog.Logger) { +func (bot *Bot[T]) addTokenReplacer(loggers ...*sneklog.Logger) { if bot.token == "" { return } @@ -28,7 +29,7 @@ func (bot *Bot[T]) addTokenReplacer(loggers ...*slog.Logger) { } } -func appendUniqueLogger(loggers []*slog.Logger, logger *slog.Logger) []*slog.Logger { +func appendUniqueLogger(loggers []*sneklog.Logger, logger *sneklog.Logger) []*sneklog.Logger { if logger == nil { return loggers } @@ -38,8 +39,8 @@ func appendUniqueLogger(loggers []*slog.Logger, logger *slog.Logger) []*slog.Log return append(loggers, logger) } -func (bot *Bot[T]) managedExtraLoggers() []*slog.Logger { - loggers := append([]*slog.Logger(nil), bot.extraLoggers...) +func (bot *Bot[T]) managedExtraLoggers() []*sneklog.Logger { + loggers := append([]*sneklog.Logger(nil), bot.extraLoggers...) if bot.api != nil { loggers = appendUniqueLogger(loggers, bot.api.GetLogger()) } @@ -70,36 +71,38 @@ func (bot *Bot[T]) startUpdateWorkers(ctx context.Context) { } func (bot *Bot[T]) initLoggers(opts *BotOpts) { - level := slog.FATAL + level := sneklog.FATAL if opts.Debug { - level = slog.DEBUG + level = sneklog.DEBUG } - bot.logger = utils.CreateLogger("BOT", level) - if opts.WriteToFile { - path := fmt.Sprintf("%s/main.log", strings.TrimRight(opts.LoggerBasePath, "/")) - logger, err := utils.CreateFileLogger("BOT", level, path) - if err != nil { - bot.logger.Errorln(err) - } else { - bot.logger = logger + if bot.logger == nil { + bot.logger = utils.CreateLogger("BOT", level) + if opts.WriteToFile { + path := fmt.Sprintf("%s/main.log", strings.TrimRight(opts.LoggerBasePath, "/")) + logger, err := utils.CreateFileLogger("BOT", level, path) + if err != nil { + bot.logger.Errorln(err) + } else { + bot.logger = logger + } } } - if opts.UseRequestLogger { - bot.RequestLogger = utils.CreateLogger("REQUESTS", level) + if opts.UseRequestLogger && bot.requestLogger == nil { + bot.requestLogger = utils.CreateLogger("REQUESTS", level) if opts.WriteToFile { path := fmt.Sprintf("%s/requests.log", strings.TrimRight(opts.LoggerBasePath, "/")) logger, err := utils.CreateFileLogger("REQUESTS", level, path) if err != nil { bot.logger.Errorln(err) } else { - bot.RequestLogger = logger + bot.requestLogger = logger } } } - bot.addTokenReplacer(bot.logger, bot.RequestLogger) + bot.addTokenReplacer(bot.logger, bot.requestLogger) bot.addTokenReplacer(bot.managedExtraLoggers()...) } diff --git a/bot_webhook_test.go b/bot_webhook_test.go index e58d06c..328df04 100644 --- a/bot_webhook_test.go +++ b/bot_webhook_test.go @@ -11,7 +11,7 @@ import ( "testing" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) func TestEnqueueUpdateCopiesValue(t *testing.T) { @@ -35,7 +35,7 @@ func TestEnqueueUpdateCopiesValue(t *testing.T) { func TestUpdateHandlerEnqueuesUpdate(t *testing.T) { bot := &Bot[NoData]{ updateQueue: make(chan *tgapi.Update, 1), - webHookLogger: slog.CreateLogger(), + webHookLogger: sneklog.CreateLogger(), } t.Cleanup(func() { _ = bot.webHookLogger.Close() @@ -66,7 +66,7 @@ func TestUpdateHandlerEnqueuesUpdate(t *testing.T) { func TestRunWebhookRuntimeRejectsSecondRun(t *testing.T) { bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), updateQueue: make(chan *tgapi.Update, 1), maxWorkers: 1, } @@ -86,7 +86,7 @@ func TestRunWebhookRuntimeExecutesRunners(t *testing.T) { var calls atomic.Int32 bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), updateQueue: make(chan *tgapi.Update, 1), maxWorkers: 1, runners: []Runner[NoData]{ @@ -185,7 +185,7 @@ func TestValidateWebhookTLSFiles(t *testing.T) { func TestUpdateHandlerRejectsOversizedBody(t *testing.T) { bot := &Bot[NoData]{ updateQueue: make(chan *tgapi.Update, 1), - webHookLogger: slog.CreateLogger(), + webHookLogger: sneklog.CreateLogger(), } t.Cleanup(func() { _ = bot.webHookLogger.Close() @@ -222,7 +222,7 @@ func TestStatusHandlerRequiresMatchingSecret(t *testing.T) { bot := &Bot[NoData]{ api: api, - webHookLogger: slog.CreateLogger(), + webHookLogger: sneklog.CreateLogger(), } t.Cleanup(func() { _ = bot.webHookLogger.Close() diff --git a/cmd_generator_test.go b/cmd_generator_test.go index 064b2b0..56157df 100644 --- a/cmd_generator_test.go +++ b/cmd_generator_test.go @@ -11,7 +11,7 @@ import ( "testing" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) type roundTripFunc func(*http.Request) (*http.Response, error) @@ -51,7 +51,7 @@ func TestAutoGenerateCommandsChecksLimitBeforeDelete(t *testing.T) { bot := &Bot[NoData]{ api: api, - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), plugins: []Plugin[NoData]{*plugin}, } diff --git a/drafts_test.go b/drafts_test.go index 757aa16..230414b 100644 --- a/drafts_test.go +++ b/drafts_test.go @@ -6,7 +6,7 @@ import ( "testing" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) func TestDraftFlushRequiresChatID(t *testing.T) { @@ -24,7 +24,7 @@ func TestMsgContextNewDraftWorksWithoutLimiter(t *testing.T) { Msg: &tgapi.Message{ Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate}, }, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), draftProvider: NewRandomDraftProvider(&tgapi.API{}), } diff --git a/go.mod b/go.mod index 7d1b6e3..48585f1 100644 --- a/go.mod +++ b/go.mod @@ -6,14 +6,7 @@ retract v1.0.0-rc.5 require ( git.scuroneko.dev/scuroneko/extypes v1.2.3 - git.scuroneko.dev/scuroneko/slog v1.2.0 - github.com/alitto/pond/v2 v2.7.0 + git.scuroneko.dev/scuroneko/sneklog/v2 v2.0.1 + github.com/alitto/pond/v2 v2.7.1 golang.org/x/time v0.15.0 ) - -require ( - github.com/fatih/color v1.19.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.21 // indirect - golang.org/x/sys v0.43.0 // indirect -) diff --git a/go.sum b/go.sum index 7b7f275..8e3936a 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,8 @@ git.scuroneko.dev/scuroneko/extypes v1.2.3 h1:n7QsfTZEn9fJNZLXGH/LkNq4cADaRk+LTu6LNMv9y6s= git.scuroneko.dev/scuroneko/extypes v1.2.3/go.mod h1:MhYpXC6sloLOpoM2guf64eSOrz+ET/QJZ8toobc3Ors= -git.scuroneko.dev/scuroneko/slog v1.2.0 h1:xbwzrMcmN0NG/zTgEn508mn2JVnfZN5z/Zsi3PREfDM= -git.scuroneko.dev/scuroneko/slog v1.2.0/go.mod h1:r+oz9NzvvdtWd9/PjeS+n5vQoNHL38BdcdLoBtJPvFU= -github.com/alitto/pond/v2 v2.7.0 h1:c76L+yN916m/DRXjGCeUBHHu92uWnh/g1bwVk4zyyXg= -github.com/alitto/pond/v2 v2.7.0/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE= -github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= -github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= -github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +git.scuroneko.dev/scuroneko/sneklog/v2 v2.0.1 h1:nDGLsvxbYoSxSk9wrGCmb+tn5jPM+KYlBX+3ciyC2Yo= +git.scuroneko.dev/scuroneko/sneklog/v2 v2.0.1/go.mod h1:q8XnLXzLdGjW0Jtcbh9/+G9WmfD68rsPQvLXEPxvum4= +github.com/alitto/pond/v2 v2.7.1 h1:QxMbcfjcVTa0pyxX5Ib1226mM8u8D7gKUVkCUU4DYIw= +github.com/alitto/pond/v2 v2.7.1/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= diff --git a/handler_test.go b/handler_test.go index 2f1fdcf..47406dc 100644 --- a/handler_test.go +++ b/handler_test.go @@ -6,7 +6,7 @@ import ( "testing" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) type recordingObserver struct { @@ -55,7 +55,7 @@ func TestCheckPrefixesSkipsEmptyPrefixes(t *testing.T) { } func TestBotMiddlewareReceivesLogger(t *testing.T) { - logger := slog.CreateLogger() + logger := sneklog.CreateLogger() called := false bot := &Bot[NoData]{ @@ -468,7 +468,7 @@ func TestHandleUpdateHandlersPopulateFromContext(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), plugins: []Plugin[NoData]{clonePlugin(plugin)}, } @@ -514,7 +514,7 @@ func TestHandleUpdateHandlersReceiveIsolatedContexts(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), plugins: []Plugin[NoData]{ clonePlugin(first), clonePlugin(second), @@ -543,7 +543,7 @@ func TestHandleUpdateObserverEmitsUpdateErrors(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), plugins: []Plugin[NoData]{clonePlugin(plugin)}, observer: observer, } @@ -607,7 +607,7 @@ func TestHandleMessageFallbackRunsAfterCommandMiss(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, observer: observer, } @@ -658,7 +658,7 @@ func TestHandleMessageFallbackRunsForPlainText(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, } bot.AddPlugins(plugin) @@ -691,7 +691,7 @@ func TestHandleMessageFallbackRespectsMiddleware(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, } bot.AddPlugins(plugin) @@ -726,7 +726,7 @@ func TestHandleMessageFallbackDoesNotRunWhenCommandMatches(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, } bot.AddPlugins(plugin) @@ -771,7 +771,7 @@ func TestHandleChannelPostCommandWithSenderChat(t *testing.T) { }, "ping") bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, plugins: []Plugin[NoData]{clonePlugin(plugin)}, } @@ -808,7 +808,7 @@ func TestCommandHandlerBindArgsEndToEnd(t *testing.T) { ) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, plugins: []Plugin[NoData]{clonePlugin(plugin)}, } @@ -845,7 +845,7 @@ func TestPayloadHandlerBindArgsEndToEnd(t *testing.T) { ) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), payloadType: BotPayloadJson, plugins: []Plugin[NoData]{clonePlugin(plugin)}, } @@ -898,7 +898,7 @@ func TestHandleEditedMessageStaysOutOfCommandFlow(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, plugins: []Plugin[NoData]{clonePlugin(plugin)}, } @@ -940,7 +940,7 @@ func TestHandleEditedChannelPostStaysOutOfCommandFlow(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, plugins: []Plugin[NoData]{clonePlugin(plugin)}, } @@ -993,7 +993,7 @@ func TestHandleCallbackPopulatesMessageTargets(t *testing.T) { }, "approve") bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), payloadType: BotPayloadJson, plugins: []Plugin[NoData]{clonePlugin(plugin)}, } @@ -1052,7 +1052,7 @@ func TestHandleCallbackPopulatesInlineTargets(t *testing.T) { }, "inline.approve") bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), payloadType: BotPayloadJson, plugins: []Plugin[NoData]{clonePlugin(plugin)}, } @@ -1086,7 +1086,7 @@ func TestHandleCallbackObserverEmitsPayloadEvents(t *testing.T) { }, "approve") bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), payloadType: BotPayloadJson, plugins: []Plugin[NoData]{clonePlugin(plugin)}, observer: observer, @@ -1137,7 +1137,7 @@ func TestHandleCallbackObserverEmitsPayloadErrors(t *testing.T) { }, "approve") bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), payloadType: BotPayloadJson, plugins: []Plugin[NoData]{clonePlugin(plugin)}, observer: observer, @@ -1182,7 +1182,7 @@ func TestHandleCallbackObserverEmitsPayloadErrors(t *testing.T) { func TestHandleCallbackObserverEmitsDecodeErrors(t *testing.T) { observer := &recordingObserver{} bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), payloadType: BotPayloadJson, observer: observer, } diff --git a/methods.go b/methods.go index 4d4ac04..b72802b 100644 --- a/methods.go +++ b/methods.go @@ -53,13 +53,13 @@ func (bot *Bot[T]) Updates(ctx context.Context) ([]tgapi.Update, error) { return nil, err } - if bot.RequestLogger != nil { + if bot.requestLogger != nil { for _, u := range updates { j, err := json.Marshal(u) if err != nil { bot.GetLogger().Error(err) } - bot.RequestLogger.Debugf("UPDATE %s\n", j) + bot.requestLogger.Debugf("UPDATE %s\n", j) } } if len(updates) > 0 { diff --git a/msg_context.go b/msg_context.go index 8f8f8e7..bf8ce89 100644 --- a/msg_context.go +++ b/msg_context.go @@ -11,7 +11,7 @@ import ( "time" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) // MsgContext holds the normalized per-update context passed to command, payload, @@ -46,7 +46,7 @@ type MsgContext struct { // Logger is the logger assigned by the matched plugin for the current handler call. // It may fall back to the bot logger when the plugin has no dedicated logger. - Logger *slog.Logger + Logger *sneklog.Logger // InlineMsgId is the inline message identifier for callback queries that target // an inline message instead of a chat message. diff --git a/msg_context_test.go b/msg_context_test.go index 78c67e0..a6dee1e 100644 --- a/msg_context_test.go +++ b/msg_context_test.go @@ -10,7 +10,7 @@ import ( "testing" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) func TestAnswerPhotoIncludesDirectMessagesTopicID(t *testing.T) { @@ -50,7 +50,7 @@ func TestAnswerPhotoIncludesDirectMessagesTopicID(t *testing.T) { Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate}, DirectMessageTopic: &tgapi.DirectMessageTopic{TopicID: 77}, }, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), } answer := ctx.AnswerPhoto("photo-id", "caption") @@ -202,7 +202,7 @@ func TestErrorDefaultRemainsUserVisibleForMessageFlow(t *testing.T) { ctx := &MsgContext{ Api: api, Msg: &tgapi.Message{Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate}}, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), errorTemplate: "Error: %s", } @@ -238,7 +238,7 @@ func TestErrorInternalSkipsUserReplyForMessageFlow(t *testing.T) { ctx := &MsgContext{ Api: api, Msg: &tgapi.Message{Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate}}, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), errorTemplate: "Error: %s", } @@ -266,7 +266,7 @@ func TestErrorInternalSkipsCallbackAnswer(t *testing.T) { ctx := &MsgContext{ Api: api, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), errorTemplate: "%s", CallbackQueryId: "cb-1", } @@ -309,7 +309,7 @@ func TestErrorUserVisibleAnswersCallback(t *testing.T) { ctx := &MsgContext{ Api: api, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), errorTemplate: "Oops: %s", CallbackQueryId: "cb-1", } @@ -327,7 +327,7 @@ func TestErrorUserVisibleAnswersCallback(t *testing.T) { func TestAnswerRejectsEmptyMessage(t *testing.T) { ctx := &MsgContext{ Msg: &tgapi.Message{Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate}}, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), } if answer := ctx.Answer(""); answer != nil { @@ -357,7 +357,7 @@ func TestAnswerRejectsLongMessageWithoutSendingRequest(t *testing.T) { ctx := &MsgContext{ Api: api, Msg: &tgapi.Message{Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate}}, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), } if answer := ctx.Answer(strings.Repeat("a", maxMessageTextLen+1)); answer != nil { @@ -441,7 +441,7 @@ func TestAnswerLongSplitsRequestsAndAttachesKeyboardToLastChunk(t *testing.T) { ctx := &MsgContext{ Api: api, Msg: &tgapi.Message{Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate}}, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), } kb := NewInlineKeyboardJson(1).AddCallbackButton("A", "cmd") text := strings.Repeat("a", maxMessageTextLen) + " " + strings.Repeat("b", 32) diff --git a/plugins.go b/plugins.go index 014322b..6190bf4 100644 --- a/plugins.go +++ b/plugins.go @@ -7,7 +7,7 @@ import ( "git.scuroneko.dev/scuroneko/extypes" "git.scuroneko.dev/scuroneko/laniakea/tgapi" "git.scuroneko.dev/scuroneko/laniakea/utils" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) // CommandValueType defines the expected type of command argument. @@ -169,7 +169,7 @@ type Plugin[T AppData] struct { scenes map[string]*Scene[T] // Optional scenes for multi-step interactions middlewares extypes.Slice[Middleware[T]] // Shared middlewares for all commands/payloads skipAutoCmd bool // If true, all commands in this plugin are excluded from auto-help - logger *slog.Logger + logger *sneklog.Logger messageFallback CommandExecutor[T] handlers map[tgapi.UpdateType]CommandExecutor[T] @@ -293,7 +293,7 @@ func (p *Plugin[T]) SkipCommandAutoGen() *Plugin[T] { // // Call this before Bot.AddPlugins. If the plugin is already registered, changing // the original *Plugin does not update the Bot's internal copy. -func (p *Plugin[T]) SetLogger(l *slog.Logger) *Plugin[T] { +func (p *Plugin[T]) SetLogger(l *sneklog.Logger) *Plugin[T] { p.logger = l return p } diff --git a/policy_test.go b/policy_test.go index 1952c44..d0925d4 100644 --- a/policy_test.go +++ b/policy_test.go @@ -10,7 +10,7 @@ import ( "testing" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) func TestRequirePolicyStopsExecutionOnDeniedPolicy(t *testing.T) { @@ -49,7 +49,7 @@ func TestRequirePolicyStopsExecutionOnDeniedPolicy(t *testing.T) { ctx := &MsgContext{ Api: api, Msg: &tgapi.Message{Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate}}, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), errorTemplate: "Error: %s", } @@ -73,7 +73,7 @@ func TestRequirePrivateChatAllowsPrivateChat(t *testing.T) { Msg: &tgapi.Message{ Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate}, }, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), } if err := RequirePrivateChat[NoData]()(ctx, NoData{}); err != nil { @@ -86,7 +86,7 @@ func TestRequirePrivateChatDeniesNonPrivateChat(t *testing.T) { Msg: &tgapi.Message{ Chat: &tgapi.Chat{ID: -100, Type: tgapi.ChatTypeSupergroup}, }, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), } err := RequirePrivateChat[NoData]()(ctx, NoData{}) @@ -140,7 +140,7 @@ func TestRequireChatAdminUsesNormalizedIDs(t *testing.T) { Api: api, ChatID: -2001, FromID: 55, - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), } if err := RequireChatAdmin[NoData]()(ctx, NoData{}); err != nil { @@ -168,7 +168,7 @@ func TestAllPoliciesReturnsFirstError(t *testing.T) { }, ) - err := policy(&MsgContext{Logger: slog.CreateLogger()}, NoData{}) + err := policy(&MsgContext{Logger: sneklog.CreateLogger()}, NoData{}) if !errors.Is(err, want) { t.Fatalf("expected first policy error, got %v", err) } @@ -180,7 +180,7 @@ func TestAnyPolicyAllowsLaterSuccessAfterInternalError(t *testing.T) { func(ctx *MsgContext, data NoData) error { return nil }, ) - if err := policy(&MsgContext{Logger: slog.CreateLogger()}, NoData{}); err != nil { + if err := policy(&MsgContext{Logger: sneklog.CreateLogger()}, NoData{}); err != nil { t.Fatalf("expected later success to allow access, got %v", err) } } @@ -192,7 +192,7 @@ func TestAnyPolicyReturnsInternalErrorWhenNonePass(t *testing.T) { func(ctx *MsgContext, data NoData) error { return internal }, ) - err := policy(&MsgContext{Logger: slog.CreateLogger()}, NoData{}) + err := policy(&MsgContext{Logger: sneklog.CreateLogger()}, NoData{}) if !errors.Is(err, internal) { t.Fatalf("expected internal error, got %v", err) } @@ -205,7 +205,7 @@ func TestAnyPolicyReturnsFirstDenyWhenNoPolicyPasses(t *testing.T) { func(ctx *MsgContext, data NoData) error { return AsUserError(errors.New("second deny")) }, ) - err := policy(&MsgContext{Logger: slog.CreateLogger()}, NoData{}) + err := policy(&MsgContext{Logger: sneklog.CreateLogger()}, NoData{}) if !errors.Is(err, first) { t.Fatalf("expected first deny error, got %v", err) } @@ -215,7 +215,7 @@ func TestNotPolicyInvertsUserDenyButPreservesInternalErrors(t *testing.T) { inverted := NotPolicy(func(ctx *MsgContext, data NoData) error { return AsUserError(errors.New("denied")) }) - if err := inverted(&MsgContext{Logger: slog.CreateLogger()}, NoData{}); err != nil { + if err := inverted(&MsgContext{Logger: sneklog.CreateLogger()}, NoData{}); err != nil { t.Fatalf("expected inverted deny to succeed, got %v", err) } @@ -223,7 +223,7 @@ func TestNotPolicyInvertsUserDenyButPreservesInternalErrors(t *testing.T) { preserve := NotPolicy(func(ctx *MsgContext, data NoData) error { return internal }) - err := preserve(&MsgContext{Logger: slog.CreateLogger()}, NoData{}) + err := preserve(&MsgContext{Logger: sneklog.CreateLogger()}, NoData{}) if !errors.Is(err, internal) { t.Fatalf("expected internal error to be preserved, got %v", err) } @@ -233,7 +233,7 @@ func TestRequirePolicyEmitsObserverEvents(t *testing.T) { t.Run("allow", func(t *testing.T) { observer := &recordingObserver{} ctx := &MsgContext{ - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), ctx: context.Background(), observer: observer, FromID: 10, @@ -258,7 +258,7 @@ func TestRequirePolicyEmitsObserverEvents(t *testing.T) { t.Run("deny", func(t *testing.T) { observer := &recordingObserver{} ctx := &MsgContext{ - Logger: slog.CreateLogger(), + Logger: sneklog.CreateLogger(), ctx: context.Background(), observer: observer, errorTemplate: "%s", diff --git a/runners_test.go b/runners_test.go index cbeefd4..351150a 100644 --- a/runners_test.go +++ b/runners_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) type runnerObserver struct { @@ -17,7 +17,7 @@ type runnerObserver struct { func TestExecRunnersRunsOnetimeSyncRunner(t *testing.T) { var calls atomic.Int32 bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), runners: []Runner[NoData]{ NewRunner("sync-once", func(*Bot[NoData]) error { calls.Add(1) @@ -39,7 +39,7 @@ func TestExecRunnersStopsBackgroundRunnerOnCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), runners: []Runner[NoData]{ NewRunner("background", func(*Bot[NoData]) error { if calls.Add(1) == 1 { @@ -71,7 +71,7 @@ func TestExecRunnersEmitObserverEvents(t *testing.T) { wantErr := errors.New("runner failed") bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), observer: observer, runners: []Runner[NoData]{ NewRunner("sync-once", func(*Bot[NoData]) error { diff --git a/scene_test.go b/scene_test.go index 93d5f04..a465386 100644 --- a/scene_test.go +++ b/scene_test.go @@ -6,7 +6,7 @@ import ( "testing" "git.scuroneko.dev/scuroneko/laniakea/tgapi" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) type failingSessionStore struct { @@ -56,7 +56,7 @@ func TestBotAddPluginsPreservesScenesAndHandlesThem(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -149,7 +149,7 @@ func TestEnterSceneRejectsMissingEntryConfiguration(t *testing.T) { plugin.NewScene("signup") bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, } @@ -172,7 +172,7 @@ func TestEnterSceneRejectsMissingEntryConfiguration(t *testing.T) { plugin.NewScene("signup").SetEntry("start") bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, } @@ -231,7 +231,7 @@ func TestSceneCommandHandlerRunsBeforeStep(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -279,7 +279,7 @@ func TestSceneCommandObserverEmitsLifecycleEvents(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -331,7 +331,7 @@ func TestSceneStepObserverEmitsLifecycleEvents(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -386,7 +386,7 @@ func TestSceneMessageObserverEmitsLifecycleEvents(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -453,7 +453,7 @@ func TestScenePayloadHandlerRunsBeforeStep(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), payloadType: BotPayloadJson, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -509,7 +509,7 @@ func TestScenePayloadObserverEmitsLifecycleEvents(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), payloadType: BotPayloadJson, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -572,7 +572,7 @@ func TestSceneUnmatchedPayloadFallsThroughWithoutRunningStep(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), payloadType: BotPayloadJson, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -648,7 +648,7 @@ func TestScenePassDoesNotPersistSessionData(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -724,7 +724,7 @@ func TestSceneUnmatchedCommandFallsThroughWithoutRunningStep(t *testing.T) { }, "ping") bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -793,7 +793,7 @@ func TestSceneMessageFallbackRunsWhenNoCommandOrStepMatch(t *testing.T) { }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), prefixes: []string{"/"}, sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, @@ -838,7 +838,7 @@ func TestSceneMessageFallbackRunsWhenNoCommandOrStepMatch(t *testing.T) { func TestFindSceneSessionSupportsUserScopeWithoutMessage(t *testing.T) { bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), sessionStore: NewMemorySessionStore(), sceneScopePriority: []SceneScope{SceneScopeUser, SceneScopeChat, SceneScopeUserChat}, } @@ -865,7 +865,7 @@ func TestSceneStoreErrorsPropagate(t *testing.T) { t.Run("find scene session get error", func(t *testing.T) { bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), sessionStore: failingSessionStore{getErr: getErr}, sceneScopePriority: []SceneScope{SceneScopeUser}, } @@ -881,7 +881,7 @@ func TestSceneStoreErrorsPropagate(t *testing.T) { return ctx.Stay(), nil }) bot := &Bot[NoData]{ - logger: slog.CreateLogger(), + logger: sneklog.CreateLogger(), sessionStore: failingSessionStore{setErr: setErr}, sceneScopePriority: []SceneScope{SceneScopeUserChat, SceneScopeChat, SceneScopeUser}, } diff --git a/tgapi/api.go b/tgapi/api.go index 34c10b8..9235ce4 100644 --- a/tgapi/api.go +++ b/tgapi/api.go @@ -10,7 +10,7 @@ import ( "time" "git.scuroneko.dev/scuroneko/laniakea/utils" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) // APIOpts holds configuration options for initializing the Telegram API client. @@ -85,7 +85,7 @@ func (opts *APIOpts) SetLimiterDrop(b bool) *APIOpts { type API struct { token string client *http.Client - logger *slog.Logger + logger *sneklog.Logger useTestServer bool apiUrl string @@ -137,7 +137,7 @@ func (api *API) Close() error { // GetLogger returns the internal logger for custom logging. // See https://core.telegram.org/bots/api -func (api *API) GetLogger() *slog.Logger { +func (api *API) GetLogger() *sneklog.Logger { return api.logger } diff --git a/tgapi/uploader_api.go b/tgapi/uploader_api.go index 9f812c1..04074d0 100644 --- a/tgapi/uploader_api.go +++ b/tgapi/uploader_api.go @@ -11,7 +11,7 @@ import ( "time" "git.scuroneko.dev/scuroneko/laniakea/utils" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) const ( @@ -65,7 +65,7 @@ func (f UploaderFile) SetType(t UploaderFileType) UploaderFile { // (InputFile/multipart). For JSON-only calls (file_id, URL, plain params), use API. type Uploader struct { api *API - logger *slog.Logger + logger *sneklog.Logger } // NewUploader creates a multipart uploader bound to an API client. @@ -85,7 +85,7 @@ func (u *Uploader) Close() error { return u.logger.Close() } // GetLogger returns uploader logger instance. // See https://core.telegram.org/bots/api -func (u *Uploader) GetLogger() *slog.Logger { return u.logger } +func (u *Uploader) GetLogger() *sneklog.Logger { return u.logger } // UploaderRequest is a low-level multipart upload request wrapper. // diff --git a/utils/utils.go b/utils/utils.go index c665508..d9a554e 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -3,22 +3,22 @@ package utils import ( "os" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) // GetLoggerLevel returns DEBUG when DEBUG=true in env, otherwise FATAL. -func GetLoggerLevel() slog.LogLevel { - level := slog.FATAL +func GetLoggerLevel() sneklog.LogLevel { + level := sneklog.FATAL if os.Getenv("DEBUG") == "true" { - level = slog.DEBUG + level = sneklog.DEBUG } return level } // CreateLogger creates a logger with the shared default policy: // JSON stdout output, provided prefix, and provided level. -func CreateLogger(prefix string, level slog.LogLevel) *slog.Logger { - logger := slog.CreateLogger().Level(level) +func CreateLogger(prefix string, level sneklog.LogLevel) *sneklog.Logger { + logger := sneklog.CreateLogger().Level(level) if prefix != "" { logger.Prefix(prefix) } @@ -31,7 +31,7 @@ func CreateLogger(prefix string, level slog.LogLevel) *slog.Logger { // // The returned logger is always non-nil. When file writer creation fails, the // logger still writes to stdout and the error is returned to the caller. -func CreateFileLogger(prefix string, level slog.LogLevel, filePath string) (*slog.Logger, error) { +func CreateFileLogger(prefix string, level sneklog.LogLevel, filePath string) (*sneklog.Logger, error) { logger := CreateLogger(prefix, level) fileWriter, err := logger.CreateTextFileWriter(filePath) if err != nil { diff --git a/utils/utils_test.go b/utils/utils_test.go index 16e9a05..c69eacc 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -6,13 +6,13 @@ import ( "strings" "testing" - "git.scuroneko.dev/scuroneko/slog" + "git.scuroneko.dev/scuroneko/sneklog/v2" ) func TestCreateFileLoggerWritesToConfiguredFile(t *testing.T) { logPath := filepath.Join(t.TempDir(), "main.log") - logger, err := CreateFileLogger("TEST", slog.DEBUG, logPath) + logger, err := CreateFileLogger("TEST", sneklog.DEBUG, logPath) if err != nil { t.Fatalf("CreateFileLogger returned error: %v", err) } @@ -28,7 +28,7 @@ func TestCreateFileLoggerWritesToConfiguredFile(t *testing.T) { if !strings.Contains(string(data), "hello from file logger") { t.Fatalf("expected log message in file, got %q", string(data)) } - if !strings.Contains(string(data), "[TEST]") { + if !strings.Contains(string(data), "TEST:") { t.Fatalf("expected prefix in file, got %q", string(data)) } }