Slice by state in CountMetricProducer
- When a log event is matched to the "what" atom of a metric,
MetricProducer parses the event for state primary key(s) and queries
StateTracker for the correct state values
- CountMetricProducer writes state information to the dump report proto
- Added e2e CountMetric tests that push events to StatsLogProcessor and
check that the resulting dump report is correct
Test: bit statsd_test:*
Bug: 136566566
Bug: 142124705
Change-Id: I9ca5b097151c0c73a617f3b16c8c3584b068cd82
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 2a700ef..2c8f0e3 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -16,8 +16,11 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
+
#include "MetricProducer.h"
+#include "state/StateTracker.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_ENUM;
using android::util::FIELD_TYPE_INT32;
@@ -92,9 +95,43 @@
condition = mCondition == ConditionState::kTrue;
}
+ // Stores atom id to primary key pairs for each state atom that the metric is
+ // sliced by.
+ std::map<int, HashableDimensionKey> statePrimaryKeys;
+
+ // For states with primary fields, use MetricStateLinks to get the primary
+ // field values from the log event. These values will form a primary key
+ // that will be used to query StateTracker for the correct state value.
+ for (const auto& stateLink : mMetric2StateLinks) {
+ getDimensionForState(event.getValues(), stateLink,
+ &statePrimaryKeys[stateLink.stateAtomId]);
+ }
+
+ // For each sliced state, query StateTracker for the state value using
+ // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
+ //
+ // Expected functionality: for any case where the MetricStateLinks are
+ // initialized incorrectly (ex. # of state links != # of primary fields, no
+ // links are provided for a state with primary fields, links are provided
+ // in the wrong order, etc.), StateTracker will simply return kStateUnknown
+ // when queried using an incorrect key.
+ HashableDimensionKey stateValuesKey;
+ for (auto atomId : mSlicedStateAtoms) {
+ FieldValue value;
+ if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
+ // found a primary key for this state, query using the key
+ getMappedStateValue(atomId, statePrimaryKeys[atomId], &value);
+ } else {
+ // if no MetricStateLinks exist for this state atom,
+ // query using the default dimension key (empty HashableDimensionKey)
+ getMappedStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
+ }
+ stateValuesKey.addValue(value);
+ }
+
HashableDimensionKey dimensionInWhat;
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
- MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY);
+ MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
onMatchedLogEventInternalLocked(
matcherIndex, metricKey, conditionKey, condition, event);
}
@@ -227,6 +264,31 @@
}
}
+void MetricProducer::getMappedStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+ FieldValue* value) {
+ if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
+ value->mValue = Value(StateTracker::kStateUnknown);
+ ALOGW("StateTracker not found for state atom %d", atomId);
+ return;
+ }
+
+ // check if there is a state map for this atom
+ auto atomIt = mStateGroupMap.find(atomId);
+ if (atomIt == mStateGroupMap.end()) {
+ return;
+ }
+ auto valueIt = atomIt->second.find(value->mValue.int_value);
+ if (valueIt == atomIt->second.end()) {
+ // state map exists, but value was not put in a state group
+ // so set mValue to kStateUnknown
+ // TODO(tsaichristine): handle incomplete state maps
+ value->mValue.setInt(StateTracker::kStateUnknown);
+ } else {
+ // set mValue to group_id
+ value->mValue.setLong(valueIt->second);
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android