diff --git a/airbyte-metrics/build.gradle b/airbyte-metrics/build.gradle index 924f6e781378..49a8cccd8dbe 100644 --- a/airbyte-metrics/build.gradle +++ b/airbyte-metrics/build.gradle @@ -10,4 +10,6 @@ dependencies { implementation 'io.prometheus:simpleclient_hotspot:0.12.0' // basic client instrumentation implementation 'io.prometheus:simpleclient_httpserver:0.12.0' // basic server to serve prometheus port implementation 'io.prometheus:simpleclient_pushgateway:0.12.0' // push libs for basic server + + implementation 'com.datadoghq:java-dogstatsd-client:4.0.0' } diff --git a/airbyte-metrics/src/main/java/io/airbyte/metrics/DogstatsdMetricSingleton.java b/airbyte-metrics/src/main/java/io/airbyte/metrics/DogstatsdMetricSingleton.java new file mode 100644 index 000000000000..0bf54812a80b --- /dev/null +++ b/airbyte-metrics/src/main/java/io/airbyte/metrics/DogstatsdMetricSingleton.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021 Airbyte, Inc., all rights reserved. + */ + +package io.airbyte.metrics; + +import com.timgroup.statsd.NonBlockingStatsDClientBuilder; +import com.timgroup.statsd.StatsDClient; +import lombok.extern.slf4j.Slf4j; + +/** + * Light wrapper around the DogsStatsD client to make using the client slightly more ergonomic. + *
+ * This class mainly exists to help Airbyte instrument/debug application on Airbyte Cloud. + *
+ * Open source users are free to turn this on and consume the same metrics. + */ +@Slf4j +public class DogstatsdMetricSingleton { + + private static DogstatsdMetricSingleton instance; + private final StatsDClient statsDClient; + private final boolean instancePublish; + + public DogstatsdMetricSingleton(final String appName, final boolean publish) { + instancePublish = publish; + statsDClient = new NonBlockingStatsDClientBuilder() + .prefix(appName) + .hostname(System.getenv("DD_AGENT_HOST")) + .port(Integer.parseInt(System.getenv("DD_DOGSTATSD_PORT"))) + .build(); + } + + public static synchronized DogstatsdMetricSingleton getInstance() { + if (instance == null) { + throw new RuntimeException("You must initialize configuration with the initialize() method before getting an instance."); + } + return instance; + } + + public synchronized static void initialize(final String appName, final boolean publish) { + if (instance != null) { + throw new RuntimeException("You cannot initialize configuration more than once."); + } + if (publish) { + log.info("Starting DogStatsD client.."); + // The second constructor argument ('true') makes this server start as a separate daemon thread. + // http://prometheus.github.io/client_java/io/prometheus/client/exporter/HTTPServer.html#HTTPServer-int-boolean- + instance = new DogstatsdMetricSingleton(appName, publish); + } + } + + /** + * Increment or decrement a counter. + * + * @param name of counter. + * @param amt to adjust. + * @param tags + */ + public void count(final String name, final double amt, final String... tags) { + if (instancePublish) { + log.info("publishing count, name: {}, value: {}", name, amt); + statsDClient.count(name, amt, tags); + } + } + + /** + * Record the latest value for a gauge. + * + * @param name of gauge. + * @param val to record. + * @param tags + */ + public void gauge(final String name, final double val, final String... tags) { + if (instancePublish) { + log.info("publishing gauge, name: {}, value: {}", name, val); + statsDClient.gauge(name, val, tags); + } + } + + /** + * Submit a single execution time aggregated locally by the Agent. Use this if approximate stats are + * sufficient. + * + * @param name of histogram. + * @param val of time to record. + * @param tags + */ + public void recordTimeLocal(final String name, final double val, final String... tags) { + if (instancePublish) { + log.info("recording histogram, name: {}, value: {}", name, val); + statsDClient.histogram(name, val, tags); + } + } + + /** + * Submit a single execution time aggregated globally by Datadog. Use this for precise stats. + * + * @param name of distribution. + * @param val of time to record. + * @param tags + */ + public void recordTimeGlobal(final String name, final double val, final String... tags) { + if (instancePublish) { + log.info("recording distribution, name: {}, value: {}", name, val); + statsDClient.distribution(name, val, tags); + } + } + + /** + * Wrapper of {@link #recordTimeGlobal(String, double, String...)} with a runnable for convenience. + * + * @param name + * @param runnable + * @param tags + */ + public void recordTimeGlobal(final String name, final Runnable runnable, final String... tags) { + final long start = System.currentTimeMillis(); + runnable.run(); + final long end = System.currentTimeMillis(); + final long val = end - start; + recordTimeGlobal(name, val, tags); + } + +} diff --git a/airbyte-metrics/src/main/java/io/airbyte/metrics/PrometheusBaseExample.java b/airbyte-metrics/src/main/java/io/airbyte/metrics/PrometheusBaseExample.java deleted file mode 100644 index ebe85ad4746a..000000000000 --- a/airbyte-metrics/src/main/java/io/airbyte/metrics/PrometheusBaseExample.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2021 Airbyte, Inc., all rights reserved. - */ - -package io.airbyte.metrics; - -import io.prometheus.client.Counter; -import io.prometheus.client.Gauge; -import io.prometheus.client.Histogram; -import io.prometheus.client.exporter.HTTPServer; -import java.io.IOException; - -public class PrometheusBaseExample { - - // The following represents a class we want to add instrumentation - // (metrics) to: - public static class AnInstrumentedClass { - - // Number and type of metrics per class is left on discretion - // of a Developer. - // A "namespace()" here sets the prefix of a metric. - static final Counter counter = Counter.build().namespace("app_prom_java").name("my_counter").help("This is my counter").register(); - static final Gauge gauge = Gauge.build().name("test_metric_gauges").help("test scheduler metric").register(); - static final Histogram histogram = Histogram.build().namespace("app_prom_java").name("my_histogram").help("This is my histogram").register(); - // static final Summary summary = - // Summary.build().namespace("app_prom_java").name("my_summary").help("This is my - // summary").register(); - - public static void doSomething() { - // Here goes some business logic. Whenever we want to report - // something to a monitoring system -- we update a corresponding - // metrics object, i.e.: - - // counter.inc(rand(0, 5)); - - gauge.set(rand(-5, 10)); - // histogram.observe(rand(0, 5)); - // summary.observe(rand(0, 5)); - } - - private static double rand(final double min, final double max) { - return min + (Math.random() * (max - min)); - } - - } - - public static void main(final String[] args) { - try { - // The second constructor argument ('true') makes this server start as a separate daemon thread. - // http://prometheus.github.io/client_java/io/prometheus/client/exporter/HTTPServer.html#HTTPServer-int-boolean- - new HTTPServer(Integer.parseInt("8081"), true); - } catch (final IOException e) {} - - // The following block along with an instance of the instrumented - // class simulates activity inside instrumented class object, which - // we may track later by watching metrics' values: - while (true) { - try { - AnInstrumentedClass.doSomething(); - - Thread.sleep(1000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - - } - -} diff --git a/airbyte-metrics/src/main/java/io/airbyte/metrics/MetricSingleton.java b/airbyte-metrics/src/main/java/io/airbyte/metrics/PrometheusMetricSingleton.java similarity index 95% rename from airbyte-metrics/src/main/java/io/airbyte/metrics/MetricSingleton.java rename to airbyte-metrics/src/main/java/io/airbyte/metrics/PrometheusMetricSingleton.java index 2b19dbb18630..96f1882bb7bb 100644 --- a/airbyte-metrics/src/main/java/io/airbyte/metrics/MetricSingleton.java +++ b/airbyte-metrics/src/main/java/io/airbyte/metrics/PrometheusMetricSingleton.java @@ -27,10 +27,11 @@ *
* Open source users are free to turn this on and consume the same metrics.
*/
-public class MetricSingleton {
+@Deprecated
+public class PrometheusMetricSingleton {
- private static final Logger LOGGER = LoggerFactory.getLogger(MetricSingleton.class);
- private static MetricSingleton instance;
+ private static final Logger LOGGER = LoggerFactory.getLogger(PrometheusMetricSingleton.class);
+ private static PrometheusMetricSingleton instance;
private final Map