上个月,我从 Amazon 收到了一张价值 1,200 美元的 Cloudwatch 服务发票(特别是“AmazonCloudWatch PutLogEvents”中的 2 TB 日志数据摄取),而我原本预计会收到几十美元。我已经登录到 AWS 控制台的 Cloudwatch 部分,可以看到我的一个日志组使用了大约 2TB 的数据,但是该日志组中有数千个不同的日志流,我怎么知道哪一个使用了这个数量数据的?
5 回答
在 CloudWatch 控制台上,使用 IncomingBytes 指标通过指标页面查找特定时间段内每个日志组摄取的数据量(以未压缩字节为单位)。请按照以下步骤操作 -
- 转到 CloudWatch 指标页面并单击 AWS 命名空间“日志”->“日志组指标”。
- 选择所需日志组的 IncomingBytes 指标,然后单击“图表指标”选项卡以查看图表。
- 将开始时间和结束时间更改为相差 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
由于意外签入,我们有一个 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))
尽管问题的作者和其他人已经很好地回答了这个问题,但我会尝试有一个通用的解决方案,可以在不知道导致过多日志的确切日志组名称的情况下应用。
为此,我们不能使用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 中最有问题的日志组。
*** 更新 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 = json
in~/.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 倍!
您还可以单击 cloudwatch 日志仪表板上齿轮上的齿轮,然后选择存储字节列。
我还单击了任何“永不过期”的内容并将日志更改为过期。