request ids
parent
b920892155
commit
59ed0fc7da
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var global struct {
|
||||||
|
hostname string
|
||||||
|
machineId []byte
|
||||||
|
pid int
|
||||||
|
requestCounter uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
global.pid = os.Getpid()
|
||||||
|
var err error
|
||||||
|
global.hostname, err = os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
panic("failed to get hostname: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
hw := md5.New()
|
||||||
|
if _, err := hw.Write([]byte(global.hostname)); err != nil {
|
||||||
|
panic("unable to md5 hostname: " + err.Error())
|
||||||
|
}
|
||||||
|
global.machineId = make([]byte, 3)
|
||||||
|
copy(global.machineId, hw.Sum(nil))
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewObjectId returns a new unique ObjectId.
|
||||||
|
// This function causes a runtime error if it fails to get the hostname
|
||||||
|
// of the current machine.
|
||||||
|
func newRequestId() RequestId {
|
||||||
|
b := make([]byte, 12)
|
||||||
|
// Timestamp, 4 bytes, big endian
|
||||||
|
binary.BigEndian.PutUint32(b, uint32(time.Now().Unix()))
|
||||||
|
b[4] = global.machineId[0]
|
||||||
|
b[5] = global.machineId[1]
|
||||||
|
b[6] = global.machineId[2]
|
||||||
|
// Pid, 2 bytes, specs don't specify endianness, but we use big endian.
|
||||||
|
b[7] = byte(global.pid >> 8)
|
||||||
|
b[8] = byte(global.pid)
|
||||||
|
// Increment, 3 bytes, big endian
|
||||||
|
i := atomic.AddUint32(&global.requestCounter, 1)
|
||||||
|
b[9] = byte(i >> 16)
|
||||||
|
b[10] = byte(i >> 8)
|
||||||
|
b[11] = byte(i)
|
||||||
|
return RequestId(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestId is used for tagging each incoming http request for logging
|
||||||
|
// purposes. The actual implementation is just the ObjectId implementation
|
||||||
|
// found in launchpad.net/mgo/bson. This will most likely change and evolve
|
||||||
|
// into its own format.
|
||||||
|
type RequestId string
|
||||||
|
|
||||||
|
func (id RequestId) String() string {
|
||||||
|
return fmt.Sprintf("%x", string(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time returns the timestamp part of the id.
|
||||||
|
// It's a runtime error to call this method with an invalid id.
|
||||||
|
func (id RequestId) Time() time.Time {
|
||||||
|
secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4)))
|
||||||
|
return time.Unix(secs, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// byteSlice returns byte slice of id from start to end.
|
||||||
|
// Calling this function with an invalid id will cause a runtime panic.
|
||||||
|
func (id RequestId) byteSlice(start, end int) []byte {
|
||||||
|
if len(id) != 12 {
|
||||||
|
panic(fmt.Sprintf("Invalid RequestId: %q", string(id)))
|
||||||
|
}
|
||||||
|
return []byte(string(id)[start:end])
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type logWrapper struct {
|
||||||
|
http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := newRequestId()
|
||||||
|
log.Println(id, r.Method, r.URL)
|
||||||
|
statsThing := &writerWatcher{ResponseWriter: w}
|
||||||
|
start := time.Now()
|
||||||
|
l.Handler.ServeHTTP(statsThing, r)
|
||||||
|
log.Println(id, statsThing.headerWritten, statsThing.written, time.Since(start))
|
||||||
|
}
|
||||||
|
|
||||||
|
type writerWatcher struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
written int
|
||||||
|
headerWritten int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *writerWatcher) Write(b []byte) (int, error) {
|
||||||
|
n, err := w.ResponseWriter.Write(b)
|
||||||
|
w.written += n
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *writerWatcher) WriteHeader(status int) {
|
||||||
|
if w.headerWritten != 0 {
|
||||||
|
log.Println("somehow we wrote two headers.")
|
||||||
|
}
|
||||||
|
w.headerWritten = status
|
||||||
|
w.ResponseWriter.WriteHeader(status)
|
||||||
|
}
|
Loading…
Reference in New Issue