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?

+7


source to share


4 answers


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
)

      

+6


source


You can't how it works:

Each PutLogEvents request MUST include the Token sequence derived from the response from the previous request. Loading into a newly created log stream does not require a Token sequence.



( source )

+4


source


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.

+4


source


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)

      

0


source







All Articles