26

上个月,我从 Amazon 收到了一张价值 1,200 美元的 Cloudwatch 服务发票(特别是“AmazonCloudWatch PutLogEvents”中的 2 TB 日志数据摄取),而我原本预计会收到几十美元。我已经登录到 AWS 控制台的 Cloudwatch 部分,可以看到我的一个日志组使用了大约 2TB 的数据,但是该日志组中有数千个不同的日志流,我怎么知道哪一个使用了这个数量数据的?

4

5 回答 5

60

在 CloudWatch 控制台上,使用 IncomingBytes 指标通过指标页面查找特定时间段内每个日志组摄取的数据量(以未压缩字节为单位)。请按照以下步骤操作 -

  1. 转到 CloudWatch 指标页面并单击 AWS 命名空间“日志”->“日志组指标”。
  2. 选择所需日志组的 IncomingBytes 指标,然后单击“图表指标”选项卡以查看图表。
  3. 将开始时间和结束时间更改为相差 30 天,并将期间更改为 30 天。这样,我们将只得到一个数据点。还将图表更改为数字,将统计信息更改为总和。

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

在此处输入图像描述

您还可以使用 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

样本输出 -

{
    "Datapoints": [
        {
            "Timestamp": "2018-05-01T00:00:00Z", 
            "Sum": 1686361672.0, 
            "Unit": "Bytes"
        }
    ], 
    "Label": "IncomingBytes"
}

要为特定的日志组找到相同的内容,您可以更改命令以适应诸如 -

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

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

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

OP 提供的解决方案提供了存储日志量的数据,这些数据与摄取的日志不同。

有什么区别?

每月摄取的数据与数据存储字节数不同。将数据摄取到 CloudWatch 后,它由 CloudWatch 存档,其中包括每个日志事件 26 字节的元数据,并使用 gzip 6 级压缩进行压缩。所以存储字节是指 Cloudwatch 用于存储日志被摄取后的存储空间。

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

于 2018-07-24T11:49:39.420 回答
13

由于意外签入,我们有一个 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))
        

于 2018-09-07T13:00:54.860 回答
9

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

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

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

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

aws logs describe-log-groups

此命令将列出您的 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"
        }
    ]
}

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

aws logs describe-log-groups --log-group-name-prefix /aws/lambda

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

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

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

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

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

{"logGroupName":"/aws/lambda/test1","storedBytes":3045647212}
{"logGroupName":"/aws/lambda/projectTest","storedBytes":200165401}
{"logGroupName":"/aws/lambda/projectTest2","storedBytes":200}

请注意,输出结果不会按 storedBytes 排序,因此我们要对它们进行排序,以便得到最有问题的日志组。

我们将使用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) | .[]'

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

{"logGroupName":"/aws/lambda/projectTest2","storedBytes":200}
{"logGroupName":"/aws/lambda/projectTest","storedBytes":200165401}
{"logGroupName":"/aws/lambda/test1","storedBytes":3045647212}

此列表底部的元素是与其关联的日志最多的元素。您可以将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 '

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

3245812813

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

于 2020-03-28T17:36:50.727 回答
6

*** 更新 20210907 - 正如@davur 在下面的评论之一中指出的那样,AWS 已弃用单个 LogStreams 的存储字节,因此此答案中描述的方法不再满足要求,尽管它可能在其他方面很有趣 ***

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

您可以结合使用 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 倍!

于 2017-04-10T16:24:19.647 回答
5

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

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

使用 cloudwatch logs gear 并选择“Stored Bytes”列

于 2020-03-24T20:22:39.020 回答