/*
* Copyright 2012 Nodeable Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.streamreduce.storm.bolts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.streamreduce.ConnectionTypeConstants;
import com.streamreduce.Constants;
import com.streamreduce.ProviderIdConstants;
import com.streamreduce.analytics.MetricName;
import com.streamreduce.analytics.MetricCriteria;
import com.streamreduce.core.event.EventId;
import com.streamreduce.core.metric.MetricModeType;
import com.streamreduce.storm.MongoClient;
import com.streamreduce.storm.utils.MetricsUtils;
import org.apache.log4j.Logger;
/**
* Class that all metrics bolts will extend.
*/
public abstract class AbstractMetricsBolt extends NodeableUnreliableBolt {
private static final MongoClient MESSAGE_DB_MONGO_CLIENT = new MongoClient(MongoClient.MESSAGEDB_CONFIG_ID);
private static final Logger LOGGER = Logger.getLogger(AbstractMetricsBolt.class);
private static final String GLOBAL_ACCOUNT_ID = "global";
protected static final Map<MetricCriteria, String> EMPTY_CRITERIA = Collections.emptyMap();
/**
* Handler for customized event handling (above and beyond the built-in handling).
*
* @param id the event's unique id
* @param timestamp the event's timestamp
* @param eventId the event's {@link EventId}
* @param accountId the event's account id
* @param userId the event's user id
* @param targetId the event's target id
* @param metadata the event's metadata
*/
public abstract void handleEvent(String id, Long timestamp, EventId eventId, String accountId, String userId,
String targetId, Map<String, Object> metadata);
/**
* {@inheritDoc}
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {
outputFieldsDeclarer.declare(new Fields(
"metricAccount", // The account id the metric is associated with
"metricName", // The low-level name of the metric
"metricMode", // The metric type
"metricCriteria", // The metric criteria
"metricTimestamp", // The timestemp of the metric
"metricValue", // The value of the metric
"metricId" // The unique identifier for the metric
));
}
/**
* {@inheritDoc}
*/
@Override
public void realExecute(Tuple tuple) {
Map<String, Object> event = (Map<String, Object>) tuple.getValue(0);
String id = event.get("_id") != null ? event.get("_id").toString() : null;
Long timestamp = event.get("timestamp") != null ? (Long) event.get("timestamp") : null;
EventId eventId = event.get("eventId") != null ? EventId.valueOf(event.get("eventId").toString()) : null;
String accountId = event.get("accountId") != null ? event.get("accountId").toString() : null;
String userId = event.get("userId") != null ? event.get("userId").toString() : null;
String targetId = event.get("targetId") != null ? event.get("targetId").toString() : null;
Map<String, Object> metadata = event.get("metadata") != null ?
(Map<String, Object>) event.get("metadata") :
new HashMap<String, Object>();
String targetType = metadata.get("targetType") != null ? metadata.get("targetType").toString() : null;
// Handle type-based counts
handleObjectCounts(id, timestamp, eventId, accountId, userId, targetId, targetType, metadata);
// Send down stream to allow for deeper level metrics
handleEvent(id, timestamp, eventId, accountId, userId, targetId, metadata);
}
/**
* Handler for built-in event handling. This handler will do CRUD/activity counts based on the event type and
* event id.
*
* @param id the event's unique id
* @param timestamp the event's timestamp
* @param eventId the event's {@link EventId}
* @param accountId the event's account id
* @param userId the event's user id
* @param targetId the event's target id
* @param targetType the event's target type
* @param metadata the event's metadata
*/
private void handleObjectCounts(String id, Long timestamp, EventId eventId, String accountId, String userId,
String targetId, String targetType, Map<String, Object> metadata) {
Float eventValue = getEventValue(eventId);
MetricName metricName = getEventMetricName(eventId, targetType);
// Nothing we can do if we cannot figure out the event type or there is no value
if (Float.isNaN(eventValue) || metricName == null) {
LOGGER.debug("Unable to calculate built-in metrics: (eventValue=" + eventValue +
", metricName=" + metricName + ")");
return;
}
List<Values> metrics = new ArrayList<>();
String providerId = metadata.get("targetProviderId") != null ?
metadata.get("targetProviderId").toString() :
null;
String providerType = metadata.get("targetProviderType") != null ?
metadata.get("targetProviderType").toString() :
null;
switch(metricName) {
case ACCOUNT_COUNT:
metrics.add(createBuiltInMetric(GLOBAL_ACCOUNT_ID, metricName, EMPTY_CRITERIA, timestamp,
eventValue));
break;
case CONNECTION_COUNT:
case CONNECTION_ACTIVITY_COUNT:
if (providerId != null && providerType != null) {
for (String account : ImmutableSet.of( GLOBAL_ACCOUNT_ID, accountId )) {
metrics.add(createBuiltInMetric(account, metricName, EMPTY_CRITERIA, timestamp,
eventValue));
// For AWS connections, our polling creates activity and is not a useful metric
if (!(providerId.equals(ProviderIdConstants.AWS_PROVIDER_ID)) ||
metricName != MetricName.CONNECTION_ACTIVITY_COUNT) {
metrics.add(createBuiltInMetric(account, metricName, ImmutableMap.of(
MetricCriteria.PROVIDER_TYPE, providerType
), timestamp, eventValue));
}
// For AWS connections, our polling creates activity and is not a useful metric
if (!(providerId.equals(ProviderIdConstants.AWS_PROVIDER_ID)) ||
metricName != MetricName.CONNECTION_ACTIVITY_COUNT) {
metrics.add(createBuiltInMetric(account, metricName, ImmutableMap.of(
MetricCriteria.PROVIDER_ID, providerId
), timestamp, eventValue));
}
// Only emit the connection activity count for a connection id in the account
if (account.equals(accountId) && metricName == MetricName.CONNECTION_ACTIVITY_COUNT) {
// For AWS connections, our polling creates activity and is not a useful metric
if (!providerId.equals(ProviderIdConstants.AWS_PROVIDER_ID)) {
metrics.add(createBuiltInMetric(account, metricName, ImmutableMap.of(
MetricCriteria.CONNECTION_ID, targetId
), timestamp, eventValue));
}
}
}
} else {
LOGGER.warn(getClass() + " is unable to create built-in metrics for Connection events due to " +
"the providerId and/or providerType being null: {providerId=" + providerId + "," +
"providerType=" + providerType + "}");
}
break;
case INVENTORY_ITEM_COUNT:
case INVENTORY_ITEM_ACTIVITY_COUNT:
if (providerId != null && providerType != null) {
// Temporarily set the metricName to CONNECTION_ACTIVITY_COUNT, if necessary, to get all the
// necessary CONNECTION_ACTIVITY_COUNT metrics created.
metricName = MetricName.CONNECTION_ACTIVITY_COUNT;
for (String account : ImmutableSet.of( GLOBAL_ACCOUNT_ID, accountId )) {
// For AWS connections, our polling creates activity and is not a useful metric
if (!(providerId.equals(ProviderIdConstants.AWS_PROVIDER_ID)) ||
metricName != MetricName.CONNECTION_ACTIVITY_COUNT) {
metrics.add(createBuiltInMetric(account, metricName, ImmutableMap.of(
MetricCriteria.PROVIDER_TYPE, providerType
), timestamp, eventValue));
}
// For AWS connections, our polling creates activity and is not a useful metric
if (!(providerId.equals(ProviderIdConstants.AWS_PROVIDER_ID)) ||
metricName != MetricName.CONNECTION_ACTIVITY_COUNT) {
metrics.add(createBuiltInMetric(account, metricName, ImmutableMap.of(
MetricCriteria.PROVIDER_ID, providerId
), timestamp, eventValue));
}
// Only emit the connection activity count for a connection id in the account
if (account.equals(accountId) && metricName == MetricName.CONNECTION_ACTIVITY_COUNT) {
// For AWS connections, our polling creates activity and is not a useful metric
if (!providerId.equals(ProviderIdConstants.AWS_PROVIDER_ID)) {
metrics.add(createBuiltInMetric(account, metricName, ImmutableMap.of(
MetricCriteria.CONNECTION_ID, targetId
), timestamp, eventValue));
}
}
}
// Set the metricName back to INVENTORY_ITEM_ACTIVITY_COUNT if necessary
metricName = MetricName.INVENTORY_ITEM_ACTIVITY_COUNT;
// Make sure to emit the inventory item specific count
metrics.add(createBuiltInMetric(accountId, metricName, ImmutableMap.of(
MetricCriteria.OBJECT_ID, targetId
), timestamp, eventValue));
String objectType = getExternalObjectType(metadata);
String connectionId = metadata.get("targetConnectionId") != null ?
metadata.get("targetConnectionId").toString() :
null;
// Emit object type values, if available
if (objectType != null) {
// Global
metrics.add(createBuiltInMetric(GLOBAL_ACCOUNT_ID, metricName, ImmutableMap.of(
MetricCriteria.OBJECT_TYPE, objectType
), timestamp, eventValue));
// Account
metrics.add(createBuiltInMetric(accountId, metricName, ImmutableMap.of(
MetricCriteria.OBJECT_TYPE, objectType
), timestamp, eventValue));
// Connection
if (connectionId != null) {
metrics.add(createBuiltInMetric(accountId, metricName, ImmutableMap.of(
MetricCriteria.CONNECTION_ID, "",
MetricCriteria.OBJECT_TYPE, objectType
), timestamp, eventValue));
}
}
} else {
LOGGER.warn(getClass() + " is unable to create built-in metrics for InventoryItem events due to " +
"the providerId and/or providerType being null: {providerId=" + providerId +
",providerType=" + providerType + "}");
}
break;
case MESSAGE_COUNT:
providerType = metadata.get("messageProviderType") != null ?
(String)metadata.get("messageProviderType") :
null;
providerId = metadata.get("messageProviderId") != null ?
(String)metadata.get("messageProviderId") :
null;
String originatingEventTargetId = metadata.get("messageEventTargetId") != null ?
(String)metadata.get("messageEventTargetId") :
null;
String originatingEventTargetType = metadata.get("messageEventTargetType") != null ?
(String)metadata.get("messageEventTargetType") :
null;
String connectionId = metadata.get("messageConnectionId") != null ?
(String)metadata.get("messageConnectionId") :
null;
// Global message count
metrics.add(createBuiltInMetric(GLOBAL_ACCOUNT_ID, metricName, EMPTY_CRITERIA, timestamp, eventValue));
if (accountId != null) {
// Global message count
metrics.add(createBuiltInMetric(accountId, metricName, EMPTY_CRITERIA, timestamp, eventValue));
if (userId != null) {
// Message count per user
metrics.add(createBuiltInMetric(accountId, metricName, ImmutableMap.of(
MetricCriteria.USER_ID, userId
), timestamp, eventValue));
}
if (originatingEventTargetId != null &&
!originatingEventTargetId.equals(accountId) &&
(userId != null && !originatingEventTargetId.equals(userId))) {
// Message count per object and type (if this wasn't already counted above)
metrics.add(createBuiltInMetric(accountId, metricName, ImmutableMap.of(
MetricCriteria.OBJECT_TYPE, originatingEventTargetType,
MetricCriteria.OBJECT_ID, originatingEventTargetId
), timestamp, eventValue));
}
if (connectionId != null && (originatingEventTargetId != null &&
connectionId.equals(originatingEventTargetId))) {
// Connection specific counts (if this wasn't already counted above)
metrics.add(createBuiltInMetric(accountId, metricName, ImmutableMap.of(
MetricCriteria.CONNECTION_ID, connectionId
), timestamp, eventValue));
}
if (providerType != null) {
// Global message count per provider type
metrics.add(createBuiltInMetric(GLOBAL_ACCOUNT_ID, metricName, ImmutableMap.of(
MetricCriteria.PROVIDER_TYPE, providerType
), timestamp, eventValue));
// Account message count per provider type
metrics.add(createBuiltInMetric(accountId, metricName, ImmutableMap.of(
MetricCriteria.PROVIDER_TYPE, providerType
), timestamp, eventValue));
}
if (providerId != null) {
// Global message count per provider id
metrics.add(createBuiltInMetric(GLOBAL_ACCOUNT_ID, metricName, ImmutableMap.of(
MetricCriteria.PROVIDER_ID, providerId
), timestamp, eventValue));
// Account message count per provider id
metrics.add(createBuiltInMetric(accountId, metricName, ImmutableMap.of(
MetricCriteria.PROVIDER_ID, providerId
), timestamp, eventValue));
}
}
break;
case USER_COUNT:
boolean duplicateUserRequest = (metadata.containsKey("userRequestIsNew") &&
!(Boolean)metadata.get("userRequestIsNew"));
if (!duplicateUserRequest) {
for (String account : ImmutableSet.of(GLOBAL_ACCOUNT_ID, accountId)) {
metrics.add(createBuiltInMetric(account, metricName, EMPTY_CRITERIA, timestamp, eventValue));
if (ImmutableSet.of(EventId.CREATE_USER_INVITE_REQUEST, EventId.CREATE_USER_REQUEST,
EventId.DELETE_USER_INVITE_REQUEST).contains(eventId)) {
metrics.add(createBuiltInMetric(account, MetricName.PENDING_USER_COUNT, EMPTY_CRITERIA,
timestamp, eventValue));
}
}
}
break;
}
emitMetricsAndHashagMetrics(metrics, eventId, targetId, targetType, metadata);
}
protected void emitMetricsAndHashagMetrics(List<Values> metrics, EventId eventId, String targetId,
String targetType, Map<String, Object> metadata) {
for (Values metric : metrics) {
Float metricValue = (Float)metric.get(5);
// Do not emit the actual 'UPDATE' metrics as they are only useful for the hashtag handling below
if (metricValue != 0.0f) {
// Emit the metric
emitMetric(metric);
}
}
// For all but the 'Account' target type, emit hashtag versions of each metric
if (!targetType.equals("Account")) {
String hashtagsKey = targetType.equals("SobaMessage") ? "messageHashtags" : "targetHashtags";
Map<String, Float> hashtagChanges = getHashtagChanges(eventId, targetId, metadata, hashtagsKey);
for (Map.Entry<String, Float> hashtagChange : hashtagChanges.entrySet()) {
for (Values metric : metrics) {
MetricName metricName = MetricName.valueOf(metric.get(1).toString());
Values metricWithHashtag = (Values)metric.clone();
Map<String, String> criteria = (Map<String, String>)metric.get(3);
String theHashtag = hashtagChange.getKey();
if (criteria.keySet().contains(MetricCriteria.CONNECTION_ID.toString())) {
// Hashtag metrics for anything specific to a connection makes no sense
continue;
} else if (criteria.keySet().contains(MetricCriteria.PROVIDER_ID.toString()) &&
('#' + criteria.get(MetricCriteria.PROVIDER_ID.toString())).equals(theHashtag)) {
// We automatically add hashtags for the provider id and storing its hashtag variant is
// redundant. Example: {PROVIDER_ID=github,HASHTAG=#github}
continue;
} else if (criteria.keySet().contains(MetricCriteria.PROVIDER_TYPE.toString()) &&
('#' + criteria.get(MetricCriteria.PROVIDER_TYPE.toString())).equals(theHashtag)) {
// We automatically add hashtags for the provider type and storing its hashtag variant is
// redundant. Example: {PROVIDER_TYPE=cloud,HASHTAG=#cloud}
continue;
} else if (metricName == MetricName.CONNECTION_ACTIVITY_COUNT &&
ImmutableSet.of('#' + ConnectionTypeConstants.CLOUD_TYPE,
'#' + ProviderIdConstants.AWS_PROVIDER_ID).contains(theHashtag)) {
// For AWS connections, our polling creates activity and is not a useful metric
continue;
}
criteria.put(MetricCriteria.HASHTAG.toString(), hashtagChange.getKey());
metricWithHashtag.set(3, criteria);
metricWithHashtag.set(5, hashtagChange.getValue());
metricWithHashtag.set(6, MetricsUtils.createUniqueMetricName(metric.get(1).toString(), criteria));
emitMetric(metricWithHashtag);
}
}
}
}
/**
* Emits a global metric.
*
* @param metricName the metric's name
* @param metricCriteria the metric's criteria
* @param metricMode the metric's mode
* @param timestamp the metric's timestamp
* @param metricValue the metric's value
*/
protected void emitGlobalMetric(MetricName metricName, Map<MetricCriteria, String> metricCriteria,
MetricModeType metricMode, Long timestamp, Float metricValue) {
emitAccountMetric(GLOBAL_ACCOUNT_ID, metricName, metricCriteria, metricMode, timestamp, metricValue);
}
/**
* Emits an account metric.
*
* @param accountId
* @param metricName
* @param metricCriteria
* @param metricMode
* @param timestamp
* @param metricValue
*/
protected void emitAccountMetric(String accountId, MetricName metricName,
Map<MetricCriteria, String> metricCriteria, MetricModeType metricMode,
Long timestamp, Float metricValue) {
emitMetric(createMetric(accountId, metricName, metricCriteria, metricMode, timestamp, metricValue));
}
/**
* Returns a map containing hashtag changes and their respective values.
*
* * Map key: The hashtag added/removed
* * Map value: 1.0 for added hashtags and -1.0 for deleted hashtags
*
* @param eventId the event's id
* @param targetId the event's target id
* @param metadata the event's metadata
* @param hashtagsKey the metadata key containing the event's hashtags
*
* @return the map
*/
protected Map<String, Float> getHashtagChanges(EventId eventId, String targetId, Map<String, Object> metadata,
String hashtagsKey) {
Float eventValue = getEventValue(eventId);
Map<String, Float> hashtagChanges = new TreeMap<>();
Set<String> hashtags = metadata.get(hashtagsKey) != null ?
(Set<String>)metadata.get(hashtagsKey) :
Collections.EMPTY_SET;
if (!Float.isNaN(eventValue)) {
if (Math.abs(eventValue) == 1.0f) {
// If the event value is 1.0 or -1.0 (CREATE/DELETE), just process as all added/deleted
for (String hashtag : hashtags) {
hashtagChanges.put(hashtag, eventValue);
}
} else if (eventValue == 0.0f) {
// If the event value is 0.0 (UPDATE), figure out the added/deleted hashtags and process as such
Integer targetVersion = metadata.get("targetVersion") != null ?
(Integer)metadata.get("targetVersion") :
0;
Map<String, Object> previousEvent =
MESSAGE_DB_MONGO_CLIENT.getEventForTargetAndVersion(targetId, --targetVersion);
Map<String, Object> previousMetadata = previousEvent != null && previousEvent.get("metadata") != null ?
(Map<String, Object>) previousEvent.get("metadata") :
Collections.<String, Object> emptyMap();
Set<String> previousHashtags = previousMetadata.get(hashtagsKey) != null ?
(Set<String>) previousMetadata.get(hashtagsKey) :
Collections.EMPTY_SET;
Set<String> differences = Sets.symmetricDifference(hashtags, previousHashtags);
for (String hashtag : differences) {
if (hashtags.contains(hashtag)) {
// Added
hashtagChanges.put(hashtag, 1.0f);
} else {
// Deleted
hashtagChanges.put(hashtag, -1.0f);
}
}
}
}
return hashtagChanges;
}
/**
* (USED FOR BUILT-IN METRICS ONLY) Returns a {@link com.streamreduce.core.metric.MetricName} based on the event id and
* target type.
*
* @param eventId the event's id
* @param targetType the event's target type
*
* @return the metric name or null if one cannot be found
*/
protected MetricName getEventMetricName(EventId eventId, String targetType) {
// Just in case
if (eventId == null || targetType == null) {
return null;
}
switch (eventId) {
case CREATE:
case UPDATE:
case DELETE:
if (targetType.equals("Account")) {
return MetricName.ACCOUNT_COUNT;
} else if (targetType.equals("Connection")) {
return MetricName.CONNECTION_COUNT;
} else if (targetType.equals("InventoryItem")) {
return MetricName.INVENTORY_ITEM_COUNT;
} else if (targetType.equals("SobaMessage")) {
return MetricName.MESSAGE_COUNT;
} else if (targetType.equals("User")) {
return MetricName.USER_COUNT;
}
case ACTIVITY:
if (targetType.equals("Connection")) {
return MetricName.CONNECTION_ACTIVITY_COUNT;
} else if (targetType.equals("InventoryItem")) {
return MetricName.INVENTORY_ITEM_ACTIVITY_COUNT;
}
case CREATE_USER_INVITE_REQUEST:
case CREATE_USER_REQUEST:
case DELETE_USER_INVITE_REQUEST:
return MetricName.USER_COUNT;
}
return null;
}
/**
* (USED FOR BUILT-IN METRICS ONLY) Returns a float representing the value of the event.
*
* @param eventId the event id
*
* @return float value representing the value of the event of {@link Float#NaN} if it's an unhandleable event id
*/
protected Float getEventValue(EventId eventId) {
// Just in case
if (eventId == null) {
return Float.NaN;
}
switch (eventId) {
case CREATE:
case ACTIVITY:
case CREATE_USER_INVITE_REQUEST:
case CREATE_USER_REQUEST:
return 1.0f;
case UPDATE:
return 0.0f;
case DELETE:
case DELETE_USER_INVITE_REQUEST:
return -1.0f;
default:
return Float.NaN;
}
}
/**
* Returns the external object type or attempts to default to one if one isn't present.
*
* @param metadata the metadata used to find or default to the appropriate object type
*
* @return the object type found/defaulted to or null if one couldn't be found
*/
protected String getExternalObjectType(Map<String, Object> metadata) {
String externalType = metadata.containsKey("targetExternalType") ?
(String)metadata.get("targetExternalType") :
null;
String providerId = (String) metadata.get("targetProviderId");
if (externalType == null) {
if (providerId.equals(ProviderIdConstants.AWS_PROVIDER_ID)) {
// Default to compute because prior to having the 'targetExternalType' attribute, everything was compute
externalType = Constants.COMPUTE_INSTANCE_TYPE;
} else if (providerId.equals(ProviderIdConstants.GITHUB_PROVIDER_ID) ||
providerId.equals(ProviderIdConstants.JIRA_PROVIDER_ID)) {
externalType = Constants.PROJECT_TYPE;
} else if (providerId.equals(ProviderIdConstants.CUSTOM_PROVIDER_ID)) {
externalType = Constants.CUSTOM_TYPE;
}
}
return externalType;
}
/**
* Creates a global metric.
*
* @param metricName the metrics's name
* @param metricCriteria the metric's criteria
* @param metricMode the metrics mode
* @param timestamp the metric's timestamp
* @param metricValue the metrics value
*
* @return the metric
*/
protected Values createGlobalMetric(MetricName metricName, Map<MetricCriteria, String> metricCriteria,
MetricModeType metricMode, Long timestamp, Float metricValue) {
return createMetric(GLOBAL_ACCOUNT_ID, metricName, metricCriteria, metricMode, timestamp, metricValue);
}
/**
* Creates an account metric.
*
* @param accountId the metric's account id
* @param metricName the metrics's name
* @param metricCriteria the metric's criteria
* @param metricMode the metrics mode
* @param timestamp the metric's timestamp
* @param metricValue the metrics value
*
* @return the metric
*/
protected Values createAccountMetric(String accountId, MetricName metricName,
Map<MetricCriteria, String> metricCriteria, MetricModeType metricMode,
Long timestamp, Float metricValue) {
return createMetric(accountId, metricName, metricCriteria, metricMode, timestamp, metricValue);
}
/**
* Emits the metric.
*
* @param values the metric
*/
private void emitMetric(Values values) {
outputCollector.emit(values);
}
/**
* Specialized helper that prepares a metric to be sent down stream.
*
* @param accountId the metric's account id
* @param metricName the metrics's name
* @param metricCriteria the metric's criteria
* @param metricMode the metrics mode
* @param timestamp the metric's timestamp
* @param metricValue the metrics value
*
* @return the prepared metric
*/
private Values createMetric(String accountId, MetricName metricName, Map<MetricCriteria, String> metricCriteria,
MetricModeType metricMode, Long timestamp, Float metricValue) {
// Convert the map keys to string to avoid serialization/deserialization issues in Storm
Map<String, String> massagedCriteria = new LinkedHashMap<>();
for (Map.Entry<MetricCriteria, String> mapEntry : metricCriteria.entrySet()) {
massagedCriteria.put(mapEntry.getKey().toString(), mapEntry.getValue());
}
// All built-in metrics will be deltas, since they are counts that increment/decrement based on the event
return new Values(accountId, metricName.toString(), metricMode, massagedCriteria,
timestamp, metricValue,
MetricsUtils.createUniqueMetricName(metricName.toString(), massagedCriteria));
}
/**
* (USED FOR BUILT-IN METRICS ONLY) Just like {@link #createMetric(String, com.streamreduce.core.metric.MetricName,
* java.util.Map, com.streamreduce.core.metric.MetricModeType, Long, Float)} but it defaults to
* {@link MetricModeType#DELTA} since all built-in metrics are counters.
*
* @param accountId the metric's account
* @param metricName the metric's name
* @param metricCriteria the metric's criteria
* @param timestamp the metric's timestamp
* @param metricValue the metric's value
*
* @return the prepared built-in metric
*/
private Values createBuiltInMetric(String accountId, MetricName metricName,
Map<MetricCriteria, String> metricCriteria, Long timestamp, Float metricValue) {
return createMetric(accountId, metricName, metricCriteria, MetricModeType.DELTA, timestamp, metricValue);
}
}