All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micrometer.signalfx.SignalFxMeterRegistry Maven / Gradle / Ivy

There is a newer version: 1.14.1
Show newest version
/*
 * Copyright 2017 VMware, 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
 *
 * https://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 io.micrometer.signalfx;

import com.signalfx.endpoint.SignalFxEndpoint;
import com.signalfx.endpoint.SignalFxReceiverEndpoint;
import com.signalfx.metrics.auth.StaticAuthToken;
import com.signalfx.metrics.connection.HttpDataPointProtobufReceiverFactory;
import com.signalfx.metrics.connection.HttpEventProtobufReceiverFactory;
import com.signalfx.metrics.errorhandler.OnSendErrorHandler;
import com.signalfx.metrics.flush.AggregateMetricSender;
import com.signalfx.metrics.protobuf.SignalFxProtocolBuffers;
import io.micrometer.common.lang.Nullable;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.config.NamingConvention;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.distribution.HistogramGauges;
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
import io.micrometer.core.instrument.step.StepMeterRegistry;
import io.micrometer.core.instrument.util.MeterPartition;
import io.micrometer.core.instrument.util.NamedThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import static com.signalfx.metrics.protobuf.SignalFxProtocolBuffers.MetricType.*;
import static java.util.stream.StreamSupport.stream;

/**
 * {@link StepMeterRegistry} for SignalFx.
 *
 * @author Jon Schneider
 * @author Johnny Lim
 * @since 1.0.0
 */
public class SignalFxMeterRegistry extends StepMeterRegistry {

    private static final ThreadFactory DEFAULT_THREAD_FACTORY = new NamedThreadFactory("signalfx-metrics-publisher");

    private final Logger logger = LoggerFactory.getLogger(SignalFxMeterRegistry.class);

    private final SignalFxConfig config;

    private final HttpDataPointProtobufReceiverFactory dataPointReceiverFactory;

    private final HttpEventProtobufReceiverFactory eventReceiverFactory;

    private final Set onSendErrorHandlerCollection = Collections
        .singleton(metricError -> this.logger.warn("failed to send metrics: {}", metricError.getMessage()));

    private final boolean publishCumulativeHistogram;

    private final boolean publishDeltaHistogram;

    public SignalFxMeterRegistry(SignalFxConfig config, Clock clock) {
        this(config, clock, DEFAULT_THREAD_FACTORY);
    }

    public SignalFxMeterRegistry(SignalFxConfig config, Clock clock, ThreadFactory threadFactory) {
        super(config, clock);
        this.config = config;

        URI apiUri = URI.create(config.uri());
        int port = apiUri.getPort();
        if (port == -1) {
            if ("http".equals(apiUri.getScheme())) {
                port = 80;
            }
            else if ("https".equals(apiUri.getScheme())) {
                port = 443;
            }
        }

        SignalFxReceiverEndpoint signalFxEndpoint = new SignalFxEndpoint(apiUri.getScheme(), apiUri.getHost(), port);
        this.dataPointReceiverFactory = new HttpDataPointProtobufReceiverFactory(signalFxEndpoint);
        this.eventReceiverFactory = new HttpEventProtobufReceiverFactory(signalFxEndpoint);
        this.publishCumulativeHistogram = config.publishCumulativeHistogram();
        this.publishDeltaHistogram = config.publishDeltaHistogram();

        config().namingConvention(new SignalFxNamingConvention());

        start(threadFactory);
    }

    @Override
    protected void publish() {
        final long timestamp = clock.wallTime();

        AggregateMetricSender metricSender = new AggregateMetricSender(this.config.source(),
                this.dataPointReceiverFactory, this.eventReceiverFactory,
                new StaticAuthToken(this.config.accessToken()), this.onSendErrorHandlerCollection);

        for (List batch : MeterPartition.partition(this, config.batchSize())) {
            try (AggregateMetricSender.Session session = metricSender.createSession()) {
                // @formatter:off
                batch.stream()
                    .map(meter -> meter.match(
                            this::addGauge,
                            this::addCounter,
                            this::addTimer,
                            this::addDistributionSummary,
                            this::addLongTaskTimer,
                            this::addTimeGauge,
                            this::addFunctionCounter,
                            this::addFunctionTimer,
                            this::addMeter))
                    .flatMap(builders -> builders.map(builder -> builder.setTimestamp(timestamp).build()))
                    .forEach(session::setDatapoint);
                // @formatter:on

                logger.debug("successfully sent {} metrics to SignalFx.", batch.size());
            }
            catch (Throwable e) {
                logger.warn("failed to send metrics", e);
            }
        }
    }

    @Override
    protected Timer newTimer(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig,
            PauseDetector pauseDetector) {
        if (!publishCumulativeHistogram && !publishDeltaHistogram) {
            return super.newTimer(id, distributionStatisticConfig, pauseDetector);
        }
        Timer timer = new SignalfxTimer(id, clock, distributionStatisticConfig, pauseDetector, getBaseTimeUnit(),
                config.step().toMillis(), publishDeltaHistogram);
        HistogramGauges.registerWithCommonFormat(timer, this);
        return timer;
    }

