我的AWS Cloudwatch账单非常庞大.我如何确定导致它的日志流?

Sam*_*ley 14 amazon-web-services amazon-cloudwatch amazon-cloudwatchlogs

我上个月从亚马逊获得了一张1200美元的Cloudwatch服务发票(特别是在"AmazonCloudWatch PutLogEvents"中提取了2 TB的日志数据),当时我预计会花几十美元.我已登录AWS控制台的Cloudwatch部分,可以看到我的一个日志组使用了大约2TB的数据,但该日志组中有数千个不同的日志流,如何判断哪个使用了该数量数据的?

Dee*_*aya 33

在CloudWatch控制台上,使用IncomingBytes指标使用Metrics页面查找特定时间段内每个日志组在未压缩字节中摄取的数据量.按照以下步骤 -

  1. 转到CloudWatch指标页面,然后单击AWS命名空间"日志" - >"日志组指标".
  2. 选择所需日志组的IncomingBytes指标,然后单击"Graphed metrics"选项卡以查看图表.
  3. 更改开始时间和结束时间,使其差异为30天,并将时间段更改为30天.这样,我们将只获得一个数据点.还将图形更改为Number,将统计信息更改为Sum.

这样,您将看到每个日志组摄取的数据量,并了解哪个日志组正在摄取多少日志组.

在此输入图像描述

您还可以使用AWS CLI获得相同的结果.您只想知道日志组摄取的数据总量(例如30天)的示例场景,您可以使用get-metric-statistics CLI命令 -

示例CLI命令 -

aws cloudwatch get-metric-statistics --metric-name IncomingBytes --start-time 2018-05-01T00:00:00Z --end-time 2018-05-30T23:59:59Z --period 2592000 --namespace AWS/Logs --statistics Sum --region us-east-1
Run Code Online (Sandbox Code Playgroud)

样本输出 -

{
    "Datapoints": [
        {
            "Timestamp": "2018-05-01T00:00:00Z", 
            "Sum": 1686361672.0, 
            "Unit": "Bytes"
        }
    ], 
    "Label": "IncomingBytes"
}
Run Code Online (Sandbox Code Playgroud)

要为特定日志组查找相同内容,可以更改命令以适应以下维度:

aws cloudwatch get-metric-statistics --metric-name IncomingBytes --start-time 2018-05-01T00:00:00Z --end-time 2018-05-30T23:59:59Z --period 2592000 --namespace AWS/Logs --statistics Sum --region us-east-1 --dimensions Name=LogGroupName,Value=test1
Run Code Online (Sandbox Code Playgroud)

您可以逐个在所有日志组上运行此命令,并检查哪个日志组负责提取数据的大部分帐单并采取纠正措施.

注意:更改特定于您的环境和要求的参数.

OP提供的解决方案提供了存储的日志数量的数据,这与记录的日志不同.

有什么不同?

每月摄取的数据与数据存储字节不同.将数据提取到CloudWatch后,它将由CloudWatch归档,每个日志事件包含26个字节的元数据,并使用gzip level 6压缩进行压缩.因此,存储字节是指Cloudwatch在摄取日志后用于存储日志的存储空间.

参考:https://docs.aws.amazon.com/cli/latest/reference/cloudwatch/get-metric-statistics.html


小智 12

您还可以单击 cloudwatch 日志仪表板上齿轮上的齿轮,然后选择存储的字节列。

我还单击了任何显示“永不过期”的内容,并将日志更改为过期。

使用cloudwatch日志装备并选择“Stored Bytes”列


小智 9

由于意外检入,我们有一个lambda记录GB的数据。这是一个基于boto3的python脚本,基于上述答案中的信息,该脚本可扫描所有日志组并在过去7天中打印出日志大于1GB的任何组。这比尝试使用更新缓慢的AWS仪表板对我有更多帮助。

#!/usr/bin/env python3

# Outputs all loggroups with > 1GB of incomingBytes in the past 7 days

import boto3
from datetime import datetime as dt
from datetime import timedelta


logs_client = boto3.client('logs')
cloudwatch_client = boto3.client('cloudwatch')

