Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfab65e6

Browse files
authored
feat(metrics): New normalization of keys, values, units (#2946)
1 parenta584653 commitfab65e6

File tree

2 files changed

+111
-41
lines changed

2 files changed

+111
-41
lines changed

‎sentry_sdk/metrics.py‎

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@
5454

5555

5656
_in_metrics=ContextVar("in_metrics",default=False)
57-
_sanitize_key=partial(re.compile(r"[^a-zA-Z0-9_/.-]+").sub,"_")
58-
_sanitize_value=partial(re.compile(r"[^\w\d\s_:/@\.{}\[\]$-]+",re.UNICODE).sub,"")
5957
_set=set# set is shadowed below
6058

6159
GOOD_TRANSACTION_SOURCES=frozenset(
@@ -67,6 +65,32 @@
6765
]
6866
)
6967

68+
_sanitize_unit=partial(re.compile(r"[^a-zA-Z0-9_]+").sub,"")
69+
_sanitize_metric_key=partial(re.compile(r"[^a-zA-Z0-9_\-.]+").sub,"_")
70+
_sanitize_tag_key=partial(re.compile(r"[^a-zA-Z0-9_\-.\/]+").sub,"")
71+
_TAG_VALUE_SANITIZATION_TABLE= {
72+
"\n":"\\n",
73+
"\r":"\\r",
74+
"\t":"\\t",
75+
"\\":"\\\\",
76+
"|":"\\u{7c}",
77+
",":"\\u{2c}",
78+
}
79+
80+
81+
def_sanitize_tag_value(value):
82+
# type: (str) -> str
83+
return"".join(
84+
[
85+
(
86+
_TAG_VALUE_SANITIZATION_TABLE[char]
87+
ifcharin_TAG_VALUE_SANITIZATION_TABLE
88+
elsechar
89+
)
90+
forcharinvalue
91+
]
92+
)
93+
7094

7195
defget_code_location(stacklevel):
7296
# type: (int) -> Optional[Dict[str, Any]]
@@ -269,7 +293,8 @@ def _encode_metrics(flushable_buckets):
269293
fortimestamp,bucketsinflushable_buckets:
270294
forbucket_key,metriciniteritems(buckets):
271295
metric_type,metric_name,metric_unit,metric_tags=bucket_key
272-
metric_name=_sanitize_key(metric_name)
296+
metric_name=_sanitize_metric_key(metric_name)
297+
metric_unit=_sanitize_unit(metric_unit)
273298
_write(metric_name.encode("utf-8"))
274299
_write(b"@")
275300
_write(metric_unit.encode("utf-8"))
@@ -285,7 +310,7 @@ def _encode_metrics(flushable_buckets):
285310
_write(b"|#")
286311
first=True
287312
fortag_key,tag_valueinmetric_tags:
288-
tag_key=_sanitize_key(tag_key)
313+
tag_key=_sanitize_tag_key(tag_key)
289314
ifnottag_key:
290315
continue
291316
iffirst:
@@ -294,7 +319,7 @@ def _encode_metrics(flushable_buckets):
294319
_write(b",")
295320
_write(tag_key.encode("utf-8"))
296321
_write(b":")
297-
_write(_sanitize_value(tag_value).encode("utf-8"))
322+
_write(_sanitize_tag_value(tag_value).encode("utf-8"))
298323

299324
_write(b"|T")
300325
_write(str(timestamp).encode("ascii"))
@@ -309,7 +334,9 @@ def _encode_locations(timestamp, code_locations):
309334

310335
forkey,locincode_locations:
311336
metric_type,name,unit=key
312-
mri="{}:{}@{}".format(metric_type,_sanitize_key(name),unit)
337+
mri="{}:{}@{}".format(
338+
metric_type,_sanitize_metric_key(name),_sanitize_unit(unit)
339+
)
313340

314341
loc["type"]="location"
315342
mapping.setdefault(mri, []).append(loc)

‎tests/test_metrics.py‎

