将AWS CodeBuild完成后的完整CloudWatch日志发送到Slack。

huangapple go评论53阅读模式
英文:

Send entire cloudwatch logs to slack when aws codebuild is finished

问题

我已经创建了一个 Lambda 函数,用于将 CodeBuild CloudWatch 日志发送到 Slack。它可以正常工作,但每次生成日志时,CodeBuild 运行时都会向 Slack 发送消息,看起来像垃圾信息。

我希望只在 CodeBuild 完成时发送整个 CodeBuild 日志一次。

我不确定如何添加触发器,因为在 Lambda 中,我使用了 CloudWatch 触发器来使 Lambda 工作。

Node.js Lambda 函数代码如下:

const zlib = require("zlib");
const https = require("https");
const SLACK_ENDPOINT = "/services/000000000000000000000000000000000000";
const SLACK_BOT = "deploy-notifications";

function doRequest(content) {
  // 根据 Slack API 格式化消息
  const payload = {
    username: SLACK_BOT,
    blocks: [
      {
        type: "header",
        text: {
          type: "plain_text",
          text: "哎呀,似乎出了点问题 😞🤕",
          emoji: true,
        },
      },
      {
        type: "section",
        fields: [
          {
            type: "mrkdwn",
            text: "<!here> API 遇到了问题",
          },
        ],
      },
      {
        type: "section",
        fields: [
          {
            type: "mrkdwn",
            text: "*环境: * 生产环境",
          },
        ],
      },
      {
        type: "section",
        fields: [
          {
            type: "mrkdwn",
            text: "*消息:* _" + content.message + "_",
          },
        ],
      },
      {
        type: "section",
        fields: [
          {
            type: "mrkdwn",
            text: "*堆栈跟踪:*",
          },
        ],
      },
      {
        type: "section",
        text: {
          type: "mrkdwn",
          text:
            "```" +
            JSON.stringify(content.original ? content.original : content) +
            "```",
        },
      },
      {
        type: "divider",
      },
    ],
  };

  const payloadStr = JSON.stringify(payload);
  const options = {
    hostname: "hooks.slack.com",
    port: 443,
    path: SLACK_ENDPOINT,
    channel: "#deploy-notifications",
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Content-Length": Buffer.byteLength(payloadStr),
    },
  };

  const postReq = https.request(options, function (res) {
    const chunks = [];
    res.setEncoding("utf8");
    res.on("data", function (chunk) {
      return chunks.push(chunk);
    });
    res.on("end", function () {
      if (res.statusCode < 400) {
        console.log("已发送!!!");
      } else if (res.statusCode < 500) {
        console.error(
          "向 Slack API 发送消息时出错: " +
            res.statusCode +
            " - " +
            res.statusMessage
        );
      } else {
        console.error(
          "处理消息时出现服务器错误: " +
            res.statusCode +
            " - " +
            res.statusMessage
        );
      }
    });
    return res;
  });
  postReq.write(payloadStr);
  postReq.end();
}

function main(event, context) {
  context.callbackWaitsForEmptyEventLoop = true;
  // 始终返回最后一个事件
  const payload = Buffer.from(event.awslogs.data, "base64");
  const log = JSON.parse(zlib.gunzipSync(payload).toString("utf8"));
  // 日志是一个包含名为 `logEvents` 的事件数组的对象,我们需要通过索引 0 访问它
  doRequest(log.logEvents[0]);
  const response = {
    statusCode: 200,
    body: "事件已发送到 Slack!",
  };
  return response;
}

exports.handler = main;

将AWS CodeBuild完成后的完整CloudWatch日志发送到Slack。

英文:

I have created a lambda function that send codebuild cloudwatch logs to slack. Its working but everytime the logs is generated while codebuild is running. It is throwing messages to slack like a spam.

I would want to send entire codebuild logs only once when the codebuild is finished.

I am not sure how to add trigger because in lambda I used cloudwatch trigger for lambda to work.

将AWS CodeBuild完成后的完整CloudWatch日志发送到Slack。

Nodejs Lambda function

