Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v4: Support for context-based structured logging #361

Open
diamondburned opened this issue Jan 1, 2023 · 1 comment
Open

v4: Support for context-based structured logging #361

diamondburned opened this issue Jan 1, 2023 · 1 comment
Milestone

Comments

@diamondburned
Copy link
Owner

Arikawa should (by now) all have context APIs that would allow the user to inject values into contexts. We could use this to inject our own structured logger in that would allow the user to easily debug their bot.

The logging should not deviate too far from stdlib log so we can maintain compatibility.

Proposed API:

package logger // import "github.com/diamondburned/arikawa/v4/utils/logger"

// Logger is a logger that prints logs. It must be safe for concurrent use.
type Logger interface {
	// WithValue returns a new Logger with the given key-value pair. No visual
	// information is required to be shown.
    WithValue(name string, value any) Logger
	// WithPrefix returns a new Logger with the given prefix. The prefix must be
	// shown before the log message.
	WithPrefix(prefix string) Logger

	Print(v ...any)
	Printf(format string, v ...any)
	Println(v ...any)
}

type stdLogger struct {
	*log.Logger
}

// NewStandardLogger returns a new Logger that uses the standard library's log
// package.
func NewStandardLogger(logger *log.Logger) Logger {
	return stdLogger{logger}
}

// DefaultLogger is the default logger used by the package. It is a standard
// library logger that prints to os.Stderr.
var DefaultLogger = NewStandardLogger(log.Default())

func (p stdLogger) WithValue(name string, value any) Logger {
	return p
}

func (p stdLogger) WithPrefix(prefix string) Logger {
	logger := log.New(p.Writer(), p.Prefix() + ": " + prefix, p.Flags())
	return stdLogger{logger}
}

// FromContext returns a Logger from the given context. If no Logger is found,
// the DefaultLogger is returned.
func FromContext(ctx context.Context) Logger {
	if logger, ok := ctx.Value(loggerKey).(Logger); ok {
		return logger
	}
	return DefaultLogger
}

Usage:

// setup
ctx = logger.WithPrefix(ctx, "gateway")
ctx = logger.WithValue(ctx, "shard", shard)
ctx = logger.WithValue(ctx, "event", event)
// use
log := logger.FromContext(ctx)
log.Println("cannot handle interaction:", err)
@diamondburned
Copy link
Owner Author

We could use x/exp/slog.

@diamondburned diamondburned added this to the v4 milestone Aug 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant