Last active
March 19, 2023 21:29
-
-
Save Semior001/6a381171af263df1fc5d9e287f11d2f4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package bot | |
// Run starts service until context is dead. | |
func (b *Bot) Run(ctx context.Context) error { | |
if err := b.notifyAdmins(ctx, "bot started"); err != nil { | |
return fmt.Errorf("send start message to admins: %w", err) | |
} | |
wg := &sync.WaitGroup{} | |
wg.Add(b.Workers) | |
for i := 0; i < b.Workers; i++ { | |
go func(idx int) { | |
b.logger.InfoCtx(ctx, "started worker", slog.Int("idx", idx)) | |
defer func() { | |
b.logger.InfoCtx(ctx, "stopped worker", slog.Int("idx", idx)) | |
wg.Done() | |
}() | |
for { | |
select { | |
case <-ctx.Done(): | |
return | |
case req := <-b.ctrl.Updates(): | |
b.handleUpdate(ctx, req) | |
} | |
} | |
}(i) | |
} | |
wg.Wait() | |
if err := b.notifyAdmins(context.Background(), "bot stopped"); err != nil { | |
return fmt.Errorf("send stop message to admins: %w", err) | |
} | |
return nil | |
} | |
func (b *Bot) handleUpdate(ctx context.Context, req route.Request) { | |
if b.Timeout > 0 { | |
var cancel func() | |
ctx, cancel = context.WithTimeout(ctx, b.Timeout) | |
defer cancel() | |
} | |
resps, err := b.h(ctx, req) | |
if err != nil { | |
b.logger.ErrorCtx(ctx, "failed to handle request", slog.Any("err", err)) | |
} | |
for _, resp := range resps { | |
if err := b.ctrl.SendMessage(ctx, resp); err != nil { | |
b.logger.WarnCtx(ctx, "failed to send message", slog.Any("err", err)) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Package logging contains logging middleware for request id. | |
package logging | |
import ( | |
"context" | |
"golang.org/x/exp/slog" | |
) | |
// HandleFunc is a function that handles a record. | |
type HandleFunc func(context.Context, slog.Record) error | |
// Middleware is a middleware for logging handler. | |
type Middleware func(HandleFunc) HandleFunc | |
// Chain is a chain of middleware. | |
type Chain struct { | |
Middleware []Middleware | |
slog.Handler | |
} | |
// Handle runs the chain of middleware and the handler. | |
func (c *Chain) Handle(ctx context.Context, rec slog.Record) error { | |
h := c.Handler.Handle | |
for i := len(c.Middleware) - 1; i >= 0; i-- { | |
h = c.Middleware[i](h) | |
} | |
return h(ctx, rec) | |
} | |
// WithGroup returns a new Chain with the given group. | |
func (c *Chain) WithGroup(group string) slog.Handler { | |
return &Chain{ | |
Middleware: c.Middleware, | |
Handler: c.Handler.WithGroup(group), | |
} | |
} | |
// WithAttrs returns a new Chain with the given attributes. | |
func (c *Chain) WithAttrs(attrs []slog.Attr) slog.Handler { | |
return &Chain{ | |
Middleware: c.Middleware, | |
Handler: c.Handler.WithAttrs(attrs), | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func setupLog() { | |
handler := slog.HandlerOptions{ | |
AddSource: false, | |
Level: slog.LevelInfo, | |
ReplaceAttr: nil, | |
} | |
if opts.Debug { | |
handler.Level = slog.LevelDebug | |
handler.AddSource = true | |
} | |
h := slog.Handler(handler.NewTextHandler(os.Stderr)) | |
if opts.JSONLogs { | |
h = handler.NewJSONHandler(os.Stderr) | |
} | |
lg := slog.New(&logging.Chain{ | |
Handler: h, | |
Middleware: []logging.Middleware{ | |
logging.RequestID(), | |
logging.StacktraceOnError(), | |
}, | |
}) | |
slog.SetDefault(lg) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package logging | |
import ( | |
"context" | |
"golang.org/x/exp/slog" | |
) | |
type requestIDKey struct{} | |
// ContextWithRequestID returns a new context with the given request ID. | |
func ContextWithRequestID(parent context.Context, reqID string) context.Context { | |
return context.WithValue(parent, requestIDKey{}, reqID) | |
} | |
// RequestIDFromContext returns request id from context. | |
func RequestIDFromContext(ctx context.Context) (string, bool) { | |
v, ok := ctx.Value(requestIDKey{}).(string) | |
return v, ok | |
} | |
// RequestID returns a middleware that adds request id to record. | |
func RequestID() Middleware { | |
return func(next HandleFunc) HandleFunc { | |
return func(ctx context.Context, rec slog.Record) error { | |
if reqID, ok := RequestIDFromContext(ctx); ok { | |
rec.AddAttrs(slog.String("request_id", reqID)) | |
} | |
return next(ctx, rec) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package logging | |
import ( | |
"context" | |
"regexp" | |
"runtime" | |
"golang.org/x/exp/slog" | |
) | |
var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`) | |
// StacktraceOnError returns a middleware that adds stacktrace to record if level is error. | |
func StacktraceOnError() Middleware { | |
return func(next HandleFunc) HandleFunc { | |
return func(ctx context.Context, rec slog.Record) error { | |
if rec.Level != slog.LevelError { | |
return next(ctx, rec) | |
} | |
stackInfo := make([]byte, 1024*1024) | |
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 { | |
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1) | |
if len(traceLines) == 0 { | |
return next(ctx, rec) | |
} | |
rec.AddAttrs(slog.String("stacktrace", traceLines[len(traceLines)-1])) | |
} | |
return next(ctx, rec) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment