/**
* Copyright 2016 StreamSets Inc.
*
* Licensed under the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.streamsets.datacollector.util;
import com.streamsets.datacollector.config.DataRuleDefinition;
import com.streamsets.datacollector.config.DriftRuleDefinition;
import com.streamsets.datacollector.config.MetricElement;
import com.streamsets.datacollector.config.MetricType;
import com.streamsets.datacollector.config.MetricsRuleDefinition;
import com.streamsets.datacollector.config.ThresholdType;
import com.streamsets.datacollector.execution.runner.common.Constants;
import com.streamsets.datacollector.record.RecordImpl;
import com.streamsets.datacollector.runner.production.RulesConfigurationChangeRequest;
import com.streamsets.pipeline.api.Field;
import com.streamsets.pipeline.api.Record;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class AggregatorUtil {
private static final Logger LOG = LoggerFactory.getLogger(AggregatorUtil.class);
public static final String METRIC_RULE_RECORD = "MetricRuleRecord";
public static final String DATA_RULE_RECORD = "DataRuleRecord";
public static final String CONFIGURATION_CHANGE = "ConfigurationChange";
public static final String AGGREGATOR = "Aggregator";
public static final String METRIC_RULE_CHANGE = "MetricRuleChange";
public static final String DATA_RULE_CHANGE = "DataRuleChange";
public static final String DATA_RULE_DISABLED = "DataRuleDisabled";
public static final String METRIC_RULE_DISABLED = "MetricRuleDisabled";
public static final String STATS_AGGREGATOR_STAGE = "StatsAggregatorStage";
public static final String METRIC_JSON_STRING = "MetricJsonString";
public static final String TIMESTAMP = "timestamp";
public static final String RULE_ID = "ruleId";
public static final String PIPELINE_BATCH_DURATION = "pipelineBatchDuration";
public static final String BATCH_COUNT = "batchCount";
public static final String BATCH_INPUT_RECORDS = "batchInputRecords";
public static final String BATCH_OUTPUT_RECORDS = "batchOutputRecords";
public static final String BATCH_ERROR_RECORDS = "batchErrorRecords";
public static final String BATCH_ERRORS = "batchErrors";
public static final String STAGE_BATCH_METRICS = "stageBatchMetrics";
public static final String PROCESSING_TIME = "processingTime";
public static final String INPUT_RECORDS = "inputRecords";
public static final String ERROR_RECORDS = "errorRecords";
public static final String OUTPUT_RECORDS = "outputRecords";
public static final String STAGE_ERROR = "stageError";
public static final String OUTPUT_RECORDS_PER_LANE = "outputRecordsPerLane";
public static final String METRIC_ALERTS_TO_REMOVE = "metricAlertsToRemove";
public static final String RULES_TO_REMOVE = "rulesToRemove";
public static final String METRIC_ID = "metricId";
public static final String ALERT_TEXT = "alertText";
public static final String CONDITION = "condition";
public static final String FAMILY = "family";
public static final String ENABLED = "enabled";
public static final String SEND_MAIL = "sendMail";
public static final String EMAILS = "emails";
public static final String VALID = "valid";
public static final String METRIC_ELEMENT = "metricElement";
public static final String METRIC_TYPE = "metricType";
public static final String LANE = "lane";
public static final String LABEL = "label";
public static final String MIN_VOLUME = "minVolume";
public static final String STREAM_NAME = "streamName";
public static final String EVALUATED_RECORDS = "evaluatedRecords";
public static final String MATCHED_RECORDS = "matchedRecords";
public static final String SAMPLING_PERCENTAGE = "samplingPercentage";
public static final String SAMPLING_RECORDS_TO_RETAIN = "samplingRecordsToRetain";
public static final String THRESHOLD_TYPE = "thresholdType";
public static final String THRESHOLD_VALUE = "thresholdValue";
public static final String ALERT_ENABLED = "alertEnabled";
public static final String METER_ENABLED = "meterEnabled";
public static final String ALERT_TEXTS = "alertTexts";
public static final String DRIFT_RULE = "driftRule";
public static final String IS_AGGREGATED = "isAggregated";
public static final String METADATA = "metadata";
public static final String SDC_ID = "sdcId";
public static final String MASTER_SDC_ID = "masterSdcId";
private AggregatorUtil() {
}
@SuppressWarnings("unchecked")
public static Record createMetricRecord(Map<String, Object> pipelineBatchMetrics) {
Record record = createRecord(METRIC_RULE_RECORD);
Map<String, Field> map = new HashMap<>();
map.put(TIMESTAMP, Field.create(System.currentTimeMillis()));
map.put(PIPELINE_BATCH_DURATION, Field.create(Field.Type.LONG, pipelineBatchMetrics.get(PIPELINE_BATCH_DURATION)));
map.put(BATCH_COUNT, Field.create(Field.Type.INTEGER, pipelineBatchMetrics.get(BATCH_COUNT)));
map.put(BATCH_INPUT_RECORDS, Field.create(Field.Type.INTEGER, pipelineBatchMetrics.get(BATCH_INPUT_RECORDS)));
map.put(BATCH_OUTPUT_RECORDS, Field.create(Field.Type.INTEGER, pipelineBatchMetrics.get(BATCH_OUTPUT_RECORDS)));
map.put(BATCH_ERROR_RECORDS, Field.create(Field.Type.INTEGER, pipelineBatchMetrics.get(BATCH_ERROR_RECORDS)));
map.put(BATCH_ERRORS, Field.create(Field.Type.INTEGER, pipelineBatchMetrics.get(BATCH_ERRORS)));
Map<String, Object> stageBatchMetrics = (Map<String, Object>)pipelineBatchMetrics.get(STAGE_BATCH_METRICS);
// This is stage instance name vs stage metrics
Map<String, Field> stageMetrics = new HashMap<>();
for (Map.Entry<String, Object> entry : stageBatchMetrics.entrySet()) {
Map<String, Field> stageBatchMetricsMap = new HashMap<>();
Map<String, Object> value = (Map<String, Object>) entry.getValue();
stageBatchMetricsMap.put(PROCESSING_TIME, Field.create(Field.Type.LONG, value.get(PROCESSING_TIME)));
stageBatchMetricsMap.put(INPUT_RECORDS, Field.create(Field.Type.INTEGER, value.get(INPUT_RECORDS)));
stageBatchMetricsMap.put(ERROR_RECORDS, Field.create(Field.Type.INTEGER, value.get(ERROR_RECORDS)));
stageBatchMetricsMap.put(OUTPUT_RECORDS, Field.create(Field.Type.INTEGER, value.get(OUTPUT_RECORDS)));
stageBatchMetricsMap.put(STAGE_ERROR, Field.create(Field.Type.INTEGER, value.get(STAGE_ERROR)));
Map<String, Integer> outputRecordsPerLane = (Map<String, Integer>) value.get(OUTPUT_RECORDS_PER_LANE);
Map<String, Field> outputRecordsPerLaneMap = new HashMap<>();
if (outputRecordsPerLane != null) {
for (Map.Entry<String, Integer> e : outputRecordsPerLane.entrySet()) {
outputRecordsPerLaneMap.put(e.getKey(), Field.create(e.getValue()));
}
}
stageBatchMetricsMap.put(OUTPUT_RECORDS_PER_LANE, Field.create(outputRecordsPerLaneMap));
stageMetrics.put(entry.getKey(), Field.create(stageBatchMetricsMap));
}
map.put(STAGE_BATCH_METRICS, Field.create(stageMetrics));
record.set(Field.create(map));
return record;
}
public static Record createDataRuleRecord(
String ruleId,
String lane,
long evaluatedRecordCount,
long matchingRecordCount,
List<String> alertTextForMatchRecords,
boolean driftRule
) {
Record record = createRecord(DATA_RULE_RECORD);
Map<String, Field> map = new HashMap<>();
map.put(STREAM_NAME, Field.create(lane));
map.put(RULE_ID, Field.create(ruleId));
map.put(TIMESTAMP, Field.create(System.currentTimeMillis()));
map.put(EVALUATED_RECORDS, Field.create(evaluatedRecordCount));
map.put(MATCHED_RECORDS, Field.create(matchingRecordCount));
map.put(DRIFT_RULE, Field.create(driftRule));
List<Field> alertTextFields = new ArrayList<>(alertTextForMatchRecords.size());
for (String alertText : alertTextForMatchRecords) {
alertTextFields.add(Field.create(alertText));
}
map.put(ALERT_TEXTS, Field.create(alertTextFields));
record.set(Field.create(map));
return record;
}
public static Record createConfigChangeRequestRecord(
RulesConfigurationChangeRequest rulesConfigurationChangeRequest
) {
Record record = createRecord(CONFIGURATION_CHANGE);
Map<String, Field> map = new HashMap<>();
map.put(
METRIC_ALERTS_TO_REMOVE,
Field.create(
createListField(
rulesConfigurationChangeRequest.getMetricAlertsToRemove()
)
)
);
map.put(
RULES_TO_REMOVE,
Field.create(
createMapField(
rulesConfigurationChangeRequest.getRulesToRemove()
)
)
);
map.put(
EMAILS,
Field.create(
createListField(
rulesConfigurationChangeRequest.getRuleDefinitions().getEmailIds()
)
)
);
map.put(AggregatorUtil.TIMESTAMP, Field.create(System.currentTimeMillis()));
record.set(Field.create(map));
return record;
}
private static List<Field> createListField(Collection<String> strings) {
List<Field> fieldList = new ArrayList<>(strings.size());
for(String s : strings) {
fieldList.add(Field.create(s));
}
return fieldList;
}
private static Map<String, Field> createMapField(Map<String, String> map) {
Map<String, Field> mapField = new HashMap<>();
for (Map.Entry<String, String> e : map.entrySet()) {
mapField.put(e.getKey(), Field.create(e.getValue()));
}
return mapField;
}
private static Record createRecord(String recordSourceId) {
RecordImpl record = new RecordImpl(AGGREGATOR, recordSourceId, null, null);
record.addStageToStagePath(STATS_AGGREGATOR_STAGE);
record.createTrackingId();
return record;
}
public static Record createMetricRuleChangeRecord(MetricsRuleDefinition metricsRuleDefinition) {
Record record = createRecord(METRIC_RULE_CHANGE);
Map<String, Field> map = new HashMap<>();
map.put(RULE_ID, Field.create(metricsRuleDefinition.getId()));
map.put(METRIC_ID, Field.create(metricsRuleDefinition.getMetricId()));
map.put(ALERT_TEXT, Field.create(metricsRuleDefinition.getAlertText()));
map.put(CONDITION, Field.create(metricsRuleDefinition.getCondition()));
map.put(FAMILY, Field.create(metricsRuleDefinition.getFamily()));
map.put(TIMESTAMP, Field.create(metricsRuleDefinition.getTimestamp()));
map.put(ENABLED, Field.create(metricsRuleDefinition.isEnabled()));
map.put(SEND_MAIL, Field.create(metricsRuleDefinition.isSendEmail()));
map.put(VALID, Field.create(metricsRuleDefinition.isValid()));
map.put(METRIC_ELEMENT, Field.create(metricsRuleDefinition.getMetricElement().name()));
map.put(METRIC_TYPE, Field.create(metricsRuleDefinition.getMetricType().name()));
record.set(Field.create(map));
return record;
}
public static Record createMetricRuleDisabledRecord(MetricsRuleDefinition metricsRuleDefinition) {
Record record = createRecord(METRIC_RULE_DISABLED);
Map<String, Field> map = new HashMap<>();
map.put(RULE_ID, Field.create(metricsRuleDefinition.getId()));
map.put(TIMESTAMP, Field.create(metricsRuleDefinition.getTimestamp()));
record.set(Field.create(map));
return record;
}
public static Record createDataRuleChangeRecord(DataRuleDefinition dataRuleDefinition) {
Record record = createRecord(DATA_RULE_CHANGE);
Map<String, Field> map = new HashMap<>();
map.put(RULE_ID, Field.create(dataRuleDefinition.getId()));
map.put(ALERT_TEXT, Field.create(dataRuleDefinition.getAlertText()));
map.put(CONDITION, Field.create(dataRuleDefinition.getCondition()));
map.put(FAMILY, Field.create(dataRuleDefinition.getFamily()));
map.put(TIMESTAMP, Field.create(dataRuleDefinition.getTimestamp()));
map.put(LANE, Field.create(dataRuleDefinition.getLane()));
map.put(LABEL, Field.create(dataRuleDefinition.getLabel()));
map.put(MIN_VOLUME, Field.create(dataRuleDefinition.getMinVolume()));
map.put(SAMPLING_PERCENTAGE, Field.create(dataRuleDefinition.getSamplingPercentage()));
map.put(SAMPLING_RECORDS_TO_RETAIN, Field.create(dataRuleDefinition.getSamplingRecordsToRetain()));
map.put(ENABLED, Field.create(dataRuleDefinition.isEnabled()));
map.put(SEND_MAIL, Field.create(dataRuleDefinition.isSendEmail()));
map.put(VALID, Field.create(dataRuleDefinition.isValid()));
map.put(THRESHOLD_TYPE, Field.create(dataRuleDefinition.getThresholdType().name()));
map.put(THRESHOLD_VALUE, Field.create(dataRuleDefinition.getThresholdValue()));
map.put(ALERT_ENABLED, Field.create(dataRuleDefinition.isAlertEnabled()));
map.put(METER_ENABLED, Field.create(dataRuleDefinition.isMeterEnabled()));
map.put(DRIFT_RULE, Field.create(dataRuleDefinition instanceof DriftRuleDefinition));
record.set(Field.create(map));
return record;
}
public static Record createDataRuleDisabledRecord(DataRuleDefinition dataRuleDefinition) {
Record record = createRecord(DATA_RULE_DISABLED);
Map<String, Field> map = new HashMap<>();
map.put(RULE_ID, Field.create(dataRuleDefinition.getId()));
map.put(LANE, Field.create(dataRuleDefinition.getLane()));
map.put(AggregatorUtil.TIMESTAMP, Field.create(dataRuleDefinition.getTimestamp()));
record.set(Field.create(map));
return record;
}
public static Record createMetricJsonRecord(
String sdcId,
String masterSdcId,
Map<String, Object> metadata,
boolean isAggregated,
String metricsJSONStr
) {
Record record = createRecord(METRIC_JSON_STRING);
Map<String, Field> map = new HashMap<>();
map.put(TIMESTAMP, Field.create(System.currentTimeMillis()));
map.put(SDC_ID, Field.create(sdcId));
map.put(MASTER_SDC_ID, Field.create(masterSdcId));
map.put(IS_AGGREGATED, Field.create(isAggregated));
map.put(METADATA, getMetadataField(metadata));
map.put(METRIC_JSON_STRING, Field.create(metricsJSONStr));
record.set(Field.create(map));
return record;
}
public static MetricsRuleDefinition getMetricRuleDefinition(Record record) {
return new MetricsRuleDefinition(
record.get("/" + AggregatorUtil.RULE_ID).getValueAsString(),
record.get("/" + AggregatorUtil.ALERT_TEXT).getValueAsString(),
record.get("/" + AggregatorUtil.METRIC_ID).getValueAsString(),
MetricType.valueOf(record.get("/" + AggregatorUtil.METRIC_TYPE).getValueAsString()),
MetricElement.valueOf(record.get("/" + AggregatorUtil.METRIC_ELEMENT).getValueAsString()),
record.get("/" + AggregatorUtil.CONDITION).getValueAsString(),
record.get("/" + AggregatorUtil.SEND_MAIL).getValueAsBoolean(),
record.get("/" + AggregatorUtil.ENABLED).getValueAsBoolean(),
record.get("/" + AggregatorUtil.TIMESTAMP).getValueAsLong()
);
}
public static DataRuleDefinition getDataRuleDefinition(Record record) {
return new DataRuleDefinition(
record.get("/" + AggregatorUtil.FAMILY).getValueAsString(),
record.get("/" + AggregatorUtil.RULE_ID).getValueAsString(),
record.get("/" + AggregatorUtil.LABEL).getValueAsString(),
record.get("/" + AggregatorUtil.LANE).getValueAsString(),
record.get("/" + AggregatorUtil.SAMPLING_PERCENTAGE).getValueAsDouble(),
record.get("/" + AggregatorUtil.SAMPLING_RECORDS_TO_RETAIN).getValueAsInteger(),
record.get("/" + AggregatorUtil.CONDITION).getValueAsString(),
record.get("/" + AggregatorUtil.ALERT_ENABLED).getValueAsBoolean(),
record.get("/" + AggregatorUtil.ALERT_TEXT).getValueAsString(),
ThresholdType.valueOf(record.get("/" + AggregatorUtil.THRESHOLD_TYPE).getValueAsString()),
record.get("/" + AggregatorUtil.THRESHOLD_VALUE).getValueAsString(),
record.get("/" + AggregatorUtil.MIN_VOLUME).getValueAsLong(),
record.get("/" + AggregatorUtil.METER_ENABLED).getValueAsBoolean(),
record.get("/" + AggregatorUtil.SEND_MAIL).getValueAsBoolean(),
record.get("/" + AggregatorUtil.ENABLED).getValueAsBoolean(),
record.get("/" + AggregatorUtil.TIMESTAMP).getValueAsLong()
);
}
public static void enqueStatsRecord(
Record record,
BlockingQueue<Record> statsQueue, Configuration configuration) {
boolean offered;
try {
offered = statsQueue.offer(
record,
configuration.get(
Constants.MAX_STATS_REQUEST_OFFER_WAIT_TIME_MS_KEY,
Constants.MAX_STATS_REQUEST_OFFER_WAIT_TIME_MS_DEFAULT
),
TimeUnit.MILLISECONDS
);
} catch (InterruptedException e) {
offered = false;
}
if(!offered) {
LOG.error("Dropping Stats Aggregator Request records as stats aggregator queue is full. " +
"Please resize the stats aggregator queue.");
}
}
public static Field getMetadataField(Map<String, Object> metadata) {
if (metadata != null && !metadata.isEmpty()) {
Map<String, Field> map = new HashMap<>(metadata.size());
for (Map.Entry<String, Object> e : metadata.entrySet()) {
if (e.getValue() instanceof String) {
map.put(e.getKey(), Field.create((String)e.getValue()));
}
}
return Field.create(map);
}
return Field.create(Field.Type.MAP, null);
}
}