Skip to content

Collecting Amazon ECS Logs with Guance


Introduction

Amazon Elastic Container Service (Amazon ECS) is a highly scalable, fast container management service that allows you to easily run, stop, and manage containers on a cluster. These containers can run on your own EC2 servers or on serverless infrastructure managed by AWS Fargate.
For tasks using the Fargate launch type, you need to start the container's awslogs log driver. Logs output by applications running in the container via STDOUT and STDERR I/O streams are sent to log groups in CloudWatch Logs. These logs are then collected by Func, which writes them into Guance through the DataKit deployed on EC2.

This document describes log collection for containers managed by AWS Fargate.

image

Prerequisites

The ECS cluster name used here is cluster-docker. Check the sample logs and log groups below. Log in to AWS, go to Elastic Container Service, click Clusters, and then select cluster-docker.

image

Click Service Name.

image

Enter the task.

image

Under the Details tab, find Log configuration below the container.

image

Click the Log tab to view the application logs. These logs will be collected next.

image

Procedure

Warning

The example uses DataKit version 1.4.18.

Step 1: AWS Configuration

1.1 User Credentials

Use the AWS account that deployed ECS. The Access key ID and Secret access key provided when creating the user will be used later.

1.2 Set AWS User Permissions

Log in to the AWS IAM console. Under Users, find the user associated with ECS, then click Add permissions.

image

Click Attach existing policies directly. In the Filter policies field, select CloudWatchLogsReadOnlyAccess and CloudWatchEventsReadOnlyAccess, then click Next: Review.

image

Step 2: Func Configuration

2.1 Configure Environment Variables

Log in to Func, go to Development > Environment Variables > Add Environment Variable. Add three environment variables:

  • AWS_LOG_KEY – value is the Access key ID of the AWS user from Step 1.1.
  • AWS_LOG_SECRET_ACCESS_KEY – value is the Secret access key of the AWS user from Step 1.1.
  • AWS_REGION_NAME – value is the REGION of the AWS user.

image

2.2 Configure Connector

Log in to Func, go to Development > Connector > Add Connector.
Here the ID must be datakit. The host is the address of the machine where DataKit is installed, and the port is the DataKit port. (In this example, the IP address is used directly, so the protocol is HTTP.)
Click Test Connectivity. If a ✅ is returned, DataKit is available.

image

2.3 Configure PIP Tool

Log in to Func, go to Management > Experimental Features, and enable the PIP Tool Module on the right.

image

Click PIP Tool on the left. Select Alibaba Cloud Mirror, enter boto3, and click Install.

image

2.4 Script Library

Log in to Func, go to Development > Script Library > Add Script Set. The ID can be customized. Click Save.

image

Find AWS Log Collection and click Add Script.

image

Enter an ID. In this example, it is set to aws_ecs__log. Click Save.

image

Click Edit.

image

Enter the following content:

