Skip to content

Commit

Permalink
Log events: allow custom timestamps
Browse files Browse the repository at this point in the history
  • Loading branch information
metachris committed Nov 21, 2024
1 parent 13c229a commit e5aec3a
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 4 deletions.
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,31 @@ $ curl localhost:3535/logs
2024-10-23T12:04:07Z this is a test
```

## Actions
#### Event timestamps

Actions are shell commands that can be executed via API. The commands are defined in the config file,
see [systemapi-config.toml](./systemapi-config.toml) for examples.
By default, events are timestamped with the current time. However, the timestamp can be overridden by
providing a valid unix timestamp (in seconds or milliseconds) as first part of the message:

```bash
# Start the server
$ go run cmd/system-api/main.go

# Add regular event
$ echo "hello world" > pipe.fifo

# Add event with custom timestamp
$ echo "1634966400 this is a test" > pipe.fifo

# Query events
$ curl localhost:3535/logs
2024-11-21T19:48:04Z hello world
2021-10-23T05:20:00Z this is a test <--- custom timestamp on this entry
```

## Actions

Actions are shell commands that can be executed via API. The commands are defined in the config file,
see [systemapi-config.toml](./systemapi-config.toml) for examples.

Actions are recorded in the event log.

Expand Down
19 changes: 19 additions & 0 deletions systemapi/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"io"
"net/http"
"os"
"strconv"
"strings"
"sync"
"syscall"
Expand Down Expand Up @@ -205,6 +206,24 @@ func (s *Server) handleLivenessCheck(w http.ResponseWriter, r *http.Request) {
func (s *Server) addEvent(event Event) {
// Add event to the list and prune if necessary
s.eventsLock.Lock()

// check for replacement timestamp. if the first part of the message is a timestamp, use it
// instead of the current time. must be a number to be a timestamp (10 characters for seconds, 13 for milliseconds)
eventMsg := event.Message
timestampStr := strings.Fields(eventMsg)[0]
timeInt, err := strconv.ParseInt(timestampStr, 10, 64)
if err == nil {
if len(timestampStr) == 10 {
// timestamp in seconds, update event
event.ReceivedAt = time.Unix(timeInt, 0).UTC()
event.Message = strings.TrimSpace(eventMsg[len(timestampStr):])
} else if len(timestampStr) == 13 {
// timestamp in milliseconds, update event
event.ReceivedAt = time.UnixMilli(timeInt).UTC()
event.Message = strings.TrimSpace(eventMsg[len(timestampStr):])
}
}

s.events = append(s.events, event)
if len(s.events) > s.cfg.General.LogMaxEntries {
s.events = s.events[1:]
Expand Down
31 changes: 30 additions & 1 deletion systemapi/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func TestBasicAuth(t *testing.T) {
}

func TestMaxEntries(t *testing.T) {
// Verify maximum number of log entries is working correctly
// Ensure maximum number of log entries is working correctly
maxEntries := 5

cfg := NewConfig()
Expand All @@ -177,3 +177,32 @@ func TestMaxEntries(t *testing.T) {
require.Equal(t, "4", srv.events[3].Message)
require.Equal(t, "5", srv.events[4].Message)
}

func TestAddEntryTimestampParsing(t *testing.T) {
// Ensure that messages with timestamps are correctly parsed
srv := newTestServer(t)

testTime1 := time.Date(2021, 1, 2, 3, 4, 5, 0, time.UTC)
testTime2 := time.Date(2022, 1, 2, 3, 4, 6, 0, time.UTC)
testTime2TimestampSec := testTime2.Unix()
testTime2TimestampMs := testTime2.UnixMilli()

// Add regular message
srv.addEvent(Event{ReceivedAt: testTime1, Message: "1"})

// Add message with timestamp prefix
srv.addEvent(Event{ReceivedAt: testTime1, Message: fmt.Sprintf("%d 2", testTime2TimestampSec)})
srv.addEvent(Event{ReceivedAt: testTime1, Message: fmt.Sprintf("%d 3", testTime2TimestampMs)})

// Check entry 1 (regular message)
require.Equal(t, "1", srv.events[0].Message)
require.Equal(t, testTime1, srv.events[0].ReceivedAt)

// Check entry 2 (timestamp in seconds)
require.Equal(t, "2", srv.events[1].Message)
require.Equal(t, testTime2, srv.events[1].ReceivedAt)

// Check entry 3 (timestamp in milliseconds)
require.Equal(t, "3", srv.events[2].Message)
require.Equal(t, testTime2, srv.events[2].ReceivedAt)
}

0 comments on commit e5aec3a

Please sign in to comment.