From 07c93aafb7aaa23988c40bcce493d8ef17ef21b7 Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Sun, 17 Jan 2016 10:48:37 +0000 Subject: [PATCH] use strings for message timestamps --- api.go | 12 +++--------- api_test.go | 2 +- server/db.go | 10 +++++++--- server/server.go | 29 ++++++++++++++++++++++------- unique_id.go | 12 ++++++------ 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/api.go b/api.go index 21bdf45..e782c30 100644 --- a/api.go +++ b/api.go @@ -11,7 +11,7 @@ import ( type Message map[string]interface{} var mandatoryAttributes = []string{ - "user", "message", "stamp", + "user", "message", "timestamp", } func (m Message) IsValid() bool { @@ -30,15 +30,9 @@ func (m Message) GetString(key string) string { return "" } -func (m Message) GetInt(key string) int64 { - if value, ok := m[key]; ok { - return int64(value.(float64)) - } - return 0 -} - func (m Message) Stamp() time.Time { - return time.Unix(m.GetInt("stamp"), 0) + t, _ := time.Parse(m.GetString("timestamp"), time.RFC3339) + return t } func (m Message) ToJSON() []byte { diff --git a/api_test.go b/api_test.go index b9d379f..63c4463 100644 --- a/api_test.go +++ b/api_test.go @@ -6,7 +6,7 @@ import ( var okTestData = ` { - "stamp": 1394393566, + "timestamp": "2009-11-10T23:00:00Z", "user": "test", "message": "sample message" } diff --git a/server/db.go b/server/db.go index 4601799..ad80a93 100644 --- a/server/db.go +++ b/server/db.go @@ -31,6 +31,9 @@ import ( // and store the message id under index/<key>/<value>/<id> (yes, the // id appears twice, it's part of the key just to ensure uniqueness). // +// Keys and values can be anything as long as they don't contain a +// null byte (which is used internally as the primary key separator). +// type DB struct { leveldb *levigo.DB cache *levigo.Cache @@ -80,7 +83,8 @@ func NewDB(path string, dbopts *DBOptions) *DB { // Exclude some keys by default. db.excludedKeys["message"] = struct{}{} - db.excludedKeys["stamp"] = struct{}{} + db.excludedKeys["stamp"] = struct{}{} // old field for timestamp + db.excludedKeys["timestamp"] = struct{}{} for _, key := range dbopts.ExcludedKeys { db.excludedKeys[key] = struct{}{} } @@ -99,7 +103,7 @@ func (db *DB) isExcluded(key string) bool { return ok } -var keySeparator = byte('|') +var keySeparator = byte(0) func makeKey(prefix string, parts ...[]byte) []byte { allParts := make([][]byte, 0, len(parts)+1) @@ -305,7 +309,7 @@ func (db *DB) Query(q map[string]string) ([]audit.Message, error) { } } if len(errors) > 0 { - return nil, fmt.Errorf("Query errors: %s", strings.Join(errors, "; ")) + return nil, fmt.Errorf("query errors: %s", strings.Join(errors, "; ")) } // Retrieve rows from the key set. diff --git a/server/server.go b/server/server.go index 057c19d..a607838 100644 --- a/server/server.go +++ b/server/server.go @@ -19,6 +19,17 @@ func NewHttpServer(db *DB) *HttpServer { return &HttpServer{database: db} } +func remoteHost(r *http.Request) string { + if host, _, err := net.SplitHostPort(r.RemoteAddr); err != nil { + return host + } + return r.RemoteAddr +} + +func dumpMsg(msg audit.Message) string { + return strings.TrimRight(string(msg.ToJSON()), "\n") +} + // Write handler: the input data is already a JSON message. func (h *HttpServer) writeHandler(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Content-Type") != "application/json" { @@ -32,18 +43,22 @@ func (h *HttpServer) writeHandler(w http.ResponseWriter, r *http.Request) { return } + // Log all received messages, even if there is an error later. + log.Printf("%s: received message: %s", remoteHost(r), dumpMsg(msg)) + + if !msg.IsValid() { + log.Printf("%s: invalid message", remoteHost(r)) + http.Error(w, "Invalid Message", http.StatusBadRequest) + return + } + if err := h.database.Write(msg); err != nil { + log.Printf("%s: error writing to database: %v", remoteHost(r), err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - // Request was successful, log it and return empty 200 OK reply. - var hostStr string - if host, _, err := net.SplitHostPort(r.RemoteAddr); err != nil { - hostStr = host - } - logText := strings.TrimRight(string(msg.ToJSON()), "\n") - log.Printf("%s: received message: %s", hostStr, logText) + // Request was successful, return an empty 200 OK reply. w.WriteHeader(200) } diff --git a/unique_id.go b/unique_id.go index 686e17c..ebe3ce1 100644 --- a/unique_id.go +++ b/unique_id.go @@ -1,7 +1,6 @@ package audit import ( - "bytes" "encoding/binary" "math/rand" "time" @@ -17,12 +16,13 @@ func init() { // resulting IDs can be sorted lexicographically preserving the time // ordering. func NewUniqueId(t time.Time) []byte { - var b bytes.Buffer + var b [16]byte // The key consists of serialized time + sufficiently large - // random number. This should allow a high insertion rate. - binary.Write(&b, binary.BigEndian, t.UnixNano()) - binary.Write(&b, binary.BigEndian, rand.Int63()) + // random number. This should allow a high insertion rate, and + // hopefully prevent conflicts considering the rand global lock. + binary.BigEndian.PutUint64(b[:8], uint64(t.UnixNano())) + binary.BigEndian.PutUint64(b[8:16], uint64(rand.Int63())) - return b.Bytes() + return b[:] } -- GitLab