You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.3 KiB
Go
85 lines
2.3 KiB
Go
10 years ago
|
package main
|
||
|
|
||
|
import (
|
||
|
"crypto/md5"
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"sync/atomic"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// 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.
|
||
|
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])
|
||
|
}
|
||
|
|
||
|
// requestIdCounter is atomically incremented when generating a new ObjectId
|
||
|
// using NewObjectId() function. It's used as a counter part of an id.
|
||
|
var requestIdCounter uint32 = 0
|
||
|
|
||
|
// machineId stores machine id generated once and used in subsequent calls
|
||
|
// to NewObjectId function.
|
||
|
var machineId []byte
|
||
|
|
||
|
// initMachineId generates machine id and puts it into the machineId global
|
||
|
// variable. If this function fails to get the hostname, it will cause
|
||
|
// a runtime error.
|
||
|
func initMachineId() {
|
||
|
var sum [3]byte
|
||
|
hostname, err := os.Hostname()
|
||
|
if err != nil {
|
||
|
panic("Failed to get hostname: " + err.Error())
|
||
|
}
|
||
|
hw := md5.New()
|
||
|
hw.Write([]byte(hostname))
|
||
|
copy(sum[:3], hw.Sum(nil))
|
||
|
machineId = sum[:]
|
||
|
}
|
||
|
|
||
|
// 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()))
|
||
|
// Machine, first 3 bytes of md5(hostname)
|
||
|
if machineId == nil {
|
||
|
initMachineId()
|
||
|
}
|
||
|
b[4] = machineId[0]
|
||
|
b[5] = machineId[1]
|
||
|
b[6] = machineId[2]
|
||
|
// Pid, 2 bytes, specs don't specify endianness, but we use big endian.
|
||
|
pid := os.Getpid()
|
||
|
b[7] = byte(pid >> 8)
|
||
|
b[8] = byte(pid)
|
||
|
// Increment, 3 bytes, big endian
|
||
|
i := atomic.AddUint32(&requestIdCounter, 1)
|
||
|
b[9] = byte(i >> 16)
|
||
|
b[10] = byte(i >> 8)
|
||
|
b[11] = byte(i)
|
||
|
return RequestId(b)
|
||
|
}
|