Skip to content

Commit dd7e7d1

Browse files
authored
Merge pull request #13 from theodoresiu/log_slack_alerts
Hourly Audit Log Slack Alerts Example
2 parents afcfacb + bd4ff2d commit dd7e7d1

File tree

5 files changed

+220
-0
lines changed

5 files changed

+220
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Logging Slack Alerts
2+
3+
Disclaimer (8/1/2019): Test Coverage has currently not been added to this example and may be unintentionally broken by future releases to the module.
4+
5+
This logging slack alerts example module schedules a job to run hourly queries of any errors which have occurred in logs which have been ingested into BigQuery. If any errors are found, the errors are sent as alerts to a slack webhook.
6+
7+
Running this module requires log exports into BigQuery in the specified project/region, which is not handled by this example.
8+
A good example of exported logging in BigQuery can be found in [Stackdriver Logging](https://cloud.google.com/logging/docs/export/).
9+
10+
## Configure a Service Account
11+
12+
If not using the default App Engine default service account (PROJECT_ID@appspot.gserviceaccount.com), which has the Editor role on the project, one can configure a service account for the cloud function which has the following IAM role - (roles/bigquery.dataViewer, roles/bigquery.jobUser). Additionally, the BigQuery API (https://bigquery.googleapis.com) needs to be enabled as well.
13+
14+
15+
[^]: (autogen_docs_start)
16+
17+
## Inputs
18+
19+
| Name | Description | Type | Default | Required |
20+
|------|-------------|:----:|:-----:|:-----:|
21+
| project\_id | The project ID to host the network in | string | n/a | yes |
22+
| region | The region the project is in (App Engine specific) | string | `"us-central1"` | no |
23+
| slack_webhook | The Slack webhook to send alerts | string | n/a | yes |
24+
| dataset_name | The BigQuery Dataset where exported logging is sent | string | n/a | yes |
25+
| audit_log_table | The BigQuery Table within the dataset where logging is sent | string | n/a | yes |
26+
| time_column | The column within the BQ Table representing logging time | string | n/a | yes |
27+
| error_message_column | The column within the BQ Table representing logging errors | string | n/a | yes |
28+
29+
30+
[^]: (autogen_docs_end)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
* Copyright 2019 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
"""
16+
17+
import os
18+
import logging
19+
import requests
20+
from google.cloud import bigquery
21+
22+
BQ_CLIENT = bigquery.Client()
23+
logging.getLogger().setLevel(logging.INFO)
24+
25+
VARIABLES = {
26+
"SLACK_WEBHOOK_URL": os.getenv("SLACK_WEBHOOK_URL"),
27+
"DATASET_NAME": os.getenv("DATASET_NAME"),
28+
"AUDIT_LOG_TABLE": os.getenv("AUDIT_LOG_TABLE"),
29+
"TIME_COLUMN": os.getenv("TIME_COLUMN"),
30+
"ERROR_MESSAGE_COLUMN": os.getenv("ERROR_MESSAGE_COLUMN")
31+
}
32+
QUERY = """
33+
WITH
34+
errors AS (
35+
SELECT
36+
{ERROR_MESSAGE_COLUMN} AS error_message,
37+
EXTRACT(HOUR FROM current_timestamp) as hr
38+
FROM
39+
{DATASET_NAME}.{AUDIT_LOG_TABLE}
40+
WHERE
41+
{ERROR_MESSAGE_COLUMN} IS NOT NULL
42+
AND EXTRACT(HOUR
43+
FROM
44+
current_timestamp) = EXTRACT(HOUR
45+
FROM
46+
{TIME_COLUMN}))
47+
SELECT
48+
error_message as Error,
49+
hr,
50+
COUNT(*) as Count
51+
FROM
52+
errors
53+
GROUP BY
54+
1,2
55+
""".format(**VARIABLES)
56+
57+
def query_for_errors(pubsub_event, pubsub_context):
58+
"""
59+
Cloud Function to query audit logs for errors
60+
and send alerts to Slack Webhook
61+
"""
62+
63+
logging.info("Running: %s", QUERY)
64+
query_job = BQ_CLIENT.query(QUERY)
65+
66+
if list(query_job):
67+
for row in list(query_job):
68+
text = ("Alert: Error {0}... has occurred {1} times"
69+
"in the past hour - {2}:00 PST. "
70+
"Please file a bug ticket to have").format(
71+
(row["Error"][:500]),
72+
str(row["Count"]),
73+
str(row["hr"]))
74+
logging.info("Posting to Slack: %s", text)
75+
req = requests.post(url=VARIABLES['SLACK_WEBHOOK_URL'],
76+
data=str({"text": text}))
77+
logging.info(req.text)
78+
79+
if __name__ == "__main__":
80+
query_for_errors(None, None)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
google-cloud-bigquery==1.14.0
2+
requests==2.21.0

examples/logs-slack-alerts/main.tf

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright 2019 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
provider "google-beta" {
18+
version = "~> 2.1"
19+
project = var.project_id
20+
region = var.region
21+
}
22+
23+
module "log_slack_alerts_example" {
24+
providers = {
25+
google = google-beta
26+
}
27+
28+
source = "../../"
29+
project_id = var.project_id
30+
job_name = "logs_query"
31+
job_description = "Scheduled time to run audit query to check for errors"
32+
job_schedule = "55 * * * *"
33+
function_entry_point = "query_for_errors"
34+
function_source_directory = "${path.module}/function_source"
35+
function_name = "logs_query_alerting"
36+
function_description = "Cloud Function to query audit logs for errors"
37+
region = var.region
38+
topic_name = "logs_query_topic"
39+
function_runtime = "python37"
40+
41+
function_environment_variables = {
42+
SLACK_WEBHOOK = var.slack_webhook
43+
DATASET_NAME = var.dataset_name
44+
AUDIT_LOG_TABLE = var.audit_log_table
45+
TIME_COLUMN = var.time_column
46+
ERROR_MESSAGE_COLUMN = var.error_message_column
47+
}
48+
}
49+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Copyright 2019 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
variable "project_id" {
18+
description = "The project ID to host the network in"
19+
type = string
20+
}
21+
22+
variable "job_schedule" {
23+
description = "The cron schedule for triggering the cloud function"
24+
type = string
25+
default = "55 * * * *"
26+
27+
}
28+
29+
variable "slack_webhook" {
30+
description = "Slack webhook to send alerts"
31+
type = string
32+
}
33+
34+
variable "dataset_name" {
35+
description = "BigQuery Dataset where logs are sent"
36+
type = string
37+
}
38+
39+
variable "audit_log_table" {
40+
description = "BigQuery Table where logs are sent"
41+
type = string
42+
}
43+
44+
variable "time_column" {
45+
description = "BigQuery Column in audit log table representing logging time"
46+
type = string
47+
}
48+
49+
variable "error_message_column" {
50+
description = "BigQuery Column in audit log table representing logging error"
51+
type = string
52+
}
53+
54+
variable "region" {
55+
description = "The region the project is in (App Engine specific)"
56+
type = string
57+
default = "us-central1"
58+
}
59+

0 commit comments

Comments
 (0)