Python / Boto - Write to AWS CloudWatch logs without sequence marker
I am trying to send logs to AWS CloudWatch logs using Python and Boto. I'm doing it:
res=logs.put_log_events("FOO", "BAR",
[{'timestamp':int(round(time.time() * 1000)),
'message':time.strftime("%m/%d/%Y %H:%M:%S")+' Scheduled monitoring check' }],
sequence_token=None)
I get an error every time I run:
boto.logs.exceptions.InvalidSequenceTokenException: InvalidSequenceTokenException: 400 Bad Request
{u'message': u'The given sequenceToken is invalid. The next expected sequenceToken is: 49540113336360065754596906019042392283494234157161146226', u'expectedSequenceToken': u'49540113336360065754596906019042392283494234157161146226', u'__type': u'InvalidSequenceTokenException'}
It's not practical for me to store this token. It doesn't make any sense, why can't I just add to the log stream?
How can I get around this?
source to share
You can get around this by first looking at the uploadSequenceToken via description_log_streams () :
Basically, the process is that you use logStreamNamePrefix to specifically identify the log stream to which you want to add. Then parse the uploadSequenceToken from the response.
Response syntax
{ 'logStreams': [ { 'logStreamName': 'string', 'creationTime': 123, 'firstEventTimestamp': 123, 'lastEventTimestamp': 123, 'lastIngestionTime': 123, 'uploadSequenceToken': 'string', 'arn': 'string', 'storedBytes': 123 }, ], 'nextToken': 'string' }
Returns all log streams associated with the specified log group. The list returned in the response is sorted in ASCII by log stream name.
By default, this operation returns up to 50 log threads. If there are more log streams in the list, the response will contain the nextToken value in the response body. You can also limit the number of log streams returned in the response by specifying the limit parameter in the request. This operation has a limit of five transactions per second, after which transactions are blocked.
Query syntax
response = client.describe_log_streams( logGroupName='string', logStreamNamePrefix='string', orderBy='LogStreamName'|'LastEventTime', descending=True|False, nextToken='string', limit=123 )
source to share
To answer the question why the guessing part: this is the nature of a scalable asynchronous service.
If Amazon doesn't ask you for a sequence number, they won't be able to scale their CloudWatch service in many cases, while still being able to ensure that your logs appear in the exact same order as they happened (and imagine how annoying log entries out of order when debugging problems). Any slight drift in clock, network latency, or other latency on the way to logical receivers will cause sequencing issues.
But since they ask you for the sequence number, they can easily and easily scale their merge-sort service of writing incoming records back while maintaining the correct logging order, your logging order.
source to share
AWS Cloud Watch Putlogevent Code
import boto3
import time
client = boto3.client('logs')
LOG_GROUP='cloudwatch_customlog'
LOG_STREAM='{}-{}'.format(time.strftime('%Y-%m-%d'),'logstream')
try:
client.create_log_group(logGroupName=LOG_GROUP)
except client.exceptions.ResourceAlreadyExistsException:
pass
try:
client.create_log_stream(logGroupName=LOG_GROUP, logStreamName=LOG_STREAM)
except client.exceptions.ResourceAlreadyExistsException:
pass
response = client.describe_log_streams(
logGroupName=LOG_GROUP,
logStreamNamePrefix=LOG_STREAM
)
event_log = {
'logGroupName': LOG_GROUP,
'logStreamName': LOG_STREAM,
'logEvents': [
{
'timestamp': int(round(time.time() * 1000)),
'message': time.strftime('%Y-%m-%d %H:%M:%S')+'\t Your custom log messages'
}
],
}
if 'uploadSequenceToken' in response['logStreams'][0]:
event_log.update({'sequenceToken': response['logStreams'][0] ['uploadSequenceToken']})
response = client.put_log_events(**event_log)
print(response)
source to share