diff --git a/WORKSPACE b/WORKSPACE index 1dd2ea37fc..6ae42f2592 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -16,6 +16,30 @@ workspace(name = "io_opentelemetry_cpp") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +# Load gRPC dependency +# Note that this dependency needs to be loaded first due to +# https://github.com/bazelbuild/bazel/issues/6664 +http_archive( + name = "com_github_grpc_grpc", + strip_prefix = "grpc-1.28.0", + urls = [ + "https://github.com/grpc/grpc/archive/v1.28.0.tar.gz", + ], +) + +load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") + +grpc_deps() + +# Load extra gRPC dependencies due to https://github.com/grpc/grpc/issues/20511 +load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps") + +grpc_extra_deps() + +load("@upb//bazel:repository_defs.bzl", "bazel_version_repository") + +bazel_version_repository(name = "upb_bazel_version") + # Uses older protobuf version because of # https://github.com/protocolbuffers/protobuf/issues/7179 http_archive( @@ -54,3 +78,16 @@ http_archive( strip_prefix = "benchmark-1.5.0", urls = ["https://github.com/google/benchmark/archive/v1.5.0.tar.gz"], ) + +# C++ Prometheus Client library. +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file") + +http_archive( + name = "com_github_jupp0r_prometheus_cpp", + strip_prefix = "prometheus-cpp-master", + urls = ["https://github.com/jupp0r/prometheus-cpp/archive/master.zip"], +) + +load("@com_github_jupp0r_prometheus_cpp//bazel:repositories.bzl", "prometheus_cpp_repositories") + +prometheus_cpp_repositories() diff --git a/exporters/prometheus/BUILD b/exporters/prometheus/BUILD new file mode 100644 index 0000000000..850ca2ed48 --- /dev/null +++ b/exporters/prometheus/BUILD @@ -0,0 +1,104 @@ +# Copyright 2020, OpenTelemetry Authors +# +# 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(default_visibility = ["//visibility:public"]) + +cc_library( + name = "prometheus_exporter", + srcs = [ + "src/prometheus_exporter.cc", + ], + hdrs = [ + "include/opentelemetry/exporters/prometheus/prometheus_collector.h", + "include/opentelemetry/exporters/prometheus/prometheus_exporter.h", + "include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h", + ], + strip_include_prefix = "include", + deps = [ + "//api", + "//sdk:headers", + "@com_github_jupp0r_prometheus_cpp//core", + "@com_github_jupp0r_prometheus_cpp//pull", + ], +) + +cc_library( + name = "prometheus_collector", + srcs = [ + "src/prometheus_collector.cc", + ], + hdrs = [ + "include/opentelemetry/exporters/prometheus/prometheus_collector.h", + "include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h", + ], + strip_include_prefix = "include", + deps = [ + ":prometheus_utils", + "//api", + "//sdk:headers", + "@com_github_jupp0r_prometheus_cpp//core", + ], +) + +cc_library( + name = "prometheus_utils", + srcs = [ + "src/prometheus_exporter_utils.cc", + ], + hdrs = [ + "include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h", + ], + strip_include_prefix = "include", + deps = [ + "//api", + "//sdk:headers", + "@com_github_jupp0r_prometheus_cpp//core", + ], +) + +cc_test( + name = "prometheus_exporter_test", + srcs = [ + "test/prometheus_exporter_test.cc", + ], + deps = [ + ":prometheus_collector", + ":prometheus_exporter", + "@com_github_jupp0r_prometheus_cpp//core", + "@com_github_jupp0r_prometheus_cpp//pull", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "prometheus_collector_test", + srcs = [ + "test/prometheus_collector_test.cc", + ], + deps = [ + ":prometheus_collector", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "prometheus_exporter_utils_test", + srcs = [ + "test/prometheus_exporter_utils_test.cc", + ], + deps = [ + ":prometheus_utils", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/exporters/prometheus/CMakeLists.txt b/exporters/prometheus/CMakeLists.txt new file mode 100644 index 0000000000..8c2ae89334 --- /dev/null +++ b/exporters/prometheus/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright 2020, OpenTelemetry Authors +# +# 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. + +include_directories(include) + +find_package(prometheus-cpp CONFIG REQUIRED) +add_library( + prometheus_exporter src/prometheus_exporter.cc src/prometheus_collector.cc + src/prometheus_exporter_utils.cc) + +if (BUILD_TESTING) + add_subdirectory(test) +endif () diff --git a/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h b/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h new file mode 100644 index 0000000000..34ed0bfb40 --- /dev/null +++ b/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h @@ -0,0 +1,189 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +#pragma once + +#include +#include + +#include "opentelemetry/sdk/metrics/record.h" +#include "prometheus/metric_family.h" + +namespace prometheus_client = ::prometheus; +namespace metric_sdk = opentelemetry::sdk::metrics; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace prometheus +{ +/** + * The Prometheus Utils contains utility functions for Prometheus Exporter + */ +class PrometheusExporterUtils +{ +public: + /** + * Helper function to convert OpenTelemetry metrics data collection + * to Prometheus metrics data collection + * + * @param records a collection of metrics in OpenTelemetry + * @return a collection of translated metrics that is acceptable by Prometheus + */ + static std::vector TranslateToPrometheus( + std::vector &records); + +private: + /** + * Set value to metric family according to record + */ + static void SetMetricFamily(metric_sdk::Record &record, + prometheus_client::MetricFamily *metric_family); + + /** + * Sanitize the given metric name or label according to Prometheus rule. + * + * This function is needed because names in OpenTelemetry can contain + * alphanumeric characters, '_', '.', and '-', whereas in Prometheus the + * name should only contain alphanumeric characters and '_'. + */ + static std::string SanitizeNames(std::string name); + + /** + * Determine whether the input name is a valid OTel name or not + * + * From the spec: + * 1. They are non-empty strings + * 2. They are case-insensitive + * 3. The first character must be non-numeric, non-space, non-punctuation + * 4. Subsequent characters must belong to the alphanumeric characters, '_', '.', and '-'. + */ + static bool IsValidName(const std::string &name); + + /** + * Set value to metric family for different aggregator + */ + template + static void SetMetricFamilyByAggregator(nostd::shared_ptr> aggregator, + std::string labels_str, + prometheus_client::MetricFamily *metric_family); + + /** + * Translate the OTel metric type to Prometheus metric type + */ + static prometheus_client::MetricType TranslateType(metric_sdk::AggregatorKind kind); + + /** + * Set metric data for: + * Counter => Prometheus Counter + * Gauge => Prometheus Gauge + */ + template + static void SetData(std::vector values, + const std::string &labels, + prometheus_client::MetricType type, + std::chrono::nanoseconds time, + prometheus_client::MetricFamily *metric_family); + + /** + * Set metric data for: + * Histogram => Prometheus Histogram + */ + template + static void SetData(std::vector values, + const std::vector &boundaries, + const std::vector &counts, + const std::string &labels, + std::chrono::nanoseconds time, + prometheus_client::MetricFamily *metric_family); + + /** + * Set metric data for: + * MinMaxSumCount => Prometheus Gauge + * Use Average (sum / count) as the gauge metric + */ + static void SetData(double value, + const std::string &labels, + std::chrono::nanoseconds time, + prometheus_client::MetricFamily *metric_family); + + /** + * Set metric data for: + * Exact => Prometheus Summary + * Sketch => Prometheus Summary + */ + template + static void SetData(std::vector values, + metric_sdk::AggregatorKind kind, + const std::vector &quantiles, + const std::string &labels, + std::chrono::nanoseconds time, + prometheus_client::MetricFamily *metric_family); + + /** + * Set time and labels to metric data + */ + static void SetMetricBasic(prometheus_client::ClientMetric &metric, + std::chrono::nanoseconds time, + const std::string &labels); + + /** + * Parse a string of labels (key:value) into a vector of pairs + * {,} + * {l1:v1,l2:v2,...,} + */ + static std::vector> ParseLabel(std::string labels); + + /** + * Build a quantiles vector from aggregator + */ + template + static std::vector GetQuantilesVector(nostd::shared_ptr> aggregator); + + /** + * Handle Counter and Gauge. + */ + template + static void SetValue(std::vector values, + prometheus_client::MetricType type, + prometheus_client::ClientMetric *metric); + + /** + * Handle Gauge from MinMaxSumCount + */ + static void SetValue(double value, prometheus_client::ClientMetric *metric); + + /** + * Handle Histogram + */ + template + static void SetValue(std::vector values, + std::vector boundaries, + std::vector counts, + prometheus_client::ClientMetric *metric); + + /** + * Handle Exact and Sketch + */ + template + static void SetValue(std::vector values, + metric_sdk::AggregatorKind kind, + std::vector quantiles, + prometheus_client::ClientMetric *metric); +}; +} // namespace prometheus +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE