1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.cts.statsd.alert; 17 18 import static com.google.common.truth.Truth.assertThat; 19 import static com.google.common.truth.Truth.assertWithMessage; 20 21 import android.cts.statsd.atom.AtomTestCase; 22 23 import com.android.internal.os.StatsdConfigProto; 24 import com.android.internal.os.StatsdConfigProto.Alert; 25 import com.android.internal.os.StatsdConfigProto.CountMetric; 26 import com.android.internal.os.StatsdConfigProto.DurationMetric; 27 import com.android.internal.os.StatsdConfigProto.FieldFilter; 28 import com.android.internal.os.StatsdConfigProto.FieldMatcher; 29 import com.android.internal.os.StatsdConfigProto.GaugeMetric; 30 import com.android.internal.os.StatsdConfigProto.IncidentdDetails; 31 import com.android.internal.os.StatsdConfigProto.PerfettoDetails; 32 import com.android.internal.os.StatsdConfigProto.StatsdConfig; 33 import com.android.internal.os.StatsdConfigProto.Subscription; 34 import com.android.internal.os.StatsdConfigProto.TimeUnit; 35 import com.android.internal.os.StatsdConfigProto.ValueMetric; 36 import com.android.os.AtomsProto.AnomalyDetected; 37 import com.android.os.AtomsProto.AppBreadcrumbReported; 38 import com.android.os.AtomsProto.Atom; 39 import com.android.os.AtomsProto.DebugElapsedClock; 40 import com.android.os.StatsLog.EventMetricData; 41 import com.android.tradefed.log.LogUtil.CLog; 42 import java.util.List; 43 44 /** 45 * Statsd Anomaly Detection tests. 46 */ 47 public class AnomalyDetectionTests extends AtomTestCase { 48 49 private static final String TAG = "Statsd.AnomalyDetectionTests"; 50 51 private static final boolean INCIDENTD_TESTS_ENABLED = false; 52 private static final boolean PERFETTO_TESTS_ENABLED = true; 53 54 private static final int WAIT_AFTER_BREADCRUMB_MS = 2000; 55 56 // Config constants 57 private static final int APP_BREADCRUMB_REPORTED_MATCH_START_ID = 1; 58 private static final int APP_BREADCRUMB_REPORTED_MATCH_STOP_ID = 2; 59 private static final int METRIC_ID = 8; 60 private static final int ALERT_ID = 11; 61 private static final int SUBSCRIPTION_ID_INCIDENTD = 41; 62 private static final int SUBSCRIPTION_ID_PERFETTO = 42; 63 private static final int ANOMALY_DETECT_MATCH_ID = 10; 64 private static final int ANOMALY_EVENT_ID = 101; 65 private static final int INCIDENTD_SECTION = -1; 66 67 private boolean defaultSystemTracingConfigurationHasChanged = false; 68 69 @Override setUp()70 protected void setUp() throws Exception { 71 super.setUp(); 72 if (!INCIDENTD_TESTS_ENABLED) { 73 CLog.w(TAG, TAG + " anomaly tests are disabled by a flag. Change flag to true to run"); 74 } 75 if (PERFETTO_TESTS_ENABLED) { 76 // Default Android configuration can only change for device type that doesn't require SystemTracingEnabled 77 // by default in CDD. 78 String chars = getDevice().getProperty("ro.build.characteristics"); 79 if (!isSystemTracingEnabled() && chars.contains("automotive")) { 80 enableSystemTracing(); 81 defaultSystemTracingConfigurationHasChanged = true; 82 } 83 } 84 } 85 86 @Override tearDown()87 protected void tearDown() throws Exception { 88 super.tearDown(); 89 if (PERFETTO_TESTS_ENABLED) { 90 // Disable SystemTracing if previously enabled at test setUp() 91 if (defaultSystemTracingConfigurationHasChanged) { 92 disableSystemTracing(); 93 } 94 // Deadline to finish trace collection 95 final long deadLine = System.currentTimeMillis() + 10000; 96 while (isSystemTracingEnabled()) { 97 if (System.currentTimeMillis() > deadLine) { 98 CLog.w("/sys/kernel/debug/tracing/tracing_on is still 1 after 10 secs : " + isSystemTracingEnabled()); 99 break; 100 } 101 CLog.d("Waiting to finish collecting traces. "); 102 Thread.sleep(WAIT_TIME_SHORT); 103 } 104 } 105 } 106 107 // Tests that anomaly detection for count works. 108 // Also tests that anomaly detection works when spanning multiple buckets. testCountAnomalyDetection()109 public void testCountAnomalyDetection() throws Exception { 110 StatsdConfig.Builder config = getBaseConfig(10, 20, 2 /* threshold: > 2 counts */) 111 .addCountMetric(CountMetric.newBuilder() 112 .setId(METRIC_ID) 113 .setWhat(APP_BREADCRUMB_REPORTED_MATCH_START_ID) 114 .setBucket(TimeUnit.CTS) // 1 second 115 // Slice by label 116 .setDimensionsInWhat(FieldMatcher.newBuilder() 117 .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) 118 .addChild(FieldMatcher.newBuilder() 119 .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER) 120 ) 121 ) 122 ); 123 uploadConfig(config); 124 125 String markTime = getCurrentLogcatDate(); 126 // count(label=6) -> 1 (not an anomaly, since not "greater than 2") 127 doAppBreadcrumbReportedStart(6); 128 Thread.sleep(500); 129 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 130 if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse(); 131 132 // count(label=6) -> 2 (not an anomaly, since not "greater than 2") 133 doAppBreadcrumbReportedStart(6); 134 Thread.sleep(500); 135 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 136 if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse(); 137 138 // count(label=12) -> 1 (not an anomaly, since not "greater than 2") 139 doAppBreadcrumbReportedStart(12); 140 Thread.sleep(1000); 141 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 142 if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse(); 143 144 doAppBreadcrumbReportedStart(6); // count(label=6) -> 3 (anomaly, since "greater than 2"!) 145 Thread.sleep(WAIT_AFTER_BREADCRUMB_MS); 146 147 List<EventMetricData> data = getEventMetricDataList(); 148 assertWithMessage("Expected anomaly").that(data).hasSize(1); 149 assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID); 150 if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue(); 151 } 152 153 // Tests that anomaly detection for duration works. 154 // Also tests that refractory periods in anomaly detection work. testDurationAnomalyDetection()155 public void testDurationAnomalyDetection() throws Exception { 156 final int APP_BREADCRUMB_REPORTED_IS_ON_PREDICATE = 1423; 157 StatsdConfig.Builder config = 158 getBaseConfig(17, 17, 10_000_000_000L /*threshold: > 10 seconds in nanoseconds*/) 159 .addDurationMetric(DurationMetric.newBuilder() 160 .setId(METRIC_ID) 161 .setWhat(APP_BREADCRUMB_REPORTED_IS_ON_PREDICATE) // predicate below 162 .setAggregationType(DurationMetric.AggregationType.SUM) 163 .setBucket(TimeUnit.CTS) // 1 second 164 ) 165 .addPredicate(StatsdConfigProto.Predicate.newBuilder() 166 .setId(APP_BREADCRUMB_REPORTED_IS_ON_PREDICATE) 167 .setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder() 168 .setStart(APP_BREADCRUMB_REPORTED_MATCH_START_ID) 169 .setStop(APP_BREADCRUMB_REPORTED_MATCH_STOP_ID) 170 ) 171 ); 172 uploadConfig(config); 173 174 // Since timing is crucial and checking logcat for incidentd is slow, we don't test for it. 175 176 // Test that alarm doesn't fire early. 177 String markTime = getCurrentLogcatDate(); 178 doAppBreadcrumbReportedStart(1); 179 Thread.sleep(6_000); // Recorded duration at end: 6s 180 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 181 182 doAppBreadcrumbReportedStop(1); 183 Thread.sleep(4_000); // Recorded duration at end: 6s 184 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 185 186 // Test that alarm does fire when it is supposed to (after 4s, plus up to 5s alarm delay). 187 doAppBreadcrumbReportedStart(1); 188 Thread.sleep(9_000); // Recorded duration at end: 13s 189 doAppBreadcrumbReported(2); 190 List<EventMetricData> data = getEventMetricDataList(); 191 assertWithMessage("Expected anomaly").that(data).hasSize(1); 192 assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID); 193 194 // Now test that the refractory period is obeyed. 195 markTime = getCurrentLogcatDate(); 196 doAppBreadcrumbReportedStop(1); 197 doAppBreadcrumbReportedStart(1); 198 Thread.sleep(3_000); // Recorded duration at end: 13s 199 // NB: the previous getEventMetricDataList also removes the report, so size is back to 0. 200 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 201 202 // Test that detection works again after refractory period finishes. 203 doAppBreadcrumbReportedStop(1); 204 Thread.sleep(8_000); // Recorded duration at end: 9s 205 doAppBreadcrumbReportedStart(1); 206 Thread.sleep(15_000); // Recorded duration at end: 15s 207 // We can do an incidentd test now that all the timing issues are done. 208 doAppBreadcrumbReported(2); 209 data = getEventMetricDataList(); 210 assertWithMessage("Expected anomaly").that(data).hasSize(1); 211 assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID); 212 if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue(); 213 214 doAppBreadcrumbReportedStop(1); 215 } 216 217 // Tests that anomaly detection for duration works even when the alarm fires too late. testDurationAnomalyDetectionForLateAlarms()218 public void testDurationAnomalyDetectionForLateAlarms() throws Exception { 219 final int APP_BREADCRUMB_REPORTED_IS_ON_PREDICATE = 1423; 220 StatsdConfig.Builder config = 221 getBaseConfig(50, 0, 6_000_000_000L /* threshold: > 6 seconds in nanoseconds */) 222 .addDurationMetric(DurationMetric.newBuilder() 223 .setId(METRIC_ID) 224 .setWhat( 225 APP_BREADCRUMB_REPORTED_IS_ON_PREDICATE) // Predicate below. 226 .setAggregationType(DurationMetric.AggregationType.SUM) 227 .setBucket(TimeUnit.CTS) // 1 second 228 ) 229 .addPredicate(StatsdConfigProto.Predicate.newBuilder() 230 .setId(APP_BREADCRUMB_REPORTED_IS_ON_PREDICATE) 231 .setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder() 232 .setStart(APP_BREADCRUMB_REPORTED_MATCH_START_ID) 233 .setStop(APP_BREADCRUMB_REPORTED_MATCH_STOP_ID) 234 ) 235 ); 236 uploadConfig(config); 237 238 doAppBreadcrumbReportedStart(1); 239 Thread.sleep(5_000); 240 doAppBreadcrumbReportedStop(1); 241 Thread.sleep(2_000); 242 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 243 244 // Test that alarm does fire when it is supposed to. 245 // The anomaly occurs in 1s, but alarms won't fire that quickly. 246 // It is likely that the alarm will only fire after this period is already over, but the 247 // anomaly should nonetheless be detected when the event stops. 248 doAppBreadcrumbReportedStart(1); 249 Thread.sleep(1_200); 250 // Anomaly should be detected here if the alarm didn't fire yet. 251 doAppBreadcrumbReportedStop(1); 252 Thread.sleep(200); 253 List<EventMetricData> data = getEventMetricDataList(); 254 if (data.size() == 2) { 255 // Although we expect that the alarm won't fire, we certainly cannot demand that. 256 CLog.w(TAG, "The anomaly was detected twice. Presumably the alarm did manage to fire."); 257 } 258 assertThat(data.size()).isAnyOf(1, 2); 259 assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID); 260 } 261 262 // Tests that anomaly detection for value works. testValueAnomalyDetection()263 public void testValueAnomalyDetection() throws Exception { 264 StatsdConfig.Builder config = getBaseConfig(4, 0, 6 /* threshold: value > 6 */) 265 .addValueMetric(ValueMetric.newBuilder() 266 .setId(METRIC_ID) 267 .setWhat(APP_BREADCRUMB_REPORTED_MATCH_START_ID) 268 .setBucket(TimeUnit.ONE_MINUTE) 269 // Get the label field's value: 270 .setValueField(FieldMatcher.newBuilder() 271 .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) 272 .addChild(FieldMatcher.newBuilder() 273 .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)) 274 ) 275 276 ); 277 uploadConfig(config); 278 279 String markTime = getCurrentLogcatDate(); 280 doAppBreadcrumbReportedStart(6); // value = 6, which is NOT > trigger 281 Thread.sleep(WAIT_AFTER_BREADCRUMB_MS); 282 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 283 if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse(); 284 285 doAppBreadcrumbReportedStart(14); // value = 14 > trigger 286 Thread.sleep(WAIT_AFTER_BREADCRUMB_MS); 287 288 List<EventMetricData> data = getEventMetricDataList(); 289 assertWithMessage("Expected anomaly").that(data).hasSize(1); 290 assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID); 291 if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue(); 292 } 293 294 // Test that anomaly detection integrates with perfetto properly. testPerfetto()295 public void testPerfetto() throws Exception { 296 String chars = getDevice().getProperty("ro.build.characteristics"); 297 if (chars.contains("watch")) { 298 return; 299 } 300 301 if (PERFETTO_TESTS_ENABLED) resetPerfettoGuardrails(); 302 303 StatsdConfig.Builder config = getBaseConfig(4, 0, 6 /* threshold: value > 6 */) 304 .addSubscription(Subscription.newBuilder() 305 .setId(SUBSCRIPTION_ID_PERFETTO) 306 .setRuleType(Subscription.RuleType.ALERT) 307 .setRuleId(ALERT_ID) 308 .setPerfettoDetails(PerfettoDetails.newBuilder() 309 .setTraceConfig(getPerfettoConfig())) 310 ) 311 .addValueMetric(ValueMetric.newBuilder() 312 .setId(METRIC_ID) 313 .setWhat(APP_BREADCRUMB_REPORTED_MATCH_START_ID) 314 .setBucket(TimeUnit.ONE_MINUTE) 315 // Get the label field's value: 316 .setValueField(FieldMatcher.newBuilder() 317 .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) 318 .addChild(FieldMatcher.newBuilder() 319 .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)) 320 ) 321 322 ); 323 uploadConfig(config); 324 325 String markTime = getCurrentLogcatDate(); 326 doAppBreadcrumbReportedStart(6); // value = 6, which is NOT > trigger 327 Thread.sleep(WAIT_AFTER_BREADCRUMB_MS); 328 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 329 if (PERFETTO_TESTS_ENABLED) assertThat(isSystemTracingEnabled()).isFalse(); 330 331 doAppBreadcrumbReportedStart(14); // value = 14 > trigger 332 Thread.sleep(WAIT_AFTER_BREADCRUMB_MS); 333 334 List<EventMetricData> data = getEventMetricDataList(); 335 assertWithMessage("Expected anomaly").that(data).hasSize(1); 336 assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID); 337 338 // Pool a few times to allow for statsd <-> traced <-> traced_probes communication to happen. 339 if (PERFETTO_TESTS_ENABLED) { 340 boolean tracingEnabled = false; 341 for (int i = 0; i < 5; i++) { 342 if (isSystemTracingEnabled()) { 343 tracingEnabled = true; 344 break; 345 } 346 Thread.sleep(1000); 347 } 348 assertThat(tracingEnabled).isTrue(); 349 } 350 } 351 352 // Tests that anomaly detection for gauge works. testGaugeAnomalyDetection()353 public void testGaugeAnomalyDetection() throws Exception { 354 StatsdConfig.Builder config = getBaseConfig(1, 20, 6 /* threshold: value > 6 */) 355 .addGaugeMetric(GaugeMetric.newBuilder() 356 .setId(METRIC_ID) 357 .setWhat(APP_BREADCRUMB_REPORTED_MATCH_START_ID) 358 .setBucket(TimeUnit.CTS) 359 // Get the label field's value into the gauge: 360 .setGaugeFieldsFilter( 361 FieldFilter.newBuilder().setFields(FieldMatcher.newBuilder() 362 .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) 363 .addChild(FieldMatcher.newBuilder() 364 .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)) 365 ) 366 ) 367 ); 368 uploadConfig(config); 369 370 String markTime = getCurrentLogcatDate(); 371 doAppBreadcrumbReportedStart(6); // gauge = 6, which is NOT > trigger 372 Thread.sleep(Math.max(WAIT_AFTER_BREADCRUMB_MS, 1_100)); // Must be >1s to push next bucket. 373 assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty(); 374 if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse(); 375 376 // We waited for >1s above, so we are now in the next bucket (which is essential). 377 doAppBreadcrumbReportedStart(14); // gauge = 14 > trigger 378 Thread.sleep(WAIT_AFTER_BREADCRUMB_MS); 379 380 List<EventMetricData> data = getEventMetricDataList(); 381 assertWithMessage("Expected anomaly").that(data).hasSize(1); 382 assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID); 383 if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue(); 384 } 385 386 // Test that anomaly detection for pulled metrics work. testPulledAnomalyDetection()387 public void testPulledAnomalyDetection() throws Exception { 388 final int ATOM_ID = Atom.DEBUG_ELAPSED_CLOCK_FIELD_NUMBER; // A pulled atom 389 final int VALUE_FIELD = 390 DebugElapsedClock.ELAPSED_CLOCK_MILLIS_FIELD_NUMBER; // Something that will be > 0. 391 final int ATOM_MATCHER_ID = 300; 392 393 StatsdConfig.Builder config = getBaseConfig(10, 20, 0 /* threshold: value > 0 */) 394 .addAllowedLogSource("AID_SYSTEM") 395 // Track the ATOM_ID pulled atom 396 .addAtomMatcher(StatsdConfigProto.AtomMatcher.newBuilder() 397 .setId(ATOM_MATCHER_ID) 398 .setSimpleAtomMatcher(StatsdConfigProto.SimpleAtomMatcher.newBuilder() 399 .setAtomId(ATOM_ID))) 400 .addGaugeMetric(GaugeMetric.newBuilder() 401 .setId(METRIC_ID) 402 .setWhat(ATOM_MATCHER_ID) 403 .setBucket(TimeUnit.CTS) 404 .setSamplingType(GaugeMetric.SamplingType.RANDOM_ONE_SAMPLE) 405 // Track the VALUE_FIELD (anomaly detection requires exactly one field here) 406 .setGaugeFieldsFilter( 407 FieldFilter.newBuilder().setFields(FieldMatcher.newBuilder() 408 .setField(ATOM_ID) 409 .addChild(FieldMatcher.newBuilder().setField(VALUE_FIELD)) 410 ) 411 ) 412 ); 413 uploadConfig(config); 414 415 Thread.sleep(6_000); // Wait long enough to ensure AlarmManager signals >= 1 pull 416 417 List<EventMetricData> data = getEventMetricDataList(); 418 assertThat(data.size()).isEqualTo(1); 419 assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID); 420 } 421 422 getBaseConfig(int numBuckets, int refractorySecs, long triggerIfSumGt)423 private final StatsdConfig.Builder getBaseConfig(int numBuckets, 424 int refractorySecs, 425 long triggerIfSumGt) throws Exception { 426 return createConfigBuilder() 427 // Items of relevance for detecting the anomaly: 428 .addAtomMatcher( 429 StatsdConfigProto.AtomMatcher.newBuilder() 430 .setId(APP_BREADCRUMB_REPORTED_MATCH_START_ID) 431 .setSimpleAtomMatcher( 432 StatsdConfigProto.SimpleAtomMatcher.newBuilder() 433 .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) 434 // Event only when the uid is this app's uid. 435 .addFieldValueMatcher(createFvm(AppBreadcrumbReported.UID_FIELD_NUMBER) 436 .setEqInt(getHostUid())) 437 .addFieldValueMatcher( 438 createFvm(AppBreadcrumbReported.STATE_FIELD_NUMBER) 439 .setEqInt(AppBreadcrumbReported.State.START.ordinal())))) 440 .addAtomMatcher( 441 StatsdConfigProto.AtomMatcher.newBuilder() 442 .setId(APP_BREADCRUMB_REPORTED_MATCH_STOP_ID) 443 .setSimpleAtomMatcher( 444 StatsdConfigProto.SimpleAtomMatcher.newBuilder() 445 .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) 446 // Event only when the uid is this app's uid. 447 .addFieldValueMatcher(createFvm(AppBreadcrumbReported.UID_FIELD_NUMBER) 448 .setEqInt(getHostUid())) 449 .addFieldValueMatcher( 450 createFvm(AppBreadcrumbReported.STATE_FIELD_NUMBER) 451 .setEqInt(AppBreadcrumbReported.State.STOP.ordinal())))) 452 .addAlert(Alert.newBuilder() 453 .setId(ALERT_ID) 454 .setMetricId(METRIC_ID) // The metric itself must yet be added by the test. 455 .setNumBuckets(numBuckets) 456 .setRefractoryPeriodSecs(refractorySecs) 457 .setTriggerIfSumGt(triggerIfSumGt)) 458 .addSubscription( 459 Subscription.newBuilder() 460 .setId(SUBSCRIPTION_ID_INCIDENTD) 461 .setRuleType(Subscription.RuleType.ALERT) 462 .setRuleId(ALERT_ID) 463 .setIncidentdDetails(IncidentdDetails.newBuilder().addSection(INCIDENTD_SECTION))) 464 // We want to trigger anomalies on METRIC_ID, but don't want the actual data. 465 .addNoReportMetric(METRIC_ID) 466 467 // Items of relevance to reporting the anomaly (we do want this data): 468 .addAtomMatcher( 469 StatsdConfigProto.AtomMatcher.newBuilder() 470 .setId(ANOMALY_DETECT_MATCH_ID) 471 .setSimpleAtomMatcher( 472 StatsdConfigProto.SimpleAtomMatcher.newBuilder() 473 .setAtomId(Atom.ANOMALY_DETECTED_FIELD_NUMBER) 474 .addFieldValueMatcher(createFvm(AnomalyDetected.CONFIG_UID_FIELD_NUMBER) 475 .setEqInt(getHostUid())) 476 .addFieldValueMatcher(createFvm(AnomalyDetected.CONFIG_ID_FIELD_NUMBER) 477 .setEqInt(CONFIG_ID)))) 478 .addEventMetric(StatsdConfigProto.EventMetric.newBuilder() 479 .setId(ANOMALY_EVENT_ID) 480 .setWhat(ANOMALY_DETECT_MATCH_ID)); 481 } 482 } 483