diff --git a/_examples/application_commands/gateway/example.go b/_examples/application_commands/gateway/example.go index fe7b6615c..5dd3d923f 100644 --- a/_examples/application_commands/gateway/example.go +++ b/_examples/application_commands/gateway/example.go @@ -2,6 +2,7 @@ package main import ( "context" + "log/slog" "os" "os/signal" "syscall" @@ -40,9 +41,8 @@ var ( ) func main() { - log.SetLevel(log.LevelInfo) - log.Info("starting example...") - log.Info("disgo version: ", disgo.Version) + slog.Info("starting example...") + slog.Info("disgo version", slog.String("version", disgo.Version)) client, err := disgo.New(token, bot.WithDefaultGateway(), diff --git a/_examples/application_commands/http/example.go b/_examples/application_commands/http/example.go index 85b2f4d13..9523a4e15 100644 --- a/_examples/application_commands/http/example.go +++ b/_examples/application_commands/http/example.go @@ -2,6 +2,7 @@ package main import ( "context" + "log/slog" "os" "os/signal" "syscall" @@ -43,9 +44,8 @@ var ( ) func main() { - log.SetLevel(log.LevelDebug) - log.Info("starting example...") - log.Info("disgo version: ", disgo.Version) + slog.Info("starting example...") + slog.Info("disgo version", slog.String("version", disgo.Version)) // use custom ed25519 verify implementation httpserver.Verify = func(publicKey httpserver.PublicKey, message, sig []byte) bool { diff --git a/_examples/application_commands/localization/example.go b/_examples/application_commands/localization/example.go index 4a7d1d3b4..bc9e995bf 100644 --- a/_examples/application_commands/localization/example.go +++ b/_examples/application_commands/localization/example.go @@ -2,6 +2,7 @@ package main import ( "context" + "log/slog" "os" "os/signal" "syscall" @@ -64,9 +65,8 @@ var ( ) func main() { - log.SetLevel(log.LevelTrace) - log.Info("starting example...") - log.Infof("disgo version: %s", disgo.Version) + slog.Info("starting example...") + slog.Info("disgo version", slog.String("version", disgo.Version)) client, err := disgo.New(token, bot.WithDefaultGateway(), diff --git a/_examples/auto_moderation/example.go b/_examples/auto_moderation/example.go index c5e638b36..989bac0db 100644 --- a/_examples/auto_moderation/example.go +++ b/_examples/auto_moderation/example.go @@ -9,8 +9,8 @@ import ( "time" "github.com/disgoorg/json" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/components/example.go b/_examples/components/example.go index 8e6b349ec..acad4554f 100644 --- a/_examples/components/example.go +++ b/_examples/components/example.go @@ -10,7 +10,7 @@ import ( "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/events" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/gateway" diff --git a/_examples/custom_cache/example.go b/_examples/custom_cache/example.go index d2f94c191..c149174db 100644 --- a/_examples/custom_cache/example.go +++ b/_examples/custom_cache/example.go @@ -8,8 +8,8 @@ import ( "syscall" "time" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/echo/echo.go b/_examples/echo/echo.go index 05b792ca9..3eac9b0a0 100644 --- a/_examples/echo/echo.go +++ b/_examples/echo/echo.go @@ -10,8 +10,8 @@ import ( "syscall" "time" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/guild_scheduled_events/example.go b/_examples/guild_scheduled_events/example.go index 27ca45e24..ae20d35b9 100644 --- a/_examples/guild_scheduled_events/example.go +++ b/_examples/guild_scheduled_events/example.go @@ -7,7 +7,7 @@ import ( "syscall" "time" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/handler/example.go b/_examples/handler/example.go index 44c2a826c..5920939f0 100644 --- a/_examples/handler/example.go +++ b/_examples/handler/example.go @@ -6,8 +6,8 @@ import ( "os/signal" "syscall" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/listening_events/example.go b/_examples/listening_events/example.go index 52bcd9e36..b37ab3e3c 100644 --- a/_examples/listening_events/example.go +++ b/_examples/listening_events/example.go @@ -6,8 +6,8 @@ import ( "os/signal" "syscall" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/message_collector/example.go b/_examples/message_collector/example.go index f9ffe3536..b304b5d16 100644 --- a/_examples/message_collector/example.go +++ b/_examples/message_collector/example.go @@ -11,7 +11,7 @@ import ( "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" diff --git a/_examples/oauth2/example.go b/_examples/oauth2/example.go index 46111f755..5d7272155 100644 --- a/_examples/oauth2/example.go +++ b/_examples/oauth2/example.go @@ -8,8 +8,8 @@ import ( "time" "github.com/disgoorg/json" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/discord" diff --git a/_examples/pagination/examplebot.go b/_examples/pagination/examplebot.go index 80a0c605f..329072794 100644 --- a/_examples/pagination/examplebot.go +++ b/_examples/pagination/examplebot.go @@ -4,7 +4,7 @@ import ( _ "embed" "os" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/rest" diff --git a/_examples/ping_pong/example.go b/_examples/ping_pong/example.go index 0a89fc7fc..f6294c903 100644 --- a/_examples/ping_pong/example.go +++ b/_examples/ping_pong/example.go @@ -6,7 +6,7 @@ import ( "os/signal" "syscall" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/proxy/example.go b/_examples/proxy/example.go index 4dfd5810d..d106ade77 100644 --- a/_examples/proxy/example.go +++ b/_examples/proxy/example.go @@ -6,8 +6,8 @@ import ( "os/signal" "syscall" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/sharding/example.go b/_examples/sharding/example.go index 135c7b8cd..967853ad0 100644 --- a/_examples/sharding/example.go +++ b/_examples/sharding/example.go @@ -6,7 +6,7 @@ import ( "os/signal" "syscall" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/test/commands.go b/_examples/test/commands.go index e3a85ab01..470bf396f 100644 --- a/_examples/test/commands.go +++ b/_examples/test/commands.go @@ -1,7 +1,7 @@ package main import ( - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/discord" diff --git a/_examples/test/examplebot.go b/_examples/test/examplebot.go index fb060f5d3..a23456f0a 100644 --- a/_examples/test/examplebot.go +++ b/_examples/test/examplebot.go @@ -7,8 +7,8 @@ import ( "os/signal" "syscall" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/test/listeners.go b/_examples/test/listeners.go index 716b4bfbd..4e97b6193 100644 --- a/_examples/test/listeners.go +++ b/_examples/test/listeners.go @@ -5,7 +5,7 @@ import ( "strings" "time" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/discord" diff --git a/_examples/threads/example.go b/_examples/threads/example.go index 94052318a..692b9b645 100644 --- a/_examples/threads/example.go +++ b/_examples/threads/example.go @@ -6,7 +6,7 @@ import ( "os/signal" "syscall" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/verified_roles/main.go b/_examples/verified_roles/main.go index cd5096b2c..207fa7be3 100644 --- a/_examples/verified_roles/main.go +++ b/_examples/verified_roles/main.go @@ -7,7 +7,7 @@ import ( "strconv" "github.com/disgoorg/json" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/voice/voice.go b/_examples/voice/voice.go index 6d316be74..c52912058 100644 --- a/_examples/voice/voice.go +++ b/_examples/voice/voice.go @@ -12,8 +12,8 @@ import ( "github.com/disgoorg/disgo/voice" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" diff --git a/_examples/webhook/example.go b/_examples/webhook/example.go index 5720020a8..ac2f4288f 100644 --- a/_examples/webhook/example.go +++ b/_examples/webhook/example.go @@ -6,8 +6,8 @@ import ( "sync" "time" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/discord" diff --git a/bot/client.go b/bot/client.go index 3ef9a888d..82f90f422 100644 --- a/bot/client.go +++ b/bot/client.go @@ -2,9 +2,7 @@ package bot import ( "context" - - "github.com/disgoorg/log" - "github.com/disgoorg/snowflake/v2" + "log/slog" "github.com/disgoorg/disgo/cache" "github.com/disgoorg/disgo/discord" @@ -13,6 +11,7 @@ import ( "github.com/disgoorg/disgo/rest" "github.com/disgoorg/disgo/sharding" "github.com/disgoorg/disgo/voice" + "github.com/disgoorg/snowflake/v2" ) var _ Client = (*clientImpl)(nil) @@ -22,7 +21,7 @@ var _ Client = (*clientImpl)(nil) // Create a new client with disgo.New. type Client interface { // Logger returns the logger for the client. - Logger() log.Logger + Logger() *slog.Logger // Close will clean up all disgo internals and close the discord gracefully. Close(ctx context.Context) @@ -116,7 +115,7 @@ type clientImpl struct { token string applicationID snowflake.ID - logger log.Logger + logger *slog.Logger restServices rest.Rest @@ -134,7 +133,7 @@ type clientImpl struct { memberChunkingManager MemberChunkingManager } -func (c *clientImpl) Logger() log.Logger { +func (c *clientImpl) Logger() *slog.Logger { return c.logger } diff --git a/bot/config.go b/bot/config.go index a0575299f..165db8cb6 100644 --- a/bot/config.go +++ b/bot/config.go @@ -3,7 +3,7 @@ package bot import ( "fmt" - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo/cache" "github.com/disgoorg/disgo/discord" @@ -18,7 +18,7 @@ import ( // DefaultConfig returns a Config with sensible defaults. func DefaultConfig(gatewayHandlers map[gateway.EventType]GatewayEventHandler, httpHandler HTTPServerEventHandler) *Config { return &Config{ - Logger: log.Default(), + Logger: slog.Default(), EventManagerConfigOpts: []EventManagerConfigOpt{WithGatewayHandlers(gatewayHandlers), WithHTTPServerHandler(httpHandler)}, MemberChunkingFilter: MemberChunkingFilterNone, } @@ -26,7 +26,7 @@ func DefaultConfig(gatewayHandlers map[gateway.EventType]GatewayEventHandler, ht // Config lets you configure your Client instance. type Config struct { - Logger log.Logger + Logger *slog.Logger RestClient rest.Client RestClientConfigOpts []rest.ConfigOpt @@ -65,8 +65,12 @@ func (c *Config) Apply(opts []ConfigOpt) { } } -// WithLogger lets you inject your own logger implementing log.Logger. -func WithLogger(logger log.Logger) ConfigOpt { +func (c *Config) SubLogger(name string) *slog.Logger { + return c.Logger.WithGroup(name) +} + +// WithLogger lets you inject your own logger implementing *slog.Logger. +func WithLogger(logger *slog.Logger) ConfigOpt { return func(config *Config) { config.Logger = logger } @@ -210,7 +214,7 @@ func WithMemberChunkingFilter(memberChunkingFilter MemberChunkingFilter) ConfigO } // BuildClient creates a new Client instance with the given token, Config, gateway handlers, http handlers os, name, github & version. -func BuildClient(token string, config Config, gatewayEventHandlerFunc func(client Client) gateway.EventHandlerFunc, httpServerEventHandlerFunc func(client Client) httpserver.EventHandlerFunc, os string, name string, github string, version string) (Client, error) { +func BuildClient(token string, cfg *Config, gatewayEventHandlerFunc func(client Client) gateway.EventHandlerFunc, httpServerEventHandlerFunc func(client Client) httpserver.EventHandlerFunc, os string, name string, github string, version string) (Client, error) { if token == "" { return nil, discord.ErrNoBotToken } @@ -220,62 +224,62 @@ func BuildClient(token string, config Config, gatewayEventHandlerFunc func(clien } client := &clientImpl{ token: token, - logger: config.Logger, + logger: cfg.Logger, } client.applicationID = *id - if config.RestClient == nil { + if cfg.RestClient == nil { // prepend standard user-agent. this can be overridden as it's appended to the front of the slice - config.RestClientConfigOpts = append([]rest.ConfigOpt{ + cfg.RestClientConfigOpts = append([]rest.ConfigOpt{ rest.WithUserAgent(fmt.Sprintf("DiscordBot (%s, %s)", github, version)), rest.WithLogger(client.logger), func(config *rest.Config) { - config.RateRateLimiterConfigOpts = append([]rest.RateLimiterConfigOpt{rest.WithRateLimiterLogger(client.logger)}, config.RateRateLimiterConfigOpts...) + config.RateRateLimiterConfigOpts = append([]rest.RateLimiterConfigOpt{rest.WithRateLimiterLogger(cfg.SubLogger("rest"))}, config.RateRateLimiterConfigOpts...) }, - }, config.RestClientConfigOpts...) + }, cfg.RestClientConfigOpts...) - config.RestClient = rest.NewClient(client.token, config.RestClientConfigOpts...) + cfg.RestClient = rest.NewClient(client.token, cfg.RestClientConfigOpts...) } - if config.Rest == nil { - config.Rest = rest.New(config.RestClient) + if cfg.Rest == nil { + cfg.Rest = rest.New(cfg.RestClient) } - client.restServices = config.Rest + client.restServices = cfg.Rest - if config.VoiceManager == nil { - config.VoiceManager = voice.NewManager(client.UpdateVoiceState, *id, append([]voice.ManagerConfigOpt{voice.WithLogger(client.logger)}, config.VoiceManagerConfigOpts...)...) + if cfg.VoiceManager == nil { + cfg.VoiceManager = voice.NewManager(client.UpdateVoiceState, *id, append([]voice.ManagerConfigOpt{voice.WithLogger(cfg.SubLogger("voice"))}, cfg.VoiceManagerConfigOpts...)...) } - client.voiceManager = config.VoiceManager + client.voiceManager = cfg.VoiceManager - if config.EventManager == nil { - config.EventManager = NewEventManager(client, config.EventManagerConfigOpts...) + if cfg.EventManager == nil { + cfg.EventManager = NewEventManager(client, cfg.EventManagerConfigOpts...) } - client.eventManager = config.EventManager + client.eventManager = cfg.EventManager - if config.Gateway == nil && len(config.GatewayConfigOpts) > 0 { + if cfg.Gateway == nil && len(cfg.GatewayConfigOpts) > 0 { var gatewayRs *discord.Gateway gatewayRs, err = client.restServices.GetGateway() if err != nil { return nil, err } - config.GatewayConfigOpts = append([]gateway.ConfigOpt{ + cfg.GatewayConfigOpts = append([]gateway.ConfigOpt{ gateway.WithURL(gatewayRs.URL), - gateway.WithLogger(client.logger), + gateway.WithLogger(cfg.SubLogger("gateway")), gateway.WithOS(os), gateway.WithBrowser(name), gateway.WithDevice(name), func(config *gateway.Config) { - config.RateRateLimiterConfigOpts = append([]gateway.RateLimiterConfigOpt{gateway.WithRateLimiterLogger(client.logger)}, config.RateRateLimiterConfigOpts...) + config.RateRateLimiterConfigOpts = append([]gateway.RateLimiterConfigOpt{gateway.WithRateLimiterLogger(cfg.SubLogger("gateway_rate_limiter"))}, config.RateRateLimiterConfigOpts...) }, - }, config.GatewayConfigOpts...) + }, cfg.GatewayConfigOpts...) - config.Gateway = gateway.New(token, gatewayEventHandlerFunc(client), nil, config.GatewayConfigOpts...) + cfg.Gateway = gateway.New(token, gatewayEventHandlerFunc(client), nil, cfg.GatewayConfigOpts...) } - client.gateway = config.Gateway + client.gateway = cfg.Gateway - if config.ShardManager == nil && len(config.ShardManagerConfigOpts) > 0 { + if cfg.ShardManager == nil && len(cfg.ShardManagerConfigOpts) > 0 { var gatewayBotRs *discord.GatewayBot gatewayBotRs, err = client.restServices.GetGatewayBot() if err != nil { @@ -287,47 +291,47 @@ func BuildClient(token string, config Config, gatewayEventHandlerFunc func(clien shardIDs[i] = i } - config.ShardManagerConfigOpts = append([]sharding.ConfigOpt{ + cfg.ShardManagerConfigOpts = append([]sharding.ConfigOpt{ sharding.WithShardCount(gatewayBotRs.Shards), sharding.WithShardIDs(shardIDs...), sharding.WithGatewayConfigOpts( gateway.WithURL(gatewayBotRs.URL), - gateway.WithLogger(client.logger), + gateway.WithLogger(cfg.SubLogger("gateway")), gateway.WithOS(os), gateway.WithBrowser(name), gateway.WithDevice(name), func(config *gateway.Config) { - config.RateRateLimiterConfigOpts = append([]gateway.RateLimiterConfigOpt{gateway.WithRateLimiterLogger(client.logger)}, config.RateRateLimiterConfigOpts...) + config.RateRateLimiterConfigOpts = append([]gateway.RateLimiterConfigOpt{gateway.WithRateLimiterLogger(cfg.SubLogger("gateway_rate_limiter"))}, config.RateRateLimiterConfigOpts...) }, ), sharding.WithLogger(client.logger), func(config *sharding.Config) { - config.RateRateLimiterConfigOpts = append([]sharding.RateLimiterConfigOpt{sharding.WithRateLimiterLogger(client.logger), sharding.WithMaxConcurrency(gatewayBotRs.SessionStartLimit.MaxConcurrency)}, config.RateRateLimiterConfigOpts...) + config.RateRateLimiterConfigOpts = append([]sharding.RateLimiterConfigOpt{sharding.WithRateLimiterLogger(cfg.SubLogger("sharding_rate_limiter")), sharding.WithMaxConcurrency(gatewayBotRs.SessionStartLimit.MaxConcurrency)}, config.RateRateLimiterConfigOpts...) }, - }, config.ShardManagerConfigOpts...) + }, cfg.ShardManagerConfigOpts...) - config.ShardManager = sharding.New(token, gatewayEventHandlerFunc(client), config.ShardManagerConfigOpts...) + cfg.ShardManager = sharding.New(token, gatewayEventHandlerFunc(client), cfg.ShardManagerConfigOpts...) } - client.shardManager = config.ShardManager + client.shardManager = cfg.ShardManager - if config.HTTPServer == nil && config.PublicKey != "" { - config.HTTPServerConfigOpts = append([]httpserver.ConfigOpt{ - httpserver.WithLogger(client.logger), - }, config.HTTPServerConfigOpts...) + if cfg.HTTPServer == nil && cfg.PublicKey != "" { + cfg.HTTPServerConfigOpts = append([]httpserver.ConfigOpt{ + httpserver.WithLogger(cfg.SubLogger("http_server")), + }, cfg.HTTPServerConfigOpts...) - config.HTTPServer = httpserver.New(config.PublicKey, httpServerEventHandlerFunc(client), config.HTTPServerConfigOpts...) + cfg.HTTPServer = httpserver.New(cfg.PublicKey, httpServerEventHandlerFunc(client), cfg.HTTPServerConfigOpts...) } - client.httpServer = config.HTTPServer + client.httpServer = cfg.HTTPServer - if config.MemberChunkingManager == nil { - config.MemberChunkingManager = NewMemberChunkingManager(client, config.Logger, config.MemberChunkingFilter) + if cfg.MemberChunkingManager == nil { + cfg.MemberChunkingManager = NewMemberChunkingManager(client, cfg.SubLogger("member_chunking_manager"), cfg.MemberChunkingFilter) } - client.memberChunkingManager = config.MemberChunkingManager + client.memberChunkingManager = cfg.MemberChunkingManager - if config.Caches == nil { - config.Caches = cache.New(config.CacheConfigOpts...) + if cfg.Caches == nil { + cfg.Caches = cache.New(cfg.CacheConfigOpts...) } - client.caches = config.Caches + client.caches = cfg.Caches return client, nil } diff --git a/bot/event_manager.go b/bot/event_manager.go index 84565fed3..223e59941 100644 --- a/bot/event_manager.go +++ b/bot/event_manager.go @@ -1,6 +1,7 @@ package bot import ( + "log/slog" "runtime/debug" "sync" @@ -12,12 +13,16 @@ var _ EventManager = (*eventManagerImpl)(nil) // NewEventManager returns a new EventManager with the EventManagerConfigOpt(s) applied. func NewEventManager(client Client, opts ...EventManagerConfigOpt) EventManager { - config := DefaultEventManagerConfig() - config.Apply(opts) + cfg := DefaultEventManagerConfig() + cfg.Apply(opts) return &eventManagerImpl{ - client: client, - config: *config, + client: client, + logger: cfg.Logger, + eventListeners: cfg.EventListeners, + asyncEventsEnabled: cfg.AsyncEventsEnabled, + gatewayHandlers: cfg.GatewayHandlers, + httpServerHandler: cfg.HTTPServerHandler, } } @@ -112,68 +117,72 @@ type HTTPServerEventHandler interface { } type eventManagerImpl struct { - client Client - eventListenerMu sync.Mutex - config EventManagerConfig - mu sync.Mutex + + client Client + logger *slog.Logger + eventListenerMu sync.Mutex + eventListeners []EventListener + asyncEventsEnabled bool + gatewayHandlers map[gateway.EventType]GatewayEventHandler + httpServerHandler HTTPServerEventHandler } func (e *eventManagerImpl) HandleGatewayEvent(gatewayEventType gateway.EventType, sequenceNumber int, shardID int, event gateway.EventData) { e.mu.Lock() defer e.mu.Unlock() - if handler, ok := e.config.GatewayHandlers[gatewayEventType]; ok { + if handler, ok := e.gatewayHandlers[gatewayEventType]; ok { handler.HandleGatewayEvent(e.client, sequenceNumber, shardID, event) } else { - e.config.Logger.Warnf("no handler for gateway event '%s' found", gatewayEventType) + e.logger.Warn("no handler for gateway event found", slog.Any("event_type", gatewayEventType)) } } func (e *eventManagerImpl) HandleHTTPEvent(respondFunc httpserver.RespondFunc, event httpserver.EventInteractionCreate) { e.mu.Lock() defer e.mu.Unlock() - e.config.HTTPServerHandler.HandleHTTPEvent(e.client, respondFunc, event) + e.httpServerHandler.HandleHTTPEvent(e.client, respondFunc, event) } func (e *eventManagerImpl) DispatchEvent(event Event) { defer func() { if r := recover(); r != nil { - e.config.Logger.Errorf("recovered from panic in event listener: %+v\nstack: %s", r, string(debug.Stack())) + e.logger.Error("recovered from panic in event listener", slog.Any("arg", r), slog.String("stack", string(debug.Stack()))) return } }() e.eventListenerMu.Lock() defer e.eventListenerMu.Unlock() - for i := range e.config.EventListeners { - if e.config.AsyncEventsEnabled { + for i := range e.eventListeners { + if e.asyncEventsEnabled { go func(i int) { defer func() { if r := recover(); r != nil { - e.config.Logger.Errorf("recovered from panic in event listener: %+v\nstack: %s", r, string(debug.Stack())) + e.logger.Error("recovered from panic in event listener", slog.Any("arg", r), slog.String("stack", string(debug.Stack()))) return } }() - e.config.EventListeners[i].OnEvent(event) + e.eventListeners[i].OnEvent(event) }(i) continue } - e.config.EventListeners[i].OnEvent(event) + e.eventListeners[i].OnEvent(event) } } func (e *eventManagerImpl) AddEventListeners(listeners ...EventListener) { e.eventListenerMu.Lock() defer e.eventListenerMu.Unlock() - e.config.EventListeners = append(e.config.EventListeners, listeners...) + e.eventListeners = append(e.eventListeners, listeners...) } func (e *eventManagerImpl) RemoveEventListeners(listeners ...EventListener) { e.eventListenerMu.Lock() defer e.eventListenerMu.Unlock() for _, listener := range listeners { - for i, l := range e.config.EventListeners { + for i, l := range e.eventListeners { if l == listener { - e.config.EventListeners = append(e.config.EventListeners[:i], e.config.EventListeners[i+1:]...) + e.eventListeners = append(e.eventListeners[:i], e.eventListeners[i+1:]...) break } } diff --git a/bot/event_manager_config.go b/bot/event_manager_config.go index e62e72d41..2bf40d427 100644 --- a/bot/event_manager_config.go +++ b/bot/event_manager_config.go @@ -1,7 +1,7 @@ package bot import ( - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo/gateway" ) @@ -9,13 +9,13 @@ import ( // DefaultEventManagerConfig returns a new EventManagerConfig with all default values. func DefaultEventManagerConfig() *EventManagerConfig { return &EventManagerConfig{ - Logger: log.Default(), + Logger: slog.Default(), } } // EventManagerConfig can be used to configure the EventManager. type EventManagerConfig struct { - Logger log.Logger + Logger *slog.Logger EventListeners []EventListener AsyncEventsEnabled bool @@ -34,7 +34,7 @@ func (c *EventManagerConfig) Apply(opts []EventManagerConfigOpt) { } // WithEventManagerLogger overrides the default logger in the EventManagerConfig. -func WithEventManagerLogger(logger log.Logger) EventManagerConfigOpt { +func WithEventManagerLogger(logger *slog.Logger) EventManagerConfigOpt { return func(config *EventManagerConfig) { config.Logger = logger } diff --git a/bot/member_chunking_filter.go b/bot/member_chunking_filter.go index 2216efb61..6a2348b73 100644 --- a/bot/member_chunking_filter.go +++ b/bot/member_chunking_filter.go @@ -1,8 +1,9 @@ package bot import ( + "slices" + "github.com/disgoorg/snowflake/v2" - "golang.org/x/exp/slices" ) // MemberChunkingFilterAll is a MemberChunkingFilter which includes all guilds. diff --git a/bot/member_chunking_manager.go b/bot/member_chunking_manager.go index 283110000..61401faa8 100644 --- a/bot/member_chunking_manager.go +++ b/bot/member_chunking_manager.go @@ -2,9 +2,9 @@ package bot import ( "context" + "log/slog" "sync" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" "github.com/disgoorg/disgo/discord" @@ -15,12 +15,12 @@ import ( var _ MemberChunkingManager = (*memberChunkingManagerImpl)(nil) // NewMemberChunkingManager returns a new MemberChunkingManager with the given MemberChunkingFilter. -func NewMemberChunkingManager(client Client, logger log.Logger, memberChunkingFilter MemberChunkingFilter) MemberChunkingManager { +func NewMemberChunkingManager(client Client, logger *slog.Logger, memberChunkingFilter MemberChunkingFilter) MemberChunkingManager { if memberChunkingFilter == nil { memberChunkingFilter = MemberChunkingFilterNone } if logger == nil { - logger = log.Default() + logger = slog.Default() } return &memberChunkingManagerImpl{ client: client, @@ -88,7 +88,7 @@ type chunkingRequest struct { type memberChunkingManagerImpl struct { client Client - logger log.Logger + logger *slog.Logger memberChunkingFilter MemberChunkingFilter chunkingRequestsMu sync.RWMutex @@ -104,7 +104,7 @@ func (m *memberChunkingManagerImpl) HandleChunk(payload gateway.EventGuildMember request, ok := m.chunkingRequests[payload.Nonce] m.chunkingRequestsMu.RUnlock() if !ok { - m.logger.Debug("received unknown member chunk event: ", payload) + m.logger.Debug("received unknown member chunk event: ", slog.Any("payload", payload)) return } diff --git a/cache/cache_policy.go b/cache/cache_policy.go index 195024f17..a54e0587f 100644 --- a/cache/cache_policy.go +++ b/cache/cache_policy.go @@ -1,10 +1,10 @@ package cache import ( - "github.com/disgoorg/snowflake/v2" - "golang.org/x/exp/slices" + "slices" "github.com/disgoorg/disgo/discord" + "github.com/disgoorg/snowflake/v2" ) // PolicyNone returns a policy that will never cache anything. diff --git a/disgo.go b/disgo.go index ec05f01ae..02818c932 100644 --- a/disgo.go +++ b/disgo.go @@ -100,7 +100,7 @@ func New(token string, opts ...bot.ConfigOpt) (bot.Client, error) { config.Apply(opts) return bot.BuildClient(token, - *config, + config, handlers.DefaultGatewayEventHandlerFunc, handlers.DefaultHTTPServerEventHandlerFunc, OS, diff --git a/gateway/gateway_config.go b/gateway/gateway_config.go index bb02be4d1..edd014d36 100644 --- a/gateway/gateway_config.go +++ b/gateway/gateway_config.go @@ -1,14 +1,15 @@ package gateway import ( - "github.com/disgoorg/log" + "log/slog" + "github.com/gorilla/websocket" ) // DefaultConfig returns a Config with sensible defaults. func DefaultConfig() *Config { return &Config{ - Logger: log.Default(), + Logger: slog.Default(), Dialer: websocket.DefaultDialer, LargeThreshold: 50, Intents: IntentsDefault, @@ -23,8 +24,8 @@ func DefaultConfig() *Config { // Config lets you configure your Gateway instance. type Config struct { - // Logger is the logger of the Gateway. Defaults to log.Default(). - Logger log.Logger + // Logger is the Logger of the Gateway. Defaults to slog.Default(). + Logger *slog.Logger // Dialer is the websocket.Dialer of the Gateway. Defaults to websocket.DefaultDialer. Dialer *websocket.Dialer // LargeThreshold is the threshold for the Gateway. Defaults to 50 @@ -80,7 +81,7 @@ func (c *Config) Apply(opts []ConfigOpt) { } // WithLogger sets the Logger for the Gateway. -func WithLogger(logger log.Logger) ConfigOpt { +func WithLogger(logger *slog.Logger) ConfigOpt { return func(config *Config) { config.Logger = logger } diff --git a/gateway/gateway_impl.go b/gateway/gateway_impl.go index d6a0d09a7..cfca9530c 100644 --- a/gateway/gateway_impl.go +++ b/gateway/gateway_impl.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "log/slog" "net" "sync" "syscall" @@ -25,6 +26,8 @@ func New(token string, eventHandlerFunc EventHandlerFunc, closeHandlerFunc Close config := DefaultConfig() config.Apply(opts) + config.Logger = config.Logger.With(slog.Int("shard_id", config.ShardID), slog.Int("shard_count", config.ShardCount)) + return &gatewayImpl{ config: *config, eventHandlerFunc: eventHandlerFunc, @@ -70,26 +73,12 @@ func (g *gatewayImpl) Intents() Intents { return g.config.Intents } -func (g *gatewayImpl) formatLogsf(format string, a ...any) string { - if g.config.ShardCount > 1 { - return fmt.Sprintf("[%d/%d] %s", g.config.ShardID, g.config.ShardCount, fmt.Sprintf(format, a...)) - } - return fmt.Sprintf(format, a...) -} - -func (g *gatewayImpl) formatLogs(a ...any) string { - if g.config.ShardCount > 1 { - return fmt.Sprintf("[%d/%d] %s", g.config.ShardID, g.config.ShardCount, fmt.Sprint(a...)) - } - return fmt.Sprint(a...) -} - func (g *gatewayImpl) Open(ctx context.Context) error { return g.reconnectTry(ctx, 0) } func (g *gatewayImpl) open(ctx context.Context) error { - g.config.Logger.Debug(g.formatLogs("opening gateway connection")) + g.config.Logger.Debug("opening gateway connection") g.connMu.Lock() defer g.connMu.Unlock() @@ -114,12 +103,12 @@ func (g *gatewayImpl) open(ctx context.Context) error { }() rawBody, bErr := io.ReadAll(rs.Body) if bErr != nil { - g.config.Logger.Error(g.formatLogs("error while reading response body: ", err)) + g.config.Logger.Error("error while reading response body", slog.String("err", bErr.Error())) } body = string(rawBody) } - g.config.Logger.Error(g.formatLogsf("error connecting to the gateway. url: %s, error: %s, body: %s", gatewayURL, err, body)) + g.config.Logger.Error("error connecting to the gateway", slog.String("err", err.Error()), slog.String("url", gatewayURL), slog.String("body", body)) return err } @@ -145,7 +134,7 @@ func (g *gatewayImpl) Close(ctx context.Context) { func (g *gatewayImpl) CloseWithCode(ctx context.Context, code int, message string) { if g.heartbeatChan != nil { - g.config.Logger.Debug(g.formatLogs("closing heartbeat goroutines...")) + g.config.Logger.Debug("closing heartbeat goroutines...") g.heartbeatChan <- struct{}{} } @@ -153,9 +142,9 @@ func (g *gatewayImpl) CloseWithCode(ctx context.Context, code int, message strin defer g.connMu.Unlock() if g.conn != nil { g.config.RateLimiter.Close(ctx) - g.config.Logger.Debug(g.formatLogsf("closing gateway connection with code: %d, message: %s", code, message)) + g.config.Logger.Debug("closing gateway connection", slog.Int("code", code), slog.String("message", message)) if err := g.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(code, message)); err != nil && !errors.Is(err, websocket.ErrCloseSent) { - g.config.Logger.Debug(g.formatLogs("error writing close code. error: ", err)) + g.config.Logger.Debug("error writing close code", slog.String("err", err.Error())) } _ = g.conn.Close() g.conn = nil @@ -198,7 +187,9 @@ func (g *gatewayImpl) send(ctx context.Context, messageType int, data []byte) er } defer g.config.RateLimiter.Unlock() - g.config.Logger.Trace(g.formatLogs("sending gateway command: ", string(data))) + if g.config.Logger.Enabled(ctx, slog.LevelDebug) { + g.config.Logger.Debug("sending gateway command", slog.String("data", string(data))) + } return g.conn.WriteMessage(messageType, data) } @@ -229,7 +220,7 @@ func (g *gatewayImpl) reconnectTry(ctx context.Context, try int) error { if errors.Is(err, discord.ErrGatewayAlreadyConnected) { return err } - g.config.Logger.Error(g.formatLogs("failed to reconnect gateway. error: ", err)) + g.config.Logger.Error("failed to reconnect gateway", slog.String("err", err.Error())) g.status = StatusDisconnected return g.reconnectTry(ctx, try+1) } @@ -239,7 +230,7 @@ func (g *gatewayImpl) reconnectTry(ctx context.Context, try int) error { func (g *gatewayImpl) reconnect() { err := g.reconnectTry(context.Background(), 0) if err != nil { - g.config.Logger.Error(g.formatLogs("failed to reopen gateway. error: ", err)) + g.config.Logger.Error("failed to reopen gateway", slog.String("err", err.Error())) } } @@ -249,7 +240,7 @@ func (g *gatewayImpl) heartbeat() { } heartbeatTicker := time.NewTicker(g.heartbeatInterval) defer heartbeatTicker.Stop() - defer g.config.Logger.Debug(g.formatLogs("exiting heartbeat goroutine...")) + defer g.config.Logger.Debug("exiting heartbeat goroutine") for { select { @@ -263,7 +254,7 @@ func (g *gatewayImpl) heartbeat() { } func (g *gatewayImpl) sendHeartbeat() { - g.config.Logger.Debug(g.formatLogs("sending heartbeat...")) + g.config.Logger.Debug("sending heartbeat") ctx, cancel := context.WithTimeout(context.Background(), g.heartbeatInterval) defer cancel() @@ -271,8 +262,10 @@ func (g *gatewayImpl) sendHeartbeat() { if errors.Is(err, discord.ErrShardNotConnected) || errors.Is(err, syscall.EPIPE) { return } - g.config.Logger.Error(g.formatLogs("failed to send heartbeat. error: ", err)) - g.CloseWithCode(context.TODO(), websocket.CloseServiceRestart, "heartbeat timeout") + g.config.Logger.Error("failed to send heartbeat", slog.String("err", err.Error())) + closeCtx, closeCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer closeCancel() + g.CloseWithCode(closeCtx, websocket.CloseServiceRestart, "heartbeat timeout") go g.reconnect() return } @@ -281,7 +274,7 @@ func (g *gatewayImpl) sendHeartbeat() { func (g *gatewayImpl) identify() { g.status = StatusIdentifying - g.config.Logger.Debug(g.formatLogs("sending Identify command...")) + g.config.Logger.Debug("sending Identify command") identify := MessageDataIdentify{ Token: g.token, @@ -297,8 +290,10 @@ func (g *gatewayImpl) identify() { Shard: &[2]int{g.ShardID(), g.ShardCount()}, } - if err := g.Send(context.TODO(), OpcodeIdentify, identify); err != nil { - g.config.Logger.Error(g.formatLogs("error sending Identify command err: ", err)) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := g.Send(ctx, OpcodeIdentify, identify); err != nil { + g.config.Logger.Error("error sending Identify command", slog.String("err", err.Error())) } g.status = StatusWaitingForReady } @@ -310,18 +305,20 @@ func (g *gatewayImpl) resume() { SessionID: *g.config.SessionID, Seq: *g.config.LastSequenceReceived, } + g.config.Logger.Debug("sending Resume command") - g.config.Logger.Debug(g.formatLogs("sending Resume command...")) - if err := g.Send(context.TODO(), OpcodeResume, resume); err != nil { - g.config.Logger.Error(g.formatLogs("error sending resume command err: ", err)) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := g.Send(ctx, OpcodeResume, resume); err != nil { + g.config.Logger.Error("error sending resume command", slog.String("err", err.Error())) } } func (g *gatewayImpl) listen(conn *websocket.Conn) { - defer g.config.Logger.Debug(g.formatLogs("exiting listen goroutine...")) + defer g.config.Logger.Debug("exiting listen goroutine") loop: for { - mt, data, err := conn.ReadMessage() + mt, r, err := conn.NextReader() if err != nil { g.connMu.Lock() sameConnection := g.conn == conn @@ -343,17 +340,22 @@ loop: g.config.SessionID = nil g.config.ResumeURL = nil } - message := g.formatLogsf("gateway close received, reconnect: %t, code: %d, error: %s", g.config.AutoReconnect && reconnect, closeError.Code, closeError.Text) + msg := "gateway close received" + args := []any{ + slog.Bool("reconnect", reconnect), + slog.Int("code", closeError.Code), + slog.String("error", closeError.Text), + } if reconnect { - g.config.Logger.Debug(message) + g.config.Logger.Debug(msg, args...) } else { - g.config.Logger.Error(message) + g.config.Logger.Error(msg, args...) } } else if errors.Is(err, net.ErrClosed) { // we closed the connection ourselves. Don't try to reconnect here reconnect = false } else { - g.config.Logger.Debug(g.formatLogs("failed to read next message from gateway. error: ", err)) + g.config.Logger.Debug("failed to read next message from gateway", slog.String("err", err.Error())) } // make sure the connection is properly closed @@ -369,9 +371,9 @@ loop: break loop } - message, err := g.parseMessage(mt, data) + message, err := g.parseMessage(mt, r) if err != nil { - g.config.Logger.Error(g.formatLogs("error while parsing gateway message. error: ", err)) + g.config.Logger.Error("error while parsing gateway message", slog.String("err", err.Error())) continue } @@ -393,7 +395,7 @@ loop: eventData, ok := message.D.(EventData) if !ok && message.D != nil { - g.config.Logger.Error(g.formatLogsf("invalid message data of type %T received", message.D)) + g.config.Logger.Error("invalid message data received", slog.String("data", fmt.Sprintf("%T", message.D))) continue } @@ -402,11 +404,11 @@ loop: g.config.SessionID = &readyEvent.SessionID g.config.ResumeURL = &readyEvent.ResumeGatewayURL g.status = StatusReady - g.config.Logger.Debug(g.formatLogs("ready message received")) + g.config.Logger.Debug("ready message received") } if unknownEvent, ok := eventData.(EventUnknown); ok { - g.config.Logger.Debug(g.formatLogsf("unknown event received: %s, data: %s", message.T, unknownEvent)) + g.config.Logger.Debug("unknown event received", slog.String("event", string(message.T)), slog.String("data", string(unknownEvent))) continue } @@ -457,31 +459,35 @@ loop: g.lastHeartbeatReceived = newHeartbeat default: - g.config.Logger.Debug(g.formatLogsf("unknown opcode received: %d, data: %s", message.Op, message.D)) + + g.config.Logger.Debug("unknown opcode received", slog.Int("opcode", int(message.Op)), slog.String("data", fmt.Sprintf("%s", message.D))) } } } -func (g *gatewayImpl) parseMessage(mt int, data []byte) (Message, error) { - var finalData []byte +func (g *gatewayImpl) parseMessage(mt int, r io.Reader) (Message, error) { if mt == websocket.BinaryMessage { - g.config.Logger.Trace(g.formatLogs("binary message received. decompressing...")) + g.config.Logger.Debug("binary message received. decompressing") - reader, err := zlib.NewReader(bytes.NewReader(data)) + reader, err := zlib.NewReader(r) if err != nil { return Message{}, fmt.Errorf("failed to decompress zlib: %w", err) } defer reader.Close() - finalData, err = io.ReadAll(reader) + r = reader + } + + if g.config.Logger.Enabled(context.Background(), slog.LevelDebug) { + buff := new(bytes.Buffer) + tr := io.TeeReader(r, buff) + data, err := io.ReadAll(tr) if err != nil { - return Message{}, fmt.Errorf("failed to read decompressed data: %w", err) + return Message{}, fmt.Errorf("failed to read message: %w", err) } - } else { - finalData = data + g.config.Logger.Debug("received gateway message", slog.String("data", string(data))) + r = buff } - g.config.Logger.Trace(g.formatLogs("received gateway message: ", string(finalData))) - var message Message - return message, json.Unmarshal(finalData, &message) + return message, json.NewDecoder(r).Decode(&message) } diff --git a/gateway/gateway_rate_limiter.go b/gateway/gateway_rate_limiter.go index 027ec7b0e..b8349ef52 100644 --- a/gateway/gateway_rate_limiter.go +++ b/gateway/gateway_rate_limiter.go @@ -4,6 +4,9 @@ import ( "context" ) +// CommandsPerMinute is the default number of commands per minute that the Gateway will allow. +const CommandsPerMinute = 120 + // RateLimiter provides handles the rate limiting logic for connecting to Discord's Gateway. type RateLimiter interface { // Close gracefully closes the RateLimiter. diff --git a/gateway/gateway_rate_limiter_config.go b/gateway/gateway_rate_limiter_config.go index 0f2afa005..fa5ff8d9c 100644 --- a/gateway/gateway_rate_limiter_config.go +++ b/gateway/gateway_rate_limiter_config.go @@ -1,20 +1,20 @@ package gateway import ( - "github.com/disgoorg/log" + "log/slog" ) // DefaultRateLimiterConfig returns a RateLimiterConfig with sensible defaults. func DefaultRateLimiterConfig() *RateLimiterConfig { return &RateLimiterConfig{ - Logger: log.Default(), - CommandsPerMinute: 120, + Logger: slog.Default(), + CommandsPerMinute: CommandsPerMinute, } } // RateLimiterConfig lets you configure your Gateway instance. type RateLimiterConfig struct { - Logger log.Logger + Logger *slog.Logger CommandsPerMinute int } @@ -29,7 +29,7 @@ func (c *RateLimiterConfig) Apply(opts []RateLimiterConfigOpt) { } // WithRateLimiterLogger sets the Logger for the Gateway. -func WithRateLimiterLogger(logger log.Logger) RateLimiterConfigOpt { +func WithRateLimiterLogger(logger *slog.Logger) RateLimiterConfigOpt { return func(config *RateLimiterConfig) { config.Logger = logger } diff --git a/gateway/gateway_rate_limiter_impl.go b/gateway/gateway_rate_limiter_impl.go index 372d70776..1bf284bb7 100644 --- a/gateway/gateway_rate_limiter_impl.go +++ b/gateway/gateway_rate_limiter_impl.go @@ -37,7 +37,7 @@ func (l *rateLimiterImpl) Reset() { } func (l *rateLimiterImpl) Wait(ctx context.Context) error { - l.config.Logger.Trace("locking gateway rate limiter") + l.config.Logger.Debug("locking gateway rate limiter") if err := l.mu.CLock(ctx); err != nil { return err } @@ -62,7 +62,7 @@ func (l *rateLimiterImpl) Wait(ctx context.Context) error { } func (l *rateLimiterImpl) Unlock() { - l.config.Logger.Trace("unlocking gateway rate limiter") + l.config.Logger.Debug("unlocking gateway rate limiter") now := time.Now() if l.reset.Before(now) { l.reset = now.Add(time.Minute) diff --git a/go.mod b/go.mod index 2517f4d80..8eb1fcb38 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/disgoorg/disgo -go 1.18 +go 1.21 require ( github.com/disgoorg/json v1.1.0 @@ -8,9 +8,8 @@ require ( github.com/disgoorg/snowflake/v2 v2.0.1 github.com/gorilla/websocket v1.5.0 github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.4 golang.org/x/crypto v0.12.0 - golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b ) require ( diff --git a/go.sum b/go.sum index 5f0f4570e..5346d25a9 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -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= github.com/disgoorg/json v1.1.0 h1:7xigHvomlVA9PQw9bMGO02PHGJJPqvX5AnwlYg/Tnys= @@ -13,21 +12,13 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b h1:qYTY2tN72LhgDj2rtWG+LI6TXFl2ygFQQ4YezfVaGQE= github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b/go.mod h1:/pA7k3zsXKdjjAiUhB5CjuKib9KJGCaLvZwtxGC8U0s= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI= -golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handler/mux.go b/handler/mux.go index 74c453098..da60ed37e 100644 --- a/handler/mux.go +++ b/handler/mux.go @@ -1,6 +1,7 @@ package handler import ( + "log/slog" "strings" "github.com/disgoorg/disgo/bot" @@ -53,7 +54,7 @@ func (r *Mux) OnEvent(event bot.Event) { } if err := r.Handle(path, make(map[string]string), e); err != nil { - event.Client().Logger().Errorf("error handling interaction: %v\n", err) + event.Client().Logger().Error("error handling interaction", slog.String("err", err.Error())) } } diff --git a/handlers/guild_emojis_update_handler.go b/handlers/guild_emojis_update_handler.go index a3477097a..b1b608335 100644 --- a/handlers/guild_emojis_update_handler.go +++ b/handlers/guild_emojis_update_handler.go @@ -1,14 +1,14 @@ package handlers import ( - "github.com/disgoorg/snowflake/v2" - "golang.org/x/exp/slices" + "slices" "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/cache" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/gateway" + "github.com/disgoorg/snowflake/v2" ) type updatedEmoji struct { diff --git a/handlers/interaction_create_handler.go b/handlers/interaction_create_handler.go index 9c7988eb9..c93ccbc0a 100644 --- a/handlers/interaction_create_handler.go +++ b/handlers/interaction_create_handler.go @@ -1,6 +1,9 @@ package handlers import ( + "fmt" + "log/slog" + "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" @@ -65,6 +68,6 @@ func handleInteraction(client bot.Client, sequenceNumber int, shardID int, respo }) default: - client.Logger().Errorf("unknown interaction with type %T received", interaction) + client.Logger().Error("unknown interaction", slog.String("type", fmt.Sprintf("%T", interaction))) } } diff --git a/handlers/presence_update_handler.go b/handlers/presence_update_handler.go index 2267796f5..997082596 100644 --- a/handlers/presence_update_handler.go +++ b/handlers/presence_update_handler.go @@ -1,13 +1,14 @@ package handlers import ( + "slices" + "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/cache" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/gateway" "github.com/disgoorg/snowflake/v2" - "golang.org/x/exp/slices" ) func gatewayHandlerPresenceUpdate(client bot.Client, sequenceNumber int, shardID int, event gateway.EventPresenceUpdate) { diff --git a/httpserver/config.go b/httpserver/config.go index 8666813ce..2f4d3e472 100644 --- a/httpserver/config.go +++ b/httpserver/config.go @@ -3,7 +3,7 @@ package httpserver import ( "net/http" - "github.com/disgoorg/log" + "log/slog" ) // DefaultConfig returns a Config with sensible defaults. @@ -18,7 +18,7 @@ func DefaultConfig() *Config { // Config lets you configure your Server instance. type Config struct { - Logger log.Logger + Logger *slog.Logger HTTPServer *http.Server ServeMux *http.ServeMux URL string @@ -38,7 +38,7 @@ func (c *Config) Apply(opts []ConfigOpt) { } // WithLogger sets the Logger of the Config. -func WithLogger(logger log.Logger) ConfigOpt { +func WithLogger(logger *slog.Logger) ConfigOpt { return func(config *Config) { config.Logger = logger } diff --git a/httpserver/server.go b/httpserver/server.go index ab25f3b72..ded77357c 100644 --- a/httpserver/server.go +++ b/httpserver/server.go @@ -5,12 +5,12 @@ import ( "context" "encoding/hex" "io" + "log/slog" "net/http" "sync" "time" "github.com/disgoorg/json" - "github.com/disgoorg/log" "github.com/disgoorg/disgo/discord" ) @@ -102,12 +102,12 @@ const ( ) // HandleInteraction handles an interaction from Discord's Outgoing Webhooks. It verifies and parses the interaction and then calls the passed EventHandlerFunc. -func HandleInteraction(publicKey PublicKey, logger log.Logger, handleFunc EventHandlerFunc) http.HandlerFunc { +func HandleInteraction(publicKey PublicKey, logger *slog.Logger, handleFunc EventHandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if ok := VerifyRequest(r, publicKey); !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) data, _ := io.ReadAll(r.Body) - logger.Trace("received http interaction with invalid signature. body: ", string(data)) + logger.Debug("received http interaction with invalid signature", slog.String("body", string(data))) return } @@ -117,7 +117,7 @@ func HandleInteraction(publicKey PublicKey, logger log.Logger, handleFunc EventH buff := new(bytes.Buffer) rqData, _ := io.ReadAll(io.TeeReader(r.Body, buff)) - logger.Trace("received http interaction. body: ", string(rqData)) + logger.Debug("received http interaction", slog.String("body", string(rqData))) var v EventInteractionCreate if err := json.NewDecoder(buff).Decode(&v); err != nil { @@ -200,6 +200,6 @@ func HandleInteraction(publicKey PublicKey, logger log.Logger, handleFunc EventH } rsData, _ := io.ReadAll(rsBody) - logger.Trace("response to http interaction. body: ", string(rsData)) + logger.Debug("response to http interaction", slog.String("body", string(rsData))) } } diff --git a/httpserver/server_impl.go b/httpserver/server_impl.go index 02f12fe51..d84fa343f 100644 --- a/httpserver/server_impl.go +++ b/httpserver/server_impl.go @@ -4,6 +4,7 @@ import ( "context" "encoding/hex" "errors" + "log/slog" "net/http" ) @@ -16,7 +17,7 @@ func New(publicKey string, eventHandlerFunc EventHandlerFunc, opts ...ConfigOpt) hexDecodedKey, err := hex.DecodeString(publicKey) if err != nil { - config.Logger.Errorf("error while decoding hex string: %s", err) + config.Logger.Debug("error while decoding hex string", slog.String("err", err.Error())) } return &serverImpl{ @@ -45,7 +46,7 @@ func (s *serverImpl) Start() { err = s.config.HTTPServer.ListenAndServe() } if !errors.Is(err, http.ErrServerClosed) { - s.config.Logger.Error("error while running http server: ", err) + s.config.Logger.Error("error while running http server", slog.String("err", err.Error())) } }() } diff --git a/internal/flags/flags.go b/internal/flags/flags.go index b7899cb9d..450f6af15 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -1,9 +1,12 @@ package flags -import "golang.org/x/exp/constraints" +// Integer is a constraint that permits any integer type. +type Integer interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} // Add allows you to add multiple bits together, producing a new bit -func Add[T constraints.Integer](f T, bits ...T) T { +func Add[T Integer](f T, bits ...T) T { for _, bit := range bits { f |= bit } @@ -11,7 +14,7 @@ func Add[T constraints.Integer](f T, bits ...T) T { } // Remove allows you to subtract multiple bits from the first, producing a new bit -func Remove[T constraints.Integer](f T, bits ...T) T { +func Remove[T Integer](f T, bits ...T) T { for _, bit := range bits { f &^= bit } @@ -19,7 +22,7 @@ func Remove[T constraints.Integer](f T, bits ...T) T { } // Has will ensure that the bit includes all the bits entered -func Has[T constraints.Integer](f T, bits ...T) bool { +func Has[T Integer](f T, bits ...T) bool { for _, bit := range bits { if (f & bit) != bit { return false @@ -29,7 +32,7 @@ func Has[T constraints.Integer](f T, bits ...T) bool { } // Missing will check whether the bit is missing any one of the bits -func Missing[T constraints.Integer](f T, bits ...T) bool { +func Missing[T Integer](f T, bits ...T) bool { for _, bit := range bits { if (f & bit) != bit { return true diff --git a/oauth2/config.go b/oauth2/config.go index 64c5a4074..0b17ea2b0 100644 --- a/oauth2/config.go +++ b/oauth2/config.go @@ -1,7 +1,7 @@ package oauth2 import ( - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo/rest" ) @@ -9,13 +9,13 @@ import ( // DefaultConfig is the configuration which is used by default func DefaultConfig() *Config { return &Config{ - Logger: log.Default(), + Logger: slog.Default(), } } // Config is the configuration for the OAuth2 client type Config struct { - Logger log.Logger + Logger *slog.Logger RestClient rest.Client RestClientConfigOpts []rest.ConfigOpt OAuth2 rest.OAuth2 @@ -43,7 +43,7 @@ func (c *Config) Apply(opts []ConfigOpt) { } // WithLogger applies a custom logger to the OAuth2 client -func WithLogger(logger log.Logger) ConfigOpt { +func WithLogger(logger *slog.Logger) ConfigOpt { return func(config *Config) { config.Logger = logger } diff --git a/oauth2/state_controller.go b/oauth2/state_controller.go index f239dca9f..cb4d45143 100644 --- a/oauth2/state_controller.go +++ b/oauth2/state_controller.go @@ -1,6 +1,6 @@ package oauth2 -import "github.com/disgoorg/log" +import "log/slog" var ( _ StateController = (*stateControllerImpl)(nil) @@ -33,14 +33,14 @@ func NewStateController(opts ...StateControllerConfigOpt) StateController { } type stateControllerImpl struct { - logger log.Logger + logger *slog.Logger states *ttlMap newStateFunc func() string } func (c *stateControllerImpl) NewState(redirectURI string) string { state := c.newStateFunc() - c.logger.Debugf("new state: %s for redirect uri: %s", state, redirectURI) + c.logger.Debug("new state: %s for redirect uri", slog.String("state", state), slog.String("redirect_uri", redirectURI)) c.states.put(state, redirectURI) return state } @@ -50,7 +50,7 @@ func (c *stateControllerImpl) UseState(state string) string { if uri == "" { return "" } - c.logger.Debugf("using state: %s for redirect uri: %s", state, uri) + c.logger.Debug("using state: %s for redirect uri", slog.String("state", state), slog.String("redirect_uri", uri)) c.states.delete(state) return uri } diff --git a/oauth2/state_controller_config.go b/oauth2/state_controller_config.go index e9cd8067b..02f43c69f 100644 --- a/oauth2/state_controller_config.go +++ b/oauth2/state_controller_config.go @@ -1,17 +1,16 @@ package oauth2 import ( + "log/slog" "time" - "github.com/disgoorg/log" - "github.com/disgoorg/disgo/internal/insecurerandstr" ) // DefaultStateControllerConfig is the default configuration for the StateController func DefaultStateControllerConfig() *StateControllerConfig { return &StateControllerConfig{ - Logger: log.Default(), + Logger: slog.Default(), States: map[string]string{}, NewStateFunc: func() string { return insecurerandstr.RandStr(32) }, MaxTTL: time.Hour, @@ -20,7 +19,7 @@ func DefaultStateControllerConfig() *StateControllerConfig { // StateControllerConfig is the configuration for the StateController type StateControllerConfig struct { - Logger log.Logger + Logger *slog.Logger States map[string]string NewStateFunc func() string MaxTTL time.Duration @@ -37,7 +36,7 @@ func (c *StateControllerConfig) Apply(opts []StateControllerConfigOpt) { } // WithStateControllerLogger sets the logger for the StateController -func WithStateControllerLogger(logger log.Logger) StateControllerConfigOpt { +func WithStateControllerLogger(logger *slog.Logger) StateControllerConfigOpt { return func(config *StateControllerConfig) { config.Logger = logger } diff --git a/rest/rest_client.go b/rest/rest_client.go index 8330178b9..6dd156788 100644 --- a/rest/rest_client.go +++ b/rest/rest_client.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "io" + "log/slog" "net/http" "net/url" "time" @@ -83,7 +84,7 @@ func (c *clientImpl) retry(endpoint *CompiledEndpoint, rqBody any, rsBody any, t return fmt.Errorf("failed to marshal request body: %w", err) } } - c.config.Logger.Tracef("request to %s, body: %s", endpoint.URL, string(rawRqBody)) + c.config.Logger.Debug("new request", slog.String("endpoint", endpoint.URL), slog.String("body", string(rawRqBody))) } rq, err := http.NewRequest(endpoint.Endpoint.Method, c.config.URL+endpoint.URL, bytes.NewReader(rawRqBody)) @@ -143,16 +144,15 @@ func (c *clientImpl) retry(endpoint *CompiledEndpoint, rqBody any, rsBody any, t if rawRsBody, err = io.ReadAll(rs.Body); err != nil { return fmt.Errorf("error reading response body in rest client: %w", err) } - c.config.Logger.Tracef("response from %s, code %d, body: %s", endpoint.URL, rs.StatusCode, string(rawRsBody)) + c.config.Logger.Debug("new response", slog.String("endpoint", endpoint.URL), slog.String("code", rs.Status), slog.String("body", string(rawRsBody))) } switch rs.StatusCode { case http.StatusOK, http.StatusCreated, http.StatusNoContent: if rsBody != nil && rs.Body != nil { if err = json.Unmarshal(rawRsBody, rsBody); err != nil { - wErr := fmt.Errorf("error unmarshalling response body: %w", err) - c.config.Logger.Error(wErr) - return wErr + c.config.Logger.Error("error unmarshalling response body", slog.String("err", err.Error()), slog.String("endpoint", endpoint.URL), slog.String("code", rs.Status), slog.String("body", string(rawRsBody))) + return fmt.Errorf("error unmarshalling response body: %w", err) } } return nil diff --git a/rest/rest_config.go b/rest/rest_config.go index 799b49037..45356338a 100644 --- a/rest/rest_config.go +++ b/rest/rest_config.go @@ -2,16 +2,15 @@ package rest import ( "fmt" + "log/slog" "net/http" "time" - - "github.com/disgoorg/log" ) // DefaultConfig is the configuration which is used by default func DefaultConfig() *Config { return &Config{ - Logger: log.Default(), + Logger: slog.Default(), HTTPClient: &http.Client{Timeout: 20 * time.Second}, URL: fmt.Sprintf("%sv%d", API, Version), } @@ -19,7 +18,7 @@ func DefaultConfig() *Config { // Config is the configuration for the rest client type Config struct { - Logger log.Logger + Logger *slog.Logger HTTPClient *http.Client RateLimiter RateLimiter RateRateLimiterConfigOpts []RateLimiterConfigOpt @@ -41,7 +40,7 @@ func (c *Config) Apply(opts []ConfigOpt) { } // WithLogger applies a custom logger to the rest rate limiter -func WithLogger(logger log.Logger) ConfigOpt { +func WithLogger(logger *slog.Logger) ConfigOpt { return func(config *Config) { config.Logger = logger } diff --git a/rest/rest_rate_limiter.go b/rest/rest_rate_limiter.go index d9be6977a..55a031213 100644 --- a/rest/rest_rate_limiter.go +++ b/rest/rest_rate_limiter.go @@ -3,6 +3,7 @@ package rest import ( "context" "fmt" + "log/slog" "net/http" "strconv" "sync" @@ -11,6 +12,13 @@ import ( "github.com/sasha-s/go-csync" ) +const ( + // MaxRetries is the maximum number of retries the client should do + MaxRetries = 10 + // CleanupInterval is the interval at which the rate limiter cleans up old buckets + CleanupInterval = time.Second * 10 +) + // RateLimiter can be used to supply your own rate limit implementation type RateLimiter interface { // MaxRetries returns the maximum number of retries the client should do @@ -83,13 +91,13 @@ func (l *rateLimiterImpl) doCleanup() { continue } if b.Reset.Before(now) { - l.config.Logger.Debugf("cleaning up bucket, Hash: %s, ID: %s, Reset: %s", hash, b.ID, b.Reset) + l.config.Logger.Debug("cleaning up bucket", slog.String("hash", hash), slog.String("id", b.ID), slog.Time("reset", b.Reset)) delete(l.buckets, hash) } b.mu.Unlock() } if before != len(l.buckets) { - l.config.Logger.Debugf("cleaned up %d rate limit buckets", before-len(l.buckets)) + l.config.Logger.Debug("cleaned up rate limit buckets", slog.Int("before", before), slog.Int("after", len(l.buckets)), slog.Int("removed", before-len(l.buckets))) } } @@ -132,10 +140,10 @@ func (l *rateLimiterImpl) getRouteHash(endpoint *CompiledEndpoint) string { func (l *rateLimiterImpl) getBucket(endpoint *CompiledEndpoint, create bool) *bucket { hash := l.getRouteHash(endpoint) - l.config.Logger.Trace("locking buckets") + l.config.Logger.Debug("locking buckets") l.bucketsMu.Lock() defer func() { - l.config.Logger.Trace("unlocking buckets") + l.config.Logger.Debug("unlocking buckets") l.bucketsMu.Unlock() }() b, ok := l.buckets[hash] @@ -156,7 +164,7 @@ func (l *rateLimiterImpl) getBucket(endpoint *CompiledEndpoint, create bool) *bu func (l *rateLimiterImpl) WaitBucket(ctx context.Context, endpoint *CompiledEndpoint) error { b := l.getBucket(endpoint, true) - l.config.Logger.Tracef("locking rest bucket, ID: %s, Limit: %d, Remaining: %d, Reset: %s", b.ID, b.Limit, b.Remaining, b.Reset) + l.config.Logger.Debug("locking rest bucket", slog.String("id", b.ID), slog.Int("limit", b.Limit), slog.Int("remaining", b.Remaining), slog.Time("reset", b.Reset)) if err := b.mu.CLock(ctx); err != nil { return err } @@ -192,7 +200,7 @@ func (l *rateLimiterImpl) UnlockBucket(endpoint *CompiledEndpoint, rs *http.Resp return nil } defer func() { - l.config.Logger.Tracef("unlocking rest bucket, ID: %s, Limit: %d, Remaining: %d, Reset: %s", b.ID, b.Limit, b.Remaining, b.Reset) + l.config.Logger.Debug("unlocking rest bucket", slog.String("id", b.ID), slog.Int("limit", b.Limit), slog.Int("remaining", b.Remaining), slog.Time("reset", b.Reset)) b.mu.Unlock() }() @@ -217,7 +225,7 @@ func (l *rateLimiterImpl) UnlockBucket(endpoint *CompiledEndpoint, rs *http.Resp resetAfterHeader := rs.Header.Get("X-RateLimit-Reset-After") retryAfterHeader := rs.Header.Get("Retry-After") - l.config.Logger.Tracef("code: %d, headers: global %t, cloudflare: %t, remaining: %s, limit: %s, reset: %s, retryAfter: %s", rs.StatusCode, global, cloudflare, remainingHeader, limitHeader, resetHeader, retryAfterHeader) + l.config.Logger.Debug("ratelimit response headers", slog.Int("code", rs.StatusCode), slog.Bool("global", global), slog.Bool("cloudflare", cloudflare), slog.String("remaining", remainingHeader), slog.String("limit", limitHeader), slog.String("reset", resetHeader), slog.String("reset_after", resetAfterHeader), slog.String("retry_after", retryAfterHeader)) // we hit a rate limit. let's see if it was global cloudflare or a route specific one if rs.StatusCode == http.StatusTooManyRequests { @@ -228,14 +236,14 @@ func (l *rateLimiterImpl) UnlockBucket(endpoint *CompiledEndpoint, rs *http.Resp reset := time.Now().Add(time.Second * time.Duration(retryAfter)) if global { l.global = reset - l.config.Logger.Warnf("global rate limit exceeded, retry after: %ds", retryAfter) + l.config.Logger.Warn("global rate limit exceeded", slog.Int("retry_after", retryAfter)) } else if cloudflare { l.global = reset - l.config.Logger.Warnf("cloudflare rate limit exceeded, retry after: %ds", retryAfter) + l.config.Logger.Warn("cloudflare rate limit exceeded", slog.Int("retry_after", retryAfter)) } else { b.Remaining = 0 b.Reset = reset - l.config.Logger.Warnf("rate limit on route %s exceeded, retry after: %ds", endpoint.URL, retryAfter) + l.config.Logger.Warn("rate limit exceeded", slog.String("endpoint", endpoint.URL), slog.Int("retry_after", retryAfter)) } return nil } @@ -243,7 +251,7 @@ func (l *rateLimiterImpl) UnlockBucket(endpoint *CompiledEndpoint, rs *http.Resp if limitHeader != "" { limit, err := strconv.Atoi(limitHeader) if err != nil { - return fmt.Errorf("invalid limit %s: %s", limitHeader, err) + return fmt.Errorf("invalid limit %s: %w", limitHeader, err) } b.Limit = limit } @@ -251,7 +259,7 @@ func (l *rateLimiterImpl) UnlockBucket(endpoint *CompiledEndpoint, rs *http.Resp if remainingHeader != "" { remaining, err := strconv.Atoi(remainingHeader) if err != nil { - return fmt.Errorf("invalid remaining %s: %s", remainingHeader, err) + return fmt.Errorf("invalid remaining %s: %w", remainingHeader, err) } b.Remaining = remaining } @@ -260,14 +268,14 @@ func (l *rateLimiterImpl) UnlockBucket(endpoint *CompiledEndpoint, rs *http.Resp if resetAfterHeader != "" { resetAfter, err := strconv.ParseFloat(resetAfterHeader, 64) if err != nil { - return fmt.Errorf("invalid reset after %s: %s", resetAfterHeader, err) + return fmt.Errorf("invalid reset after %s: %w", resetAfterHeader, err) } b.Reset = time.Now().Add(time.Duration(resetAfter) * time.Second) } else if resetHeader != "" { reset, err := strconv.ParseFloat(resetHeader, 64) if err != nil { - return fmt.Errorf("invalid reset %s: %s", resetHeader, err) + return fmt.Errorf("invalid reset %s: %w", resetHeader, err) } sec := int64(reset) diff --git a/rest/rest_rate_limiter_config.go b/rest/rest_rate_limiter_config.go index bd36f261c..b829a27c6 100644 --- a/rest/rest_rate_limiter_config.go +++ b/rest/rest_rate_limiter_config.go @@ -1,23 +1,22 @@ package rest import ( + "log/slog" "time" - - "github.com/disgoorg/log" ) // DefaultRateLimiterConfig is the configuration which is used by default. func DefaultRateLimiterConfig() *RateLimiterConfig { return &RateLimiterConfig{ - Logger: log.Default(), - MaxRetries: 10, - CleanupInterval: time.Second * 10, + Logger: slog.Default(), + MaxRetries: MaxRetries, + CleanupInterval: CleanupInterval, } } // RateLimiterConfig is the configuration for the rate limiter. type RateLimiterConfig struct { - Logger log.Logger + Logger *slog.Logger MaxRetries int CleanupInterval time.Duration } @@ -33,7 +32,7 @@ func (c *RateLimiterConfig) Apply(opts []RateLimiterConfigOpt) { } // WithRateLimiterLogger applies a custom logger to the rest rate limiter. -func WithRateLimiterLogger(logger log.Logger) RateLimiterConfigOpt { +func WithRateLimiterLogger(logger *slog.Logger) RateLimiterConfigOpt { return func(config *RateLimiterConfig) { config.Logger = logger } diff --git a/sharding/shard_manager.go b/sharding/shard_manager.go index d41af9bc6..236303751 100644 --- a/sharding/shard_manager.go +++ b/sharding/shard_manager.go @@ -8,6 +8,9 @@ import ( "github.com/disgoorg/disgo/gateway" ) +// ShardSplitCount is the default count a shard should be split into when it needs re-sharding. +const ShardSplitCount = 2 + // ShardManager manages multiple gateway.Gateway connections. // For more information on sharding see: https://discord.com/developers/docs/topics/gateway#sharding type ShardManager interface { diff --git a/sharding/shard_manager_config.go b/sharding/shard_manager_config.go index bb2ecb3d4..3ae1b77a1 100644 --- a/sharding/shard_manager_config.go +++ b/sharding/shard_manager_config.go @@ -1,7 +1,7 @@ package sharding import ( - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo/gateway" ) @@ -9,16 +9,16 @@ import ( // DefaultConfig returns a Config with sensible defaults. func DefaultConfig() *Config { return &Config{ - Logger: log.Default(), + Logger: slog.Default(), GatewayCreateFunc: gateway.New, - ShardSplitCount: 2, + ShardSplitCount: ShardSplitCount, } } // Config lets you configure your ShardManager instance. type Config struct { // Logger is the logger of the ShardManager. Defaults to log.Default() - Logger log.Logger + Logger *slog.Logger // ShardIDs is a map of shardIDs the ShardManager should manage. Leave this nil to manage all shards. ShardIDs map[int]struct{} // ShardCount is the total shard count of the ShardManager. Leave this at 0 to let Discord calculate the shard count for you. @@ -51,7 +51,7 @@ func (c *Config) Apply(opts []ConfigOpt) { } // WithLogger sets the logger of the ShardManager. -func WithLogger(logger log.Logger) ConfigOpt { +func WithLogger(logger *slog.Logger) ConfigOpt { return func(config *Config) { config.Logger = logger } diff --git a/sharding/shard_manager_impl.go b/sharding/shard_manager_impl.go index e26c0139c..3aa398274 100644 --- a/sharding/shard_manager_impl.go +++ b/sharding/shard_manager_impl.go @@ -3,6 +3,8 @@ package sharding import ( "context" "errors" + "fmt" + "log/slog" "sync" "github.com/disgoorg/snowflake/v2" @@ -40,7 +42,7 @@ func (m *shardManagerImpl) closeHandler(shard gateway.Gateway, err error) { if !m.config.AutoScaling || !errors.As(err, &closeError) || gateway.CloseEventCodeByCode(closeError.Code) != gateway.CloseEventCodeShardingRequired { return } - m.config.Logger.Debugf("shard %d requires re-sharding", shard.ShardID()) + m.config.Logger.Debug("shard requires re-sharding", slog.Int("shardID", shard.ShardID())) // make sure shard is closed shard.Close(context.TODO()) @@ -70,7 +72,7 @@ func (m *shardManagerImpl) closeHandler(shard gateway.Gateway, err error) { go func() { defer wg.Done() if err := m.config.RateLimiter.WaitBucket(context.TODO(), shardID); err != nil { - m.config.Logger.Errorf("failed to wait shard bucket %d: %s", shardID, err) + m.config.Logger.Error("failed to wait shard bucket", slog.String("err", err.Error()), slog.Int("shard_id", shardID)) return } defer m.config.RateLimiter.UnlockBucket(shardID) @@ -78,16 +80,16 @@ func (m *shardManagerImpl) closeHandler(shard gateway.Gateway, err error) { newShard := m.config.GatewayCreateFunc(m.token, m.eventHandlerFunc, m.closeHandler, append(m.config.GatewayConfigOpts, gateway.WithShardID(shardID), gateway.WithShardCount(newShardCount))...) m.shards[shardID] = newShard if err := newShard.Open(context.TODO()); err != nil { - m.config.Logger.Errorf("failed to re shard %d, error: %s", shardID, err) + m.config.Logger.Error("failed to re shard", slog.String("err", err.Error()), slog.Int("shard_id", shardID)) } }() } wg.Wait() - m.config.Logger.Debugf("re-sharded shard %d into newShards: %d, newShardCount: %d", shard.ShardID(), newShardIDs, newShardCount) + m.config.Logger.Debug("re-sharded shard", slog.Int("shard_id", shard.ShardID()), slog.String("new_shard_ids", fmt.Sprint(newShardIDs)), slog.Int("new_shard_count", newShardCount)) } func (m *shardManagerImpl) Open(ctx context.Context) { - m.config.Logger.Debugf("opening %+v shards...", m.config.ShardIDs) + m.config.Logger.Debug("opening shards", slog.String("shard_ids", fmt.Sprint(m.config.ShardIDs))) var wg sync.WaitGroup m.shardsMu.Lock() @@ -102,7 +104,7 @@ func (m *shardManagerImpl) Open(ctx context.Context) { go func() { defer wg.Done() if err := m.config.RateLimiter.WaitBucket(ctx, shardID); err != nil { - m.config.Logger.Errorf("failed to wait shard bucket %d: %s", shardID, err) + m.config.Logger.Error("failed to wait shard bucket", slog.String("err", err.Error()), slog.Int("shard_id", shardID)) return } defer m.config.RateLimiter.UnlockBucket(shardID) @@ -110,7 +112,7 @@ func (m *shardManagerImpl) Open(ctx context.Context) { shard := m.config.GatewayCreateFunc(m.token, m.eventHandlerFunc, m.closeHandler, append(m.config.GatewayConfigOpts, gateway.WithShardID(shardID), gateway.WithShardCount(m.config.ShardCount))...) m.shards[shardID] = shard if err := shard.Open(ctx); err != nil { - m.config.Logger.Errorf("failed to open shard %d: %s", shardID, err) + m.config.Logger.Error("failed to open shard", slog.String("err", err.Error()), slog.Int("shard_id", shardID)) } }() } @@ -118,7 +120,7 @@ func (m *shardManagerImpl) Open(ctx context.Context) { } func (m *shardManagerImpl) Close(ctx context.Context) { - m.config.Logger.Debugf("closing %v shards...", m.config.ShardIDs) + m.config.Logger.Debug("closing shards", slog.String("shard_ids", fmt.Sprint(m.config.ShardIDs))) var wg sync.WaitGroup m.shardsMu.Lock() @@ -140,7 +142,7 @@ func (m *shardManagerImpl) OpenShard(ctx context.Context, shardID int) error { } func (m *shardManagerImpl) openShard(ctx context.Context, shardID int, shardCount int) error { - m.config.Logger.Debugf("opening shard %d...", shardID) + m.config.Logger.Debug("opening shard", slog.Int("shard_id", shardID)) if err := m.config.RateLimiter.WaitBucket(ctx, shardID); err != nil { return err @@ -156,7 +158,7 @@ func (m *shardManagerImpl) openShard(ctx context.Context, shardID int, shardCoun } func (m *shardManagerImpl) CloseShard(ctx context.Context, shardID int) { - m.config.Logger.Debugf("closing shard %d...", shardID) + m.config.Logger.Debug("closing shard", slog.Int("shard_id", shardID)) m.shardsMu.Lock() defer m.shardsMu.Unlock() shard, ok := m.shards[shardID] diff --git a/sharding/shard_rate_limiter.go b/sharding/shard_rate_limiter.go index 613cc0513..da21a281e 100644 --- a/sharding/shard_rate_limiter.go +++ b/sharding/shard_rate_limiter.go @@ -4,6 +4,9 @@ import ( "context" ) +// MaxConcurrency is the default number of shards that can log in at the same time. +const MaxConcurrency = 1 + // RateLimiter limits how many shards can log in to Discord at the same time. type RateLimiter interface { // Close gracefully closes the RateLimiter. diff --git a/sharding/shard_rate_limiter_config.go b/sharding/shard_rate_limiter_config.go index 7193d7191..eb5eaf22b 100644 --- a/sharding/shard_rate_limiter_config.go +++ b/sharding/shard_rate_limiter_config.go @@ -1,20 +1,20 @@ package sharding import ( - "github.com/disgoorg/log" + "log/slog" ) // DefaultRateLimiterConfig returns a RateLimiterConfig with sensible defaults. func DefaultRateLimiterConfig() *RateLimiterConfig { return &RateLimiterConfig{ - Logger: log.Default(), - MaxConcurrency: 1, + Logger: slog.Default(), + MaxConcurrency: MaxConcurrency, } } // RateLimiterConfig lets you configure your RateLimiter instance. type RateLimiterConfig struct { - Logger log.Logger + Logger *slog.Logger MaxConcurrency int } @@ -29,7 +29,7 @@ func (c *RateLimiterConfig) Apply(opts []RateLimiterConfigOpt) { } // WithRateLimiterLogger sets the logger for the RateLimiter. -func WithRateLimiterLogger(logger log.Logger) RateLimiterConfigOpt { +func WithRateLimiterLogger(logger *slog.Logger) RateLimiterConfigOpt { return func(config *RateLimiterConfig) { config.Logger = logger } diff --git a/sharding/shard_rate_limiter_impl.go b/sharding/shard_rate_limiter_impl.go index ed4305275..e313fb458 100644 --- a/sharding/shard_rate_limiter_impl.go +++ b/sharding/shard_rate_limiter_impl.go @@ -2,6 +2,7 @@ package sharding import ( "context" + "log/slog" "sync" "time" @@ -46,10 +47,10 @@ func (r *rateLimiterImpl) Close(ctx context.Context) { } func (r *rateLimiterImpl) getBucket(shardID int, create bool) *bucket { - r.config.Logger.Debug("locking shard srate limiter") + r.config.Logger.Debug("locking shard rate limiter") r.mu.Lock() defer func() { - r.config.Logger.Debug("unlocking shard srate limiter") + r.config.Logger.Debug("unlocking shard rate limiter") r.mu.Unlock() }() key := ShardMaxConcurrencyKey(shardID, r.config.MaxConcurrency) @@ -69,7 +70,7 @@ func (r *rateLimiterImpl) getBucket(shardID int, create bool) *bucket { func (r *rateLimiterImpl) WaitBucket(ctx context.Context, shardID int) error { b := r.getBucket(shardID, true) - r.config.Logger.Debugf("locking shard bucket: Key: %d, Reset: %s", b.Key, b.Reset) + r.config.Logger.Debug("locking shard bucket", slog.Int("key", b.Key), slog.Time("reset", b.Reset)) if err := b.mu.CLock(ctx); err != nil { return err } @@ -102,7 +103,7 @@ func (r *rateLimiterImpl) UnlockBucket(shardID int) { return } defer func() { - r.config.Logger.Debugf("unlocking shard bucket: Key: %d, Reset: %s", b.Key, b.Reset) + r.config.Logger.Debug("unlocking shard bucket", slog.Int("key", b.Key), slog.Time("reset", b.Reset)) b.mu.Unlock() }() diff --git a/voice/audio_receiver.go b/voice/audio_receiver.go index 57cad5ab1..10ea67d64 100644 --- a/voice/audio_receiver.go +++ b/voice/audio_receiver.go @@ -3,15 +3,15 @@ package voice import ( "context" "errors" + "log/slog" "net" - "github.com/disgoorg/log" "github.com/disgoorg/snowflake/v2" ) type ( // AudioReceiverCreateFunc is used to create a new AudioReceiver reading audio from the given Conn. - AudioReceiverCreateFunc func(logger log.Logger, receiver OpusFrameReceiver, connection Conn) AudioReceiver + AudioReceiverCreateFunc func(logger *slog.Logger, receiver OpusFrameReceiver, connection Conn) AudioReceiver // UserFilterFunc is used as a filter for which users to receive audio from. UserFilterFunc func(userID snowflake.ID) bool @@ -42,7 +42,7 @@ type ( ) // NewAudioReceiver creates a new AudioReceiver reading audio to the given OpusFrameReceiver from the given Conn. -func NewAudioReceiver(logger log.Logger, opusReceiver OpusFrameReceiver, conn Conn) AudioReceiver { +func NewAudioReceiver(logger *slog.Logger, opusReceiver OpusFrameReceiver, conn Conn) AudioReceiver { return &defaultAudioReceiver{ logger: logger, opusReceiver: opusReceiver, @@ -51,7 +51,7 @@ func NewAudioReceiver(logger log.Logger, opusReceiver OpusFrameReceiver, conn Co } type defaultAudioReceiver struct { - logger log.Logger + logger *slog.Logger cancelFunc context.CancelFunc opusReceiver OpusFrameReceiver conn Conn @@ -62,7 +62,7 @@ func (s *defaultAudioReceiver) Open() { } func (s *defaultAudioReceiver) open() { - defer s.logger.Debugf("closing audio receiver") + defer s.logger.Debug("closing audio receiver") ctx, cancel := context.WithCancel(context.Background()) s.cancelFunc = cancel defer cancel() @@ -88,12 +88,12 @@ func (s *defaultAudioReceiver) receive() { return } if err != nil { - s.logger.Errorf("error while reading packet: %s", err) + s.logger.Error("error while reading packet", slog.String("err", err.Error())) return } if s.opusReceiver != nil { if err = s.opusReceiver.ReceiveOpusFrame(s.conn.UserIDBySSRC(packet.SSRC), packet); err != nil { - s.logger.Errorf("error while receiving opus frame: %s", err) + s.logger.Error("error while receiving opus frame", slog.String("err", err.Error())) } } diff --git a/voice/audio_sender.go b/voice/audio_sender.go index b7276e30c..31474c18a 100644 --- a/voice/audio_sender.go +++ b/voice/audio_sender.go @@ -4,10 +4,9 @@ import ( "context" "errors" "io" + "log/slog" "net" "time" - - "github.com/disgoorg/log" ) // SilenceAudioFrame is a 20ms opus frame of silence. @@ -26,7 +25,7 @@ const ( type ( // AudioSenderCreateFunc is used to create a new AudioSender sending audio to the given Conn. - AudioSenderCreateFunc func(logger log.Logger, provider OpusFrameProvider, conn Conn) AudioSender + AudioSenderCreateFunc func(logger *slog.Logger, provider OpusFrameProvider, conn Conn) AudioSender // AudioSender is used to send audio to a Conn. AudioSender interface { @@ -45,7 +44,7 @@ type ( ) // NewAudioSender creates a new AudioSender sending audio from the given OpusFrameProvider to the given Conn. -func NewAudioSender(logger log.Logger, opusProvider OpusFrameProvider, conn Conn) AudioSender { +func NewAudioSender(logger *slog.Logger, opusProvider OpusFrameProvider, conn Conn) AudioSender { return &defaultAudioSender{ logger: logger, opusProvider: opusProvider, @@ -55,7 +54,7 @@ func NewAudioSender(logger log.Logger, opusProvider OpusFrameProvider, conn Conn } type defaultAudioSender struct { - logger log.Logger + logger *slog.Logger cancelFunc context.CancelFunc opusProvider OpusFrameProvider conn Conn @@ -102,7 +101,7 @@ func (s *defaultAudioSender) send() { } opus, err := s.opusProvider.ProvideOpusFrame() if err != nil && err != io.EOF { - s.logger.Errorf("error while reading opus frame: %s", err) + s.logger.Error("error while reading opus frame", slog.String("err", err.Error())) return } if len(opus) == 0 { @@ -144,7 +143,7 @@ func (s *defaultAudioSender) handleErr(err error) { s.Close() return } - s.logger.Errorf("failed to send audio: %s", err) + s.logger.Error("failed to send audio", slog.String("err", err.Error())) } func (s *defaultAudioSender) Close() { diff --git a/voice/conn_config.go b/voice/conn_config.go index 4480fd396..9f55b802f 100644 --- a/voice/conn_config.go +++ b/voice/conn_config.go @@ -1,13 +1,13 @@ package voice import ( - "github.com/disgoorg/log" + "log/slog" ) // DefaultConnConfig returns a ConnConfig with sensible defaults. func DefaultConnConfig() *ConnConfig { return &ConnConfig{ - Logger: log.Default(), + Logger: slog.Default(), GatewayCreateFunc: NewGateway, UDPConnCreateFunc: NewUDPConn, AudioSenderCreateFunc: NewAudioSender, @@ -17,7 +17,7 @@ func DefaultConnConfig() *ConnConfig { // ConnConfig is used to configure a Conn. type ConnConfig struct { - Logger log.Logger + Logger *slog.Logger GatewayCreateFunc GatewayCreateFunc GatewayConfigOpts []GatewayConfigOpt @@ -42,7 +42,7 @@ func (c *ConnConfig) Apply(opts []ConnConfigOpt) { } // WithConnLogger sets the Conn(s) used Logger. -func WithConnLogger(logger log.Logger) ConnConfigOpt { +func WithConnLogger(logger *slog.Logger) ConnConfigOpt { return func(config *ConnConfig) { config.Logger = logger } diff --git a/voice/gateway.go b/voice/gateway.go index 5e4849224..80785f143 100644 --- a/voice/gateway.go +++ b/voice/gateway.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "log/slog" "sync" "syscall" "time" @@ -135,13 +136,13 @@ func (g *gatewayImpl) Open(ctx context.Context, state State) error { g.status = StatusConnecting gatewayURL := fmt.Sprintf("wss://%s?v=%d", state.Endpoint, GatewayVersion) - g.config.Logger.Debugf("connecting to voice gateway at: %s", gatewayURL) + g.config.Logger.Debug("connecting to voice gateway at", slog.String("url", gatewayURL)) g.lastHeartbeatSent = time.Now().UTC() conn, rs, err := g.config.Dialer.DialContext(ctx, gatewayURL, nil) if err != nil { g.Close() defer rs.Body.Close() - return fmt.Errorf("error connecting to voice gateway. err: %w", err) + return fmt.Errorf("error connecting to voice gateway: %w", err) } conn.SetCloseHandler(func(code int, text string) error { @@ -161,7 +162,7 @@ func (g *gatewayImpl) Close() { func (g *gatewayImpl) CloseWithCode(code int, message string) { if g.heartbeatTicker != nil { - g.config.Logger.Debug("closing heartbeat goroutines...") + g.config.Logger.Debug("closing heartbeat goroutines") g.heartbeatTicker.Stop() g.heartbeatTicker = nil } @@ -169,9 +170,9 @@ func (g *gatewayImpl) CloseWithCode(code int, message string) { g.connMu.Lock() defer g.connMu.Unlock() if g.conn != nil { - g.config.Logger.Debugf("closing voice gateway connection with code: %d, message: %s", code, message) + g.config.Logger.Debug("closing voice gateway connection", slog.Int("code", code), slog.String("message", message)) if err := g.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(code, message)); err != nil && !errors.Is(err, websocket.ErrCloseSent) { - g.config.Logger.Debug("error writing close code. error: ", err) + g.config.Logger.Debug("error writing close code", slog.String("err", err.Error())) } _ = g.conn.Close() g.conn = nil @@ -186,7 +187,7 @@ func (g *gatewayImpl) CloseWithCode(code int, message string) { func (g *gatewayImpl) heartbeat() { g.heartbeatTicker = time.NewTicker(g.heartbeatInterval) defer g.heartbeatTicker.Stop() - defer g.config.Logger.Debug("exiting voice heartbeat goroutine...") + defer g.config.Logger.Debug("exiting voice heartbeat goroutine") for range g.heartbeatTicker.C { g.sendHeartbeat() @@ -202,7 +203,7 @@ func (g *gatewayImpl) sendHeartbeat() { if !errors.Is(err, ErrGatewayNotConnected) || errors.Is(err, syscall.EPIPE) { return } - g.config.Logger.Error("failed to send heartbeat. error: ", err) + g.config.Logger.Error("failed to send heartbeat", slog.String("err", err.Error())) g.CloseWithCode(websocket.CloseServiceRestart, "heartbeat timeout") go g.reconnect() return @@ -211,7 +212,7 @@ func (g *gatewayImpl) sendHeartbeat() { } func (g *gatewayImpl) listen(conn *websocket.Conn) { - defer g.config.Logger.Debug("exiting listen goroutine...") + defer g.config.Logger.Debug("exiting listen goroutine") loop: for { _, reader, err := conn.NextReader() @@ -242,7 +243,7 @@ loop: message, err := g.parseMessage(reader) if err != nil { - g.config.Logger.Error("error while parsing voice gateway event. error: ", err) + g.config.Logger.Error("error while parsing voice gateway event", slog.String("err", err.Error())) continue } @@ -283,7 +284,7 @@ loop: case GatewayMessageDataHeartbeatACK: if int64(d) != g.lastNonce { - g.config.Logger.Errorf("received heartbeat ack with nonce: %d, expected nonce: %d", int64(d), g.lastNonce) + g.config.Logger.Error("received heartbeat ack with nonce", slog.Int64("nonce", int64(d)), slog.Int64("last_nonce", g.lastNonce)) go g.reconnect() break loop } @@ -315,7 +316,7 @@ func (g *gatewayImpl) send(ctx context.Context, messageType int, data []byte) er return ErrGatewayNotConnected } - g.config.Logger.Trace("sending message to voice gateway. data: ", string(data)) + g.config.Logger.Debug("sending message to voice gateway", slog.String("data", string(data))) deadline, ok := ctx.Deadline() if ok { if err := g.conn.SetWriteDeadline(deadline); err != nil { @@ -343,12 +344,12 @@ func (g *gatewayImpl) reconnectTry(ctx context.Context, try int) error { case <-timer.C: } - g.config.Logger.Debug("reconnecting voice gateway...") + g.config.Logger.Debug("reconnecting voice gateway") if err := g.Open(ctx, g.state); err != nil { if errors.Is(err, discord.ErrGatewayAlreadyConnected) { return err } - g.config.Logger.Error("failed to reconnect voice gateway. error: ", err) + g.config.Logger.Error("failed to reconnect voice gateway", slog.String("err", err.Error())) g.status = StatusDisconnected return g.reconnectTry(ctx, try+1) } @@ -357,14 +358,14 @@ func (g *gatewayImpl) reconnectTry(ctx context.Context, try int) error { func (g *gatewayImpl) reconnect() { if err := g.reconnectTry(context.Background(), 0); err != nil { - g.config.Logger.Error("failed to reopen voice gateway", err) + g.config.Logger.Error("failed to reopen voice gateway", slog.String("err", err.Error())) } } func (g *gatewayImpl) parseMessage(r io.Reader) (GatewayMessage, error) { buff := &bytes.Buffer{} data, _ := io.ReadAll(io.TeeReader(r, buff)) - g.config.Logger.Tracef("received message from voice gateway. data: %s", string(data)) + g.config.Logger.Debug("received message from voice gateway", slog.String("data", string(data))) var message GatewayMessage if err := json.NewDecoder(buff).Decode(&message); err != nil { diff --git a/voice/gateway_config.go b/voice/gateway_config.go index 53d847007..392b33f3c 100644 --- a/voice/gateway_config.go +++ b/voice/gateway_config.go @@ -1,14 +1,15 @@ package voice import ( - "github.com/disgoorg/log" + "log/slog" + "github.com/gorilla/websocket" ) // DefaultGatewayConfig returns a GatewayConfig with sensible defaults. func DefaultGatewayConfig() *GatewayConfig { return &GatewayConfig{ - Logger: log.Default(), + Logger: slog.Default(), Dialer: websocket.DefaultDialer, AutoReconnect: true, } @@ -16,7 +17,7 @@ func DefaultGatewayConfig() *GatewayConfig { // GatewayConfig is used to configure a Gateway. type GatewayConfig struct { - Logger log.Logger + Logger *slog.Logger Dialer *websocket.Dialer AutoReconnect bool } @@ -32,7 +33,7 @@ func (c *GatewayConfig) Apply(opts []GatewayConfigOpt) { } // WithGatewayLogger sets the Gateway(s) used Logger. -func WithGatewayLogger(logger log.Logger) GatewayConfigOpt { +func WithGatewayLogger(logger *slog.Logger) GatewayConfigOpt { return func(config *GatewayConfig) { config.Logger = logger } diff --git a/voice/manager.go b/voice/manager.go index 5eefebcf0..0002eb01d 100644 --- a/voice/manager.go +++ b/voice/manager.go @@ -2,6 +2,7 @@ package voice import ( "context" + "log/slog" "sync" "github.com/disgoorg/snowflake/v2" @@ -60,7 +61,7 @@ type managerImpl struct { } func (m *managerImpl) HandleVoiceStateUpdate(update gateway.EventVoiceStateUpdate) { - m.config.Logger.Debugf("VoiceStateUpdate for guild: %s", update.GuildID) + m.config.Logger.Debug("new VoiceStateUpdate", slog.Int64("guild_id", int64(update.GuildID))) conn := m.GetConn(update.GuildID) if conn == nil { @@ -70,7 +71,7 @@ func (m *managerImpl) HandleVoiceStateUpdate(update gateway.EventVoiceStateUpdat } func (m *managerImpl) HandleVoiceServerUpdate(update gateway.EventVoiceServerUpdate) { - m.config.Logger.Debugf("VoiceServerUpdate for guild: %s", update.GuildID) + m.config.Logger.Debug("new VoiceServerUpdate", slog.Int64("guild_id", int64(update.GuildID))) conn := m.GetConn(update.GuildID) if conn == nil { @@ -80,7 +81,7 @@ func (m *managerImpl) HandleVoiceServerUpdate(update gateway.EventVoiceServerUpd } func (m *managerImpl) CreateConn(guildID snowflake.ID) Conn { - m.config.Logger.Debugf("Creating new voice conn for guild: %s", guildID) + m.config.Logger.Debug("Creating new voice conn", slog.Int64("guild_id", int64(guildID))) if conn := m.GetConn(guildID); conn != nil { return conn } @@ -112,7 +113,7 @@ func (m *managerImpl) ForEachCon(f func(connection Conn)) { } func (m *managerImpl) RemoveConn(guildID snowflake.ID) { - m.config.Logger.Debugf("Removing voice conn for guild: %s", guildID) + m.config.Logger.Debug("Removing voice conn", slog.Int64("guild_id", int64(guildID))) conn := m.GetConn(guildID) if conn == nil { return diff --git a/voice/manager_config.go b/voice/manager_config.go index 17009c664..b0e9f1a8d 100644 --- a/voice/manager_config.go +++ b/voice/manager_config.go @@ -1,18 +1,18 @@ package voice -import "github.com/disgoorg/log" +import "log/slog" // DefaultManagerConfig returns the default ManagerConfig with sensible defaults. func DefaultManagerConfig() *ManagerConfig { return &ManagerConfig{ - Logger: log.Default(), + Logger: slog.Default(), ConnCreateFunc: NewConn, } } // ManagerConfig is a function that configures a Manager. type ManagerConfig struct { - Logger log.Logger + Logger *slog.Logger ConnCreateFunc ConnCreateFunc ConnOpts []ConnConfigOpt @@ -29,7 +29,7 @@ func (c *ManagerConfig) Apply(opts []ManagerConfigOpt) { } // WithLogger sets the logger for the webhook client -func WithLogger(logger log.Logger) ManagerConfigOpt { +func WithLogger(logger *slog.Logger) ManagerConfigOpt { return func(ManagerConfig *ManagerConfig) { ManagerConfig.Logger = logger } diff --git a/voice/udp_conn.go b/voice/udp_conn.go index eb050111d..9ee382657 100644 --- a/voice/udp_conn.go +++ b/voice/udp_conn.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "log/slog" "net" "strconv" "strings" @@ -15,8 +16,13 @@ import ( "golang.org/x/crypto/nacl/secretbox" ) -// OpusPacketHeaderSize is the size of the opus packet header. -const OpusPacketHeaderSize = 12 +const ( + // OpusPacketHeaderSize is the size of the opus packet header. + OpusPacketHeaderSize = 12 + + // UDPTimeout is the timeout for UDP connections. + UDPTimeout = 30 * time.Second +) // ErrDecryptionFailed is returned when the packet decryption fails. var ErrDecryptionFailed = errors.New("decryption failed") @@ -148,7 +154,7 @@ func (u *udpConnImpl) Open(ctx context.Context, ip string, port int, ssrc uint32 u.connMu.Lock() defer u.connMu.Unlock() host := net.JoinHostPort(ip, strconv.Itoa(port)) - u.config.Logger.Debugf("Opening UDPConn connection to: %s\n", host) + u.config.Logger.Debug("Opening UDPConn connection", slog.String("host", host)) var err error u.conn, err = u.config.Dialer.DialContext(ctx, "udp", host) if err != nil { diff --git a/voice/udp_conn_config.go b/voice/udp_conn_config.go index 7e860b0ad..f1f0af98f 100644 --- a/voice/udp_conn_config.go +++ b/voice/udp_conn_config.go @@ -1,23 +1,21 @@ package voice import ( + "log/slog" "net" - "time" - - "github.com/disgoorg/log" ) func DefaultUDPConnConfig() UDPConnConfig { return UDPConnConfig{ - Logger: log.Default(), + Logger: slog.Default(), Dialer: &net.Dialer{ - Timeout: 30 * time.Second, + Timeout: UDPTimeout, }, } } type UDPConnConfig struct { - Logger log.Logger + Logger *slog.Logger Dialer *net.Dialer } @@ -29,7 +27,7 @@ func (c *UDPConnConfig) Apply(opts []UDPConnConfigOpt) { } } -func WithUDPConnLogger(logger log.Logger) UDPConnConfigOpt { +func WithUDPConnLogger(logger *slog.Logger) UDPConnConfigOpt { return func(config *UDPConnConfig) { config.Logger = logger } diff --git a/webhook/webhook_config.go b/webhook/webhook_config.go index 4ebe725ba..69eb010bc 100644 --- a/webhook/webhook_config.go +++ b/webhook/webhook_config.go @@ -1,7 +1,7 @@ package webhook import ( - "github.com/disgoorg/log" + "log/slog" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/rest" @@ -10,14 +10,14 @@ import ( // DefaultConfig is the default configuration for the webhook client func DefaultConfig() *Config { return &Config{ - Logger: log.Default(), + Logger: slog.Default(), DefaultAllowedMentions: &discord.DefaultAllowedMentions, } } // Config is the configuration for the webhook client type Config struct { - Logger log.Logger + Logger *slog.Logger RestClient rest.Client RestClientConfigOpts []rest.ConfigOpt Webhooks rest.Webhooks @@ -41,7 +41,7 @@ func (c *Config) Apply(opts []ConfigOpt) { } // WithLogger sets the logger for the webhook client -func WithLogger(logger log.Logger) ConfigOpt { +func WithLogger(logger *slog.Logger) ConfigOpt { return func(config *Config) { config.Logger = logger }