feat: add optional `skip` argument to `Helper` function
The problem
The current Helper function is hardcoded to only skip the immediate function that called it. This makes it really unergonomic to use in an abstraction.
I have an abstraction type which provides a bunch of convenience methods, e.g. extracts known values from context.Context and passes them as attributes to the various log functions. This abstraction also manages multiple different instances of Logger under the hood. It all works quite well, except when it comes to Helper.
Broken abstraction
func AbstractedHelper() {
// Calling the Helper function on these Logger instances
// will just mark AbstractedHelper for skipping.
loggerA.Helper()
loggerB.Helper()
}
func example() {
common()
}
func common() {
AbstractedHelper()
Info("Hello") // Incorrectly prints this line as the source
}
Working but unergonomic abstraction
func AbstractedHelper() []func() {
// Don't call the functions yet, just return them.
return []func(){loggerA.Helper, loggerB.Helper}
}
func example() {
common() // Correctly prints this line as the source
}
func common() {
for _, helper := range AbstractedHelper() {
helper()
}
Info("Hello")
}
The solution
The internal helper function already takes a skip argument that would solve this issue. The public function doesn't yet expose this and that is exactly what this PR does.
In order to achieve backwards compatibility, the skip argument will be optional by making the public Helper a variadic function.
Ergonomic abstraction achieved
func AbstractedHelper() {
// We tell Helper to also skip the abstraction function
loggerA.Helper(2)
loggerB.Helper(2)
}
func example() {
common() // Correctly prints this line as the source
}
func common() {
AbstractedHelper()
Info("Hello")
}