const zlib = require(&quot;zlib&quot;);
const https = require(&quot;https&quot;);
const SLACK_ENDPOINT =&quot;/services/000000000000000000000000000000000000&quot;
const SLACK_BOT = &quot;deploy-notifications&quot;;
function doRequest(content) {
// formatting the message according Slack API
const payload = {
username: SLACK_BOT,
blocks: [
{
type: &quot;header&quot;,
text: {
type: &quot;plain_text&quot;,
text: &quot;Whoops, looks like something went wrong &#128542;&#129301;&quot;,
emoji: true,
},
},
{
type: &quot;section&quot;,
fields: [
{
type: &quot;mrkdwn&quot;,
text: &quot;&lt;!here&gt; the API is running into an issue&quot;,
},
],
},
{
type: &quot;section&quot;,
fields: [
{
type: &quot;mrkdwn&quot;,
text: &quot;*Environment: * Production&quot;,
},
],
},
{
type: &quot;section&quot;,
fields: [
{
type: &quot;mrkdwn&quot;,
text: &quot;*Message:* _&quot; + content.message + &quot;_&quot;,
},
],
},
{
type: &quot;section&quot;,
fields: [
{
type: &quot;mrkdwn&quot;,
text: &quot;*Stacktrace:*&quot;,
},
],
},
{
type: &quot;section&quot;,
text: {
type: &quot;mrkdwn&quot;,
text:
&quot;```&quot; +
JSON.stringify(content.original ? content.original : content) +
&quot;```&quot;,
},
},
{
type: &quot;divider&quot;,
},
],
};
const payloadStr = JSON.stringify(payload);
const options = {
hostname: &quot;hooks.slack.com&quot;,
port: 443,
path: SLACK_ENDPOINT,
channel: &quot;#deploy-notifications&quot;,
method: &quot;POST&quot;,
headers: {
&quot;Content-Type&quot;: &quot;application/json&quot;,
&quot;Content-Length&quot;: Buffer.byteLength(payloadStr),
},
};
const postReq = https.request(options, function (res) {
const chunks = [];
res.setEncoding(&quot;utf8&quot;);
res.on(&quot;data&quot;, function (chunk) {
return chunks.push(chunk);
});
res.on(&quot;end&quot;, function () {
if (res.statusCode &lt; 400) {
console.log(&quot;sent!!!&quot;);
} else if (res.statusCode &lt; 500) {
console.error(
&quot;Error posting message to Slack API: &quot; +
res.statusCode +
&quot; - &quot; +
res.statusMessage
);
} else {
console.error(
&quot;Server error when processing message: &quot; +
res.statusCode +
&quot; - &quot; +
res.statusMessage
);
}
});
return res;
});
postReq.write(payloadStr);
postReq.end();
}
function main(event, context) {
context.callbackWaitsForEmptyEventLoop = true;
// always returns the last event
const payload = Buffer.from(event.awslogs.data, &quot;base64&quot;);
const log = JSON.parse(zlib.gunzipSync(payload).toString(&quot;utf8&quot;));
// the log is an object that contains an array of events called `logEvents` and we need access it bypassing the index 0
doRequest(log.logEvents[0]);
const response = {
statusCode: 200,
body: JSON.stringify(&quot;Event sent to Slack!&quot;),
};
return response;
}
exports.handler = main;

答案1

得分: 2

@abdullah的回答应该可以解决你的问题。然而,我将发布一种替代方法

你可以简单地在构建项目上启用通知。你可以配置你想要的事件,可以是构建状态或构建阶段,还可以配置通知的目标为SNS。

在你的SNS上订阅Lambda,就这样。

你不必创建自定义事件规则或其他任何东西,只需在你的CodeBuild项目上配置事件通知规则。

如何配置通知:

  • 进入你的CodeBuild项目
  • 在顶部,点击通知
  • 输入通知规则名称,选择你想要的事件,然后选择目标

将AWS CodeBuild完成后的完整CloudWatch日志发送到Slack。

注意: 我的回答假设你的Lambda正在执行你打算执行的操作。

英文:

@abdullah's answer should solve your problem. However I will post an alternate approach

You can simply enable notifications on your build project. You can configure which event you want either on Build state or Build phase and configure target for this notification as sns.

In your sns subscribe to lambda and thats it.

You won't have to create a custom event rule or anything just configure event notification rule on your codebuild project.

How to configure notification :

  • go to your codebuild project
  • on top, click on notify
  • Enter notification rule name, chose which event you want and select target

将AWS CodeBuild完成后的完整CloudWatch日志发送到Slack。

Note: My answer assumes your lambda is doing what you intend to do.

答案2

得分: 1

第1步

您应该更改AWS CloudWatch事件/AWS EventBridge规则的触发器以实现此目的。不要使用AWS CloudWatch日志事件,而应该使用AWS CodeBuild Build State Change事件作为详细类型,aws.codebuild作为源。

以下是一个示例:

{
  "source": ["aws.codebuild"],
  "detail-type": ["CodeBuild Build State Change"],
  "detail": {
    "project-name": ["<PROJECT_NAME>"],
    "build-status": ["SUCCEEDED", "FAILED", "STOPPED"]
  }
}

这将在AWS CodeBuild项目生成任何上述4种状态之一时触发AWS Lambda函数。它还会在事件负载中发送AWS CodeBuild项目生成ID。

第2步

