Skip to content

Commit

Permalink
Improve error logging; can now pass a standard logger struct to the u…
Browse files Browse the repository at this point in the history
…pdater and dispatcher to change log settings. Defaults to stderr.
  • Loading branch information
PaulSonOfLars committed Dec 13, 2020
1 parent 59540a1 commit f5fb1b1
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 14 deletions.
49 changes: 42 additions & 7 deletions ext/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ type Dispatcher struct {
// Error handles any errors that occur during handler execution.
Error func(ctx *Context, err error)
// Panic handles any panics that occur during handler execution.
// If no panic is defined, the stack is logged to stderr.
// If this field is nil, the stack is logged to stderr.
Panic func(ctx *Context, stack []byte)
// ErrorLog is the output to log to in the case of a library error.
ErrorLog *log.Logger

// handlerGroups represents the list of available handler groups, numerically sorted.
handlerGroups []int
Expand All @@ -33,13 +35,43 @@ type Dispatcher struct {
waitGroup sync.WaitGroup
}

type DispatcherOpts struct {
// Error handles any errors that occur during handler execution.
Error func(ctx *Context, err error)
// Panic handles any panics that occur during handler execution.
// If no panic is defined, the stack is logged to
Panic func(ctx *Context, stack []byte)

// MaxRoutines is used to decide how to limit the number of goroutines spawned by the dispatcher.
// If MaxRoutines == 0, DefaultMaxRoutines is used instead.
// If MaxRoutines < 0, no limits are imposed.
MaxRoutines int
ErrorLog *log.Logger
}

// NewDispatcher creates a new dispatcher, which process and handles incoming updates from the updates channel.
// The maxRoutines argument is used to decide how to limit the number of goroutines spawned by the dispatcher.
// If maxRoutines == 0, DefaultMaxRoutines is used instead.
// If maxRoutines < 0, no limits are imposed.
func NewDispatcher(updates chan json.RawMessage, maxRoutines int) *Dispatcher {
func NewDispatcher(updates chan json.RawMessage, opts *DispatcherOpts) *Dispatcher {
var limiter chan struct{}

var errFunc func(ctx *Context, err error)
var panicFunc func(ctx *Context, stack []byte)

maxRoutines := DefaultMaxRoutines
errLog := errorLog

if opts != nil {
if opts.MaxRoutines != 0 {
maxRoutines = opts.MaxRoutines
}

if opts.ErrorLog != nil {
errLog = opts.ErrorLog
}

errFunc = opts.Error
panicFunc = opts.Panic
}

if maxRoutines >= 0 {
if maxRoutines == 0 {
maxRoutines = DefaultMaxRoutines
Expand All @@ -49,6 +81,9 @@ func NewDispatcher(updates chan json.RawMessage, maxRoutines int) *Dispatcher {
}

return &Dispatcher{
Error: errFunc,
Panic: panicFunc,
ErrorLog: errLog,
updatesChan: updates,
handlers: make(map[int][]Handler),
limiter: limiter,
Expand Down Expand Up @@ -120,7 +155,7 @@ var ContinueGroups = errors.New("group iteration continued")
func (d *Dispatcher) ProcessRawUpdate(b *gotgbot.Bot, r json.RawMessage) {
var upd gotgbot.Update
if err := json.Unmarshal(r, &upd); err != nil {
log.Println("failed to process raw update: " + err.Error())
d.ErrorLog.Println("failed to process raw update: " + err.Error())
return
}

Expand All @@ -137,7 +172,7 @@ func (d *Dispatcher) ProcessUpdate(b *gotgbot.Bot, update *gotgbot.Update) {
return
}

log.Println(debug.Stack())
d.ErrorLog.Println(debug.Stack())
}
}()

Expand Down
40 changes: 34 additions & 6 deletions ext/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"log"
"net/http"
"net/url"
"os"
"strconv"
"time"

Expand All @@ -18,23 +19,50 @@ type Updater struct {
Bot gotgbot.Bot
Dispatcher *Dispatcher
UpdateChan chan json.RawMessage
ErrorLog *log.Logger

idle bool
running bool
server *http.Server
}

var errorLog = log.New(os.Stderr, "ERROR", log.LstdFlags)

type UpdaterOpts struct {
PollingTimeout time.Duration
ErrorLog *log.Logger

DispatcherOpts DispatcherOpts
}

// NewUpdater Creates a new Updater, as well as the necessary structures for
func NewUpdater(bot *gotgbot.Bot) Updater {
func NewUpdater(bot *gotgbot.Bot, opts *UpdaterOpts) Updater {
errLog := errorLog
pollTimeout := time.Second * 10
var dispatcherOpts DispatcherOpts

if opts != nil {
if opts.PollingTimeout != 0 {
pollTimeout = opts.PollingTimeout
}

if opts.ErrorLog != nil {
errLog = opts.ErrorLog
}

dispatcherOpts = opts.DispatcherOpts
}

updateChan := make(chan json.RawMessage)
return Updater{
Bot: gotgbot.Bot{
Token: bot.Token,
APIURL: bot.APIURL,
Client: http.Client{},
GetTimeout: time.Second * 10,
GetTimeout: pollTimeout,
}, // create new bot client to allow for independent timeout changes
Dispatcher: NewDispatcher(updateChan, DefaultMaxRoutines),
ErrorLog: errLog,
Dispatcher: NewDispatcher(updateChan, &dispatcherOpts),
UpdateChan: updateChan,
}
}
Expand Down Expand Up @@ -90,7 +118,7 @@ func (u *Updater) pollingLoop(clean bool, v url.Values) {
// note: this bot instance uses a custom http.Client with longer timeouts
r, err := u.Bot.Get("getUpdates", v)
if err != nil {
log.Println("failed to get updates; sleeping 1s: " + err.Error())
u.ErrorLog.Println("failed to get updates; sleeping 1s: " + err.Error())
time.Sleep(time.Second)
continue

Expand All @@ -100,7 +128,7 @@ func (u *Updater) pollingLoop(clean bool, v url.Values) {

var rawUpdates []json.RawMessage
if err := json.Unmarshal(r, &rawUpdates); err != nil {
log.Println("failed to unmarshal updates: " + err.Error())
u.ErrorLog.Println("failed to unmarshal updates: " + err.Error())
continue
}

Expand All @@ -113,7 +141,7 @@ func (u *Updater) pollingLoop(clean bool, v url.Values) {
}

if err := json.Unmarshal(rawUpdates[len(rawUpdates)-1], &lastUpdate); err != nil {
log.Println("failed to unmarshal last update: " + err.Error())
u.ErrorLog.Println("failed to unmarshal last update: " + err.Error())
continue
}

Expand Down
2 changes: 1 addition & 1 deletion samples/echoBot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func main() {
}

// Create updater and dispatcher.
updater := ext.NewUpdater(b)
updater := ext.NewUpdater(b, nil)
dispatcher := updater.Dispatcher

// Add echo handler to reply to all messages.
Expand Down

0 comments on commit f5fb1b1

Please sign in to comment.