Lines changed: 78 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -677,56 +677,99 @@ def test_metric_summaries(
677677

678678
@minimum_python_37_with_gevent
679679
@pytest.mark.forked
680-
deftest_tag_normalization(
681-
sentry_init,capture_envelopes,maybe_monkeypatched_threading
680+
@pytest.mark.parametrize(
681+
"metric_name,metric_unit,expected_name",
682+
[
683+
("first-metric","nano-second","first-metric@nanosecond"),
684+
("another_metric?","nano second","another_metric_@nanosecond"),
685+
(
686+
"metric",
687+
"nanosecond",
688+
"metric@nanosecond",
689+
),
690+
(
691+
"my.amaze.metric I guess",
692+
"nano|\nsecond",
693+
"my.amaze.metric_I_guess@nanosecond",
694+
),
695+
# fmt: off
696+
(u"métríc",u"nanöseconď",u"m_tr_c@nansecon"),
697+
# fmt: on
698+
],
699+
)
700+
deftest_metric_name_normalization(
701+
sentry_init,
702+
capture_envelopes,
703+
metric_name,
704+
metric_unit,
705+
expected_name,
706+
maybe_monkeypatched_threading,
682707
):
683708
sentry_init(
684-
release="fun-release@1.0.0",
685-
environment="not-fun-env",
686709
_experiments={"enable_metrics":True,"metric_code_locations":False},
687710
)
688-
ts=time.time()
689711
envelopes=capture_envelopes()
690712

691-
# fmt: off
692-
metrics.distribution("a",1.0,tags={"foo-bar":"%$foo"},timestamp=ts)
693-
metrics.distribution("b",1.0,tags={"foo$$$bar":"blah{}"},timestamp=ts)
694-
metrics.distribution("c",1.0,tags={u"foö-bar":u"snöwmän"},timestamp=ts)
695-
metrics.distribution("d",1.0,tags={"route":"GET /foo"},timestamp=ts)
696-
# fmt: on
713+
metrics.distribution(metric_name,1.0,unit=metric_unit)
714+
697715
Hub.current.flush()
698716

699717
(envelope,)=envelopes
700718

701719
assertlen(envelope.items)==1
702720
assertenvelope.items[0].headers["type"]=="statsd"
703-
m=parse_metrics(envelope.items[0].payload.get_bytes())
704721

705-
assertlen(m)==4
706-
assertm[0][4]== {
707-
"foo-bar":"$foo",
708-
"release":"fun-release@1.0.0",
709-
"environment":"not-fun-env",
710-
}
722+
parsed_metrics=parse_metrics(envelope.items[0].payload.get_bytes())
723+
assertlen(parsed_metrics)==1
711724

712-
assertm[1][4]== {
713-
"foo_bar":"blah{}",
714-
"release":"fun-release@1.0.0",
715-
"environment":"not-fun-env",
716-
}
725+
name=parsed_metrics[0][1]
726+
assertname==expected_name
717727

718-
# fmt: off
719-
assertm[2][4]== {
720-
"fo_-bar":u"snöwmän",
721-
"release":"fun-release@1.0.0",
722-
"environment":"not-fun-env",
723-
}
724-
assertm[3][4]== {
725-
"release":"fun-release@1.0.0",
726-
"environment":"not-fun-env",
727-
"route":"GET /foo",
728-
}
729-
# fmt: on
728+
729+
@minimum_python_37_with_gevent
730+
@pytest.mark.forked
731+
@pytest.mark.parametrize(
732+
"metric_tag,expected_tag",
733+
[
734+
({"f-oo|bar":"%$foo/"}, {"f-oobar":"%$foo/"}),
735+
({"foo$.$.$bar":"blah{}"}, {"foo..bar":"blah{}"}),
736+
# fmt: off
737+
({u"foö-bar":u"snöwmän"}, {u"fo-bar":u"snöwmän"},),
738+
# fmt: on
739+
({"route":"GET /foo"}, {"route":"GET /foo"}),
740+
({"__bar__":"this | or , that"}, {"__bar__":"this\\u{7c} or\\u{2c} that"}),
741+
({"foo/":"hello!\n\r\t\\"}, {"foo/":"hello!\\n\\r\\t\\\\"}),
742+
],
743+
)
744+
deftest_metric_tag_normalization(
745+
sentry_init,
746+
capture_envelopes,
747+
metric_tag,
748+
expected_tag,
749+
maybe_monkeypatched_threading,
750+
):
751+
sentry_init(
752+
_experiments={"enable_metrics":True,"metric_code_locations":False},
753+
)
754+
envelopes=capture_envelopes()
755+
756+
metrics.distribution("a",1.0,tags=metric_tag)
757+
758+
Hub.current.flush()
759+
760+
(envelope,)=envelopes
761+
762+
assertlen(envelope.items)==1
763+
assertenvelope.items[0].headers["type"]=="statsd"
764+
765+
parsed_metrics=parse_metrics(envelope.items[0].payload.get_bytes())
766+
assertlen(parsed_metrics)==1
767+
768+
tags=parsed_metrics[0][4]
769+
770+
expected_tag_key,expected_tag_value=expected_tag.popitem()
771+
assertexpected_tag_keyintags
772+
asserttags[expected_tag_key]==expected_tag_value
730773

731774

732775
@minimum_python_37_with_gevent

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp