log icon indicating copy to clipboard operation
log copied to clipboard

feat: add optional `skip` argument to `Helper` function

Open xStrom opened this issue 6 months ago • 0 comments

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")
}

xStrom avatar Jun 21 '25 12:06 xStrom