Skip to content

Commit cbb069b

Browse files
author
Rupayan Ghosh
committed
emit multi-line logs with timestamps
1 parent 970e9c1 commit cbb069b

File tree

2 files changed

+26
-11
lines changed

2 files changed

+26
-11
lines changed

awslambdaric/bootstrap.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -312,19 +312,19 @@ class FramedTelemetryLogSink(object):
312312
framing protocol so message boundaries can be determined. Each frame can be visualized as follows:
313313
<pre>
314314
{@code
315-
+----------------------+------------------------+-----------------------+
316-
| Frame Type - 4 bytes | Length (len) - 4 bytes | Message - 'len' bytes |
317-
+----------------------+------------------------+-----------------------+
315+
+----------------------+------------------------+---------------------+-----------------------+
316+
| Frame Type - 4 bytes | Length (len) - 4 bytes | Timestamp - 8 bytes | Message - 'len' bytes |
317+
+----------------------+------------------------+---------------------+-----------------------+
318318
}
319319
</pre>
320-
The first 4 bytes indicate the type of the frame - log frames have a type defined as the hex value 0xa55a0001. The
321-
second 4 bytes should indicate the message's length. The next 'len' bytes contain the message. The byte order is
322-
big-endian.
320+
The first 4 bytes indicate the type of the frame - log frames have a type defined as the hex value 0xa55a0003. The
321+
second 4 bytes should indicate the message's length. The next 8 bytes should indicate the timestamp of the message.
322+
The next 'len' bytes contain the message. The byte order is big-endian.
323323
"""
324324

325325
def __init__(self, fd):
326326
self.fd = int(fd)
327-
self.frame_type = 0xA55A0001.to_bytes(4, "big")
327+
self.frame_type = 0xA55A0003.to_bytes(4, "big")
328328

329329
def __enter__(self):
330330
self.file = os.fdopen(self.fd, "wb", 0)
@@ -335,7 +335,8 @@ def __exit__(self, exc_type, exc_value, exc_tb):
335335

336336
def log(self, msg):
337337
encoded_msg = msg.encode("utf8")
338-
log_msg = self.frame_type + len(encoded_msg).to_bytes(4, "big") + encoded_msg
338+
timestamp = int(time.time_ns() / 1000) # UNIX timestamp in microseconds
339+
log_msg = self.frame_type + len(encoded_msg).to_bytes(4, "big") + timestamp.to_bytes(8, "big") + encoded_msg
339340
self.file.write(log_msg)
340341

341342
def log_error(self, message_lines):

tests/test_bootstrap.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import re
99
import tempfile
10+
import time
1011
import traceback
1112
import unittest
1213
from io import StringIO
@@ -1090,45 +1091,58 @@ def test_create_framed_telemetry_log_sinks(self):
10901091
def test_single_frame(self):
10911092
with NamedTemporaryFile() as temp_file:
10921093
message = "hello world\nsomething on a new line!\n"
1094+
before = int(time.time_ns() / 1000)
10931095
with bootstrap.FramedTelemetryLogSink(
10941096
os.open(temp_file.name, os.O_CREAT | os.O_RDWR)
10951097
) as ls:
10961098
ls.log(message)
1099+
after = int(time.time_ns() / 1000)
10971100
with open(temp_file.name, "rb") as f:
10981101
content = f.read()
10991102

11001103
frame_type = int.from_bytes(content[:4], "big")
1101-
self.assertEqual(frame_type, 0xA55A0001)
1104+
self.assertEqual(frame_type, 0xA55A0003)
11021105

11031106
length = int.from_bytes(content[4:8], "big")
11041107
self.assertEqual(length, len(message))
11051108

1106-
actual_message = content[8:].decode()
1109+
timestamp = int.from_bytes(content[8:16], "big")
1110+
self.assertTrue(before <= timestamp)
1111+
self.assertTrue(timestamp <= after)
1112+
1113+
actual_message = content[16:].decode()
11071114
self.assertEqual(actual_message, message)
11081115

11091116
def test_multiple_frame(self):
11101117
with NamedTemporaryFile() as temp_file:
11111118
first_message = "hello world\nsomething on a new line!"
11121119
second_message = "hello again\nhere's another message\n"
11131120

1121+
before = int(time.time_ns() / 1000)
11141122
with bootstrap.FramedTelemetryLogSink(
11151123
os.open(temp_file.name, os.O_CREAT | os.O_RDWR)
11161124
) as ls:
11171125
ls.log(first_message)
11181126
ls.log(second_message)
1127+
after = int(time.time_ns() / 1000)
11191128

11201129
with open(temp_file.name, "rb") as f:
11211130
content = f.read()
11221131
pos = 0
11231132
for message in [first_message, second_message]:
11241133
frame_type = int.from_bytes(content[pos : pos + 4], "big")
1125-
self.assertEqual(frame_type, 0xA55A0001)
1134+
self.assertEqual(frame_type, 0xA55A0003)
11261135
pos += 4
11271136

11281137
length = int.from_bytes(content[pos : pos + 4], "big")
11291138
self.assertEqual(length, len(message))
11301139
pos += 4
11311140

1141+
timestamp = int.from_bytes(content[pos : pos + 8], "big")
1142+
self.assertTrue(before <= timestamp)
1143+
self.assertTrue(timestamp <= after)
1144+
pos += 8
1145+
11321146
actual_message = content[pos : pos + len(message)].decode()
11331147
self.assertEqual(actual_message, message)
11341148
pos += len(message)

0 commit comments

Comments
 (0)