001/*
002 * (C) Copyright 2020 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bdelbosc
018 */
019package org.nuxeo.runtime.metrics.reporter;
020
021import static org.apache.commons.lang3.StringUtils.isBlank;
022
023import java.util.Arrays;
024import java.util.EnumSet;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028import java.util.concurrent.TimeUnit;
029import java.util.stream.Collectors;
030
031import org.apache.logging.log4j.LogManager;
032import org.apache.logging.log4j.Logger;
033import org.coursera.metrics.datadog.DefaultMetricNameFormatter;
034import org.coursera.metrics.datadog.transport.HttpTransport;
035import org.coursera.metrics.datadog.transport.Transport;
036import org.coursera.metrics.datadog.transport.UdpTransport;
037import org.nuxeo.runtime.metrics.AbstractMetricsReporter;
038import org.nuxeo.runtime.metrics.reporter.patch.NuxeoDatadogReporter;
039
040import io.dropwizard.metrics5.MetricAttribute;
041import io.dropwizard.metrics5.MetricFilter;
042import io.dropwizard.metrics5.MetricRegistry;
043
044/**
045 * Reports metrics to Datadog.
046 *
047 * @since 11.1
048 */
049public class DatadogReporter extends AbstractMetricsReporter {
050
051    private static final Logger log = LogManager.getLogger(DatadogReporter.class);
052
053    protected NuxeoDatadogReporter reporter;
054
055    protected String hostname;
056
057    protected List<String> tags;
058
059    @Override
060    public void init(long pollInterval, Map<String, String> options) {
061        super.init(pollInterval, options);
062        hostname = getHostname();
063        tags = getTags();
064    }
065
066    private String getHostname() {
067        String value = options.get("hostname");
068        if (!isBlank(value)) {
069            return value;
070        }
071        value = getHostnameFromNuxeoUrl();
072        if (!isBlank(value)) {
073            return value;
074        }
075        return getCurrentHostname();
076    }
077
078    @Override
079    public void start(MetricRegistry registry, MetricFilter filter, Set<MetricAttribute> deniedExpansions) {
080        Transport transport;
081        if (getOptionAsBoolean("udp", false)) {
082            String host = requireOption("host", "when using UDP");
083            int port = getOptionAsInt("port", 8125);
084            log.warn("Connecting using UDP {}:{} reporting every {}s from {}", host, port, pollInterval, hostname);
085            transport = new UdpTransport.Builder().withStatsdHost(host).withPort(port).withRetryingLookup(true).build();
086        } else {
087            String apiKey = requireOption("apiKey", "when using HTTP");
088            transport = new HttpTransport.Builder().withApiKey(apiKey).build();
089            log.warn("Connecting using HTTP transport using apiKey reporting every {}s from {}", pollInterval,
090                    hostname);
091        }
092        reporter = NuxeoDatadogReporter.forRegistry(registry)
093                                       .withHost(hostname)
094                                       .withTags(tags)
095                                       .withTransport(transport)
096                                       .withExpansions(getExpansions(deniedExpansions))
097                                       .filter(filter)
098                                       .withMetricNameFormatter(new DefaultMetricNameFormatter())
099                                       .build();
100        reporter.start(getPollInterval(), TimeUnit.SECONDS);
101    }
102
103    protected List<String> getTags() {
104        String value = getOption("tags", "nuxeo");
105        return Arrays.asList(value.split(","));
106    }
107
108    protected EnumSet<NuxeoDatadogReporter.Expansion> getExpansions(Set<MetricAttribute> deniedExpansions) {
109        if (deniedExpansions.isEmpty()) {
110            return NuxeoDatadogReporter.Expansion.ALL;
111        } else {
112            return NuxeoDatadogReporter.Expansion.ALL.stream().filter(e -> {
113                switch (e) {
114                case MAX:
115                    return !deniedExpansions.contains(MetricAttribute.MAX);
116                case MIN:
117                    return !deniedExpansions.contains(MetricAttribute.MIN);
118                case MEAN:
119                    return !deniedExpansions.contains(MetricAttribute.MEAN);
120                case MEDIAN:
121                    return !deniedExpansions.contains(MetricAttribute.P50);
122                case P75:
123                    return !deniedExpansions.contains(MetricAttribute.P75);
124                case P95:
125                    return !deniedExpansions.contains(MetricAttribute.P95);
126                case P98:
127                    return !deniedExpansions.contains(MetricAttribute.P98);
128                case P99:
129                    return !deniedExpansions.contains(MetricAttribute.P99);
130                case P999:
131                    return !deniedExpansions.contains(MetricAttribute.P999);
132                case COUNT:
133                    return !deniedExpansions.contains(MetricAttribute.COUNT);
134                case STD_DEV:
135                    return !deniedExpansions.contains(MetricAttribute.STDDEV);
136                case RATE_MEAN:
137                    return !deniedExpansions.contains(MetricAttribute.MEAN_RATE);
138                case RATE_1_MINUTE:
139                    return !deniedExpansions.contains(MetricAttribute.M1_RATE);
140                case RATE_5_MINUTE:
141                    return !deniedExpansions.contains(MetricAttribute.M5_RATE);
142                case RATE_15_MINUTE:
143                    return !deniedExpansions.contains(MetricAttribute.M15_RATE);
144                default:
145                    return false;
146                }
147            }).collect(Collectors.toCollection(() -> EnumSet.noneOf(NuxeoDatadogReporter.Expansion.class)));
148        }
149    }
150
151    @Override
152    public void stop() {
153        log.debug("Stop reporting");
154        reporter.stop();
155    }
156
157}