HiveBrain v1.2.0
Get Started
← Back to all entries
patterngoMinor

Strip a newline and add more text to a Go bytes.Buffer

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
buffernewlinetextmorebytesandstripadd

Problem

I wrote a little timer middleware to append the request duration to the end of the log message returned by the excellent Gorilla Toolkit's CombinedLoggingHandler from the handlers package.

That handler accepts an io.Writer and an http.Handler, and fills the Writer with a newline-terminate string containing the Apache Combined Format log entry for the current server request. I would like to remove the trailing newline and add " completed in xxx", where xxx is the amount of time the request took.

The following code works, but I am not sure that I made the best use of the Go bytes.Buffer in the way I stripped the newline:

func timerHandler(h http.Handler) http.Handler {
    return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
        var b bytes.Buffer
        hand := handlers.CombinedLoggingHandler(&b, h)

        defer func(start time.Time) {
            prefix := bytes.TrimRight(b.Bytes(), "\n")
            os.Stdout.Write(prefix)
            fmt.Printf(" completed in %v\n", time.Since(start))
        }(time.Now())

        hand.ServeHTTP(res, req)
    })
}


Is there a better way to write this in Go?

Solution

I believe you are over-complicating the handler function by introducing the deferred function with the start time closure. Additionally, you are performing 2 writes to accomplish the logging, when one would be better.

So, Why have the "defer"? This code:

var b bytes.Buffer
    hand := handlers.CombinedLoggingHandler(&b, h)

    defer func(start time.Time) {
        prefix := bytes.TrimRight(b.Bytes(), "\n")
        os.Stdout.Write(prefix)
        fmt.Printf(" completed in %v\n", time.Since(start))
    }(time.Now())

    hand.ServeHTTP(res, req)


would be simpler to understand as:

start := time.Now()
    var b bytes.Buffer
    hand := handlers.CombinedLoggingHandler(&b, h)

    hand.ServeHTTP(res, req)

    prefix := bytes.TrimRight(b.Bytes(), "\n")
    os.Stdout.Write(prefix)
    fmt.Printf(" completed in %v\n", time.Since(start))


Then, in addition, I would manipulate the bytes to trim and add the completion message, and then output them...., so I would change the last part of the code to be something like:

msg := bytes.TrimRight(b.Bytes(), "\n")
    msg := append(msg, []byte(fmt.Sprintf(" completed in %v\n", time.Since(start)...)
    os.Stdout.Write(msg)


That still irks me a bit, because i don't like the os.Stdout.Write being a constant value in there.... I would make that a parameter somehow, or a constant of some sort....

var logDest io.Writer = os.Stdout

Code Snippets

var b bytes.Buffer
    hand := handlers.CombinedLoggingHandler(&b, h)

    defer func(start time.Time) {
        prefix := bytes.TrimRight(b.Bytes(), "\n")
        os.Stdout.Write(prefix)
        fmt.Printf(" completed in %v\n", time.Since(start))
    }(time.Now())

    hand.ServeHTTP(res, req)
start := time.Now()
    var b bytes.Buffer
    hand := handlers.CombinedLoggingHandler(&b, h)

    hand.ServeHTTP(res, req)

    prefix := bytes.TrimRight(b.Bytes(), "\n")
    os.Stdout.Write(prefix)
    fmt.Printf(" completed in %v\n", time.Since(start))
msg := bytes.TrimRight(b.Bytes(), "\n")
    msg := append(msg, []byte(fmt.Sprintf(" completed in %v\n", time.Since(start)...)
    os.Stdout.Write(msg)
var logDest io.Writer = os.Stdout

Context

StackExchange Code Review Q#150601, answer score: 4

Revisions (0)

No revisions yet.