您需要相应地更新AWS Lambda函数代码,以使用事件负载中接收的AWS CodeBuild项目生成ID获取AWS CloudWatch日志。AWS CodeBuild项目生成的AWS CloudWatch日志组名称和流名称可能如下所示:/aws/codebuild/<PROJECT_NAME>/<BUILD_ID>。这取决于您在AWS CodeBuild项目上的配置方式。

以下是用于Python的此AWS Lambda函数的示例代码:

import boto3
import requests
import json

def lambda_handler(event, context):
    build_id = event['detail']['build-id'].split(':')[-1]

    logs_client = boto3.client('logs')

    log_group_name = "/aws/codebuild/" + event['detail']['project-name']
    log_stream_name = build_id

    response = logs_client.get_log_events(
        logGroupName=log_group_name,
        logStreamName=log_stream_name,
        startFromHead=True
    )

    message = {
        'text': 'Logs for build {0}:\n'.format(build_id)
    }

    for event in response['events']:
        message['text'] += event['message'] + '\n'

    slack_webhook_url = <SLACK_WEBHOOK_URL>

    response = requests.post(slack_webhook_url, json=message)

    if response.status_code == 200:
        return {
            'statusCode': 200,
            'body': 'Message is sent to Slack successfully.'
        }
    else:
        return {
            'statusCode': response.status_code,
            'body': 'Failed to send message to Slack.'
        }

这是有关Slack Webhook URL的文档:使用传入的Webhooks发送消息

注:

  • <PROJECT_NAME>是AWS CodeBuild项目的名称。
  • <BUILD_ID>是特定生成的AWS CodeBuild项目的生成ID。
  • <SLACK_WEBHOOK_URL>是Slack Webhook URL。
英文:

STEP 1

You should change your trigger for AWS CloudWatch Events / AWS EventBridge rule to achieve this. Instead of using AWS CloudWatch logs event, you should use AWS CodeBuild Build State Change event as detail-type and aws.codebuild as source.

Below is a sample for that:

{
  &quot;source&quot;: [&quot;aws.codebuild&quot;],
  &quot;detail-type&quot;: [&quot;CodeBuild Build State Change&quot;],
  &quot;detail&quot;: {
    &quot;project-name&quot;: [&quot;&lt;PROJECT_NAME&gt;&quot;],
    &quot;build-status&quot;: [&quot;SUCCEEDED&quot;, &quot;FAILED&quot;, &quot;STOPPED&quot;]
  }
}

This will trigger the AWS Lambda function when the AWS CodeBuild project build has finished with any of the above mentioned 4 states. It will also send AWS CodeBuild project build ID in event payload.

STEP 2

You need to update your AWS Lambda function code accordingly to fetch AWS CloudWatch logs using AWS CodeBuild project build id received in event payload. Your AWS CloudWatch log group name and stream for an AWS CodeBuild project build may look like this /aws/codebuild/&lt;PROJECT_NAME&gt;/&lt;BUILD_ID&gt;. It depends upon how you configured it on AWS CodeBuild project.

Below is a sample code for AWS Lambda function for this in Python:

import boto3
import requests
import json

def lambda_handler(event, context):
    build_id = event[&#39;detail&#39;][&#39;build-id&#39;].split(&#39;:&#39;)[-1]

    logs_client = boto3.client(&#39;logs&#39;)

    log_group_name = &quot;/aws/codebuild/&quot; + event[&#39;detail&#39;][&#39;project-name&#39;]
    log_stream_name = build_id

    response = logs_client.get_log_events(
        logGroupName=log_group_name,
        logStreamName=log_stream_name,
        startFromHead=True
    )

    message = {
        &#39;text&#39;: &#39;Logs for build {0}:\n&#39;.format(build_id)
    }

    for event in response[&#39;events&#39;]:
        message[&#39;text&#39;] += event[&#39;message&#39;] + &#39;\n&#39;

    slack_webhook_url = &lt;SLACK_WEBHOOK_URL&gt;

    response = requests.post(slack_webhook_url, json=message)

    if response.status_code == 200:
        return {
            &#39;statusCode&#39;: 200,
            &#39;body&#39;: &#39;Message is sent to Slack successfully.&#39;
        }
    else:
        return {
            &#39;statusCode&#39;: response.status_code,
            &#39;body&#39;: &#39;Failed to send message to Slack.&#39;
        }

Here is the documentation by Slack regarding Webhook URL: Sending messages using Incoming Webhooks

Notes:

  • &lt;PROJECT_NAME&gt; is the name of the AWS CodeBuild project
  • &lt;BUILD_ID&gt; is the build id of the AWS CodeBuild project for a specific build.
  • &lt;SLACK_WEBHOOK_URL&gt; is the Slack Webhook URL.

huangapple
  • 本文由 发表于 2023年3月31日 17:05:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75896672.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定