{"id":557,"date":"2026-04-21T17:40:11","date_gmt":"2026-04-22T00:40:11","guid":{"rendered":"https:\/\/blog.mozilla.org\/performance\/?p=557"},"modified":"2026-04-22T09:22:16","modified_gmt":"2026-04-22T16:22:16","slug":"telemetry-alerting-how-it-works","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/performance\/2026\/04\/21\/telemetry-alerting-how-it-works\/","title":{"rendered":"Telemetry Alerting: How It Works"},"content":{"rendered":"<p>We recently released the telemetry alerting beta, and announced it in <a href=\"https:\/\/blog.mozilla.org\/performance\/2026\/04\/21\/telemetry-alerting-beta-announcement\/\">the blog post here<\/a>! This blog post will dive into the details of how it works across Treeherder, and Mozdetect. At a high level, MozDetect handles the change point detection for telemetry probes, and Treeherder handles storing the detections, and producing the emails\/bugs for these.<\/p>\n<h2>MozDetect<\/h2>\n<p>All of the existing, and any future change detection point techniques used for telemetry alerting are built in <a href=\"https:\/\/github.com\/gmierz\/mozdetect\">MozDetect<\/a>. Having these live outside of Treeherder gives a low-barrier to entry for adding new features, and testing existing ones without having to set up everything needed for alerting in Treeherder. It\u2019s built as a python module that is run through <a href=\"https:\/\/docs.astral.sh\/uv\/\">uv<\/a>. This makes it very easy for anyone to run the code because of uv\u2019s excellent python version, and dependency management. How to work with the code in this repository is <a href=\"https:\/\/github.com\/gmierz\/mozdetect?tab=readme-ov-file#setup-and-development\">outlined here<\/a>, along with <a href=\"https:\/\/github.com\/gmierz\/mozdetect?tab=readme-ov-file#testing-change-detection-techniques\">how to add your own techniques to it<\/a> (note the access to mozdata through gcloud is required for this).<\/p>\n<p>Detectors are split into two parts: (i) a detector that performs a comparison between two groups, and (ii) a detector that performs detection on a time series (using the detector from (i)). Our default detection technique, called \u00a0<code>cdf_squared<\/code>\u00a0 lives <a href=\"https:\/\/github.com\/gmierz\/mozdetect\/blob\/9b195d0d0edcf9d925d2d94a184bfef69fd9c7ae\/src\/mozdetect\/timeseries_detectors\/cdf_squared.py#L24\">here<\/a>. The\u00a0 <code>timeseries_detector_name<\/code>\u00a0 is the name that will be used to access the detector from the telemetry probe side through the\u00a0 <code>change_detection_technique<\/code>\u00a0 field. The only method that absolutely needs to be implemented by these is the <a href=\"https:\/\/github.com\/gmierz\/mozdetect\/blob\/9b195d0d0edcf9d925d2d94a184bfef69fd9c7ae\/src\/mozdetect\/timeseries_detectors\/cdf_squared.py#L258\">detect_changes<\/a> method and it must return a list of <a href=\"https:\/\/github.com\/gmierz\/mozdetect\/blob\/master\/src\/mozdetect\/timeseries_detectors\/detection.py#L6\">Detection<\/a> objects. These detection objects contain all the necessary information for producing an alert. There is also an\u00a0 <code>optional_detection_info<\/code>\u00a0 field that can contain additional things like <a href=\"https:\/\/github.com\/gmierz\/mozdetect\/blob\/9b195d0d0edcf9d925d2d94a184bfef69fd9c7ae\/src\/mozdetect\/timeseries_detectors\/cdf_squared.py#L187\">attachments<\/a> that would be added to Bugzilla bugs, and <a href=\"https:\/\/github.com\/gmierz\/mozdetect\/blob\/9b195d0d0edcf9d925d2d94a184bfef69fd9c7ae\/src\/mozdetect\/timeseries_detectors\/cdf_squared.py#L176\">additional_data<\/a> that can hold JSON data for storage in the DB. The <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cumulative_distribution_function\">cumulative distribution function<\/a> (CDF) squared technique uses these to store the CDF before and after the detection along with a graph of these as an attachment for the <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=2030847\">Bugzilla bug<\/a>.<\/p>\n<div id=\"attachment_559\" style=\"width: 544px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-559\" decoding=\"async\" loading=\"lazy\" class=\" wp-image-559\" src=\"http:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14.png\" alt=\"\" width=\"534\" height=\"293\" srcset=\"https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14.png 3010w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14-580x318.png 580w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14-940x516.png 940w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14-768x421.png 768w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14-1536x843.png 1536w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14-2048x1123.png 2048w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14-1400x770.png 1400w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14-600x330.png 600w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14-300x165.png 300w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/cdf_difference_2025-11-14-1000x549.png 1000w\" sizes=\"(max-width: 534px) 100vw, 534px\" \/><p id=\"caption-attachment-559\" class=\"wp-caption-text\">Example of a CDF graph that is provided in bugs.<\/p><\/div>\n<h2>CDF Squared Detection Technique<\/h2>\n<p>The CDF squared technique detects changes in time-series histogram data by comparing CDFs between consecutive windows. It takes two CDFs, each representing the distribution of measurements over a time window, and computes the sum of squared differences between the two CDFs at each bin. The sign of the summed linear difference is then used to assign a direction to the squared difference score so that the output encodes whether the distribution moved to higher values (right shift) or lower values (left shift).<\/p>\n<p>For time-series detection, this base comparison is applied in a rolling fashion across the full history of data. Each day&#8217;s 7-day smoothed CDF is compared against the next one, producing a continuous signal of squared CDF differences over time. A <a href=\"https:\/\/en.wikipedia.org\/wiki\/Butterworth_filter\">Butterworth low-pass filter<\/a> is then applied to that signal to remove high-frequency noise while preserving genuine trend changes. Finally, <a href=\"https:\/\/docs.scipy.org\/doc\/scipy\/reference\/generated\/scipy.signal.find_peaks.html\">scipy\u2019s find_peaks<\/a> function is used to locate statistically significant peaks and valleys in the filtered signal using a dynamic alert threshold based on the historical data. Information is extracted from those areas and then used to build the detection information needed for the alert generation process.<\/p>\n<p>&nbsp;<\/p>\n<h2>Alerting<\/h2>\n<p>Our alerting tooling lives <a href=\"https:\/\/github.com\/mozilla\/treeherder\/tree\/90f1ecd41c23c060cd2a8663775a738465eb991b\/treeherder\/perf\/auto_perf_sheriffing\/telemetry_alerting\">in the Treeherder codebase<\/a>. It\u2019s run through our PerfSheriff Bot (called Sherlock) and runs once per day. When a detection is produced from MozDetect, a telemetry alert is added to the database and then the <a href=\"https:\/\/github.com\/mozilla\/treeherder\/blob\/eb32f0d83242c1a10cacede6168a32e905c8c4ab\/treeherder\/perf\/auto_perf_sheriffing\/telemetry_alerting\/alert_manager.py#L32\">TelemetryAlertManager<\/a> is called to handle it. The manager\u2019s tasks are split into <a href=\"https:\/\/github.com\/mozilla\/treeherder\/blob\/master\/treeherder\/perf\/auto_perf_sheriffing\/base_alert_manager.py#L34-L83\">6 ordered phases<\/a>:<\/p>\n<ol>\n<li aria-level=\"1\"><b>Update alerts with changes from Bugzilla.<\/b> This step ensures that any changes that happen in the bugs filed are mirrored into our database. Currently, we only track resolution changes here.<\/li>\n<li aria-level=\"1\"><b>Comment on existing bugs<\/b>. This step is for updating existing bugs with information from new alerts. This step is not currently being used. In the future, this could be used to inform probe owners that a probe which doesn\u2019t produce bugs has produced an alert in the same time range.<\/li>\n<li aria-level=\"1\"><b>File new bugs for alerts.<\/b> This step handles filing bugs for any new alerts on probes set up for producing bugs.<\/li>\n<li aria-level=\"1\"><b>Modify existing bugs with new alerts. <\/b>This step handles any modifications needed to existing bugs based on the new bugs that were created. Currently, the &#8220;See Also&#8221; field is modified for existing bugs to include the new bugs.<\/li>\n<li aria-level=\"1\"><b>Produce emails for new alerts.<\/b> This step handles producing emails for any alerts set up to produce emails.<\/li>\n<li aria-level=\"1\"><b>Housekeeping.<\/b> This step handles redoing any failures that happen above in either the current run or past runs. Currently, it\u2019s being used to retry bug modifications and sending emails when we encounter a failure there. This excludes retrying bug filling since we delete the alert in that case and retry it the next time the alert is generated.<\/li>\n<\/ol>\n<p>After the housekeeping step, the manager is done for the day and runs again on the next day to handle any updates and new alerts. Contrary to how alerting works for performance tests in CI, this process is fully automated and requires no human input at any point.<\/p>\n<p>Setting up telemetry probes for alerting happens on the mozilla-central side in their probe schema using the new\u00a0 <code>monitor<\/code>\u00a0 field in the\u00a0 <code>metadata<\/code>\u00a0 section (<a href=\"https:\/\/searchfox.org\/firefox-main\/rev\/dab03896ede1413be148884e054b311767bcf1a0\/dom\/metrics.yaml#29\">example for email alerts<\/a>, <a href=\"https:\/\/searchfox.org\/firefox-main\/rev\/bd4dbb8971753f7082ad484540007c164cf112bc\/netwerk\/metrics.yaml#1932\">example for bug alerts<\/a>). The <a href=\"https:\/\/firefox-source-docs.mozilla.org\/testing\/perfdocs\/telemetry-alerting.html\">telemetry alerting documentation<\/a> has information about how to do this. We then use an <a href=\"https:\/\/dictionary.telemetry.mozilla.org\/data\/firefox_desktop\/index.json\">index.json<\/a> file from the telemetry dictionary to gather all the probes that should be alerting. The information there is supplemented by <a href=\"https:\/\/dictionary.telemetry.mozilla.org\/data\/firefox_desktop\/metrics\/data_perf_largest_contentful_paint.json\">more granular information<\/a> later in the pipeline to gather things like the time unit used for the probe to be able to better format the Bugzilla bug table.<\/p>\n<p>Once a telemetry probe is set up for alerting and is found by our system, the owners (those listed in the email notification fields) will begin either receiving emails or have bugs produced for them. These can also be viewed by everyone on <a href=\"https:\/\/gmierz.github.io\/telemetry-alert-dashboard\/?view=without-bugs\">this dashboard<\/a>.<\/p>\n<div id=\"attachment_558\" style=\"width: 1493px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-558\" decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-558\" src=\"http:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/Screenshot-2026-04-21-at-13-05-09-Telemetry-Alert-Dashboard.png\" alt=\"\" width=\"1483\" height=\"920\" srcset=\"https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/Screenshot-2026-04-21-at-13-05-09-Telemetry-Alert-Dashboard.png 1483w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/Screenshot-2026-04-21-at-13-05-09-Telemetry-Alert-Dashboard-580x360.png 580w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/Screenshot-2026-04-21-at-13-05-09-Telemetry-Alert-Dashboard-940x583.png 940w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/Screenshot-2026-04-21-at-13-05-09-Telemetry-Alert-Dashboard-768x476.png 768w, https:\/\/blog.mozilla.org\/performance\/files\/2026\/04\/Screenshot-2026-04-21-at-13-05-09-Telemetry-Alert-Dashboard-1000x620.png 1000w\" sizes=\"(max-width: 1483px) 100vw, 1483px\" \/><p id=\"caption-attachment-558\" class=\"wp-caption-text\">Example of an alert being viewed in <a href=\"https:\/\/gmierz.github.io\/telemetry-alert-dashboard\/?view=without-bugs&amp;alertSummaryId=361\">the dashboard<\/a>.<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2>Acknowledgements<\/h2>\n<p>Getting the project to this point involved work from people across multiple teams here at Mozilla. Special thanks to <strong>Eduardo Filho<\/strong> for his support on the telemetry probe side, to <strong>Bas Schouten<\/strong> for his guidance and work on the CDF Squared detection technique, and to <strong>Andrej Glavic<\/strong> and <strong>Beatrice Acasandrei<\/strong> for their help in reviewing the Treeherder-related changes.<\/p>\n<p>If you hit any issues with the telemetry alerting system, or have any suggestions feel free to file a bug in the <a href=\"https:\/\/bugzilla.mozilla.org\/enter_bug.cgi?product=Testing&amp;component=Performance&amp;status_whiteboard=[fxp]\">Testing :: Performance component<\/a> or reach out to us in either #perf-help on Slack or in <a href=\"https:\/\/matrix.to\/#\/#perftest:mozilla.org\">#perftest on Matrix<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We recently released the telemetry alerting beta, and announced it in the blog post here! This blog post will dive into the details of how it works across Treeherder, and &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/performance\/2026\/04\/21\/telemetry-alerting-how-it-works\/\">Read more<\/a><\/p>\n","protected":false},"author":1804,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[457027],"tags":[457028,457016],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/performance\/wp-json\/wp\/v2\/posts\/557"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/performance\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/performance\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/performance\/wp-json\/wp\/v2\/users\/1804"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/performance\/wp-json\/wp\/v2\/comments?post=557"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/performance\/wp-json\/wp\/v2\/posts\/557\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/performance\/wp-json\/wp\/v2\/media?parent=557"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/performance\/wp-json\/wp\/v2\/categories?post=557"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/performance\/wp-json\/wp\/v2\/tags?post=557"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}