Skip to content

Commit 307f49b

Browse files
committed
created helper class for incorporation of sentry in monitor alerts
1 parent 5a0ebf3 commit 307f49b

File tree

4 files changed

+76
-5
lines changed

4 files changed

+76
-5
lines changed

shub_workflow/contrib/__init__.py

Whitespace-only changes.

shub_workflow/contrib/sentry.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import logging
2+
3+
from typing import List
4+
5+
from spidermon.contrib.actions.sentry import SendSentryMessage
6+
7+
from shub_workflow.script import BaseScriptProtocol
8+
from shub_workflow.utils import resolve_shub_jobkey
9+
from shub_workflow.utils.monitor import BaseMonitorProtocol
10+
11+
12+
LOG = logging.getLogger(__name__)
13+
14+
15+
class SentryMixin(BaseScriptProtocol):
16+
"""
17+
A class for adding sentry alert capabilities to a shub_workflow class.
18+
"""
19+
20+
def __init__(self):
21+
self.sentry_handler = SendSentryMessage(
22+
fake=self.project_settings.get("SPIDERMON_SENTRY_FAKE"),
23+
sentry_dsn=self.project_settings.get("SPIDERMON_SENTRY_DSN"),
24+
sentry_log_level=self.project_settings.get("SPIDERMON_SENTRY_LOG_LEVEL"),
25+
project_name=self.project_settings.get("SPIDERMON_SENTRY_PROJECT_NAME"),
26+
environment=self.project_settings.get("SPIDERMON_SENTRY_ENVIRONMENT_TYPE"),
27+
)
28+
self.messages: List[str] = []
29+
30+
def send_sentry_message(self):
31+
if self.messages:
32+
message = dict()
33+
title = f"{self.sentry_handler.project_name} | {self.sentry_handler.environment} | Monitor notification"
34+
message["title"] = title
35+
message["failure_reasons"] = "/n".join(self.messages)
36+
job_key = resolve_shub_jobkey()
37+
if job_key:
38+
message["job_link"] = f"https://app.zyte.com/p/{job_key}"
39+
if self.sentry_handler.fake:
40+
LOG.info(message)
41+
else:
42+
self.sentry_handler.send_message(message)
43+
44+
def append_message(self, message: str):
45+
self.messages.append(message)
46+
47+
48+
class MonitorSentryMixin(SentryMixin, BaseMonitorProtocol):
49+
"""
50+
Mixin for adding sentry capabilities to shub_workflow monitors.
51+
"""
52+
def close(self):
53+
super().close() # type: ignore
54+
self.send_sentry_message()

shub_workflow/utils/__init__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ def hashstr(text: str) -> str:
1919
return u.hexdigest()
2020

2121

22+
def resolve_shub_jobkey() -> Optional[str]:
23+
return os.environ.get("SHUB_JOBKEY")
24+
25+
2226
def resolve_project_id(project_id=None) -> Optional[int]:
2327
"""
2428
Gets project id from following sources in following order of precedence:
@@ -37,8 +41,9 @@ def resolve_project_id(project_id=None) -> Optional[int]:
3741
return int(os.environ["PROJECT_ID"])
3842

3943
# for ScrapyCloud jobs:
40-
if os.environ.get("SHUB_JOBKEY"):
41-
return int(os.environ["SHUB_JOBKEY"].split("/")[0])
44+
jobkey = resolve_shub_jobkey()
45+
if jobkey:
46+
return int(jobkey.split("/")[0])
4247

4348
# read from scrapinghub.yml
4449
try:

shub_workflow/utils/monitor.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import re
2+
import abc
23
import time
34
import logging
45
import inspect
5-
from typing import Dict, Type, Tuple, Optional
6+
from typing import Dict, Type, Tuple, Optional, Protocol
67
from datetime import timedelta, datetime
78
from collections import defaultdict
89

910
import dateparser
1011
from scrapy import Spider
1112

12-
from shub_workflow.script import BaseScript, SpiderName, JobDict
13+
from shub_workflow.script import BaseScript, BaseScriptProtocol, SpiderName, JobDict
1314

1415
LOG = logging.getLogger(__name__)
1516

@@ -21,7 +22,14 @@ def _get_number(txt: str) -> Optional[int]:
2122
return None
2223

2324

24-
class BaseMonitor(BaseScript):
25+
class BaseMonitorProtocol(BaseScriptProtocol, Protocol):
26+
27+
@abc.abstractmethod
28+
def close(self):
29+
...
30+
31+
32+
class BaseMonitor(BaseScript, BaseMonitorProtocol):
2533

2634
# a map from spiders classes to check, to a stats prefix to identify the aggregated stats.
2735
target_spider_classes: Dict[Type[Spider], str] = {Spider: ""}
@@ -108,6 +116,7 @@ def run(self):
108116
self.run_stats_hooks(start_limit, end_limit)
109117
self.upload_stats()
110118
self.print_stats()
119+
self.close()
111120

112121
def run_stats_hooks(self, start_limit, end_limit):
113122
for stat, val in self.stats.get_stats().items():
@@ -261,3 +270,6 @@ def check_script_logs(self, start_limit, end_limit):
261270
self.stats.inc_value(stat, val)
262271
if stat_suffix:
263272
self.stats.inc_value(stat + f"/{stat_suffix}", val)
273+
274+
def close(self):
275+
pass

0 commit comments

Comments
 (0)