FAQ
Hi folks,

Is it a bad idea to embed the interface http.ResponseWriter in order to
record statistics like bytes written and request duration? I had written
the following:

// WriteCounter wraps an http.ResponseWriter and records start and end
// times, bytes written, and the status code.
type WriteCounter struct {
http.ResponseWriter
start time.Time
end time.Time
status int
count int
}

func NewWriteCounter(w http.ResponseWriter) *WriteCounter {
return &WriteCounter{
ResponseWriter: w,
start: time.Now(),
end: time.Time{},
status: -1,
count: -1,
}
}

func (wc *WriteCounter) WriteHeader(code int) {
wc.ResponseWriter.WriteHeader(code)
wc.end = time.Now()
wc.status = code
}

func (wc *WriteCounter) Write(data []byte) (n int, err error) {
n, err = wc.ResponseWriter.Write(data)
wc.end = time.Now()
wc.count += n
return
}

Later on somebody pointed out some other interesting features of the http
library, such as the CloseNotifier interface, and I realized the above code
might not be such a good idea. Since I'm embedding an *interface*, I can't
automatically inherit *http.response's implementations of CloseNotifier and
Flusher.

Are there alternative methods that people would recommend for recording the
per-response data I'm looking for? One that doesn't require me to try
extending http.ResponseWriter? I found myself writing code like the
following:

func (wc *WriteCounter) Flush() {
if f, ok := wc.ResponseWriter.(http.Flusher); ok {
f.Flush()
}
}

func (wc *WriteCounter) CloseNotify() <-chan bool {
if cn, ok := wc.ResponseWriter.(http.CloseNotifier); ok {
return cn.CloseNotify()
}
return nil
}

which strikes me as an ugly hack and an indication that I'm probably Doing
It Wrong.

