/** * Copyright (C) 2014 Stratio (http://stratio.com) * * 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.stratio.decision.metrics; import com.codahale.metrics.*; import com.codahale.metrics.Timer; import com.stratio.decision.commons.constants.ColumnType; import com.stratio.decision.commons.messages.ColumnNameTypeValue; import com.stratio.decision.exception.ServiceException; import com.stratio.decision.service.StreamOperationServiceWithoutMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.concurrent.TimeUnit; public class SiddhiStreamReporter extends ScheduledReporter { private static final Logger LOGGER = LoggerFactory.getLogger(SiddhiStreamReporter.class); private static final String GAUGE_STREAM_NAME = "streaming-gauge-metrics"; private static final Set<Map.Entry<String, ColumnType>> GAUGE_PROPERTIES = new LinkedHashSet<Map.Entry<String, ColumnType>>() {{ add(new AbstractMap.SimpleEntry<>("name", ColumnType.STRING)); add(new AbstractMap.SimpleEntry<>("timestamp", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("value", ColumnType.STRING)); }}; private static final String COUNTER_STREAM_NAME = "streaming-counter-metrics"; private static final Set<Map.Entry<String, ColumnType>> COUNTER_PROPERTIES = new LinkedHashSet<Map.Entry<String, ColumnType>>() {{ add(new AbstractMap.SimpleEntry<>("name", ColumnType.STRING)); add(new AbstractMap.SimpleEntry<>("timestamp", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("count", ColumnType.DOUBLE)); }}; private static final String HISTOGRAM_STREAM_NAME = "streaming-histogram-metrics"; private static final Set<Map.Entry<String, ColumnType>> HISTOGRAM_PROPERTIES = new LinkedHashSet<Map.Entry<String, ColumnType>>() {{ add(new AbstractMap.SimpleEntry<>("name", ColumnType.STRING)); add(new AbstractMap.SimpleEntry<>("timestamp", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("count", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("max", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("mean", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("min", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("stddev", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p50", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p75", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p95", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p98", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p99", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p999", ColumnType.FLOAT)); }}; private static final String METER_STREAM_NAME = "streaming-meter-metrics"; private static final Set<Map.Entry<String, ColumnType>> METER_PROPERTIES = new LinkedHashSet<Map.Entry<String, ColumnType>>() {{ add(new AbstractMap.SimpleEntry<>("name", ColumnType.STRING)); add(new AbstractMap.SimpleEntry<>("timestamp", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("count", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("mean_rate", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("m1_rate", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("m5_rate", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("m15_rate", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("rate_unit", ColumnType.STRING)); }}; private static final String TIMER_STREAM_NAME = "streaming-timer-metrics"; private static final Set<Map.Entry<String, ColumnType>> TIMER_PROPERTIES = new LinkedHashSet<Map.Entry<String, ColumnType>>() {{ add(new AbstractMap.SimpleEntry<>("name", ColumnType.STRING)); add(new AbstractMap.SimpleEntry<>("timestamp", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("count", ColumnType.DOUBLE)); add(new AbstractMap.SimpleEntry<>("max", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("mean", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("min", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("stddev", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p50", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p75", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p95", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p98", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p99", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("p999", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("mean_rate", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("m1_rate", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("m5_rate", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("m15_rate", ColumnType.FLOAT)); add(new AbstractMap.SimpleEntry<>("rate_unit", ColumnType.STRING)); add(new AbstractMap.SimpleEntry<>("duration_unit", ColumnType.STRING)); }}; private final StreamOperationServiceWithoutMetrics streamOperationService; private final Clock clock; private SiddhiStreamReporter(MetricRegistry registry, StreamOperationServiceWithoutMetrics streamOperationService, TimeUnit rateUnit, TimeUnit durationUnit, Clock clock, MetricFilter filter) { super(registry, "siddhi-reporter", filter, rateUnit, durationUnit); this.streamOperationService = streamOperationService; this.clock = clock; // Create metric streams createStream(GAUGE_STREAM_NAME, GAUGE_PROPERTIES); createStream(COUNTER_STREAM_NAME, COUNTER_PROPERTIES); createStream(HISTOGRAM_STREAM_NAME, HISTOGRAM_PROPERTIES); createStream(METER_STREAM_NAME, METER_PROPERTIES); createStream(TIMER_STREAM_NAME, TIMER_PROPERTIES); } @Override public void report(SortedMap<String, Gauge> gauges, SortedMap<String, Counter> counters, SortedMap<String, Histogram> histograms, SortedMap<String, Meter> meters, SortedMap<String, Timer> timers) { final long timestamp = TimeUnit.MILLISECONDS.toSeconds(clock.getTime()); for (Map.Entry<String, Gauge> entry : gauges.entrySet()) { reportGauge(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry<String, Counter> entry : counters.entrySet()) { reportCounter(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry<String, Histogram> entry : histograms.entrySet()) { reportHistogram(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry<String, Meter> entry : meters.entrySet()) { reportMeter(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry<String, Timer> entry : timers.entrySet()) { reportTimer(timestamp, entry.getKey(), entry.getValue()); } } private void reportTimer(long timestamp, String name, Timer timer) { final Snapshot snapshot = timer.getSnapshot(); report(TIMER_STREAM_NAME, TIMER_PROPERTIES, name, timestamp, timer.getCount(), convertDuration(snapshot.getMax()), convertDuration(snapshot.getMean()), convertDuration(snapshot.getMin()), convertDuration(snapshot.getStdDev()), convertDuration(snapshot.getMedian()), convertDuration(snapshot.get75thPercentile()), convertDuration(snapshot.get95thPercentile()), convertDuration(snapshot.get98thPercentile()), convertDuration(snapshot.get99thPercentile()), convertDuration(snapshot.get999thPercentile()), convertRate(timer.getMeanRate()), convertRate(timer.getOneMinuteRate()), convertRate(timer.getFiveMinuteRate()), convertRate(timer.getFifteenMinuteRate()), getRateUnit(), getDurationUnit()); } private void reportMeter(long timestamp, String name, Meter meter) { report(METER_STREAM_NAME, METER_PROPERTIES, name, timestamp, meter.getCount(), convertRate(meter.getMeanRate()), convertRate(meter.getOneMinuteRate()), convertRate(meter.getFiveMinuteRate()), convertRate(meter.getFifteenMinuteRate()), getRateUnit()); } private void reportHistogram(long timestamp, String name, Histogram histogram) { final Snapshot snapshot = histogram.getSnapshot(); report(HISTOGRAM_STREAM_NAME, HISTOGRAM_PROPERTIES, name, timestamp, histogram.getCount(), snapshot.getMax(), snapshot.getMean(), snapshot.getMin(), snapshot.getStdDev(), snapshot.getMedian(), snapshot.get75thPercentile(), snapshot.get95thPercentile(), snapshot.get98thPercentile(), snapshot.get99thPercentile(), snapshot.get999thPercentile()); } private void reportCounter(long timestamp, String name, Counter counter) { report(COUNTER_STREAM_NAME, COUNTER_PROPERTIES, name, timestamp, counter.getCount()); } private void reportGauge(long timestamp, String name, Gauge gauge) { report(GAUGE_STREAM_NAME, GAUGE_PROPERTIES, name, timestamp, gauge.getValue().toString()); } private void report(String streamName, Set<Map.Entry<String, ColumnType>> properties, Object... values) { try { List<ColumnNameTypeValue> columns = new ArrayList<>(); int i = 0; for (Map.Entry<String, ColumnType> entry : properties) { columns.add(new ColumnNameTypeValue(entry.getKey(), entry.getValue(), values[i])); i++; } streamOperationService.send(streamName, columns); } catch (ServiceException e) { LOGGER.error("Metric event not sended to stream {}", streamName, e); } catch (Exception e) { LOGGER.error("FATAL ERROR", e); } } private void createStream(String name, Set<Map.Entry<String, ColumnType>> attributes) { List<ColumnNameTypeValue> columns = new ArrayList<>(); for (Map.Entry<String, ColumnType> attribute : attributes) { columns.add(new ColumnNameTypeValue(attribute.getKey(), attribute.getValue(), null)); } streamOperationService.createInternalStream(name, columns); } public static Builder forRegistry(MetricRegistry registry, StreamOperationServiceWithoutMetrics streamOperationService) { return new Builder(registry, streamOperationService); } public static class Builder { private final StreamOperationServiceWithoutMetrics streamOperationService; private final MetricRegistry registry; private TimeUnit rateUnit; private TimeUnit durationUnit; private Clock clock; private MetricFilter filter; private Builder(MetricRegistry registry, StreamOperationServiceWithoutMetrics streamOperationService) { this.streamOperationService = streamOperationService; this.registry = registry; this.rateUnit = TimeUnit.SECONDS; this.durationUnit = TimeUnit.MILLISECONDS; this.clock = Clock.defaultClock(); this.filter = MetricFilter.ALL; } public Builder convertRatesTo(TimeUnit rateUnit) { this.rateUnit = rateUnit; return this; } public Builder convertDurationsTo(TimeUnit durationUnit) { this.durationUnit = durationUnit; return this; } public Builder withClock(Clock clock) { this.clock = clock; return this; } public Builder filter(MetricFilter filter) { this.filter = filter; return this; } public SiddhiStreamReporter build() { return new SiddhiStreamReporter(registry, streamOperationService, rateUnit, durationUnit, clock, filter); } } }