Merge pull request #3175 from matrix-org/erikj/escape_metric_values

Escape label values in prometheus metrics
pull/3183/head
Erik Johnston 2018-05-03 10:01:04 +01:00 committed by GitHub
commit 53a5fdf312
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 3 deletions

View File

@ -16,6 +16,7 @@
from itertools import chain from itertools import chain
import logging import logging
import re
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -56,8 +57,7 @@ class BaseMetric(object):
return not len(self.labels) return not len(self.labels)
def _render_labelvalue(self, value): def _render_labelvalue(self, value):
# TODO: escape backslashes, quotes and newlines return '"%s"' % (_escape_label_value(value),)
return '"%s"' % (value)
def _render_key(self, values): def _render_key(self, values):
if self.is_scalar(): if self.is_scalar():
@ -299,3 +299,29 @@ class MemoryUsageMetric(object):
"process_psutil_rss:total %d" % sum_rss, "process_psutil_rss:total %d" % sum_rss,
"process_psutil_rss:count %d" % len_rss, "process_psutil_rss:count %d" % len_rss,
] ]
def _escape_character(m):
"""Replaces a single character with its escape sequence.
Args:
m (re.MatchObject): A match object whose first group is the single
character to replace
Returns:
str
"""
c = m.group(1)
if c == "\\":
return "\\\\"
elif c == "\"":
return "\\\""
elif c == "\n":
return "\\n"
return c
def _escape_label_value(value):
"""Takes a label value and escapes quotes, newlines and backslashes
"""
return re.sub(r"([\n\"\\])", _escape_character, value)

View File

@ -16,7 +16,8 @@
from tests import unittest from tests import unittest
from synapse.metrics.metric import ( from synapse.metrics.metric import (
CounterMetric, CallbackMetric, DistributionMetric, CacheMetric CounterMetric, CallbackMetric, DistributionMetric, CacheMetric,
_escape_label_value,
) )
@ -171,3 +172,21 @@ class CacheMetricTestCase(unittest.TestCase):
'cache:size{name="cache_name"} 1', 'cache:size{name="cache_name"} 1',
'cache:evicted_size{name="cache_name"} 2', 'cache:evicted_size{name="cache_name"} 2',
]) ])
class LabelValueEscapeTestCase(unittest.TestCase):
def test_simple(self):
string = "safjhsdlifhyskljfksdfh"
self.assertEqual(string, _escape_label_value(string))
def test_escape(self):
self.assertEqual(
"abc\\\"def\\nghi\\\\",
_escape_label_value("abc\"def\nghi\\"),
)
def test_sequence_of_escapes(self):
self.assertEqual(
"abc\\\"def\\nghi\\\\\\n",
_escape_label_value("abc\"def\nghi\\\n"),
)