end_date = dt.today().isoformat(timespec='seconds')
start_date = (dt.today() - timedelta(days=7)).isoformat(timespec='seconds')
print("looking from %s to %s" % (start_date, end_date))

paginator = logs_client.get_paginator('describe_log_groups')
pages = paginator.paginate()
for page in pages:
     for json_data in page['logGroups']:
        log_group_name = json_data.get("logGroupName") 

        cw_response = cloudwatch_client.get_metric_statistics(
           Namespace='AWS/Logs',    
           MetricName='IncomingBytes',
           Dimensions=[
            {
                'Name': 'LogGroupName',
                'Value': log_group_name
            },
            ],
            StartTime= start_date,
            EndTime=end_date,
            Period=3600 * 24 * 7,
            Statistics=[
                'Sum'
            ],
            Unit='Bytes'
        )
        if len(cw_response.get("Datapoints")):
            stats_data = cw_response.get("Datapoints")[0]
            stats_sum = stats_data.get("Sum")   
            sum_GB = stats_sum /  (1000 * 1000 * 1000)
            if sum_GB > 1.0:
                print("%s = %.2f GB" % (log_group_name , sum_GB))
        
Run Code Online (Sandbox Code Playgroud)


Aja*_*ary 9

尽管问题的作者和其他人已经很好地回答了这个问题,但我将尝试提供一个通用的解决方案,可以在不知道导致过多日志的确切日志组名称的情况下应用解决方案。

为此,我们不能使用describe-log-streams函数,因为这需要--log-group-name并且正如我之前所说的,我不知道我的 log-group-name 的值。

我们可以使用describe-log-groups 函数,因为该函数不需要任何参数。

请注意,我假设您在~/.aws/config文件中配置了所需的标志 (--region),并且您的 EC2 实例具有执行此命令所需的权限。

aws logs describe-log-groups
Run Code Online (Sandbox Code Playgroud)