    @Override
    protected DistributionSummary newDistributionSummary(Meter.Id id,
            DistributionStatisticConfig distributionStatisticConfig, double scale) {
        if (!publishCumulativeHistogram && !publishDeltaHistogram) {
            return super.newDistributionSummary(id, distributionStatisticConfig, scale);
        }
        DistributionSummary summary = new SignalfxDistributionSummary(id, clock, distributionStatisticConfig, scale,
                config.step().toMillis(), publishDeltaHistogram);
        HistogramGauges.registerWithCommonFormat(summary, this);
        return summary;
    }

    Stream addMeter(Meter meter) {
        return stream(meter.measure().spliterator(), false).flatMap(measurement -> {
            String statSuffix = NamingConvention.camelCase.tagKey(measurement.getStatistic().toString());
            switch (measurement.getStatistic()) {
                case TOTAL:
                case TOTAL_TIME:
                case COUNT:
                case DURATION:
                    return Stream.of(addDatapoint(meter, COUNTER, statSuffix, measurement.getValue()));
                case MAX:
                case VALUE:
                case UNKNOWN:
                case ACTIVE_TASKS:
                    return Stream.of(addDatapoint(meter, GAUGE, statSuffix, measurement.getValue()));
            }
            return Stream.empty();
        });
    }

    private SignalFxProtocolBuffers.DataPoint.Builder addDatapoint(Meter meter,
            SignalFxProtocolBuffers.MetricType metricType, @Nullable String statSuffix, Number value) {
        SignalFxProtocolBuffers.Datum.Builder datumBuilder = SignalFxProtocolBuffers.Datum.newBuilder();
        SignalFxProtocolBuffers.Datum datum = (value instanceof Double ? datumBuilder.setDoubleValue((Double) value)
                : datumBuilder.setIntValue(value.longValue()))
            .build();

        String metricName = config().namingConvention()
            .name(statSuffix == null ? meter.getId().getName() : meter.getId().getName() + "." + statSuffix,
                    meter.getId().getType(), meter.getId().getBaseUnit());

        SignalFxProtocolBuffers.DataPoint.Builder dataPointBuilder = SignalFxProtocolBuffers.DataPoint.newBuilder()
            .setMetric(metricName)
            .setMetricType(metricType)
            .setValue(datum);

        for (Tag tag : getConventionTags(meter.getId())) {
            dataPointBuilder.addDimensions(SignalFxProtocolBuffers.Dimension.newBuilder()
                .setKey(tag.getKey())
                .setValue(tag.getValue())
                .build());
        }

        return dataPointBuilder;
    }

    // VisibleForTesting
    Stream addLongTaskTimer(LongTaskTimer longTaskTimer) {
        return Stream.of(addDatapoint(longTaskTimer, GAUGE, "activeTasks", longTaskTimer.activeTasks()),
                addDatapoint(longTaskTimer, COUNTER, "duration", longTaskTimer.duration(getBaseTimeUnit())));
    }

    Stream addTimeGauge(TimeGauge timeGauge) {
        return Stream.of(addDatapoint(timeGauge, GAUGE, null, timeGauge.value(getBaseTimeUnit())));
    }

    Stream addGauge(Gauge gauge) {
        if (publishDeltaHistogram && gauge.getId().syntheticAssociation() != null
                && gauge.getId().getName().endsWith(".histogram")) {
            return Stream.of(addDatapoint(gauge, COUNTER, null, gauge.value()));
        }
        if (publishCumulativeHistogram && gauge.getId().syntheticAssociation() != null
                && gauge.getId().getName().endsWith(".histogram")) {
            return Stream.of(addDatapoint(gauge, CUMULATIVE_COUNTER, null, gauge.value()));
        }
        return Stream.of(addDatapoint(gauge, GAUGE, null, gauge.value()));
    }

    Stream addCounter(Counter counter) {
        return Stream.of(addDatapoint(counter, COUNTER, null, counter.count()));
    }

    Stream addFunctionCounter(FunctionCounter counter) {
        return Stream.of(addDatapoint(counter, COUNTER, null, counter.count()));
    }

    Stream addTimer(Timer timer) {
        return Stream.of(addDatapoint(timer, COUNTER, "count", timer.count()),
                addDatapoint(timer, COUNTER, "totalTime", timer.totalTime(getBaseTimeUnit())),
                addDatapoint(timer, GAUGE, "avg", timer.mean(getBaseTimeUnit())),
                addDatapoint(timer, GAUGE, "max", timer.max(getBaseTimeUnit())));
    }

    Stream addFunctionTimer(FunctionTimer timer) {
        return Stream.of(addDatapoint(timer, COUNTER, "count", timer.count()),
                addDatapoint(timer, COUNTER, "totalTime", timer.totalTime(getBaseTimeUnit())),
                addDatapoint(timer, GAUGE, "avg", timer.mean(getBaseTimeUnit())));
    }

    Stream addDistributionSummary(DistributionSummary summary) {
        return Stream.of(addDatapoint(summary, COUNTER, "count", summary.count()),
                addDatapoint(summary, COUNTER, "totalTime", summary.totalAmount()),
                addDatapoint(summary, GAUGE, "avg", summary.mean()),
                addDatapoint(summary, GAUGE, "max", summary.max()));
    }

    @Override
    protected TimeUnit getBaseTimeUnit() {
        return TimeUnit.SECONDS;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy