https://github.com/xyproto/sheepcounter
:sheep: ResponseWriter that can count bytes written to the client
https://github.com/xyproto/sheepcounter
byte-counting go http-server logging middleware
Last synced: 8 months ago
JSON representation
:sheep: ResponseWriter that can count bytes written to the client
- Host: GitHub
- URL: https://github.com/xyproto/sheepcounter
- Owner: xyproto
- License: bsd-3-clause
- Created: 2018-10-05T08:13:18.000Z (over 7 years ago)
- Default Branch: main
- Last Pushed: 2025-01-09T14:44:45.000Z (over 1 year ago)
- Last Synced: 2025-08-13T21:21:23.881Z (11 months ago)
- Topics: byte-counting, go, http-server, logging, middleware
- Language: Go
- Homepage:
- Size: 23.4 KB
- Stars: 7
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# SheepCounter [](http://godoc.org/github.com/xyproto/sheepcounter) [](http://goreportcard.com/report/xyproto/sheepcounter)
A `http.ResponseWriter` that can count the bytes written to the client so far.
# Why?
If you want to create an access log of how many bytes are sent to which clients, one method would be to write data to a buffer, count the bytes and then send the data to the client. This may be problematic for large files, since it eats up a lot of memory. It is also costly performance wise, since the data would then have to be counted while or after the data is sent to the client.
A better way is to store use the number returned by the `Write` function directly. This is not straightforward with `http.ResponseWriter` without wrapping it somehow, which is what this module does. A lightweight struct wraps both a `http.ResponseWriter` and an `uint64`, for keeping track of the written bytes.
# Examples
## Count the bytes sent, per response
~~~go
package main
import (
"fmt"
"github.com/xyproto/sheepcounter"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
sc := sheepcounter.New(w)
fmt.Fprintf(sc, "Hi %s!", r.URL.Path[1:])
fmt.Println("COUNTED:", sc.Counter()) // Counts the bytes sent, for this response only
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Serving on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
~~~
## Count the total amount of bytes sent
~~~go
package main
import (
"fmt"
"log"
"net/http"
"os"
"sync/atomic"
"github.com/xyproto/sheepcounter"
)
const (
title = "SheepCounter"
style = `body { margin: 4em; background: wheat; color: black; font-family: terminus, "courier new", courier; font-size: 1.1em; } a:link { color: #403020; } a:visited { color: #403020; } a:hover { color: #605040; } a:active { color: #605040; } #counter { color: red; }`
page = "%s%s%s"
)
var totalBytesWritten uint64
func helloHandler(w http.ResponseWriter, r *http.Request) {
sc := sheepcounter.New(w)
body := `
Here are the counted bytes.
`
fmt.Fprintf(sc, page, style, title, body)
written, err := sc.UCounter2()
if err != nil {
// Log an error and return
log.Printf("error: %s\n", err)
return
}
atomic.AddUint64(&totalBytesWritten, written)
log.Printf("counted %d bytes\n", written)
}
func counterHandler(w http.ResponseWriter, r *http.Request) {
sc := sheepcounter.New(w)
body := fmt.Sprintf(`
Total bytes sent from the server (without counting this response): %d
`, atomic.LoadUint64(&totalBytesWritten))
fmt.Fprintf(sc, page, style, title, body)
written, err := sc.UCounter2()
if err != nil {
// Log an error and return
log.Printf("error: %s\n", err)
return
}
atomic.AddUint64(&totalBytesWritten, written)
log.Printf("counted %d bytes\n", written)
}
func main() {
http.HandleFunc("/", helloHandler)
http.HandleFunc("/counter", counterHandler)
httpAddr := os.Getenv("HTTP_ADDR")
if httpAddr == "" {
httpAddr = ":4000"
}
log.Println("Serving on " + httpAddr)
log.Fatal(http.ListenAndServe(httpAddr, nil))
}
~~~
# Requirements
* Go 1.18 or later
# General information
* Version: 1.6.2
* Alexander F. Rødseth <xyproto@archlinux.org>
* License: BSD-3