此命令将列出您的 aws 帐户中的所有日志组。这的样本输出将是

 {
    "logGroups": [
        {
            "metricFilterCount": 0,
            "storedBytes": 62299573,
            "arn": "arn:aws:logs:ap-southeast-1:855368385138:log-group:RDSOSMetrics:*",
            "retentionInDays": 30,
            "creationTime": 1566472016743,
            "logGroupName": "/aws/lambda/us-east-1.test"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

如果您只对日志组的特定前缀模式感兴趣,则可以像这样使用--log-group-name-prefix

aws logs describe-log-groups --log-group-name-prefix /aws/lambda
Run Code Online (Sandbox Code Playgroud)

此命令的输出 JSON 也与上述输出类似。

如果您的帐户中有太多的日志组,分析它的输出就会变得困难,我们需要一些命令行实用程序来简要了解结果。我们将使用“ jq ”命令行实用程序来获取所需的内容。目的是获取哪个日志组产生了最多的日志,从而产生了更多的钱

从输出 JSON 中,我们分析所需的字段将是“logGroupName”和“storedBytes”。所以在'jq'命令中使用这两个字段。

aws logs describe-log-groups --log-group-name-prefix /aws/ 
| jq -M -r '.logGroups[] | "{\"logGroupName\":\"\(.logGroupName)\",
\"storedBytes\":\(.storedBytes)}"'
Run Code Online (Sandbox Code Playgroud)

在命令中使用 '\' 进行转义,因为我们希望输出为 JSON 格式,使用jq的sort_by函数。示例输出如下所示:

{"logGroupName":"/aws/lambda/test1","storedBytes":3045647212}
{"logGroupName":"/aws/lambda/projectTest","storedBytes":200165401}
{"logGroupName":"/aws/lambda/projectTest2","storedBytes":200}
Run Code Online (Sandbox Code Playgroud)

请注意,输出结果不会按存储字节排序,因此我们希望对它们进行排序以获取哪个日志组是最有问题的日志组。

我们将使用jq 的sort_by函数来完成此操作。示例命令将是这样的

aws logs describe-log-groups --log-group-name-prefix /aws/ 
| jq -M -r '.logGroups[] | "{\"logGroupName\":\"\(.logGroupName)\",
\"storedBytes\":\(.storedBytes)}"' 
|  jq -s -c 'sort_by(.storedBytes) | .[]'
Run Code Online (Sandbox Code Playgroud)

这将为上述示例输出产生以下结果

{"logGroupName":"/aws/lambda/projectTest2","storedBytes":200}
{"logGroupName":"/aws/lambda/projectTest","storedBytes":200165401}
{"logGroupName":"/aws/lambda/test1","storedBytes":3045647212}
Run Code Online (Sandbox Code Playgroud)

此列表底部的元素是与它相关联的日志最多的元素。您可以将Expire Events After 属性设置为这些日志组的有限期限,例如 1 个月。

如果您想知道所有日志字节的总和是多少,那么您可以使用 jq 的“map”和“add”函数,如下所示。

 aws logs describe-log-groups --log-group-name-prefix /aws/ 
        | jq -M -r '.logGroups[] | "{\"logGroupName\":\"\(.logGroupName)\",
\"storedBytes\":\(.storedBytes)}"'
        |  jq -s -c 'sort_by(.storedBytes) | .[]' 
        | jq -s 'map(.storedBytes) | add '
Run Code Online (Sandbox Code Playgroud)

对于上述示例输出,此命令的输出将是

3245812813
Run Code Online (Sandbox Code Playgroud)

答案变得冗长,但我希望它有助于找出 cloudwatch 中最有问题的日志组。


Sam*_*ley 5

好的,我在这里回答我自己的问题,但是在这里,我们走了(欢迎所有其他答案):

您可以结合使用AWS CLI工具,csvfix CSV软件包和电子表格来解决此问题。

  • 登录到AWS Cloudwatch控制台,并获取已生成所有数据的日志组的名称。在我的情况下,它称为“ test01-ecs”。
  • 不幸的是,在Cloudwatch控制台中,您无法按“存储的字节”对流进行排序(这将告诉您最大的流)。如果日志组中的流太多,无法在控制台中浏览,则需要以某种方式转储它们。为此,您可以使用AWS CLI工具:

    $ aws logs describe-log-streams --log-group-name test01-ecs

  • 上面的命令将为您提供JSON输出(假设您的AWS CLI工具设置为JSON输出- 如果未设置为output = jsonin ~/.aws/config),它将看起来像这样:

    { "logStreams": [ { "creationTime": 1479218045690, "arn": "arn:aws:logs:eu-west-1:902720333704:log-group:test01-ecs:log-stream:test-spec/test-spec/0307d251-7764-459e-a68c-da47c3d9ecd9", "logStreamName": "test-spec/test-spec/0308d251-7764-4d9f-b68d-da47c3e9ebd8", "storedBytes": 7032 } ] }

  • 通过管道将此输出传输到JSON文件-在我的情况下,文件大小为31 MB:

    $ aws logs describe-log-streams --log-group-name test01-ecs >> ./cloudwatch-output.json

  • 使用in2csv包(部分csvfix)的JSON文件,可以很容易地导入到电子表格CSV文件转换,确保你定义logStreams键被用来进口上:

    $ in2csv cloudwatch-output.json --key logStreams >> ./cloudwatch-output.csv

  • 将生成的CSV文件导入电子表格(我自己使用LibreOffice,因为它在处理CSV方面似乎很棒),请确保将storedBytes字段导入为整数。

  • 对电子表格中的storedBytes列进行排序,以找出哪个或多个日志流生成最多的数据。

就我而言,这行得通-事实证明,我的一个日志流(redis实例中来自断开的TCP管道中的日志)是所有其他流总和的4,000倍!

  • `aws 日志描述日志流 --log-group-name LOG_GROUP_NAME --query 'logStreams[?storedBytes > \`0\`].[logStreamName, storeBytes] | 排序依据(@,&[1])| 反向(@)'`以获得更简单的解决方案 (2认同)
  • 遗憾的是 LogStreams 上的 storageBytes 已被弃用。“重要提示:2019 年 6 月 17 日,此参数已在日志流中弃用,并且始终报告为零。此更改仅适用于日志流。日志组的storedBytes 参数不受影响。” (2认同)