Content to Enter
    import boto3
    import json
    import time
    scope_id='ecs_log'

    @DFF.API('aws_ecs log', timeout=500, api_timeout=180)
    def run(measurement, logGroupName, interval):
        print(measurement, logGroupName, interval)
        get_log_data(measurement, logGroupName, interval)
        # if data is not None:
        #     push_log(data)
        # else:
        #     print("None")


    def get_cron_time(interval, measurement):
        cache = DFF.CACHE.get('last_time_%s' %measurement,scope=scope_id)
        if cache == None:
            currentTime = int(round(time.time() * 1000))
            startTime = currentTime - int(interval) * 1000
            endTime = currentTime
        else:
            currentTime = int(round(time.time() * 1000))
            if currentTime - int(cache) > 10 * 60 * 1000:
                startTime = currentTime - int(interval) * 1000
                endTime = currentTime
            else:
                startTime = int(cache) + 1
                endTime = currentTime
        print(startTime, endTime)
        return  startTime, endTime

    def get_log_data(measurement, logGroupName, interval):
        logTime = get_cron_time(interval, measurement)
        startTime = logTime[0]
        endTime = logTime[1]
        isPush = False
        client = boto3.client(
            'logs',
            aws_access_key_id=DFF.ENV('AWS_LOG_KEY'),
            aws_secret_access_key=DFF.ENV('AWS_LOG_SECRET_ACCESS_KEY'),
            region_name=DFF.ENV('AWS_REGION_NAME')
        )# print(client.meta.config)
        try:
            nextToken = 'frist'
            logData = []
            while nextToken != '':
                if nextToken == 'frist':
                    nextToken = ''
                    response = client.filter_log_events(
                        logGroupName=logGroupName,
                        startTime=startTime,
                        endTime=endTime,
                        limit=1000,
                        #filterPattern="?ERROR ?WARN ?error ?warn",
                        interleaved=False
                    )
                else:
                    response = client.filter_log_events(
                        logGroupName=logGroupName,
                        startTime=startTime,
                        endTime=endTime,
                        nextToken=nextToken,
                        limit=1000,
                        #filterPattern="?ERROR ?WARN ?error ?warn",
                        interleaved=False
                    )
                try:
                    if len(response['events']) > 0:
                        data = []
                        lastTimeList = []
                        for i in response['events']:
                            # print("hii", i['logStreamName'])
                            log = {
                                'measurement': measurement,
                                'tags': {
                                    'logGroupName': logGroupName,
                                    'logStreamName': i['logStreamName'],
                                    'host': '127.0.0.1'
                                },
                                'fields': {
                                    'message': i['message'],
                                    'time': i['timestamp']
                                }
                            }
                            data.append(log)
                            lastTimeList.append(i['timestamp'])
                        push_log(data)
                        print("max %s"  % max(lastTimeList))
                        DFF.CACHE.set('last_time_%s' % measurement, max(lastTimeList), scope=scope_id, expire=None)
                        isPush = True
                    else:
                        DFF.CACHE.set('last_time_%s' % measurement, endTime , scope=scope_id, expire=None)
                    nextToken = response['nextToken']
                except:
                    nextToken = ''
        except Exception as  e:
            print('Error: %s' % e )
            return None
        if not isPush:
            DFF.CACHE.set('last_time_%s' % measurement, endTime , scope=scope_id, expire=None)

    def push_log(data):
        datakit = DFF.SRC('datakit')
        status_code, result = datakit.write_logging_many(data=data)
        if status_code == 200:
            print("total %d"  % len(data))
            print(status_code, result)
Warning
  • The ecs_log on line 4 of the above content must be unique within the same Func instance. It can be changed to another string.
  • The awc_ecs on line 6 is the script set ID just added.
  • The AWS_LOG_KEY, AWS_LOG_SECRET_ACCESS_KEY, and AWS_REGION_NAME on lines 40, 41, and 42 correspond to the environment variable names from Step 2.1. If the environment variable names are changed, they must be updated accordingly.

2.5 Test the Script

Select run as shown in the image. In the content of the second red box:

  • measurement: enter ecs_log_source. This value corresponds to the log source in Guance.
  • logGroupName: enter the value of awslogs-group found in the log configuration from the Prerequisites.
  • interval: set the collection frequency. In this example, it is 60 seconds.

image

Click Execute. The output shows total 8, meaning 8 logs were reported.

image

Log in to Guance, go to the Logs module, and select the data source ecs_log_source. You can see the logs.

image

Click Publish in the top-right corner.

image

Click End Editing in the top-right corner.

image

2.6 Automate Log Collection

Log in to Func, go to Management > Auto Trigger Configuration > Create. Enter the parameters from the previous execution.

{
  "measurement": "ecs_log_source",
  "logGroupName": "/ecs/demo-task",
  "interval": 60
}

image

Set the frequency to every minute or every 5 minutes, then click Save.

image

The Auto Trigger Configuration list now contains a record for aws_ecs log.

image

Click Recent Executions to view the execution status.

image

Feedback

Is this page helpful?