Jim

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Search Discussions

  • Brad Fitzpatrick at Jul 9, 2013 at 1:40 am
    No, you're sadly doing it right.


    On Mon, Jul 8, 2013 at 5:06 PM, Jim Robinson wrote:

    Hi folks,

    Is it a bad idea to embed the interface http.ResponseWriter in order to
    record statistics like bytes written and request duration? I had written
    the following:

    // WriteCounter wraps an http.ResponseWriter and records start and end
    // times, bytes written, and the status code.
    type WriteCounter struct {
    http.ResponseWriter
    start time.Time
    end time.Time
    status int
    count int
    }

    func NewWriteCounter(w http.ResponseWriter) *WriteCounter {
    return &WriteCounter{
    ResponseWriter: w,
    start: time.Now(),
    end: time.Time{},
    status: -1,
    count: -1,
    }
    }

    func (wc *WriteCounter) WriteHeader(code int) {
    wc.ResponseWriter.WriteHeader(code)
    wc.end = time.Now()
    wc.status = code
    }

    func (wc *WriteCounter) Write(data []byte) (n int, err error) {
    n, err = wc.ResponseWriter.Write(data)
    wc.end = time.Now()
    wc.count += n
    return
    }

    Later on somebody pointed out some other interesting features of the http
    library, such as the CloseNotifier interface, and I realized the above code
    might not be such a good idea. Since I'm embedding an *interface*, I can't
    automatically inherit *http.response's implementations of CloseNotifier and
    Flusher.

    Are there alternative methods that people would recommend for recording
    the per-response data I'm looking for? One that doesn't require me to try
    extending http.ResponseWriter? I found myself writing code like the
    following:

    func (wc *WriteCounter) Flush() {
    if f, ok := wc.ResponseWriter.(http.Flusher); ok {
    f.Flush()
    }
    }

    func (wc *WriteCounter) CloseNotify() <-chan bool {
    if cn, ok := wc.ResponseWriter.(http.CloseNotifier); ok {
    return cn.CloseNotify()
    }
    return nil
    }

    which strikes me as an ugly hack and an indication that I'm probably Doing
    It Wrong.

    Jim

    --
    You received this message because you are subscribed to the Google Groups
    "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an
    email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Martin Angers at Jul 9, 2013 at 12:27 pm
    That's very good to know. I did use the embedded ResponseWriter approach
    quite a bit, and never implemented those other Flusher and CloseNotifier
    interfaces. I see there's also the Hijacker interface that could be of
    interest, for completeness' sake.

    Le lundi 8 juillet 2013 21:40:09 UTC-4, bradfitz a écrit :
    No, you're sadly doing it right.



    On Mon, Jul 8, 2013 at 5:06 PM, Jim Robinson <jim.ro...@gmail.com<javascript:>
    wrote:
    Hi folks,

    Is it a bad idea to embed the interface http.ResponseWriter in order to
    record statistics like bytes written and request duration? I had written
    the following:

    // WriteCounter wraps an http.ResponseWriter and records start and end
    // times, bytes written, and the status code.
    type WriteCounter struct {
    http.ResponseWriter
    start time.Time
    end time.Time
    status int
    count int
    }

    func NewWriteCounter(w http.ResponseWriter) *WriteCounter {
    return &WriteCounter{
    ResponseWriter: w,
    start: time.Now(),
    end: time.Time{},
    status: -1,
    count: -1,
    }
    }

    func (wc *WriteCounter) WriteHeader(code int) {
    wc.ResponseWriter.WriteHeader(code)
    wc.end = time.Now()
    wc.status = code
    }

    func (wc *WriteCounter) Write(data []byte) (n int, err error) {
    n, err = wc.ResponseWriter.Write(data)
    wc.end = time.Now()
    wc.count += n
    return
    }

    Later on somebody pointed out some other interesting features of the http
    library, such as the CloseNotifier interface, and I realized the above code
    might not be such a good idea. Since I'm embedding an *interface*, I can't
    automatically inherit *http.response's implementations of CloseNotifier and
    Flusher.

    Are there alternative methods that people would recommend for recording
    the per-response data I'm looking for? One that doesn't require me to try
    extending http.ResponseWriter? I found myself writing code like the
    following:

    func (wc *WriteCounter) Flush() {
    if f, ok := wc.ResponseWriter.(http.Flusher); ok {
    f.Flush()
    }
    }

    func (wc *WriteCounter) CloseNotify() <-chan bool {
    if cn, ok := wc.ResponseWriter.(http.CloseNotifier); ok {
    return cn.CloseNotify()
    }
    return nil
    }

    which strikes me as an ugly hack and an indication that I'm probably
    Doing It Wrong.

    Jim

    --
    You received this message because you are subscribed to the Google Groups
    "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an
    email to golang-nuts...@googlegroups.com <javascript:>.
    For more options, visit https://groups.google.com/groups/opt_out.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • James A. Robinson at Jul 9, 2013 at 12:59 pm

    On Tue, Jul 9, 2013 at 5:26 AM, Martin Angers wrote:
    I did use the embedded ResponseWriter approach quite a bit, and
    never implemented those other Flusher and CloseNotifier
    interfaces. I see there's also the Hijacker interface that could be
    of interest, for completeness' sake
    It's unfortunate that what the code I posted *implies* it handles
    flushing and close notification, but really it can't guarantee it, and
    it could silently fail for anyone who extends my code (but passes it
    something other than the stock http.ResponseWriter implementation).
    My code example claims it implements the two methods, but is relying
    on the underlying ResponseWriter implementation. I'll document the
    problem with my methods of course, but I could see this being a gotcha
    for the next developer.


    Jim

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Martin Angers at Jul 9, 2013 at 1:12 pm
    Well you *could* do it in the NewCustomWriter(w), if it returns an
    interface (http.ResponseWriter). It could check what the base `w`
    ResponseWriter implements, and create a wrapper ResponseWriter that
    provides those implementations, or create a different wrapper that fulfills
    only the ResponseWriter interface if `w` doesn't implement anything else.

    That's quite a lot of work for all combinations, though! And from a quick
    peek in the source code, it seems you should also implement io.ReaderFrom
    (the `*response` struct implements it), so that's 4 additional interfaces.

    Le mardi 9 juillet 2013 08:59:04 UTC-4, Jim Robinson a écrit :
    On Tue, Jul 9, 2013 at 5:26 AM, Martin Angers wrote:
    I did use the embedded ResponseWriter approach quite a bit, and
    never implemented those other Flusher and CloseNotifier
    interfaces. I see there's also the Hijacker interface that could be
    of interest, for completeness' sake
    It's unfortunate that what the code I posted *implies* it handles
    flushing and close notification, but really it can't guarantee it, and
    it could silently fail for anyone who extends my code (but passes it
    something other than the stock http.ResponseWriter implementation).
    My code example claims it implements the two methods, but is relying
    on the underlying ResponseWriter implementation. I'll document the
    problem with my methods of course, but I could see this being a gotcha
    for the next developer.


    Jim
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Daniel Morsing at Jul 9, 2013 at 1:38 pm

    On Tue, Jul 9, 2013 at 2:06 AM, Jim Robinson wrote:
    Are there alternative methods that people would recommend for recording the
    per-response data I'm looking for? One that doesn't require me to try
    extending http.ResponseWriter? I found myself writing code like the
    following:
    This is what I came up with. http://play.golang.org/p/qBPmPtnDVD

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Martin Angers at Jul 9, 2013 at 3:00 pm
    Yes, that's along the lines of what I was suggesting, although this is an
    all-or-nothing implementation (you either implement ResponseWriter only, or
    ResponseWriter, Flusher and CloseNotifier). As I mentioned, getting all
    possible combinations (and adding Hijacker and ReaderFrom to the mix) is a
    mouthful!

    That's a shame because I really prefer extending the ResponseWriter to add
    functionality instead of using global / external state with a mutex. And it
    felt good doing it until I read this thread :)

    Le mardi 9 juillet 2013 09:38:50 UTC-4, Daniel Morsing a écrit :
    On Tue, Jul 9, 2013 at 2:06 AM, Jim Robinson wrote:
    Are there alternative methods that people would recommend for recording the
    per-response data I'm looking for? One that doesn't require me to try
    extending http.ResponseWriter? I found myself writing code like the
    following:
    This is what I came up with. http://play.golang.org/p/qBPmPtnDVD
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedJul 9, '13 at 12:06a
activeJul 9, '13 at 3:00p
posts7
users4
websitegolang.org

People

Translate

site design / logo © 2021 Grokbase