Skip to content

Commit 9aef4a2

Browse files
committed
feat: add the ignore user_agent ability
1 parent dc30dd2 commit 9aef4a2

File tree

4 files changed

+38
-12
lines changed

4 files changed

+38
-12
lines changed

sentry_dynamic_sampling_lib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def init_wrapper():
4949
app_key = build_app_key(client.options)
5050
controller_endpoint = urljoin(CONTROLLER_HOST, CONTROLLER_PATH)
5151
metric_endpoint = urljoin(CONTROLLER_HOST, METRIC_PATH)
52-
print(f"Sentry Wrapper: Injecting TracesSampler. App Key : {app_key}")
52+
LOGGER.warning("Sentry Wrapper: Injecting TracesSampler. App Key : %s", app_key)
5353
client.options["traces_sampler"] = TraceSampler(
5454
poll_interval=POLL_INTERVAL,
5555
metric_interval=METRIC_INTERVAL,

sentry_dynamic_sampling_lib/sampler.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def update_metrics(self):
9393
data = {
9494
"app": self.app_key,
9595
"type": metric_type.value,
96-
"data": dict(data.most_common(10)),
96+
"data": dict(data),
9797
}
9898
try:
9999
self.session.post(
@@ -154,9 +154,11 @@ def __call__(self, sampling_context):
154154
if sampling_context:
155155
if "wsgi_environ" in sampling_context:
156156
path = sampling_context["wsgi_environ"].get("PATH_INFO", "")
157-
if path in self.app_config.ignored_paths:
157+
user_agent = sampling_context["wsgi_environ"].get("HTTP_USER_AGENT", "")
158+
if path in self.app_config.ignored_paths or user_agent.startswith(self.app_config.ignored_user_agents):
158159
return 0
159160
self.metrics.count_path(path)
161+
self.metrics.count_user_agent(user_agent)
160162
if "celery_job" in sampling_context:
161163
task = sampling_context["celery_job"].get("task", "")
162164
if task in self.app_config.ignored_tasks:

sentry_dynamic_sampling_lib/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# default value overridden by controller
44
DEFAULT_IGNORED_PATH = {"/health", "/healthz", "/health/", "/healthz/"}
55
DEFAULT_IGNORED_TASK = set()
6+
DEFAULT_IGNORED_USER_AGENT = set()
67
DEFAULT_SAMPLE_RATE = 0.0
78

89
# controller variables

sentry_dynamic_sampling_lib/shared.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from sentry_dynamic_sampling_lib.settings import (
77
DEFAULT_IGNORED_PATH,
88
DEFAULT_IGNORED_TASK,
9+
DEFAULT_IGNORED_USER_AGENT,
910
DEFAULT_SAMPLE_RATE,
1011
)
1112
from sentry_dynamic_sampling_lib.utils import synchronized
@@ -18,6 +19,7 @@ def __init__(self) -> None:
1819
self._lock = RLock()
1920
self._sample_rate = DEFAULT_SAMPLE_RATE
2021
self._ignored_paths = DEFAULT_IGNORED_PATH
22+
self._ignored_user_agents = tuple(DEFAULT_IGNORED_USER_AGENT)
2123
self._ignored_tasks = DEFAULT_IGNORED_TASK
2224

2325
@property
@@ -40,6 +42,16 @@ def ignored_paths(self):
4042
def ignored_paths(self, new_ignored_paths):
4143
self._ignored_paths = set(new_ignored_paths)
4244

45+
@property
46+
@synchronized
47+
def ignored_user_agents(self):
48+
return self._ignored_user_agents
49+
50+
@ignored_user_agents.setter
51+
@synchronized
52+
def ignored_user_agents(self, new_ignored_user_agents):
53+
self._ignored_user_agents = set(new_ignored_user_agents)
54+
4355
@property
4456
@synchronized
4557
def ignored_tasks(self):
@@ -55,6 +67,7 @@ def update(self, data):
5567
self._sample_rate = data["active_sample_rate"]
5668
self._ignored_paths = set(data["wsgi_ignore_path"])
5769
self._ignored_tasks = set(data["celery_ignore_task"])
70+
self._ignored_user_agents = tuple(data.get("wsgi_ignore_user_agent", []))
5871

5972

6073
class MetricType(Enum):
@@ -66,8 +79,8 @@ class Metric:
6679
def __init__(self) -> None:
6780
self._lock = RLock()
6881
self._metrics = {
69-
MetricType.WSGI: {"activated": False, "data": Counter()},
70-
MetricType.CELERY: {"activated": False, "data": Counter()},
82+
MetricType.WSGI: {"activated": False, "data": {"path": Counter(), "user_agent": Counter()}},
83+
MetricType.CELERY: {"activated": False, "data": {"task": Counter()}},
7184
}
7285

7386
def set_mode(self, _type, mode):
@@ -80,13 +93,19 @@ def get_mode(self, _type):
8093
def count_path(self, path):
8194
metric = self._metrics[MetricType.WSGI]
8295
if metric["activated"]:
83-
metric["data"][path] += 1
96+
metric["data"]["path"][path] += 1
97+
98+
@synchronized
99+
def count_user_agent(self, user_agent):
100+
metric = self._metrics[MetricType.WSGI]
101+
if metric["activated"]:
102+
metric["data"]["user_agent"][user_agent] += 1
84103

85104
@synchronized
86105
def count_task(self, path):
87106
metric = self._metrics[MetricType.CELERY]
88107
if metric["activated"]:
89-
metric["data"][path] += 1
108+
metric["data"]["task"][path] += 1
90109

91110
def __iter__(self):
92111
"""
@@ -101,12 +120,16 @@ def __iter__(self):
101120
LOGGER.debug("Metric %s disabled", metric_type.value)
102121
continue
103122
with self._lock:
104-
# check im metric is empty
105-
if len(metric["data"]) == 0:
106-
LOGGER.debug("Metric %s is empty", metric_type.value)
123+
data = {}
124+
for name, counter in metric["data"].items():
125+
# check if metric is empty
126+
if len(counter) == 0:
127+
LOGGER.debug("Metric %s:%s is empty", metric_type.value, name)
128+
continue
129+
data[name] = dict(counter.most_common(10))
130+
metric["data"][name] = Counter()
131+
if not data:
107132
continue
108-
data = metric["data"]
109-
metric["data"] = Counter()
110133

111134
# yield outside of the lock to not block write while callee execute
112135
yield metric_type, data

0 commit comments

Comments
 (0)