Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| efd3086af2 |
@@ -28,3 +28,6 @@
|
||||
[submodule "libraries/libfc/libraries/ff"]
|
||||
path = libraries/libfc/libraries/ff
|
||||
url = https://github.com/AntelopeIO/libff
|
||||
[submodule "libraries/prometheus-cpp"]
|
||||
path = libraries/prometheus-cpp
|
||||
url = https://github.com/jupp0r/prometheus-cpp.git
|
||||
|
||||
@@ -47,4 +47,4 @@ set_property(GLOBAL PROPERTY CTEST_CUSTOM_TESTS_IGNORE
|
||||
yubico_otp wrap_data wrap info import_rsa import_authkey generate_hmac generate_ec\
|
||||
attest pbkdf2 parsing ${_CTEST_CUSTOM_TESTS_IGNORE}")
|
||||
|
||||
add_subdirectory( prometheus )
|
||||
add_subdirectory(prometheus)
|
||||
@@ -1,3 +1,20 @@
|
||||
file(GLOB HEADERS "include/prometheus/*.hpp")
|
||||
add_library( prometheus ${HEADERS} src/counter.cpp)
|
||||
target_include_directories( prometheus PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
add_library(prometheus-core
|
||||
prometheus-cpp/core/src/check_names.cc
|
||||
prometheus-cpp/core/src/counter.cc
|
||||
prometheus-cpp/core/src/detail/builder.cc
|
||||
prometheus-cpp/core/src/detail/ckms_quantiles.cc
|
||||
prometheus-cpp/core/src/detail/time_window_quantiles.cc
|
||||
prometheus-cpp/core/src/detail/utils.cc
|
||||
prometheus-cpp/core/src/family.cc
|
||||
prometheus-cpp/core/src/gauge.cc
|
||||
prometheus-cpp/core/src/histogram.cc
|
||||
prometheus-cpp/core/src/info.cc
|
||||
prometheus-cpp/core/src/registry.cc
|
||||
prometheus-cpp/core/src/serializer.cc
|
||||
prometheus-cpp/core/src/summary.cc
|
||||
prometheus-cpp/core/src/text_serializer.cc
|
||||
)
|
||||
|
||||
target_include_directories( prometheus-core PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/prometheus-cpp/core/include)
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
//#include "prometheus/detail/core_export.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
struct /* PROMETHEUS_CPP_CORE_EXPORT */ client_metric {
|
||||
// label_t
|
||||
|
||||
struct label_t {
|
||||
std::string name;
|
||||
std::string value;
|
||||
|
||||
bool operator<(const label_t& rhs) const {
|
||||
return std::tie(name, value) < std::tie(rhs.name, rhs.value);
|
||||
}
|
||||
|
||||
bool operator==(const label_t& rhs) const {
|
||||
return std::tie(name, value) == std::tie(rhs.name, rhs.value);
|
||||
}
|
||||
};
|
||||
std::vector<label_t> label;
|
||||
|
||||
// counter_t
|
||||
|
||||
struct counter_t {
|
||||
double value = 0.0;
|
||||
};
|
||||
counter_t counter;
|
||||
|
||||
// gauge_t
|
||||
|
||||
struct gauge_t {
|
||||
double value = 0.0;
|
||||
};
|
||||
gauge_t gauge;
|
||||
|
||||
// info_t
|
||||
|
||||
struct info_t {
|
||||
double value = 1.0;
|
||||
};
|
||||
info_t info;
|
||||
|
||||
// summary_t
|
||||
|
||||
struct quantile_t {
|
||||
double quantile = 0.0;
|
||||
double value = 0.0;
|
||||
};
|
||||
|
||||
struct summary_t {
|
||||
std::uint64_t sample_count = 0;
|
||||
double sample_sum = 0.0;
|
||||
std::vector<quantile_t> quantile;
|
||||
};
|
||||
summary_t summary;
|
||||
|
||||
// histogram_metric
|
||||
|
||||
struct bucket_t {
|
||||
std::uint64_t cumulative_count = 0;
|
||||
double upper_bound = 0.0;
|
||||
};
|
||||
|
||||
struct histogram_metric {
|
||||
std::uint64_t sample_count = 0;
|
||||
double sample_sum = 0.0;
|
||||
std::vector<bucket_t> bucket;
|
||||
};
|
||||
histogram_metric histogram;
|
||||
|
||||
// untyped_t
|
||||
|
||||
struct untyped_t {
|
||||
double value = 0;
|
||||
};
|
||||
untyped_t untyped;
|
||||
|
||||
// Timestamp
|
||||
|
||||
std::int64_t timestamp_ms = 0;
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
# define PROMETHEUS_CPP_CORE_EXPORT
|
||||
# define PROMETHEUS_CPP_CORE_NO_EXPORT
|
||||
@@ -0,0 +1 @@
|
||||
3rdparty
|
||||
@@ -0,0 +1,10 @@
|
||||
build:asan --strip=never
|
||||
build:asan --copt -fsanitize=address
|
||||
build:asan --copt -DADDRESS_SANITIZER
|
||||
build:asan --copt -O0
|
||||
build:asan --copt -fno-omit-frame-pointer
|
||||
build:asan --copt -g
|
||||
build:asan --linkopt -fsanitize=address
|
||||
build:asan --cc_output_directory_tag=asan
|
||||
|
||||
build:ssl --define=with_civetweb_ssl=true
|
||||
@@ -0,0 +1,2 @@
|
||||
BasedOnStyle: Google
|
||||
DerivePointerAlignment: false
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Bazel CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Bazel on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
include:
|
||||
- os: macOS-latest
|
||||
bazel_args: "--cxxopt=-std=c++14"
|
||||
- os: ubuntu-latest
|
||||
bazel_args: "--cxxopt=-std=c++14"
|
||||
- os: windows-latest
|
||||
bazel_args: "--cxxopt=/std:c++14"
|
||||
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Generate German locale on Ubuntu
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get remove -y --purge man-db # avoid time-consuming trigger
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y locales
|
||||
sudo locale-gen de_DE.UTF-8 # used by SerializerTest
|
||||
|
||||
- name: Install telegraf on Ubuntu
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
|
||||
source /etc/lsb-release
|
||||
echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y telegraf
|
||||
|
||||
- name: Install telegraf on macOS
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install telegraf
|
||||
|
||||
- name: Build with SSL
|
||||
run: bazel build --config=ssl ${{ matrix.bazel_args }} //...
|
||||
|
||||
- name: Test with SSL
|
||||
run: bazel test --config=ssl ${{ matrix.bazel_args }} --test_output=all //...
|
||||
|
||||
- name: Build
|
||||
run: bazel build ${{ matrix.bazel_args }} //...
|
||||
|
||||
- name: Test
|
||||
run: bazel test ${{ matrix.bazel_args }} --test_output=all //...
|
||||
|
||||
- name: Scraping Test
|
||||
if: runner.os != 'Windows'
|
||||
run: bazel test ${{ matrix.bazel_args }} --test_output=all //pull/tests/integration:scrape-test
|
||||
|
||||
- name: Benchmark
|
||||
run: bazel run ${{ matrix.bazel_args }} -c opt //core/benchmarks
|
||||
@@ -0,0 +1,131 @@
|
||||
name: CMake CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: CMake on ${{ matrix.os }} with ${{ matrix.dependencies }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macOS-latest, ubuntu-18.04, windows-2019]
|
||||
dependencies: [submodule, vcpkg]
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: ${{ matrix.dependencies == 'submodule' }}
|
||||
|
||||
- name: Mount vcpkg cache
|
||||
if: matrix.dependencies == 'vcpkg'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "~/.cache/vcpkg/archives"
|
||||
key: vcpkg-${{ matrix.os }}
|
||||
|
||||
- name: Default to x64-windows vcpkg triplet
|
||||
if: matrix.dependencies == 'vcpkg' && runner.os == 'Windows'
|
||||
run: echo "VCPKG_DEFAULT_TRIPLET=x64-windows" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
|
||||
|
||||
- name: Install vcpkg dependencies
|
||||
if: matrix.dependencies == 'vcpkg'
|
||||
run: vcpkg install benchmark civetweb curl[core] gtest zlib
|
||||
|
||||
- name: Generate German locale on Ubuntu
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get remove -y --purge man-db # avoid time-consuming trigger
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y locales
|
||||
sudo locale-gen de_DE.UTF-8 # used by SerializerTest
|
||||
|
||||
- name: Install ninja on Ubuntu
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get install -y ninja-build
|
||||
|
||||
- name: Install ninja on macOS
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install ninja
|
||||
|
||||
- name: "Configure for Unix with internal dependencies"
|
||||
if: matrix.dependencies == 'submodule' && runner.os != 'Windows'
|
||||
run: cmake -DUSE_THIRDPARTY_LIBRARIES=ON -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install -DENABLE_WARNINGS_AS_ERRORS=ON -DENABLE_COMPRESSION=OFF -DENABLE_PUSH=OFF -DCMAKE_DEBUG_POSTFIX=_d -DCMAKE_CONFIGURATION_TYPES='Release;Debug' -G"Ninja Multi-Config" -S ${{ github.workspace }} -B ${{ github.workspace }}/_build
|
||||
|
||||
- name: "Configure for Windows with internal dependencies"
|
||||
if: matrix.dependencies == 'submodule' && runner.os == 'Windows'
|
||||
run: cmake -DUSE_THIRDPARTY_LIBRARIES=ON -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install -DENABLE_WARNINGS_AS_ERRORS=ON -DENABLE_COMPRESSION=OFF -DENABLE_PUSH=OFF -DCMAKE_DEBUG_POSTFIX=_d -S ${{ github.workspace }} -B ${{ github.workspace }}/_build
|
||||
|
||||
- name: "Configure for Unix with vcpkg dependencies"
|
||||
if: matrix.dependencies == 'vcpkg' && runner.os != 'Windows'
|
||||
run: cmake -DUSE_THIRDPARTY_LIBRARIES=OFF -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install "-DCMAKE_TOOLCHAIN_FILE=${VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DCMAKE_DEBUG_POSTFIX=_d -DCMAKE_CONFIGURATION_TYPES='Release;Debug' -G"Ninja Multi-Config" -S ${{ github.workspace }} -B ${{ github.workspace }}/_build
|
||||
|
||||
- name: "Configure for Windows with vcpkg dependencies"
|
||||
if: matrix.dependencies == 'vcpkg' && runner.os == 'Windows'
|
||||
run: cmake -DUSE_THIRDPARTY_LIBRARIES=OFF -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install "-DCMAKE_TOOLCHAIN_FILE=${Env:VCPKG_INSTALLATION_ROOT}\scripts\buildsystems\vcpkg.cmake" -DCMAKE_DEBUG_POSTFIX=_d -S ${{ github.workspace }} -B ${{ github.workspace }}/_build
|
||||
|
||||
- name: "Build Debug"
|
||||
run: cmake --build ${{ github.workspace }}/_build --config Debug
|
||||
|
||||
- name: "Build Release"
|
||||
run: cmake --build ${{ github.workspace }}/_build --config Release
|
||||
|
||||
- name: "Test Debug"
|
||||
run: ctest -C Debug -V -LE Benchmark
|
||||
working-directory: "${{ github.workspace }}/_build"
|
||||
|
||||
- name: "Test Release"
|
||||
run: ctest -C Release -V -LE Benchmark
|
||||
working-directory: "${{ github.workspace }}/_build"
|
||||
|
||||
- name: "Run Benchmark"
|
||||
run: ctest -C Release -V -L Benchmark
|
||||
working-directory: "${{ github.workspace }}/_build"
|
||||
|
||||
- name: "Install Debug"
|
||||
run: cmake --install ${{ github.workspace }}/_build --config Debug
|
||||
|
||||
- name: "Install Release"
|
||||
run: cmake --install ${{ github.workspace }}/_build --config Release
|
||||
|
||||
- name: "Configure CMake import for Unix with internal dependencies"
|
||||
if: matrix.dependencies == 'submodule' && runner.os != 'Windows'
|
||||
run: cmake -Dprometheus-cpp_DIR=${{ github.workspace }}/_install/lib/cmake/prometheus-cpp -DCMAKE_CONFIGURATION_TYPES='Release;Debug' -G"Ninja Multi-Config" -S ${{ github.workspace }}/cmake/project-import-cmake -B ${{ github.workspace }}/_import_cmake
|
||||
|
||||
- name: "Configure CMake import for Windows with internal dependencies"
|
||||
if: matrix.dependencies == 'submodule' && runner.os == 'Windows'
|
||||
run: cmake -Dprometheus-cpp_DIR=${{ github.workspace }}/_install/lib/cmake/prometheus-cpp -S ${{ github.workspace }}/cmake/project-import-cmake -B ${{ github.workspace }}/_import_cmake
|
||||
|
||||
- name: "Configure CMake import for Unix with vcpkg dependencies"
|
||||
if: matrix.dependencies == 'vcpkg' && runner.os != 'Windows'
|
||||
run: cmake -Dprometheus-cpp_DIR=${{ github.workspace }}/_install/lib/cmake/prometheus-cpp "-DCMAKE_TOOLCHAIN_FILE=${VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DCMAKE_CONFIGURATION_TYPES='Release;Debug' -G"Ninja Multi-Config" -S ${{ github.workspace }}/cmake/project-import-cmake -B ${{ github.workspace }}/_import_cmake
|
||||
|
||||
- name: "Configure CMake import for Windows with vcpkg dependencies"
|
||||
if: matrix.dependencies == 'vcpkg' && runner.os == 'Windows'
|
||||
run: cmake -Dprometheus-cpp_DIR=${{ github.workspace }}/_install/lib/cmake/prometheus-cpp "-DCMAKE_TOOLCHAIN_FILE=${Env:VCPKG_INSTALLATION_ROOT}\scripts\buildsystems\vcpkg.cmake" -S ${{ github.workspace }}/cmake/project-import-cmake -B ${{ github.workspace }}/_import_cmake
|
||||
|
||||
- name: "Build CMake import Debug"
|
||||
run: cmake --build ${{ github.workspace }}/_import_cmake --config Debug
|
||||
|
||||
- name: "Build CMake import Release"
|
||||
run: cmake --build ${{ github.workspace }}/_import_cmake --config Release
|
||||
|
||||
- name: "Configure for Unix Shared Libs with internal dependencies"
|
||||
if: matrix.dependencies == 'submodule' && runner.os != 'Windows'
|
||||
run: cmake -DUSE_THIRDPARTY_LIBRARIES=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install_shared -DENABLE_WARNINGS_AS_ERRORS=ON -DENABLE_COMPRESSION=OFF -DENABLE_PUSH=OFF -DCMAKE_DEBUG_POSTFIX=_d -GNinja -S ${{ github.workspace }} -B ${{ github.workspace }}/_build_shared
|
||||
|
||||
- name: "Build for Unix Shared Libs"
|
||||
if: matrix.dependencies == 'submodule' && runner.os != 'Windows'
|
||||
run: cmake --build ${{ github.workspace }}/_build_shared
|
||||
|
||||
- name: "Install for Unix Shared Libs"
|
||||
if: matrix.dependencies == 'submodule' && runner.os != 'Windows'
|
||||
run: cmake --install ${{ github.workspace }}/_build_shared
|
||||
|
||||
- name: "Configure pkg-config import for Unix"
|
||||
if: matrix.dependencies == 'submodule' && runner.os != 'Windows'
|
||||
run: cmake -DCMAKE_PREFIX_PATH=${{ github.workspace }}/_install_shared -GNinja -S ${{ github.workspace }}/cmake/project-import-pkgconfig -B ${{ github.workspace }}/_import_pkgconfig
|
||||
|
||||
- name: "Build pkg-config import for Unix"
|
||||
if: matrix.dependencies == 'submodule' && runner.os != 'Windows'
|
||||
run: cmake --build ${{ github.workspace }}/_import_pkgconfig
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Coverage
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Code Coverage
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Mount vcpkg cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "~/.cache/vcpkg/archives"
|
||||
key: vcpkg-${{ runner.os }}
|
||||
|
||||
- name: Install vcpkg dependencies
|
||||
run: vcpkg install benchmark civetweb curl[core] gtest zlib
|
||||
|
||||
- name: Generate German locale on Ubuntu
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get remove -y --purge man-db # avoid time-consuming trigger
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y locales
|
||||
sudo locale-gen de_DE.UTF-8 # used by SerializerTest
|
||||
|
||||
- name: Install ninja on Ubuntu
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get install -y ninja-build
|
||||
|
||||
- name: Install lcov
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get install -y lcov
|
||||
|
||||
- name: "CMake Configure for Unix with vcpkg dependencies"
|
||||
env:
|
||||
CFLAGS: "--coverage"
|
||||
CXXFLAGS: "--coverage"
|
||||
LDFLAGS: "--coverage"
|
||||
run: cmake -DUSE_THIRDPARTY_LIBRARIES=OFF "-DCMAKE_TOOLCHAIN_FILE=${VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -GNinja -S ${{ github.workspace }} -B ${{ github.workspace }}/_build
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{ github.workspace }}/_build
|
||||
|
||||
- name: Test
|
||||
run: ctest -V -LE Benchmark
|
||||
working-directory: "${{ github.workspace }}/_build"
|
||||
|
||||
- name: Run lcov
|
||||
run: lcov --capture --directory "${{ github.workspace }}/_build" --output-file coverage.info --no-external --directory "${{ github.workspace }}" --exclude '*/tests/*'
|
||||
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-lcov: coverage.info
|
||||
@@ -0,0 +1,30 @@
|
||||
name: Doxygen
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Code Coverage
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install doxygen
|
||||
run: |
|
||||
sudo apt-get remove -y --purge man-db # avoid time-consuming trigger
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y doxygen graphviz
|
||||
|
||||
- name: Generate doxygen
|
||||
run: doxygen
|
||||
working-directory: "${{ github.workspace }}/doc"
|
||||
|
||||
- name: Deploy documentation
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
force_orphan: true
|
||||
publish_dir: ./doc/html
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Linting
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
iwyu:
|
||||
name: Include What You Use
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: debian:sid-slim
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends ca-certificates clang-14 cmake git iwyu libbenchmark-dev libcurl4-openssl-dev ninja-build zlib1g-dev
|
||||
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "CMake Configure"
|
||||
run: cmake -GNinja -DRUN_IWYU=ON -DCMAKE_C_COMPILER=clang-14 -DCMAKE_CXX_COMPILER=clang++-14 -S ${GITHUB_WORKSPACE} -B ${GITHUB_WORKSPACE}/_build
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${GITHUB_WORKSPACE}/_build 2>&1 | tee ${GITHUB_WORKSPACE}/output.txt
|
||||
|
||||
- name: Check build output
|
||||
run: if egrep -q 'should (add|remove) these lines' ${GITHUB_WORKSPACE}/output.txt; then exit 1; fi
|
||||
|
||||
#- name: "CMake Configure"
|
||||
# run: cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -S ${GITHUB_WORKSPACE} -B ${GITHUB_WORKSPACE}/_build
|
||||
|
||||
#- name: "Run IWYU"
|
||||
# run: iwyu_tool -p ${GITHUB_WORKSPACE}/_build core push pull -- -Xiwyu --mapping_file=${GITHUB_WORKSPACE}/cmake/googletest.imp -Xiwyu --no_fwd_decls 2>&1 | tee ${{ github.workspace }}/output.txt
|
||||
|
||||
format:
|
||||
name: Clang Format
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# clang-format comes pre-installed
|
||||
# https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-Readme.md
|
||||
#- name: Install dependencies
|
||||
# run: |
|
||||
# sudo apt-get remove -y --purge man-db # avoid time-consuming trigger
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y clang-format-12
|
||||
|
||||
- name: Run clang-format
|
||||
run: find . -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.cxx' -o -name '*.o' -o -name '*.h' -o -name '*.hpp' -o -name '*.hxx' \) -exec clang-format-12 -style=file -i {} \;
|
||||
|
||||
- name: Check for changes
|
||||
run: git diff --exit-code
|
||||
@@ -0,0 +1,34 @@
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
name: Handle Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Upload Release Asset
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: true
|
||||
|
||||
- name: Create tarball including submodules
|
||||
shell: bash
|
||||
env:
|
||||
PREFIX: prometheus-cpp-with-submodules
|
||||
run: |
|
||||
git archive --prefix "${PREFIX}/" -o "${PREFIX}.tar" HEAD
|
||||
git submodule foreach --recursive "git archive --prefix=${PREFIX}/\$path/ --output=\$sha1.tar HEAD && tar --concatenate --file=$(pwd)/${PREFIX}.tar \$sha1.tar && rm \$sha1.tar"
|
||||
gzip "${PREFIX}.tar"
|
||||
|
||||
# using the official actions/upload-release-asset action would be preferred but is blocked by
|
||||
# https://github.com/actions/upload-release-asset/pull/41
|
||||
- name: Upload the artifacts
|
||||
uses: skx/github-action-publish-binaries@75ce5546020fc1848da842f40240f9fa03e7a3a8 # release-0.14
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
args: prometheus-cpp-with-submodules.tar.gz
|
||||
@@ -0,0 +1,7 @@
|
||||
/bazel-*
|
||||
cmake-build-*/
|
||||
_*/
|
||||
.idea/
|
||||
doc/html/
|
||||
.vscode
|
||||
build
|
||||
@@ -0,0 +1,6 @@
|
||||
[submodule "googletest"]
|
||||
path = 3rdparty/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
[submodule "civetweb"]
|
||||
path = 3rdparty/civetweb
|
||||
url = https://github.com/civetweb/civetweb.git
|
||||
@@ -0,0 +1,11 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
config_setting(
|
||||
name = "windows",
|
||||
values = {"cpu": "x64_windows"},
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "windows_msvc",
|
||||
values = {"cpu": "x64_windows_msvc"},
|
||||
)
|
||||
@@ -0,0 +1,193 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW) # recognize CMAKE_MSVC_RUNTIME_LIBRARY
|
||||
endif()
|
||||
|
||||
project(prometheus-cpp
|
||||
VERSION 1.1.0
|
||||
DESCRIPTION "Prometheus Client Library for Modern C++"
|
||||
HOMEPAGE_URL "https://github.com/jupp0r/prometheus-cpp"
|
||||
)
|
||||
|
||||
include(GenerateExportHeader)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build libraries as shared ones" OFF)
|
||||
option(ENABLE_PULL "Build prometheus-cpp pull library" ON)
|
||||
option(ENABLE_PUSH "Build prometheus-cpp push library" ON)
|
||||
option(ENABLE_COMPRESSION "Enable gzip compression" ON)
|
||||
option(ENABLE_TESTING "Build tests" ON)
|
||||
option(USE_THIRDPARTY_LIBRARIES "Use 3rdParty submodules" ON)
|
||||
option(THIRDPARTY_CIVETWEB_WITH_SSL "Enable SSL support for embedded civetweb source code")
|
||||
option(OVERRIDE_CXX_STANDARD_FLAGS "Force building with -std=c++11 even if the CXXLFAGS are configured differently" ON)
|
||||
option(GENERATE_PKGCONFIG "Generate and install pkg-config files" ${UNIX})
|
||||
option(RUN_IWYU "Run include-what-you-use" OFF)
|
||||
|
||||
if(OVERRIDE_CXX_STANDARD_FLAGS)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_EXTENSIONS Off)
|
||||
endif()
|
||||
|
||||
# Set default directory permissions until
|
||||
# https://gitlab.kitware.com/cmake/cmake/issues/15163
|
||||
# is fixed
|
||||
set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
|
||||
OWNER_READ
|
||||
OWNER_WRITE
|
||||
OWNER_EXECUTE
|
||||
GROUP_READ
|
||||
GROUP_EXECUTE
|
||||
WORLD_READ
|
||||
WORLD_EXECUTE)
|
||||
|
||||
# Put DLLs and binaries into same directory
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
# Hide things by default for shared libraries
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
|
||||
endif()
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
find_package(Threads)
|
||||
|
||||
# include-what-you-use
|
||||
|
||||
if(RUN_IWYU)
|
||||
find_program(IWYU_EXECUTABLE NAMES include-what-you-use iwyu)
|
||||
if(NOT IWYU_EXECUTABLE)
|
||||
message(FATAL_ERROR "Include-what-you-use not found")
|
||||
endif()
|
||||
|
||||
set(IWYU_ARGS
|
||||
"${IWYU_EXECUTABLE}"
|
||||
"-Xiwyu" "--no_fwd_decls"
|
||||
"-Xiwyu" "--mapping_file=${CMAKE_CURRENT_SOURCE_DIR}/cmake/googletest.imp"
|
||||
)
|
||||
|
||||
set(CMAKE_C_INCLUDE_WHAT_YOU_USE ${IWYU_ARGS})
|
||||
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${IWYU_ARGS})
|
||||
endif()
|
||||
|
||||
# check for required libatomic
|
||||
|
||||
include(CheckAtomic)
|
||||
|
||||
if(ENABLE_TESTING)
|
||||
if(USE_THIRDPARTY_LIBRARIES)
|
||||
find_package(googlemock-3rdparty CONFIG REQUIRED)
|
||||
else()
|
||||
find_package(GTest 1.8.1 CONFIG REQUIRED)
|
||||
endif()
|
||||
find_package(benchmark CONFIG)
|
||||
enable_testing()
|
||||
endif()
|
||||
|
||||
# build flags for CI system
|
||||
|
||||
if(ENABLE_WARNINGS_AS_ERRORS AND NOT MSVC)
|
||||
add_compile_options(
|
||||
$<$<AND:$<STREQUAL:$<COMPILE_LANGUAGE>,CXX>,$<CXX_COMPILER_ID:AppleClang>>:-Werror>
|
||||
$<$<AND:$<STREQUAL:$<COMPILE_LANGUAGE>,CXX>,$<CXX_COMPILER_ID:AppleClang>>:-Wall>
|
||||
$<$<AND:$<STREQUAL:$<COMPILE_LANGUAGE>,CXX>,$<CXX_COMPILER_ID:AppleClang>>:-Wextra>
|
||||
$<$<AND:$<STREQUAL:$<COMPILE_LANGUAGE>,CXX>,$<CXX_COMPILER_ID:AppleClang>>:-pedantic-errors>
|
||||
$<$<AND:$<STREQUAL:$<COMPILE_LANGUAGE>,CXX>,$<CXX_COMPILER_ID:GNU>>:-Werror>
|
||||
$<$<AND:$<STREQUAL:$<COMPILE_LANGUAGE>,CXX>,$<CXX_COMPILER_ID:GNU>>:-Wall>
|
||||
$<$<AND:$<STREQUAL:$<COMPILE_LANGUAGE>,CXX>,$<CXX_COMPILER_ID:GNU>>:-pedantic-errors>
|
||||
)
|
||||
endif()
|
||||
|
||||
# pkgconfig
|
||||
|
||||
if(GENERATE_PKGCONFIG)
|
||||
# see https://github.com/jupp0r/prometheus-cpp/issues/587
|
||||
if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(PROMETHEUS_CPP_PKGCONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
else()
|
||||
set(PROMETHEUS_CPP_PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
|
||||
if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
|
||||
set(PROMETHEUS_CPP_PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
|
||||
else()
|
||||
set(PROMETHEUS_CPP_PKGCONFIG_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# prometheus-cpp
|
||||
|
||||
add_subdirectory(core)
|
||||
|
||||
if(ENABLE_PULL)
|
||||
add_subdirectory(pull)
|
||||
endif()
|
||||
|
||||
if(ENABLE_PUSH)
|
||||
add_subdirectory(push)
|
||||
endif()
|
||||
|
||||
# install
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
install(
|
||||
EXPORT ${PROJECT_NAME}-targets
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
FILE ${PROJECT_NAME}-targets.cmake
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PROJECT_NAME}-config.cmake.in"
|
||||
${PROJECT_NAME}-config.cmake
|
||||
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
PATH_VARS CMAKE_INSTALL_INCLUDEDIR
|
||||
)
|
||||
|
||||
install(
|
||||
FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
||||
)
|
||||
|
||||
# packaging
|
||||
|
||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(CPACK_PACKAGE_CONTACT "prometheus-cpp@@noreply.github.com")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}")
|
||||
set(CPACK_PACKAGE_RELOCATABLE OFF)
|
||||
set(CPACK_PACKAGE_VENDOR "The prometheus-cpp authors")
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "3.12")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
|
||||
endif()
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON)
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
|
||||
|
||||
set(CPACK_RPM_PACKAGE_AUTOREQPROV ON)
|
||||
set(CPACK_RPM_FILE_NAME RPM-DEFAULT)
|
||||
|
||||
include(CPack)
|
||||
endif()
|
||||
|
||||
# summary
|
||||
|
||||
include(FeatureSummary)
|
||||
add_feature_info("Pull" "${ENABLE_PULL}" "support for pulling metrics")
|
||||
add_feature_info("Push" "${ENABLE_PUSH}" "support for pushing metrics to a push-gateway")
|
||||
add_feature_info("Compression" "${ENABLE_COMPRESSION}" "support for zlib compression of metrics")
|
||||
add_feature_info("pkg-config" "${GENERATE_PKGCONFIG}" "generate pkg-config files")
|
||||
add_feature_info("IYWU" "${RUN_IWYU}" "include-what-you-use")
|
||||
feature_summary(WHAT ALL)
|
||||
@@ -0,0 +1,25 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2021 Jupp Mueller
|
||||
Copyright (c) 2017-2022 Gregor Jasny
|
||||
|
||||
And many contributors, see
|
||||
https://github.com/jupp0r/prometheus-cpp/graphs/contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,382 @@
|
||||
# Prometheus Client Library for Modern C++
|
||||
|
||||
[](https://github.com/jupp0r/prometheus-cpp/actions?workflow=Continuous+Integration)
|
||||
[](https://travis-ci.org/jupp0r/prometheus-cpp)
|
||||
[](https://coveralls.io/github/jupp0r/prometheus-cpp?branch=master)
|
||||
[](https://scan.coverity.com/projects/jupp0r-prometheus-cpp)
|
||||
|
||||
This library aims to enable
|
||||
[Metrics-Driven Development](https://sookocheff.com/post/mdd/mdd/) for
|
||||
C++ services. It implements the
|
||||
[Prometheus Data Model](https://prometheus.io/docs/concepts/data_model/),
|
||||
a powerful abstraction on which to collect and expose metrics. We
|
||||
offer the possibility for metrics to be collected by Prometheus, but
|
||||
other push/pull collections can be added as plugins.
|
||||
|
||||
## Usage
|
||||
|
||||
See https://jupp0r.github.io/prometheus-cpp for more detailed interface documentation.
|
||||
|
||||
``` c++
|
||||
#include <prometheus/counter.h>
|
||||
#include <prometheus/exposer.h>
|
||||
#include <prometheus/registry.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
int main() {
|
||||
using namespace prometheus;
|
||||
|
||||
// create an http server running on port 8080
|
||||
Exposer exposer{"127.0.0.1:8080"};
|
||||
|
||||
// create a metrics registry
|
||||
// @note it's the users responsibility to keep the object alive
|
||||
auto registry = std::make_shared<Registry>();
|
||||
|
||||
// add a new counter family to the registry (families combine values with the
|
||||
// same name, but distinct label dimensions)
|
||||
//
|
||||
// @note please follow the metric-naming best-practices:
|
||||
// https://prometheus.io/docs/practices/naming/
|
||||
auto& packet_counter = BuildCounter()
|
||||
.Name("observed_packets_total")
|
||||
.Help("Number of observed packets")
|
||||
.Register(*registry);
|
||||
|
||||
// add and remember dimensional data, incrementing those is very cheap
|
||||
auto& tcp_rx_counter =
|
||||
packet_counter.Add({{"protocol", "tcp"}, {"direction", "rx"}});
|
||||
auto& tcp_tx_counter =
|
||||
packet_counter.Add({{"protocol", "tcp"}, {"direction", "tx"}});
|
||||
auto& udp_rx_counter =
|
||||
packet_counter.Add({{"protocol", "udp"}, {"direction", "rx"}});
|
||||
auto& udp_tx_counter =
|
||||
packet_counter.Add({{"protocol", "udp"}, {"direction", "tx"}});
|
||||
|
||||
// add a counter whose dimensional data is not known at compile time
|
||||
// nevertheless dimensional values should only occur in low cardinality:
|
||||
// https://prometheus.io/docs/practices/naming/#labels
|
||||
auto& http_requests_counter = BuildCounter()
|
||||
.Name("http_requests_total")
|
||||
.Help("Number of HTTP requests")
|
||||
.Register(*registry);
|
||||
|
||||
// ask the exposer to scrape the registry on incoming HTTP requests
|
||||
exposer.RegisterCollectable(registry);
|
||||
|
||||
for (;;) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
const auto random_value = std::rand();
|
||||
|
||||
if (random_value & 1) tcp_rx_counter.Increment();
|
||||
if (random_value & 2) tcp_tx_counter.Increment();
|
||||
if (random_value & 4) udp_rx_counter.Increment();
|
||||
if (random_value & 8) udp_tx_counter.Increment();
|
||||
|
||||
const std::array<std::string, 4> methods = {"GET", "PUT", "POST", "HEAD"};
|
||||
auto method = methods.at(random_value % methods.size());
|
||||
// dynamically calling Family<T>.Add() works but is slow and should be
|
||||
// avoided
|
||||
http_requests_counter.Add({{"method", method}}).Increment();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
Using `prometheus-cpp` requires a C++11 compliant compiler. It has been successfully tested with GNU GCC 7.4 on Ubuntu Bionic (18.04) and Visual Studio 2017 (but Visual Studio 2015 should work, too).
|
||||
|
||||
## Building
|
||||
|
||||
There are two supported ways to build
|
||||
`prometheus-cpp` - [CMake](https://cmake.org)
|
||||
and [bazel](https://bazel.io). Both are tested in CI and should work
|
||||
on master and for all releases.
|
||||
|
||||
In case these instructions don't work for you, looking at
|
||||
the [GitHub Workflows](.github/workflows) might help.
|
||||
|
||||
### via CMake
|
||||
|
||||
For CMake builds don't forget to fetch the submodules first. Please note that
|
||||
[zlib](https://zlib.net/) and [libcurl](https://curl.se/) are not provided by
|
||||
the included submodules. In the example below their usage is disabled.
|
||||
|
||||
Then build as usual.
|
||||
|
||||
``` shell
|
||||
# fetch third-party dependencies
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
mkdir _build
|
||||
cd _build
|
||||
|
||||
# run cmake
|
||||
cmake .. -DBUILD_SHARED_LIBS=ON -DENABLE_PUSH=OFF -DENABLE_COMPRESSION=OFF
|
||||
|
||||
# build
|
||||
cmake --build . --parallel 4
|
||||
|
||||
# run tests
|
||||
ctest -V
|
||||
|
||||
# install the libraries and headers
|
||||
cmake --install .
|
||||
```
|
||||
|
||||
### via Bazel
|
||||
|
||||
Install [bazel](https://www.bazel.io). Bazel makes it easy to add
|
||||
this repo to your project as a dependency. Just add the following
|
||||
to your `WORKSPACE`:
|
||||
|
||||
```python
|
||||
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()
|
||||
```
|
||||
|
||||
Then, you can reference this library in your own `BUILD` file, as
|
||||
demonstrated with the sample server included in this repository:
|
||||
|
||||
```python
|
||||
cc_binary(
|
||||
name = "sample_server",
|
||||
srcs = ["sample_server.cc"],
|
||||
deps = ["@com_github_jupp0r_prometheus_cpp//pull"],
|
||||
)
|
||||
```
|
||||
|
||||
When you call `prometheus_cpp_repositories()` in your `WORKSPACE` file,
|
||||
you load the following dependencies, if they do not exist yet, into your project:
|
||||
|
||||
* `civetweb` for [Civetweb](https://github.com/civetweb/civetweb)
|
||||
* `com_google_googletest` for [Google Test](https://github.com/google/googletest)
|
||||
* `com_github_google_benchmark` for [Google Benchmark](https://github.com/google/benchmark)
|
||||
* `com_github_curl` for [curl](https://curl.haxx.se/)
|
||||
* `net_zlib_zlib` for [zlib](http://www.zlib.net/)
|
||||
|
||||
The list of dependencies is also available from file [repositories.bzl](bazel/repositories.bzl).
|
||||
|
||||
## Packaging
|
||||
|
||||
By configuring CPack you can generate an installer like a
|
||||
Debian package (.deb) or RPM (.rpm) for the static or dynamic
|
||||
libraries so they can be easily installed on
|
||||
other systems.
|
||||
|
||||
Please refer to the [CPack](https://cmake.org/cmake/help/latest/module/CPack.html)
|
||||
documentation for all available generators and their
|
||||
configuration options.
|
||||
|
||||
To generate a Debian package you could follow these steps:
|
||||
|
||||
``` shell
|
||||
# fetch third-party dependencies
|
||||
git submodule update --init
|
||||
|
||||
# run cmake
|
||||
cmake -B_build -DCPACK_GENERATOR=DEB -DBUILD_SHARED_LIBS=ON # or OFF for static libraries
|
||||
|
||||
# build and package
|
||||
cmake --build _build --target package --parallel $(nproc)
|
||||
```
|
||||
|
||||
This will place an appropriately named .deb in the
|
||||
`_build` folder. To build a RPM package set the `CPACK_GENERATOR`
|
||||
variable to `RPM`.
|
||||
|
||||
## Consuming the installed project
|
||||
|
||||
### CMake
|
||||
|
||||
Consuming prometheus-cpp via CMake is the preferred way because all the dependencies
|
||||
between the three prometheus-cpp libraries are handled correctly.
|
||||
|
||||
The `cmake/project-import` directory contains an
|
||||
example project and minimal [CMakeLists.txt](cmake/project-import-cmake/CMakeLists.txt).
|
||||
|
||||
### vcpkg
|
||||
|
||||
The [vcpkg](https://github.com/microsoft/vcpkg) package manager contains a
|
||||
prometheus-cpp port which has been tested on Linux, macOS, and Windows.
|
||||
|
||||
### Conan
|
||||
|
||||
[Conan](https://conan.io/) package manager contains prometheus-cpp package as well
|
||||
in [ConanCenter](https://conan.io/center/prometheus-cpp) repository
|
||||
|
||||
### Plain Makefiles
|
||||
|
||||
When manually linking prometheus-cpp the library order matters. The needed
|
||||
libraries depend on the individual use case but the following should work for the pull metrics approach:
|
||||
|
||||
```
|
||||
-lprometheus-cpp-pull -lprometheus-cpp-core -lz
|
||||
```
|
||||
|
||||
For the push-workflow please try:
|
||||
|
||||
```
|
||||
-lprometheus-cpp-push -lprometheus-cpp-core -lcurl -lz
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Please adhere to the [Google C++ Style
|
||||
Guide](https://google.github.io/styleguide/cppguide.html). Make sure
|
||||
to clang-format your patches before opening a PR. Also make sure to
|
||||
adhere to [these commit message
|
||||
guidelines](https://chris.beams.io/posts/git-commit/).
|
||||
|
||||
You can check out this repo and build the library using
|
||||
``` bash
|
||||
bazel build //... # build everything
|
||||
bazel build //core //pull # build just the libraries
|
||||
```
|
||||
|
||||
Run the unit tests using
|
||||
```
|
||||
bazel test //...
|
||||
```
|
||||
|
||||
There is also an integration test that
|
||||
uses [telegraf](https://github.com/influxdata/telegraf) to scrape a
|
||||
sample server. With telegraf installed, it can be run using
|
||||
```
|
||||
bazel test //pull/tests/integration:scrape-test
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
There's a benchmark suite you can run:
|
||||
|
||||
```
|
||||
bazel run -c opt //core/benchmarks
|
||||
|
||||
INFO: Analysed target //core/benchmarks:benchmarks (0 packages loaded, 0 targets configured).
|
||||
INFO: Found 1 target...
|
||||
Target //core/benchmarks:benchmarks up-to-date:
|
||||
bazel-bin/core/benchmarks/benchmarks
|
||||
INFO: Elapsed time: 0.356s, Critical Path: 0.01s, Remote (0.00% of the time): [queue: 0.00%, setup: 0.00%, process: 0.00%]
|
||||
INFO: 0 processes.
|
||||
INFO: Build completed successfully, 1 total action
|
||||
INFO: Build completed successfully, 1 total action
|
||||
2018-11-30 15:13:14
|
||||
Run on (4 X 2200 MHz CPU s)
|
||||
CPU Caches:
|
||||
L1 Data 32K (x2)
|
||||
L1 Instruction 32K (x2)
|
||||
L2 Unified 262K (x2)
|
||||
L3 Unified 4194K (x1)
|
||||
-----------------------------------------------------------------------------------
|
||||
Benchmark Time CPU Iterations
|
||||
-----------------------------------------------------------------------------------
|
||||
BM_Counter_Increment 13 ns 12 ns 55616469
|
||||
BM_Counter_Collect 7 ns 7 ns 99823170
|
||||
BM_Gauge_Increment 12 ns 12 ns 51511873
|
||||
BM_Gauge_Decrement 12 ns 12 ns 56831098
|
||||
BM_Gauge_SetToCurrentTime 184 ns 183 ns 3928964
|
||||
BM_Gauge_Collect 6 ns 6 ns 117223478
|
||||
BM_Histogram_Observe/0 134 ns 124 ns 5665310
|
||||
BM_Histogram_Observe/1 122 ns 120 ns 5937185
|
||||
BM_Histogram_Observe/8 137 ns 135 ns 4652863
|
||||
BM_Histogram_Observe/64 143 ns 143 ns 4835957
|
||||
BM_Histogram_Observe/512 259 ns 257 ns 2334750
|
||||
BM_Histogram_Observe/4096 1545 ns 1393 ns 620754
|
||||
BM_Histogram_Collect/0 103 ns 102 ns 5654829
|
||||
BM_Histogram_Collect/1 100 ns 100 ns 7015153
|
||||
BM_Histogram_Collect/8 608 ns 601 ns 1149652
|
||||
BM_Histogram_Collect/64 1438 ns 1427 ns 515236
|
||||
BM_Histogram_Collect/512 5178 ns 5159 ns 114619
|
||||
BM_Histogram_Collect/4096 33527 ns 33280 ns 20785
|
||||
BM_Registry_CreateFamily 320 ns 316 ns 2021567
|
||||
BM_Registry_CreateCounter/0 128 ns 128 ns 5487140
|
||||
BM_Registry_CreateCounter/1 2066 ns 2058 ns 386002
|
||||
BM_Registry_CreateCounter/8 7672 ns 7634 ns 91328
|
||||
BM_Registry_CreateCounter/64 63270 ns 62761 ns 10780
|
||||
BM_Registry_CreateCounter/512 560714 ns 558328 ns 1176
|
||||
BM_Registry_CreateCounter/4096 18672798 ns 18383000 ns 35
|
||||
BM_Summary_Observe/0/iterations:262144 9351 ns 9305 ns 262144
|
||||
BM_Summary_Observe/1/iterations:262144 9242 ns 9169 ns 262144
|
||||
BM_Summary_Observe/8/iterations:262144 14344 ns 14195 ns 262144
|
||||
BM_Summary_Observe/64/iterations:262144 19176 ns 18950 ns 262144
|
||||
BM_Summary_Collect/0/0 31 ns 30 ns 24873766
|
||||
BM_Summary_Collect/1/0 166 ns 166 ns 4266706
|
||||
BM_Summary_Collect/8/0 1040 ns 1036 ns 660527
|
||||
BM_Summary_Collect/64/0 4529 ns 4489 ns 155600
|
||||
BM_Summary_Collect/0/1 28 ns 28 ns 24866697
|
||||
BM_Summary_Collect/1/1 190 ns 188 ns 3930354
|
||||
BM_Summary_Collect/8/1 1372 ns 1355 ns 535779
|
||||
BM_Summary_Collect/64/1 9901 ns 9822 ns 64632
|
||||
BM_Summary_Collect/0/8 29 ns 29 ns 24922651
|
||||
BM_Summary_Collect/1/8 217 ns 215 ns 3278381
|
||||
BM_Summary_Collect/8/8 2275 ns 2256 ns 282503
|
||||
BM_Summary_Collect/64/8 56790 ns 55804 ns 13878
|
||||
BM_Summary_Collect/0/64 32 ns 31 ns 22548350
|
||||
BM_Summary_Collect/1/64 395 ns 389 ns 1817073
|
||||
BM_Summary_Collect/8/64 10187 ns 10064 ns 71928
|
||||
BM_Summary_Collect/64/64 374835 ns 373560 ns 1812
|
||||
BM_Summary_Collect/0/512 28 ns 28 ns 25234228
|
||||
BM_Summary_Collect/1/512 1710 ns 1639 ns 802285
|
||||
BM_Summary_Collect/8/512 50355 ns 49335 ns 15975
|
||||
BM_Summary_Collect/64/512 2520972 ns 2493417 ns 295
|
||||
BM_Summary_Collect/0/4096 31 ns 31 ns 24059034
|
||||
BM_Summary_Collect/1/4096 2719 ns 2698 ns 286186
|
||||
BM_Summary_Collect/8/4096 121689 ns 119995 ns 5647
|
||||
BM_Summary_Collect/64/4096 5660131 ns 5587634 ns 134
|
||||
BM_Summary_Collect/0/32768 29 ns 29 ns 22217567
|
||||
BM_Summary_Collect/1/32768 4344 ns 4294 ns 138135
|
||||
BM_Summary_Collect/8/32768 331563 ns 326403 ns 2017
|
||||
BM_Summary_Collect/64/32768 16363553 ns 16038182 ns 44
|
||||
BM_Summary_Collect/0/262144 27 ns 27 ns 23923036
|
||||
BM_Summary_Collect/1/262144 10457 ns 10332 ns 67690
|
||||
BM_Summary_Collect/8/262144 930434 ns 869234 ns 792
|
||||
BM_Summary_Collect/64/262144 39217069 ns 39054846 ns 13
|
||||
BM_Summary_Observe_Common/iterations:262144 5587 ns 5557 ns 262144
|
||||
BM_Summary_Collect_Common/0 676 ns 673 ns 1054630
|
||||
BM_Summary_Collect_Common/1 709 ns 705 ns 990659
|
||||
BM_Summary_Collect_Common/8 1030 ns 1025 ns 685649
|
||||
BM_Summary_Collect_Common/64 2066 ns 2055 ns 339969
|
||||
BM_Summary_Collect_Common/512 5754 ns 5248 ns 156895
|
||||
BM_Summary_Collect_Common/4096 23894 ns 23292 ns 31096
|
||||
BM_Summary_Collect_Common/32768 49831 ns 49292 ns 13492
|
||||
BM_Summary_Collect_Common/262144 128723 ns 126987 ns 5579
|
||||
```
|
||||
|
||||
## Project Status
|
||||
Stable and used in production.
|
||||
|
||||
Parts of the library are instrumented by itself
|
||||
(bytes scraped, number of scrapes, scrape request latencies). There
|
||||
is a working [example](pull/tests/integration/sample_server.cc) that's
|
||||
scraped by telegraf as part of integration tests.
|
||||
|
||||
## FAQ
|
||||
|
||||
### What scrape formats do you support
|
||||
|
||||
Only the [Prometheus Text Exposition
|
||||
Format](https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md#text-format-details).
|
||||
Support for the protobuf format was removed because it's been removed
|
||||
from Prometheus 2.0.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,5 @@
|
||||
workspace(name = "com_github_jupp0r_prometheus_cpp")
|
||||
|
||||
load("//bazel:repositories.bzl", "prometheus_cpp_repositories")
|
||||
|
||||
prometheus_cpp_repositories()
|
||||
@@ -0,0 +1,7 @@
|
||||
exports_files(
|
||||
glob([
|
||||
"*.BUILD",
|
||||
"*.tpl",
|
||||
]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,103 @@
|
||||
licenses(["notice"]) # MIT license
|
||||
|
||||
config_setting(
|
||||
name = "osx",
|
||||
constraint_values = [
|
||||
"@bazel_tools//platforms:osx",
|
||||
],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "windows",
|
||||
constraint_values = [
|
||||
"@bazel_tools//platforms:windows",
|
||||
],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "with_ssl",
|
||||
define_values = {
|
||||
"with_civetweb_ssl": "true",
|
||||
},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
COPTS = [
|
||||
"-DUSE_IPV6",
|
||||
"-DNDEBUG",
|
||||
"-DNO_CGI",
|
||||
"-DNO_CACHING",
|
||||
"-DNO_FILES",
|
||||
"-UDEBUG",
|
||||
] + select({
|
||||
":with_ssl": [
|
||||
"-DOPENSSL_API_1_1",
|
||||
"-DNO_SSL_DL",
|
||||
],
|
||||
"@//conditions:default": [
|
||||
"-DNO_SSL",
|
||||
],
|
||||
})
|
||||
|
||||
DEPS = select({
|
||||
":with_ssl": [
|
||||
"@boringssl//:crypto",
|
||||
"@boringssl//:ssl",
|
||||
],
|
||||
"@//conditions:default": [],
|
||||
})
|
||||
|
||||
cc_library(
|
||||
name = "libcivetweb",
|
||||
srcs = [
|
||||
"src/civetweb.c",
|
||||
"src/response.inl",
|
||||
],
|
||||
hdrs = [
|
||||
"include/civetweb.h",
|
||||
],
|
||||
copts = COPTS,
|
||||
includes = [
|
||||
"include",
|
||||
],
|
||||
linkopts = select({
|
||||
":windows": [],
|
||||
"//conditions:default": ["-lpthread"],
|
||||
}) + select({
|
||||
":osx": [],
|
||||
":windows": [],
|
||||
"//conditions:default": ["-lrt"],
|
||||
}),
|
||||
textual_hdrs = [
|
||||
"src/md5.inl",
|
||||
"src/handle_form.inl",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = DEPS,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "civetweb",
|
||||
srcs = [
|
||||
"src/CivetServer.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"include/CivetServer.h",
|
||||
],
|
||||
copts = COPTS,
|
||||
includes = [
|
||||
"include",
|
||||
],
|
||||
linkopts = select({
|
||||
":windows": [],
|
||||
"//conditions:default": ["-lpthread"],
|
||||
}) + select({
|
||||
":osx": [],
|
||||
":windows": [],
|
||||
"//conditions:default": ["-lrt"],
|
||||
}),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":libcivetweb",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,72 @@
|
||||
# Copyright 2017, OpenCensus 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.
|
||||
|
||||
# copied from: https://github.com/census-instrumentation/opencensus-cpp/blob/master/WORKSPACE
|
||||
|
||||
licenses(["notice"]) # MIT/X derivative license
|
||||
|
||||
load("@com_github_jupp0r_prometheus_cpp//bazel:curl.bzl", "CURL_COPTS")
|
||||
|
||||
package(features = ["no_copts_tokenization"])
|
||||
|
||||
config_setting(
|
||||
name = "windows",
|
||||
constraint_values = [
|
||||
"@bazel_tools//platforms:windows",
|
||||
],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "osx",
|
||||
constraint_values = [
|
||||
"@bazel_tools//platforms:osx",
|
||||
],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "curl",
|
||||
srcs = glob([
|
||||
"lib/**/*.c",
|
||||
]),
|
||||
hdrs = glob([
|
||||
"include/curl/*.h",
|
||||
"lib/**/*.h",
|
||||
]),
|
||||
copts = CURL_COPTS + [
|
||||
'-DOS="os"',
|
||||
],
|
||||
defines = ["CURL_STATICLIB"],
|
||||
includes = [
|
||||
"include/",
|
||||
"lib/",
|
||||
],
|
||||
linkopts = select({
|
||||
"//:windows": [
|
||||
"-DEFAULTLIB:ws2_32.lib",
|
||||
"-DEFAULTLIB:advapi32.lib",
|
||||
"-DEFAULTLIB:crypt32.lib",
|
||||
"-DEFAULTLIB:Normaliz.lib",
|
||||
],
|
||||
"//:osx": [
|
||||
"-framework SystemConfiguration",
|
||||
"-lpthread",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"-lpthread",
|
||||
],
|
||||
}),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,201 @@
|
||||
# Copyright 2018, OpenCensus 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.
|
||||
|
||||
# Compiler options for building libcurl.
|
||||
# copied from: https://github.com/census-instrumentation/opencensus-cpp/blob/master/opencensus/curl.bzl
|
||||
|
||||
BASE_CURL_COPTS = [
|
||||
# Disable everything else except HTTP protocol.
|
||||
"-DHTTP_ONLY=1",
|
||||
"-DENABLE_IPV6=1",
|
||||
"-DGETHOSTNAME_TYPE_ARG2=size_t",
|
||||
"-DGETSERVBYPORT_R_ARGS=6",
|
||||
"-DGETSERVBYPORT_R_BUFSIZE=4096",
|
||||
"-DHAVE_ALARM=1",
|
||||
"-DHAVE_ALLOCA_H=1",
|
||||
"-DHAVE_ARPA_INET_H=1",
|
||||
"-DHAVE_ARPA_TFTP_H=1",
|
||||
"-DHAVE_ASSERT_H=1",
|
||||
"-DHAVE_BASENAME=1",
|
||||
"-DHAVE_BOOL_T=1",
|
||||
"-DHAVE_CLOCK_GETTIME_MONOTONIC=1",
|
||||
"-DHAVE_CONNECT=1",
|
||||
"-DHAVE_DLFCN_H=1",
|
||||
"-DHAVE_ENGINE_LOAD_BUILTIN_ENGINES=1",
|
||||
"-DHAVE_ERRNO_H=1",
|
||||
"-DHAVE_FCNTL=1",
|
||||
"-DHAVE_FCNTL_H=1",
|
||||
"-DHAVE_FCNTL_O_NONBLOCK=1",
|
||||
"-DHAVE_FDOPEN=1",
|
||||
"-DHAVE_FREEADDRINFO=1",
|
||||
"-DHAVE_FREEIFADDRS=1",
|
||||
"-DHAVE_FSETXATTR=1",
|
||||
"-DHAVE_FSETXATTR_5=1",
|
||||
"-DHAVE_FTRUNCATE=1",
|
||||
"-DHAVE_GAI_STRERROR=1",
|
||||
"-DHAVE_GETADDRINFO=1",
|
||||
"-DHAVE_GETADDRINFO_THREADSAFE=1",
|
||||
"-DHAVE_GETEUID=1",
|
||||
"-DHAVE_GETHOSTBYADDR=1",
|
||||
"-DHAVE_GETHOSTBYADDR_R=1",
|
||||
"-DHAVE_GETHOSTBYADDR_R_8=1",
|
||||
"-DHAVE_GETHOSTBYNAME=1",
|
||||
"-DHAVE_GETHOSTBYNAME_R=1",
|
||||
"-DHAVE_GETHOSTBYNAME_R_6=1",
|
||||
"-DHAVE_GETHOSTNAME=1",
|
||||
"-DHAVE_GETIFADDRS=1",
|
||||
"-DHAVE_GETPPID=1",
|
||||
"-DHAVE_GETPWUID=1",
|
||||
"-DHAVE_GETPWUID_R=1",
|
||||
"-DHAVE_GETRLIMIT=1",
|
||||
"-DHAVE_GETSERVBYPORT_R=1",
|
||||
"-DHAVE_GETTIMEOFDAY=1",
|
||||
"-DHAVE_GMTIME_R=1",
|
||||
"-DHAVE_IFADDRS_H=1",
|
||||
"-DHAVE_IF_NAMETOINDEX=1",
|
||||
"-DHAVE_INET_NTOP=1",
|
||||
"-DHAVE_INET_PTON=1",
|
||||
"-DHAVE_INTTYPES_H=1",
|
||||
"-DHAVE_IOCTL=1",
|
||||
"-DHAVE_IOCTL_FIONBIO=1",
|
||||
"-DHAVE_IOCTL_SIOCGIFADDR=1",
|
||||
"-DHAVE_LIBGEN_H=1",
|
||||
"-DHAVE_LL=1",
|
||||
"-DHAVE_LOCALE_H=1",
|
||||
"-DHAVE_LOCALTIME_R=1",
|
||||
"-DHAVE_LONGLONG=1",
|
||||
"-DHAVE_MALLOC_H=1",
|
||||
"-DHAVE_MEMORY_H=1",
|
||||
"-DHAVE_NETDB_H=1",
|
||||
"-DHAVE_NETINET_IN_H=1",
|
||||
"-DHAVE_NETINET_TCP_H=1",
|
||||
"-DHAVE_NET_IF_H=1",
|
||||
"-DHAVE_PIPE=1",
|
||||
"-DHAVE_POLL=1",
|
||||
"-DHAVE_POLL_FINE=1",
|
||||
"-DHAVE_POLL_H=1",
|
||||
"-DHAVE_POSIX_STRERROR_R=1",
|
||||
"-DHAVE_PTHREAD_H=1",
|
||||
"-DHAVE_PWD_H=1",
|
||||
"-DHAVE_RECV=1",
|
||||
"-DHAVE_SA_FAMILY_T=1",
|
||||
"-DHAVE_SELECT=1",
|
||||
"-DHAVE_SEND=1",
|
||||
"-DHAVE_SETJMP_H=1",
|
||||
"-DHAVE_SETLOCALE=1",
|
||||
"-DHAVE_SETRLIMIT=1",
|
||||
"-DHAVE_SETSOCKOPT=1",
|
||||
"-DHAVE_SGTTY_H=1",
|
||||
"-DHAVE_SIGACTION=1",
|
||||
"-DHAVE_SIGINTERRUPT=1",
|
||||
"-DHAVE_SIGNAL=1",
|
||||
"-DHAVE_SIGNAL_H=1",
|
||||
"-DHAVE_SIGSETJMP=1",
|
||||
"-DHAVE_SIG_ATOMIC_T=1",
|
||||
"-DHAVE_SOCKADDR_IN6_SIN6_SCOPE_ID=1",
|
||||
"-DHAVE_SOCKET=1",
|
||||
"-DHAVE_SOCKETPAIR=1",
|
||||
"-DHAVE_STDBOOL_H=1",
|
||||
"-DHAVE_STDINT_H=1",
|
||||
"-DHAVE_STDIO_H=1",
|
||||
"-DHAVE_STDLIB_H=1",
|
||||
"-DHAVE_STRCASECMP=1",
|
||||
"-DHAVE_STRDUP=1",
|
||||
"-DHAVE_STRERROR_R=1",
|
||||
"-DHAVE_STRINGS_H=1",
|
||||
"-DHAVE_STRING_H=1",
|
||||
"-DHAVE_STRNCASECMP=1",
|
||||
"-DHAVE_STRSTR=1",
|
||||
"-DHAVE_STRTOK_R=1",
|
||||
"-DHAVE_STRTOLL=1",
|
||||
"-DHAVE_STRUCT_SOCKADDR_STORAGE=1",
|
||||
"-DHAVE_STRUCT_TIMEVAL=1",
|
||||
"-DHAVE_SYS_IOCTL_H=1",
|
||||
"-DHAVE_SYS_PARAM_H=1",
|
||||
"-DHAVE_SYS_POLL_H=1",
|
||||
"-DHAVE_SYS_RESOURCE_H=1",
|
||||
"-DHAVE_SYS_SELECT_H=1",
|
||||
"-DHAVE_SYS_SOCKET_H=1",
|
||||
"-DHAVE_SYS_STAT_H=1",
|
||||
"-DHAVE_SYS_TIME_H=1",
|
||||
"-DHAVE_SYS_TYPES_H=1",
|
||||
"-DHAVE_SYS_UIO_H=1",
|
||||
"-DHAVE_SYS_UN_H=1",
|
||||
"-DHAVE_SYS_WAIT_H=1",
|
||||
"-DHAVE_SYS_XATTR_H=1",
|
||||
"-DHAVE_TERMIOS_H=1",
|
||||
"-DHAVE_TERMIO_H=1",
|
||||
"-DHAVE_TIME_H=1",
|
||||
"-DHAVE_UNISTD_H=1",
|
||||
"-DHAVE_UTIME=1",
|
||||
"-DHAVE_UTIMES=1",
|
||||
"-DHAVE_UTIME_H=1",
|
||||
"-DHAVE_VARIADIC_MACROS_C99=1",
|
||||
"-DHAVE_VARIADIC_MACROS_GCC=1",
|
||||
"-DHAVE_WRITABLE_ARGV=1",
|
||||
"-DHAVE_WRITEV=1",
|
||||
"-DRECV_TYPE_ARG1=int",
|
||||
"-DRECV_TYPE_ARG2=void*",
|
||||
"-DRECV_TYPE_ARG3=size_t",
|
||||
"-DRECV_TYPE_ARG4=int",
|
||||
"-DRECV_TYPE_RETV=ssize_t",
|
||||
"-DSELECT_QUAL_ARG5=",
|
||||
"-DSELECT_TYPE_ARG1=int",
|
||||
"-DSELECT_TYPE_ARG234=fd_set*",
|
||||
"-DSELECT_TYPE_RETV=int",
|
||||
"-DSEND_QUAL_ARG2=const",
|
||||
"-DSEND_TYPE_ARG1=int",
|
||||
"-DSEND_TYPE_ARG2=void*",
|
||||
"-DSEND_TYPE_ARG3=size_t",
|
||||
"-DSEND_TYPE_ARG4=int",
|
||||
"-DSEND_TYPE_RETV=ssize_t",
|
||||
"-DSIZEOF_CURL_OFF_T=8",
|
||||
"-DSIZEOF_INT=4",
|
||||
"-DSIZEOF_LONG=8",
|
||||
"-DSIZEOF_OFF_T=8",
|
||||
"-DSIZEOF_SHORT=2",
|
||||
"-DSIZEOF_SIZE_T=8",
|
||||
"-DSIZEOF_TIME_T=8",
|
||||
"-DSTDC_HEADERS=1",
|
||||
"-DSTRERROR_R_TYPE_ARG3=size_t",
|
||||
"-DTIME_WITH_SYS_TIME=1",
|
||||
"-DUSE_THREADS_POSIX=1",
|
||||
"-DUSE_UNIX_SOCKETS=1",
|
||||
|
||||
# Extra defines needed by curl
|
||||
"-DBUILDING_LIBCURL",
|
||||
"-DCURL_HIDDEN_SYMBOLS",
|
||||
]
|
||||
|
||||
LINUX_CURL_COPTS = [
|
||||
"-DHAVE_LINUX_TCP_H=1",
|
||||
"-DHAVE_MSG_NOSIGNAL=1",
|
||||
]
|
||||
|
||||
CURL_COPTS = select({
|
||||
"//:windows": [
|
||||
# Disable everything else except HTTP protocol.
|
||||
"/DHTTP_ONLY=1",
|
||||
"/DCURL_STATICLIB",
|
||||
"/DWIN32",
|
||||
"/DBUILDING_LIBCURL",
|
||||
"/DUSE_WIN32_IDN",
|
||||
"/DWANT_IDN_PROTOTYPES",
|
||||
"/DUSE_IPV6",
|
||||
"/DUSE_WINDOWS_SSPI",
|
||||
"/DUSE_SCHANNEL",
|
||||
],
|
||||
"//:osx": BASE_CURL_COPTS,
|
||||
"//conditions:default": BASE_CURL_COPTS + LINUX_CURL_COPTS,
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define {BASE_NAME}_EXPORT
|
||||
#define {BASE_NAME}_NO_EXPORT
|
||||
@@ -0,0 +1,21 @@
|
||||
def _generate_dummy_export_header_impl(ctx):
|
||||
ctx.actions.expand_template(
|
||||
template = ctx.file._template,
|
||||
output = ctx.outputs.header_file,
|
||||
substitutions = {
|
||||
"{BASE_NAME}": ctx.attr.basename,
|
||||
},
|
||||
)
|
||||
|
||||
generate_dummy_export_header = rule(
|
||||
attrs = {
|
||||
"basename": attr.string(mandatory = True),
|
||||
"header": attr.string(mandatory = True),
|
||||
"_template": attr.label(
|
||||
allow_single_file = True,
|
||||
default = Label("@com_github_jupp0r_prometheus_cpp//bazel:dummy_export.h.tpl"),
|
||||
),
|
||||
},
|
||||
implementation = _generate_dummy_export_header_impl,
|
||||
outputs = {"header_file": "%{header}"},
|
||||
)
|
||||
@@ -0,0 +1,72 @@
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
|
||||
|
||||
def prometheus_cpp_repositories():
|
||||
maybe(
|
||||
http_archive,
|
||||
name = "civetweb",
|
||||
strip_prefix = "civetweb-1.15",
|
||||
sha256 = "90a533422944ab327a4fbb9969f0845d0dba05354f9cacce3a5005fa59f593b9",
|
||||
urls = [
|
||||
"https://github.com/civetweb/civetweb/archive/v1.15.tar.gz",
|
||||
],
|
||||
build_file = "@com_github_jupp0r_prometheus_cpp//bazel:civetweb.BUILD",
|
||||
)
|
||||
|
||||
maybe(
|
||||
http_archive,
|
||||
name = "com_google_googletest",
|
||||
sha256 = "b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5",
|
||||
strip_prefix = "googletest-release-1.11.0",
|
||||
urls = [
|
||||
"https://github.com/google/googletest/archive/release-1.11.0.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
maybe(
|
||||
http_archive,
|
||||
name = "com_github_curl",
|
||||
sha256 = "3c6893d38d054d4e378267166858698899e9d87258e8ff1419d020c395384535",
|
||||
strip_prefix = "curl-7.84.0",
|
||||
urls = [
|
||||
"https://github.com/curl/curl/releases/download/curl-7_84_0/curl-7.84.0.tar.gz",
|
||||
"https://curl.haxx.se/download/curl-7.84.0.tar.gz",
|
||||
],
|
||||
build_file = "@com_github_jupp0r_prometheus_cpp//bazel:curl.BUILD",
|
||||
)
|
||||
|
||||
maybe(
|
||||
http_archive,
|
||||
name = "com_github_google_benchmark",
|
||||
sha256 = "6132883bc8c9b0df5375b16ab520fac1a85dc9e4cf5be59480448ece74b278d4",
|
||||
strip_prefix = "benchmark-1.6.1",
|
||||
urls = [
|
||||
"https://github.com/google/benchmark/archive/v1.6.1.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
maybe(
|
||||
http_archive,
|
||||
name = "net_zlib_zlib",
|
||||
sha256 = "91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9",
|
||||
strip_prefix = "zlib-1.2.12",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/zlib.net/zlib-1.2.12.tar.gz",
|
||||
"https://zlib.net/zlib-1.2.12.tar.gz",
|
||||
"https://storage.googleapis.com/bazel-mirror/zlib.net/zlib-1.2.12.tar.gz",
|
||||
],
|
||||
build_file = "@com_github_jupp0r_prometheus_cpp//bazel:zlib.BUILD",
|
||||
)
|
||||
|
||||
maybe(
|
||||
http_archive,
|
||||
name = "boringssl",
|
||||
# Use github mirror instead of https://boringssl.googlesource.com/boringssl
|
||||
# to obtain a boringssl archive with consistent sha256
|
||||
sha256 = "534fa658bd845fd974b50b10f444d392dfd0d93768c4a51b61263fd37d851c40",
|
||||
strip_prefix = "boringssl-b9232f9e27e5668bc0414879dcdedb2a59ea75f2",
|
||||
urls = [
|
||||
"https://storage.googleapis.com/grpc-bazel-mirror/github.com/google/boringssl/archive/b9232f9e27e5668bc0414879dcdedb2a59ea75f2.tar.gz",
|
||||
"https://github.com/google/boringssl/archive/b9232f9e27e5668bc0414879dcdedb2a59ea75f2.tar.gz",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
# copied from: https://github.com/bazelbuild/bazel/blob/master/third_party/zlib/BUILD
|
||||
|
||||
licenses(["notice"]) # BSD/MIT-like license (for zlib)
|
||||
|
||||
cc_library(
|
||||
name = "z",
|
||||
srcs = glob(["*.c"]),
|
||||
hdrs = glob(["*.h"]),
|
||||
# Use -Dverbose=-1 to turn off zlib's trace logging. (bazelbuild/bazel#3280)
|
||||
copts = [
|
||||
"-w",
|
||||
"-Dverbose=-1",
|
||||
],
|
||||
includes = ["."],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
[
|
||||
{ include: [ "@<gmock/.*>", private, "<gmock/gmock.h>", public ] },
|
||||
{ include: [ "@<gtest/.*>", private, "<gtest/gtest.h>", public ] },
|
||||
{ include: [ "@<bits/this_thread_sleep.h>", private, "<thread>", public ]}
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
|
||||
|
||||
project(prometheus-cpp-import)
|
||||
|
||||
find_package(prometheus-cpp CONFIG REQUIRED)
|
||||
|
||||
if(PROMETHEUS_CPP_ENABLE_PUSH)
|
||||
add_executable(sample-client sample_client.cc)
|
||||
target_link_libraries(sample-client PRIVATE prometheus-cpp::push)
|
||||
endif()
|
||||
|
||||
if(PROMETHEUS_CPP_ENABLE_PULL)
|
||||
add_executable(sample-server sample_server.cc)
|
||||
target_link_libraries(sample-server PRIVATE prometheus-cpp::pull)
|
||||
endif()
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../push/tests/integration/sample_client.cc
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../pull/tests/integration/sample_server.cc
|
||||
@@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
|
||||
|
||||
project(prometheus-cpp-import)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_check_modules(PROMETHEUS_CPP_CORE REQUIRED prometheus-cpp-core)
|
||||
pkg_check_modules(PROMETHEUS_CPP_PUSH IMPORTED_TARGET prometheus-cpp-push)
|
||||
pkg_check_modules(PROMETHEUS_CPP_PULL IMPORTED_TARGET prometheus-cpp-pull)
|
||||
|
||||
if(PROMETHEUS_CPP_PUSH_FOUND)
|
||||
add_executable(sample-client sample_client.cc)
|
||||
target_link_libraries(sample-client PRIVATE PkgConfig::PROMETHEUS_CPP_PUSH)
|
||||
endif()
|
||||
|
||||
if(PROMETHEUS_CPP_PULL_FOUND)
|
||||
add_executable(sample-server sample_server.cc)
|
||||
target_link_libraries(sample-server PRIVATE PkgConfig::PROMETHEUS_CPP_PULL)
|
||||
endif()
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../push/tests/integration/sample_client.cc
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../pull/tests/integration/sample_server.cc
|
||||
@@ -0,0 +1,34 @@
|
||||
@PACKAGE_INIT@
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
set_and_check(prometheus-cpp_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
|
||||
|
||||
set(PROMETHEUS_CPP_ENABLE_PULL @ENABLE_PULL@)
|
||||
set(PROMETHEUS_CPP_ENABLE_PUSH @ENABLE_PUSH@)
|
||||
set(PROMETHEUS_CPP_USE_COMPRESSION @ENABLE_COMPRESSION@)
|
||||
set(PROMETHEUS_CPP_USE_THIRDPARTY_LIBRARIES @USE_THIRDPARTY_LIBRARIES@)
|
||||
set(PROMETHEUS_CPP_THIRDPARTY_CIVETWEB_WITH_SSL @THIRDPARTY_CIVETWEB_WITH_SSL@)
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
find_dependency(Threads)
|
||||
unset(CMAKE_THREAD_PREFER_PTHREAD)
|
||||
|
||||
if(PROMETHEUS_CPP_ENABLE_PULL)
|
||||
if(PROMETHEUS_CPP_USE_THIRDPARTY_LIBRARIES)
|
||||
if(PROMETHEUS_CPP_THIRDPARTY_CIVETWEB_WITH_SSL)
|
||||
find_dependency(OpenSSL)
|
||||
endif()
|
||||
else()
|
||||
find_dependency(civetweb)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(PROMETHEUS_CPP_ENABLE_PULL AND PROMETHEUS_CPP_USE_COMPRESSION)
|
||||
find_dependency(ZLIB)
|
||||
endif()
|
||||
|
||||
if(PROMETHEUS_CPP_ENABLE_PUSH)
|
||||
find_dependency(CURL)
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/prometheus-cpp-targets.cmake")
|
||||
@@ -0,0 +1,14 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
includedir=@PROMETHEUS_CPP_PKGCONFIG_INCLUDEDIR@
|
||||
libdir=@PROMETHEUS_CPP_PKGCONFIG_LIBDIR@
|
||||
|
||||
Name: @PROJECT_NAME@-core
|
||||
Description: @PROJECT_DESCRIPTION@
|
||||
URL: @PROJECT_HOMEPAGE_URL@
|
||||
Version: @PROJECT_VERSION@
|
||||
Requires:
|
||||
Requires.private: @PKGCONFIG_REQUIRES@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -l@PROJECT_NAME@-core
|
||||
Libs.private: @CMAKE_THREAD_LIBS_INIT@ @PKGCONFIG_LIBS@
|
||||
@@ -0,0 +1,14 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
includedir=@PROMETHEUS_CPP_PKGCONFIG_INCLUDEDIR@
|
||||
libdir=@PROMETHEUS_CPP_PKGCONFIG_LIBDIR@
|
||||
|
||||
Name: @PROJECT_NAME@-pull
|
||||
Description: @PROJECT_DESCRIPTION@
|
||||
URL: @PROJECT_HOMEPAGE_URL@
|
||||
Version: @PROJECT_VERSION@
|
||||
Requires: @PROJECT_NAME@-core
|
||||
Requires.private: @PKGCONFIG_REQUIRES@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -l@PROJECT_NAME@-pull
|
||||
Libs.private: @CMAKE_THREAD_LIBS_INIT@ @PKGCONFIG_LIBS@
|
||||
@@ -0,0 +1,14 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
includedir=@PROMETHEUS_CPP_PKGCONFIG_INCLUDEDIR@
|
||||
libdir=@PROMETHEUS_CPP_PKGCONFIG_LIBDIR@
|
||||
|
||||
Name: @PROJECT_NAME@-push
|
||||
Description: @PROJECT_DESCRIPTION@
|
||||
URL: @PROJECT_HOMEPAGE_URL@
|
||||
Version: @PROJECT_VERSION@
|
||||
Requires: @PROJECT_NAME@-core
|
||||
Requires.private: @PKGCONFIG_REQUIRES@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -l@PROJECT_NAME@-push
|
||||
Libs.private: @CMAKE_THREAD_LIBS_INIT@ @PKGCONFIG_LIBS@
|
||||
@@ -0,0 +1,20 @@
|
||||
load("//bazel:export_header.bzl", "generate_dummy_export_header")
|
||||
|
||||
generate_dummy_export_header(
|
||||
name = "export_header",
|
||||
basename = "PROMETHEUS_CPP_CORE",
|
||||
header = "include/prometheus/detail/core_export.h",
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "core",
|
||||
srcs = glob([
|
||||
"src/**/*.cc",
|
||||
"src/**/*.h",
|
||||
]),
|
||||
hdrs = glob(
|
||||
["include/**/*.h"],
|
||||
) + [":export_header"],
|
||||
strip_include_prefix = "include",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,100 @@
|
||||
|
||||
add_library(core
|
||||
src/check_names.cc
|
||||
src/counter.cc
|
||||
src/detail/builder.cc
|
||||
src/detail/ckms_quantiles.cc
|
||||
src/detail/time_window_quantiles.cc
|
||||
src/detail/utils.cc
|
||||
src/family.cc
|
||||
src/gauge.cc
|
||||
src/histogram.cc
|
||||
src/info.cc
|
||||
src/registry.cc
|
||||
src/serializer.cc
|
||||
src/summary.cc
|
||||
src/text_serializer.cc
|
||||
)
|
||||
|
||||
add_library(${PROJECT_NAME}::core ALIAS core)
|
||||
|
||||
target_compile_features(core
|
||||
PUBLIC
|
||||
cxx_std_14
|
||||
)
|
||||
|
||||
target_link_libraries(core
|
||||
PRIVATE
|
||||
Threads::Threads
|
||||
$<$<AND:$<BOOL:UNIX>,$<NOT:$<BOOL:APPLE>>>:rt>
|
||||
)
|
||||
|
||||
if(HAVE_CXX_LIBATOMIC)
|
||||
# the exported library config must use libatomic unconditionally
|
||||
# (the HAVE_CXX_LIBATOMIC variable should not leak into the target config)
|
||||
target_link_libraries(core PUBLIC atomic)
|
||||
endif()
|
||||
|
||||
target_include_directories(core
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
|
||||
)
|
||||
|
||||
set_target_properties(core
|
||||
PROPERTIES
|
||||
OUTPUT_NAME ${PROJECT_NAME}-core
|
||||
DEFINE_SYMBOL PROMETHEUS_CPP_CORE_EXPORTS
|
||||
VERSION "${PROJECT_VERSION}"
|
||||
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
|
||||
)
|
||||
|
||||
generate_export_header(core
|
||||
BASE_NAME ${PROJECT_NAME}-core
|
||||
EXPORT_FILE_NAME include/prometheus/detail/core_export.h
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS core
|
||||
EXPORT ${PROJECT_NAME}-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY include/ ${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
if(GENERATE_PKGCONFIG)
|
||||
set(PKGCONFIG_LIBS)
|
||||
set(PKGCONFIG_REQUIRES)
|
||||
|
||||
if(HAVE_CXX_LIBATOMIC)
|
||||
string(APPEND PKGCONFIG_LIBS " -latomic")
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
${PROJECT_SOURCE_DIR}/cmake/prometheus-cpp-core.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/prometheus-cpp-core.pc
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/prometheus-cpp-core.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
unset(PKGCONFIG_LIBS)
|
||||
unset(PKGCONFIG_REQUIRES)
|
||||
endif()
|
||||
|
||||
if(ENABLE_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(benchmark_FOUND)
|
||||
add_subdirectory(benchmarks)
|
||||
endif()
|
||||
@@ -0,0 +1,12 @@
|
||||
cc_binary(
|
||||
name = "benchmarks",
|
||||
srcs = glob([
|
||||
"*.cc",
|
||||
"*.h",
|
||||
]),
|
||||
linkstatic = True,
|
||||
deps = [
|
||||
"//core",
|
||||
"@com_github_google_benchmark//:benchmark",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
add_executable(benchmarks
|
||||
main.cc
|
||||
benchmark_helpers.cc
|
||||
benchmark_helpers.h
|
||||
counter_bench.cc
|
||||
gauge_bench.cc
|
||||
histogram_bench.cc
|
||||
info_bench.cc
|
||||
registry_bench.cc
|
||||
summary_bench.cc
|
||||
)
|
||||
|
||||
target_link_libraries(benchmarks
|
||||
PRIVATE
|
||||
${PROJECT_NAME}::core
|
||||
benchmark::benchmark
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME benchmarks
|
||||
COMMAND benchmarks
|
||||
)
|
||||
|
||||
set_property(
|
||||
TEST benchmarks
|
||||
APPEND PROPERTY LABELS Benchmark
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "benchmark_helpers.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
|
||||
std::string GenerateRandomString(std::size_t length) {
|
||||
auto randchar = []() -> char {
|
||||
const char charset[] = "abcdefghijklmnopqrstuvwxyz";
|
||||
const std::size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[rand() % max_index];
|
||||
};
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
}
|
||||
|
||||
prometheus::Labels GenerateRandomLabels(std::size_t number_of_pairs) {
|
||||
const auto label_character_count = 10;
|
||||
auto label_pairs = prometheus::Labels{};
|
||||
for (std::size_t i = 0; i < number_of_pairs; i++) {
|
||||
label_pairs.insert({GenerateRandomString(label_character_count),
|
||||
GenerateRandomString(label_character_count)});
|
||||
}
|
||||
return label_pairs;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include "prometheus/labels.h"
|
||||
|
||||
std::string GenerateRandomString(std::size_t length);
|
||||
prometheus::Labels GenerateRandomLabels(std::size_t number_of_labels);
|
||||
@@ -0,0 +1,33 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/registry.h"
|
||||
|
||||
static void BM_Counter_Increment(benchmark::State& state) {
|
||||
using prometheus::BuildCounter;
|
||||
using prometheus::Counter;
|
||||
using prometheus::Registry;
|
||||
Registry registry;
|
||||
auto& counter_family =
|
||||
BuildCounter().Name("benchmark_counter").Help("").Register(registry);
|
||||
auto& counter = counter_family.Add({});
|
||||
|
||||
while (state.KeepRunning()) counter.Increment();
|
||||
}
|
||||
BENCHMARK(BM_Counter_Increment);
|
||||
|
||||
static void BM_Counter_Collect(benchmark::State& state) {
|
||||
using prometheus::BuildCounter;
|
||||
using prometheus::Counter;
|
||||
using prometheus::Registry;
|
||||
Registry registry;
|
||||
auto& counter_family =
|
||||
BuildCounter().Name("benchmark_counter").Help("").Register(registry);
|
||||
auto& counter = counter_family.Add({});
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(counter.Collect());
|
||||
};
|
||||
}
|
||||
BENCHMARK(BM_Counter_Collect);
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/gauge.h"
|
||||
#include "prometheus/registry.h"
|
||||
|
||||
static void BM_Gauge_Increment(benchmark::State& state) {
|
||||
using prometheus::BuildGauge;
|
||||
using prometheus::Gauge;
|
||||
using prometheus::Registry;
|
||||
Registry registry;
|
||||
auto& gauge_family =
|
||||
BuildGauge().Name("benchmark_gauge").Help("").Register(registry);
|
||||
auto& gauge = gauge_family.Add({});
|
||||
|
||||
while (state.KeepRunning()) gauge.Increment(2);
|
||||
}
|
||||
BENCHMARK(BM_Gauge_Increment);
|
||||
|
||||
static void BM_Gauge_Decrement(benchmark::State& state) {
|
||||
using prometheus::BuildGauge;
|
||||
using prometheus::Gauge;
|
||||
using prometheus::Registry;
|
||||
Registry registry;
|
||||
auto& gauge_family =
|
||||
BuildGauge().Name("benchmark_gauge").Help("").Register(registry);
|
||||
auto& gauge = gauge_family.Add({});
|
||||
|
||||
while (state.KeepRunning()) gauge.Decrement(2);
|
||||
}
|
||||
BENCHMARK(BM_Gauge_Decrement);
|
||||
|
||||
static void BM_Gauge_SetToCurrentTime(benchmark::State& state) {
|
||||
using prometheus::BuildGauge;
|
||||
using prometheus::Gauge;
|
||||
using prometheus::Registry;
|
||||
Registry registry;
|
||||
auto& gauge_family =
|
||||
BuildGauge().Name("benchmark_gauge").Help("").Register(registry);
|
||||
auto& gauge = gauge_family.Add({});
|
||||
|
||||
while (state.KeepRunning()) gauge.SetToCurrentTime();
|
||||
}
|
||||
BENCHMARK(BM_Gauge_SetToCurrentTime);
|
||||
|
||||
static void BM_Gauge_Collect(benchmark::State& state) {
|
||||
using prometheus::BuildGauge;
|
||||
using prometheus::Gauge;
|
||||
using prometheus::Registry;
|
||||
Registry registry;
|
||||
auto& gauge_family =
|
||||
BuildGauge().Name("benchmark_gauge").Help("").Register(registry);
|
||||
auto& gauge = gauge_family.Add({});
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(gauge.Collect());
|
||||
};
|
||||
}
|
||||
BENCHMARK(BM_Gauge_Collect);
|
||||
@@ -0,0 +1,70 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/histogram.h"
|
||||
#include "prometheus/registry.h"
|
||||
|
||||
using prometheus::Histogram;
|
||||
|
||||
static Histogram::BucketBoundaries CreateLinearBuckets(std::int64_t start,
|
||||
std::int64_t end,
|
||||
std::int64_t step) {
|
||||
auto bucket_boundaries = Histogram::BucketBoundaries{};
|
||||
for (auto i = start; i < end; i += step) {
|
||||
bucket_boundaries.push_back(i);
|
||||
}
|
||||
return bucket_boundaries;
|
||||
}
|
||||
|
||||
static void BM_Histogram_Observe(benchmark::State& state) {
|
||||
using prometheus::BuildHistogram;
|
||||
using prometheus::Histogram;
|
||||
using prometheus::Registry;
|
||||
|
||||
const auto number_of_buckets = state.range(0);
|
||||
|
||||
Registry registry;
|
||||
auto& histogram_family =
|
||||
BuildHistogram().Name("benchmark_histogram").Help("").Register(registry);
|
||||
auto bucket_boundaries = CreateLinearBuckets(0, number_of_buckets - 1, 1);
|
||||
auto& histogram = histogram_family.Add({}, bucket_boundaries);
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> d(0, number_of_buckets);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
auto observation = d(gen);
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
histogram.Observe(observation);
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
auto elapsed_seconds =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
|
||||
state.SetIterationTime(elapsed_seconds.count());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Histogram_Observe)->Range(0, 4096);
|
||||
|
||||
static void BM_Histogram_Collect(benchmark::State& state) {
|
||||
using prometheus::BuildHistogram;
|
||||
using prometheus::Histogram;
|
||||
using prometheus::Registry;
|
||||
|
||||
const auto number_of_buckets = state.range(0);
|
||||
|
||||
Registry registry;
|
||||
auto& histogram_family =
|
||||
BuildHistogram().Name("benchmark_histogram").Help("").Register(registry);
|
||||
auto bucket_boundaries = CreateLinearBuckets(0, number_of_buckets - 1, 1);
|
||||
auto& histogram = histogram_family.Add({}, bucket_boundaries);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(histogram.Collect());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Histogram_Collect)->Range(0, 4096);
|
||||
@@ -0,0 +1,20 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/info.h"
|
||||
#include "prometheus/registry.h"
|
||||
|
||||
static void BM_Info_Collect(benchmark::State& state) {
|
||||
using prometheus::BuildInfo;
|
||||
using prometheus::Info;
|
||||
using prometheus::Registry;
|
||||
Registry registry;
|
||||
auto& info_family =
|
||||
BuildInfo().Name("benchmark_info").Help("").Register(registry);
|
||||
auto& info = info_family.Add({});
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(info.Collect());
|
||||
};
|
||||
}
|
||||
BENCHMARK(BM_Info_Collect);
|
||||
@@ -0,0 +1,3 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "benchmark_helpers.h"
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/registry.h"
|
||||
|
||||
static void BM_Registry_CreateFamily(benchmark::State& state) {
|
||||
using prometheus::BuildCounter;
|
||||
using prometheus::Counter;
|
||||
using prometheus::Registry;
|
||||
Registry registry;
|
||||
|
||||
while (state.KeepRunning())
|
||||
BuildCounter().Name("benchmark_counter").Help("").Register(registry);
|
||||
}
|
||||
BENCHMARK(BM_Registry_CreateFamily);
|
||||
|
||||
static void BM_Registry_CreateCounter(benchmark::State& state) {
|
||||
using prometheus::BuildCounter;
|
||||
using prometheus::Counter;
|
||||
using prometheus::Registry;
|
||||
Registry registry;
|
||||
auto& counter_family = BuildCounter()
|
||||
.Labels(GenerateRandomLabels(10))
|
||||
.Name("benchmark_counter")
|
||||
.Help("")
|
||||
.Register(registry);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
auto labels = GenerateRandomLabels(state.range(0));
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
counter_family.Add(labels);
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
auto elapsed_seconds =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
|
||||
state.SetIterationTime(elapsed_seconds.count());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Registry_CreateCounter)->Range(0, 4096);
|
||||
@@ -0,0 +1,139 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/registry.h"
|
||||
#include "prometheus/summary.h"
|
||||
|
||||
using prometheus::Summary;
|
||||
|
||||
static const auto ITERATIONS = 262144;
|
||||
|
||||
static Summary::Quantiles CreateLinearQuantiles(int count) {
|
||||
static auto generator = [](double x) {
|
||||
static auto exp = [](double x) {
|
||||
static const double A = 2;
|
||||
return 1 - std::exp(-A * x);
|
||||
};
|
||||
|
||||
return exp(x) / exp(1);
|
||||
};
|
||||
|
||||
auto quantiles = Summary::Quantiles{};
|
||||
for (auto i = 0; i < count; ++i) {
|
||||
quantiles.emplace_back(generator(double(i) / count), 0.01);
|
||||
}
|
||||
return quantiles;
|
||||
}
|
||||
|
||||
static void BM_Summary_Observe(benchmark::State& state) {
|
||||
using prometheus::BuildSummary;
|
||||
using prometheus::Registry;
|
||||
using prometheus::Summary;
|
||||
|
||||
const auto number_of_quantiles = state.range(0);
|
||||
|
||||
Registry registry;
|
||||
auto& summary_family =
|
||||
BuildSummary().Name("benchmark_summary").Help("").Register(registry);
|
||||
auto quantiles = CreateLinearQuantiles(number_of_quantiles);
|
||||
auto& summary = summary_family.Add({}, quantiles);
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> d(0, 100);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
auto observation = d(gen);
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
summary.Observe(observation);
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
auto elapsed_seconds =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
|
||||
state.SetIterationTime(elapsed_seconds.count());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Summary_Observe)->Range(0, 64)->Iterations(ITERATIONS);
|
||||
|
||||
static void BM_Summary_Collect(benchmark::State& state) {
|
||||
using prometheus::BuildSummary;
|
||||
using prometheus::Registry;
|
||||
using prometheus::Summary;
|
||||
|
||||
const auto number_of_quantiles = state.range(0);
|
||||
const auto number_of_entries = state.range(1);
|
||||
|
||||
Registry registry;
|
||||
auto& summary_family =
|
||||
BuildSummary().Name("benchmark_summary").Help("").Register(registry);
|
||||
auto quantiles = CreateLinearQuantiles(number_of_quantiles);
|
||||
auto& summary = summary_family.Add({}, quantiles);
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> d(0, 100);
|
||||
for (auto i = 1; i <= number_of_entries; ++i) summary.Observe(d(gen));
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(summary.Collect());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Summary_Collect)->RangePair(0, 64, 0, ITERATIONS);
|
||||
|
||||
static void BM_Summary_Observe_Common(benchmark::State& state) {
|
||||
using prometheus::BuildSummary;
|
||||
using prometheus::Registry;
|
||||
using prometheus::Summary;
|
||||
|
||||
Registry registry;
|
||||
auto& summary_family =
|
||||
BuildSummary().Name("benchmark_summary").Help("").Register(registry);
|
||||
auto& summary = summary_family.Add(
|
||||
{}, Summary::Quantiles{
|
||||
{0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}});
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> d(0, 100);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
auto observation = d(gen);
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
summary.Observe(observation);
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
auto elapsed_seconds =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
|
||||
state.SetIterationTime(elapsed_seconds.count());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Summary_Observe_Common)->Iterations(ITERATIONS);
|
||||
|
||||
static void BM_Summary_Collect_Common(benchmark::State& state) {
|
||||
using prometheus::BuildSummary;
|
||||
using prometheus::Registry;
|
||||
using prometheus::Summary;
|
||||
|
||||
const auto number_of_entries = state.range(0);
|
||||
|
||||
Registry registry;
|
||||
auto& summary_family =
|
||||
BuildSummary().Name("benchmark_summary").Help("").Register(registry);
|
||||
auto& summary = summary_family.Add(
|
||||
{}, Summary::Quantiles{
|
||||
{0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}});
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> d(0, 100);
|
||||
for (auto i = 1; i <= number_of_entries; ++i) summary.Observe(d(gen));
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(summary.Collect());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Summary_Collect_Common)->Range(0, ITERATIONS);
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/metric_type.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
PROMETHEUS_CPP_CORE_EXPORT bool CheckMetricName(const std::string& name);
|
||||
PROMETHEUS_CPP_CORE_EXPORT bool CheckLabelName(const std::string& name,
|
||||
MetricType type);
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/detail/core_export.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
struct PROMETHEUS_CPP_CORE_EXPORT ClientMetric {
|
||||
// Label
|
||||
|
||||
struct Label {
|
||||
std::string name;
|
||||
std::string value;
|
||||
|
||||
bool operator<(const Label& rhs) const {
|
||||
return std::tie(name, value) < std::tie(rhs.name, rhs.value);
|
||||
}
|
||||
|
||||
bool operator==(const Label& rhs) const {
|
||||
return std::tie(name, value) == std::tie(rhs.name, rhs.value);
|
||||
}
|
||||
};
|
||||
std::vector<Label> label;
|
||||
|
||||
// Counter
|
||||
|
||||
struct Counter {
|
||||
double value = 0.0;
|
||||
};
|
||||
Counter counter;
|
||||
|
||||
// Gauge
|
||||
|
||||
struct Gauge {
|
||||
double value = 0.0;
|
||||
};
|
||||
Gauge gauge;
|
||||
|
||||
// Info
|
||||
|
||||
struct Info {
|
||||
double value = 1.0;
|
||||
};
|
||||
Info info;
|
||||
|
||||
// Summary
|
||||
|
||||
struct Quantile {
|
||||
double quantile = 0.0;
|
||||
double value = 0.0;
|
||||
};
|
||||
|
||||
struct Summary {
|
||||
std::uint64_t sample_count = 0;
|
||||
double sample_sum = 0.0;
|
||||
std::vector<Quantile> quantile;
|
||||
};
|
||||
Summary summary;
|
||||
|
||||
// Histogram
|
||||
|
||||
struct Bucket {
|
||||
std::uint64_t cumulative_count = 0;
|
||||
double upper_bound = 0.0;
|
||||
};
|
||||
|
||||
struct Histogram {
|
||||
std::uint64_t sample_count = 0;
|
||||
double sample_sum = 0.0;
|
||||
std::vector<Bucket> bucket;
|
||||
};
|
||||
Histogram histogram;
|
||||
|
||||
// Untyped
|
||||
|
||||
struct Untyped {
|
||||
double value = 0;
|
||||
};
|
||||
Untyped untyped;
|
||||
|
||||
// Timestamp
|
||||
|
||||
std::int64_t timestamp_ms = 0;
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/detail/core_export.h"
|
||||
|
||||
namespace prometheus {
|
||||
struct MetricFamily;
|
||||
}
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// @brief Interface implemented by anything that can be used by Prometheus to
|
||||
/// collect metrics.
|
||||
///
|
||||
/// A Collectable has to be registered for collection. See Registry.
|
||||
class PROMETHEUS_CPP_CORE_EXPORT Collectable {
|
||||
public:
|
||||
virtual ~Collectable() = default;
|
||||
|
||||
/// \brief Returns a list of metrics and their samples.
|
||||
virtual std::vector<MetricFamily> Collect() const = 0;
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/detail/builder.h" // IWYU pragma: export
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/gauge.h"
|
||||
#include "prometheus/metric_type.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A counter metric to represent a monotonically increasing value.
|
||||
///
|
||||
/// This class represents the metric type counter:
|
||||
/// https://prometheus.io/docs/concepts/metric_types/#counter
|
||||
///
|
||||
/// The value of the counter can only increase. Example of counters are:
|
||||
/// - the number of requests served
|
||||
/// - tasks completed
|
||||
/// - errors
|
||||
///
|
||||
/// Do not use a counter to expose a value that can decrease - instead use a
|
||||
/// Gauge.
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
class PROMETHEUS_CPP_CORE_EXPORT Counter {
|
||||
public:
|
||||
static const MetricType metric_type{MetricType::Counter};
|
||||
|
||||
/// \brief Create a counter that starts at 0.
|
||||
Counter() = default;
|
||||
|
||||
/// \brief Increment the counter by 1.
|
||||
void Increment();
|
||||
|
||||
/// \brief Increment the counter by a given amount.
|
||||
///
|
||||
/// The counter will not change if the given amount is negative.
|
||||
void Increment(double);
|
||||
|
||||
/// \brief Get the current value of the counter.
|
||||
double Value() const;
|
||||
|
||||
/// \brief Get the current value of the counter.
|
||||
///
|
||||
/// Collect is called by the Registry when collecting metrics.
|
||||
ClientMetric Collect() const;
|
||||
|
||||
private:
|
||||
Gauge gauge_{0.0};
|
||||
};
|
||||
|
||||
/// \brief Return a builder to configure and register a Counter metric.
|
||||
///
|
||||
/// @copydetails Family<>::Family()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& counter_family = prometheus::BuildCounter()
|
||||
/// .Name("some_name")
|
||||
/// .Help("Additional description.")
|
||||
/// .Labels({{"key", "value"}})
|
||||
/// .Register(*registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Labels(const Labels&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Counter metric, register it with
|
||||
/// Register(Registry&).
|
||||
PROMETHEUS_CPP_CORE_EXPORT detail::Builder<Counter> BuildCounter();
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "prometheus/labels.h"
|
||||
|
||||
// IWYU pragma: private
|
||||
// IWYU pragma: no_include "prometheus/family.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
template <typename T>
|
||||
class Family; // IWYU pragma: keep
|
||||
class Registry; // IWYU pragma: keep
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
class Builder {
|
||||
public:
|
||||
Builder& Labels(const ::prometheus::Labels& labels);
|
||||
Builder& Name(const std::string&);
|
||||
Builder& Help(const std::string&);
|
||||
Family<T>& Register(Registry&);
|
||||
|
||||
private:
|
||||
::prometheus::Labels labels_;
|
||||
std::string name_;
|
||||
std::string help_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/detail/core_export.h"
|
||||
|
||||
// IWYU pragma: private, include "prometheus/summary.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace detail {
|
||||
|
||||
class PROMETHEUS_CPP_CORE_EXPORT CKMSQuantiles {
|
||||
public:
|
||||
struct PROMETHEUS_CPP_CORE_EXPORT Quantile {
|
||||
Quantile(double quantile, double error);
|
||||
|
||||
double quantile;
|
||||
double error;
|
||||
double u;
|
||||
double v;
|
||||
};
|
||||
|
||||
private:
|
||||
struct Item {
|
||||
double value;
|
||||
int g;
|
||||
int delta;
|
||||
|
||||
Item(double value, int lower_delta, int delta);
|
||||
};
|
||||
|
||||
public:
|
||||
explicit CKMSQuantiles(const std::vector<Quantile>& quantiles);
|
||||
|
||||
void insert(double value);
|
||||
double get(double q);
|
||||
void reset();
|
||||
|
||||
private:
|
||||
double allowableError(int rank);
|
||||
bool insertBatch();
|
||||
void compress();
|
||||
|
||||
private:
|
||||
const std::reference_wrapper<const std::vector<Quantile>> quantiles_;
|
||||
|
||||
std::size_t count_;
|
||||
std::vector<Item> sample_;
|
||||
std::array<double, 500> buffer_;
|
||||
std::size_t buffer_count_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace prometheus
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/detail/ckms_quantiles.h" // IWYU pragma: export
|
||||
#include "prometheus/detail/core_export.h"
|
||||
|
||||
// IWYU pragma: private, include "prometheus/summary.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace detail {
|
||||
|
||||
class PROMETHEUS_CPP_CORE_EXPORT TimeWindowQuantiles {
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
public:
|
||||
TimeWindowQuantiles(const std::vector<CKMSQuantiles::Quantile>& quantiles,
|
||||
Clock::duration max_age_seconds, int age_buckets);
|
||||
|
||||
double get(double q) const;
|
||||
void insert(double value);
|
||||
|
||||
private:
|
||||
CKMSQuantiles& rotate() const;
|
||||
|
||||
const std::vector<CKMSQuantiles::Quantile>& quantiles_;
|
||||
mutable std::vector<CKMSQuantiles> ckms_quantiles_;
|
||||
mutable std::size_t current_bucket_;
|
||||
|
||||
mutable Clock::time_point last_rotation_;
|
||||
const Clock::duration rotation_interval_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/labels.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace detail {
|
||||
|
||||
/// \brief Label hasher for use in STL containers.
|
||||
struct PROMETHEUS_CPP_CORE_EXPORT LabelHasher {
|
||||
/// \brief Compute the hash value of a map of labels.
|
||||
///
|
||||
/// \param labels The map that will be computed the hash value.
|
||||
///
|
||||
/// \returns The hash value of the given labels.
|
||||
std::size_t operator()(const Labels& labels) const;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,156 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/collectable.h"
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/detail/utils.h"
|
||||
#include "prometheus/labels.h"
|
||||
#include "prometheus/metric_family.h"
|
||||
|
||||
// IWYU pragma: no_include "prometheus/counter.h"
|
||||
// IWYU pragma: no_include "prometheus/gauge.h"
|
||||
// IWYU pragma: no_include "prometheus/histogram.h"
|
||||
// IWYU pragma: no_include "prometheus/info.h"
|
||||
// IWYU pragma: no_include "prometheus/summary.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A metric of type T with a set of labeled dimensions.
|
||||
///
|
||||
/// One of Prometheus main feature is a multi-dimensional data model with time
|
||||
/// series data identified by metric name and key/value pairs, also known as
|
||||
/// labels. A time series is a series of data points indexed (or listed or
|
||||
/// graphed) in time order (https://en.wikipedia.org/wiki/Time_series).
|
||||
///
|
||||
/// An instance of this class is exposed as multiple time series during
|
||||
/// scrape, i.e., one time series for each set of labels provided to Add().
|
||||
///
|
||||
/// For example it is possible to collect data for a metric
|
||||
/// `http_requests_total`, with two time series:
|
||||
///
|
||||
/// - all HTTP requests that used the method POST
|
||||
/// - all HTTP requests that used the method GET
|
||||
///
|
||||
/// The metric name specifies the general feature of a system that is
|
||||
/// measured, e.g., `http_requests_total`. Labels enable Prometheus's
|
||||
/// dimensional data model: any given combination of labels for the same
|
||||
/// metric name identifies a particular dimensional instantiation of that
|
||||
/// metric. For example a label for 'all HTTP requests that used the method
|
||||
/// POST' can be assigned with `method= "POST"`.
|
||||
///
|
||||
/// Given a metric name and a set of labels, time series are frequently
|
||||
/// identified using this notation:
|
||||
///
|
||||
/// <metric name> { < label name >= <label value>, ... }
|
||||
///
|
||||
/// It is required to follow the syntax of metric names and labels given by:
|
||||
/// https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
|
||||
///
|
||||
/// The following metric and label conventions are not required for using
|
||||
/// Prometheus, but can serve as both a style-guide and a collection of best
|
||||
/// practices: https://prometheus.io/docs/practices/naming/
|
||||
///
|
||||
/// \tparam T One of the metric types Counter, Gauge, Histogram or Summary.
|
||||
template <typename T>
|
||||
class PROMETHEUS_CPP_CORE_EXPORT Family : public Collectable {
|
||||
public:
|
||||
/// \brief Create a new metric.
|
||||
///
|
||||
/// Every metric is uniquely identified by its name and a set of key-value
|
||||
/// pairs, also known as labels. Prometheus's query language allows filtering
|
||||
/// and aggregation based on metric name and these labels.
|
||||
///
|
||||
/// This example selects all time series that have the `http_requests_total`
|
||||
/// metric name:
|
||||
///
|
||||
/// http_requests_total
|
||||
///
|
||||
/// It is possible to assign labels to the metric name. These labels are
|
||||
/// propagated to each dimensional data added with Add(). For example if a
|
||||
/// label `job= "prometheus"` is provided to this constructor, it is possible
|
||||
/// to filter this time series with Prometheus's query language by appending
|
||||
/// a set of labels to match in curly braces ({})
|
||||
///
|
||||
/// http_requests_total{job= "prometheus"}
|
||||
///
|
||||
/// For further information see: [Querying Basics]
|
||||
/// (https://prometheus.io/docs/prometheus/latest/querying/basics/)
|
||||
///
|
||||
/// \param name Set the metric name.
|
||||
/// \param help Set an additional description.
|
||||
/// \param constant_labels Assign a set of key-value pairs (= labels) to the
|
||||
/// metric. All these labels are propagated to each time series within the
|
||||
/// metric.
|
||||
/// \throw std::runtime_exception on invalid metric or label names.
|
||||
Family(const std::string& name, const std::string& help,
|
||||
const Labels& constant_labels);
|
||||
|
||||
/// \brief Add a new dimensional data.
|
||||
///
|
||||
/// Each new set of labels adds a new dimensional data and is exposed in
|
||||
/// Prometheus as a time series. It is possible to filter the time series
|
||||
/// with Prometheus's query language by appending a set of labels to match in
|
||||
/// curly braces ({})
|
||||
///
|
||||
/// http_requests_total{job= "prometheus",method= "POST"}
|
||||
///
|
||||
/// \param labels Assign a set of key-value pairs (= labels) to the
|
||||
/// dimensional data. The function does nothing, if the same set of labels
|
||||
/// already exists.
|
||||
/// \param args Arguments are passed to the constructor of metric type T. See
|
||||
/// Counter, Gauge, Histogram or Summary for required constructor arguments.
|
||||
/// \return Return the newly created dimensional data or - if a same set of
|
||||
/// labels already exists - the already existing dimensional data.
|
||||
/// \throw std::runtime_exception on invalid label names.
|
||||
template <typename... Args>
|
||||
T& Add(const Labels& labels, Args&&... args) {
|
||||
return Add(labels, std::make_unique<T>(args...));
|
||||
}
|
||||
|
||||
/// \brief Remove the given dimensional data.
|
||||
///
|
||||
/// \param metric Dimensional data to be removed. The function does nothing,
|
||||
/// if the given metric was not returned by Add().
|
||||
void Remove(T* metric);
|
||||
|
||||
/// \brief Returns true if the dimensional data with the given labels exist
|
||||
///
|
||||
/// \param labels A set of key-value pairs (= labels) of the dimensional data.
|
||||
bool Has(const Labels& labels) const;
|
||||
|
||||
/// \brief Returns the name for this family.
|
||||
///
|
||||
/// \return The family name.
|
||||
const std::string& GetName() const;
|
||||
|
||||
/// \brief Returns the constant labels for this family.
|
||||
///
|
||||
/// \return All constant labels as key-value pairs.
|
||||
const Labels& GetConstantLabels() const;
|
||||
|
||||
/// \brief Returns the current value of each dimensional data.
|
||||
///
|
||||
/// Collect is called by the Registry when collecting metrics.
|
||||
///
|
||||
/// \return Zero or more samples for each dimensional data.
|
||||
std::vector<MetricFamily> Collect() const override;
|
||||
|
||||
private:
|
||||
std::unordered_map<Labels, std::unique_ptr<T>, detail::LabelHasher> metrics_;
|
||||
|
||||
const std::string name_;
|
||||
const std::string help_;
|
||||
const Labels constant_labels_;
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
ClientMetric CollectMetric(const Labels& labels, T* metric) const;
|
||||
T& Add(const Labels& labels, std::unique_ptr<T> object);
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/detail/builder.h" // IWYU pragma: export
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/metric_type.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A gauge metric to represent a value that can arbitrarily go up and
|
||||
/// down.
|
||||
///
|
||||
/// The class represents the metric type gauge:
|
||||
/// https://prometheus.io/docs/concepts/metric_types/#gauge
|
||||
///
|
||||
/// Gauges are typically used for measured values like temperatures or current
|
||||
/// memory usage, but also "counts" that can go up and down, like the number of
|
||||
/// running processes.
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
class PROMETHEUS_CPP_CORE_EXPORT Gauge {
|
||||
public:
|
||||
static const MetricType metric_type{MetricType::Gauge};
|
||||
|
||||
/// \brief Create a gauge that starts at 0.
|
||||
Gauge() = default;
|
||||
|
||||
/// \brief Create a gauge that starts at the given amount.
|
||||
explicit Gauge(double);
|
||||
|
||||
/// \brief Increment the gauge by 1.
|
||||
void Increment();
|
||||
|
||||
/// \brief Increment the gauge by the given amount.
|
||||
void Increment(double);
|
||||
|
||||
/// \brief Decrement the gauge by 1.
|
||||
void Decrement();
|
||||
|
||||
/// \brief Decrement the gauge by the given amount.
|
||||
void Decrement(double);
|
||||
|
||||
/// \brief Set the gauge to the given value.
|
||||
void Set(double);
|
||||
|
||||
/// \brief Set the gauge to the current unix time in seconds.
|
||||
void SetToCurrentTime();
|
||||
|
||||
/// \brief Get the current value of the gauge.
|
||||
double Value() const;
|
||||
|
||||
/// \brief Get the current value of the gauge.
|
||||
///
|
||||
/// Collect is called by the Registry when collecting metrics.
|
||||
ClientMetric Collect() const;
|
||||
|
||||
private:
|
||||
void Change(double);
|
||||
std::atomic<double> value_{0.0};
|
||||
};
|
||||
|
||||
/// \brief Return a builder to configure and register a Gauge metric.
|
||||
///
|
||||
/// @copydetails Family<>::Family()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& gauge_family = prometheus::BuildGauge()
|
||||
/// .Name("some_name")
|
||||
/// .Help("Additional description.")
|
||||
/// .Labels({{"key", "value"}})
|
||||
/// .Register(*registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Labels(const Labels&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Gauge metric register it with
|
||||
/// Register(Registry&).
|
||||
PROMETHEUS_CPP_CORE_EXPORT detail::Builder<Gauge> BuildGauge();
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/detail/builder.h" // IWYU pragma: export
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/gauge.h"
|
||||
#include "prometheus/metric_type.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A histogram metric to represent aggregatable distributions of events.
|
||||
///
|
||||
/// This class represents the metric type histogram:
|
||||
/// https://prometheus.io/docs/concepts/metric_types/#histogram
|
||||
///
|
||||
/// A histogram tracks the number of observations and the sum of the observed
|
||||
/// values, allowing to calculate the average of the observed values.
|
||||
///
|
||||
/// At its core a histogram has a counter per bucket. The sum of observations
|
||||
/// also behaves like a counter as long as there are no negative observations.
|
||||
///
|
||||
/// See https://prometheus.io/docs/practices/histograms/ for detailed
|
||||
/// explanations of histogram usage and differences to summaries.
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
class PROMETHEUS_CPP_CORE_EXPORT Histogram {
|
||||
public:
|
||||
using BucketBoundaries = std::vector<double>;
|
||||
|
||||
static const MetricType metric_type{MetricType::Histogram};
|
||||
|
||||
/// \brief Create a histogram with manually chosen buckets.
|
||||
///
|
||||
/// The BucketBoundaries are a list of monotonically increasing values
|
||||
/// representing the bucket boundaries. Each consecutive pair of values is
|
||||
/// interpreted as a half-open interval [b_n, b_n+1) which defines one bucket.
|
||||
///
|
||||
/// There is no limitation on how the buckets are divided, i.e, equal size,
|
||||
/// exponential etc..
|
||||
///
|
||||
/// The bucket boundaries cannot be changed once the histogram is created.
|
||||
explicit Histogram(const BucketBoundaries& buckets);
|
||||
|
||||
/// \copydoc Histogram::Histogram(const BucketBoundaries&)
|
||||
explicit Histogram(BucketBoundaries&& buckets);
|
||||
|
||||
/// \brief Observe the given amount.
|
||||
///
|
||||
/// The given amount selects the 'observed' bucket. The observed bucket is
|
||||
/// chosen for which the given amount falls into the half-open interval [b_n,
|
||||
/// b_n+1). The counter of the observed bucket is incremented. Also the total
|
||||
/// sum of all observations is incremented.
|
||||
void Observe(double value);
|
||||
|
||||
/// \brief Observe multiple data points.
|
||||
///
|
||||
/// Increments counters given a count for each bucket. (i.e. the caller of
|
||||
/// this function must have already sorted the values into buckets).
|
||||
/// Also increments the total sum of all observations by the given value.
|
||||
void ObserveMultiple(const std::vector<double>& bucket_increments,
|
||||
double sum_of_values);
|
||||
|
||||
/// \brief Get the current value of the histogram.
|
||||
///
|
||||
/// Collect is called by the Registry when collecting metrics.
|
||||
ClientMetric Collect() const;
|
||||
|
||||
private:
|
||||
BucketBoundaries bucket_boundaries_;
|
||||
mutable std::mutex mutex_;
|
||||
std::vector<Counter> bucket_counts_;
|
||||
Gauge sum_;
|
||||
};
|
||||
|
||||
/// \brief Return a builder to configure and register a Histogram metric.
|
||||
///
|
||||
/// @copydetails Family<>::Family()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& histogram_family = prometheus::BuildHistogram()
|
||||
/// .Name("some_name")
|
||||
/// .Help("Additional description.")
|
||||
/// .Labels({{"key", "value"}})
|
||||
/// .Register(*registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Labels(const Labels&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Histogram metric register it with
|
||||
/// Register(Registry&).
|
||||
PROMETHEUS_CPP_CORE_EXPORT detail::Builder<Histogram> BuildHistogram();
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/detail/builder.h" // IWYU pragma: export
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/metric_type.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A info metric to represent textual information which should not
|
||||
/// change during process lifetime.
|
||||
///
|
||||
/// This class represents the metric type info:
|
||||
/// https://github.com/OpenObservability/OpenMetrics/blob/98ae26c87b1c3bcf937909a880b32c8be643cc9b/specification/OpenMetrics.md#info
|
||||
|
||||
/// Prometheus does not provide this type directly, it is used by emulating a
|
||||
/// gauge with value 1: https://prometheus.io/docs/concepts/metric_types/#gauge
|
||||
///
|
||||
/// The value of the info cannot change. Example of infos are:
|
||||
/// - the application's version
|
||||
/// - revision control commit
|
||||
/// - version of the compiler
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
class PROMETHEUS_CPP_CORE_EXPORT Info {
|
||||
public:
|
||||
static const MetricType metric_type{MetricType::Info};
|
||||
|
||||
/// \brief Create a info.
|
||||
Info() = default;
|
||||
|
||||
/// \brief Get the current value of the info.
|
||||
///
|
||||
/// Collect is called by the Registry when collecting metrics.
|
||||
ClientMetric Collect() const;
|
||||
};
|
||||
|
||||
/// \brief Return a builder to configure and register a Info metric.
|
||||
///
|
||||
/// @copydetails Family<>::Family()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& info_family = prometheus::BuildInfo()
|
||||
/// .Name("some_name")
|
||||
/// .Help("Additional description.")
|
||||
/// .Labels({{"key", "value"}})
|
||||
/// .Register(*registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Labels(const Labels&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Info metric, register it with
|
||||
/// Register(Registry&).
|
||||
PROMETHEUS_CPP_CORE_EXPORT detail::Builder<Info> BuildInfo();
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief Multiple labels and their value.
|
||||
using Labels = std::map<std::string, std::string>;
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/metric_type.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
struct PROMETHEUS_CPP_CORE_EXPORT MetricFamily {
|
||||
std::string name;
|
||||
std::string help;
|
||||
MetricType type = MetricType::Untyped;
|
||||
std::vector<ClientMetric> metric;
|
||||
};
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
enum class MetricType {
|
||||
Counter,
|
||||
Gauge,
|
||||
Summary,
|
||||
Untyped,
|
||||
Histogram,
|
||||
Info,
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/collectable.h"
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/labels.h"
|
||||
#include "prometheus/metric_family.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
class Counter;
|
||||
class Gauge;
|
||||
class Histogram;
|
||||
class Info;
|
||||
class Summary;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
class Builder; // IWYU pragma: keep
|
||||
|
||||
}
|
||||
/// \brief Manages the collection of a number of metrics.
|
||||
///
|
||||
/// The Registry is responsible to expose data to a class/method/function
|
||||
/// "bridge", which returns the metrics in a format Prometheus supports.
|
||||
///
|
||||
/// The key class is the Collectable. This has a method - called Collect() -
|
||||
/// that returns zero or more metrics and their samples. The metrics are
|
||||
/// represented by the class Family<>, which implements the Collectable
|
||||
/// interface. A new metric is registered with BuildCounter(), BuildGauge(),
|
||||
/// BuildHistogram(), BuildInfo() or BuildSummary().
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
class PROMETHEUS_CPP_CORE_EXPORT Registry : public Collectable {
|
||||
public:
|
||||
/// \brief How to deal with repeatedly added family names for a type.
|
||||
///
|
||||
/// Adding a family with the same name but different types is always an error
|
||||
/// and will lead to an exception.
|
||||
enum class InsertBehavior {
|
||||
/// \brief If a family with the same name and labels already exists return
|
||||
/// the existing one. If no family with that name exists create it.
|
||||
/// Otherwise throw.
|
||||
Merge,
|
||||
/// \brief Throws if a family with the same name already exists.
|
||||
Throw,
|
||||
};
|
||||
|
||||
/// \brief name Create a new registry.
|
||||
///
|
||||
/// \param insert_behavior How to handle families with the same name.
|
||||
explicit Registry(InsertBehavior insert_behavior = InsertBehavior::Merge);
|
||||
|
||||
/// \brief Deleted copy constructor.
|
||||
Registry(const Registry&) = delete;
|
||||
|
||||
/// \brief Deleted copy assignment.
|
||||
Registry& operator=(const Registry&) = delete;
|
||||
|
||||
/// \brief Deleted move constructor.
|
||||
Registry(Registry&&) = delete;
|
||||
|
||||
/// \brief Deleted move assignment.
|
||||
Registry& operator=(Registry&&) = delete;
|
||||
|
||||
/// \brief name Destroys a registry.
|
||||
~Registry() override;
|
||||
|
||||
/// \brief Returns a list of metrics and their samples.
|
||||
///
|
||||
/// Every time the Registry is scraped it calls each of the metrics Collect
|
||||
/// function.
|
||||
///
|
||||
/// \return Zero or more metrics and their samples.
|
||||
std::vector<MetricFamily> Collect() const override;
|
||||
|
||||
/// \brief Removes a metrics family from the registry.
|
||||
///
|
||||
/// Please note that this operation invalidates the previously
|
||||
/// returned reference to the Family and all of their added
|
||||
/// metric objects.
|
||||
///
|
||||
/// \tparam T One of the metric types Counter, Gauge, Histogram or Summary.
|
||||
/// \param family The family to remove
|
||||
///
|
||||
/// \return True if the family was found and removed.
|
||||
template <typename T>
|
||||
bool Remove(const Family<T>& family);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
friend class detail::Builder;
|
||||
|
||||
template <typename T>
|
||||
std::vector<std::unique_ptr<Family<T>>>& GetFamilies();
|
||||
|
||||
template <typename T>
|
||||
bool NameExistsInOtherType(const std::string& name) const;
|
||||
|
||||
template <typename T>
|
||||
Family<T>& Add(const std::string& name, const std::string& help,
|
||||
const Labels& labels);
|
||||
|
||||
const InsertBehavior insert_behavior_;
|
||||
std::vector<std::unique_ptr<Family<Counter>>> counters_;
|
||||
std::vector<std::unique_ptr<Family<Gauge>>> gauges_;
|
||||
std::vector<std::unique_ptr<Family<Histogram>>> histograms_;
|
||||
std::vector<std::unique_ptr<Family<Info>>> infos_;
|
||||
std::vector<std::unique_ptr<Family<Summary>>> summaries_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/metric_family.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
class PROMETHEUS_CPP_CORE_EXPORT Serializer {
|
||||
public:
|
||||
virtual ~Serializer() = default;
|
||||
virtual std::string Serialize(const std::vector<MetricFamily>&) const;
|
||||
virtual void Serialize(std::ostream& out,
|
||||
const std::vector<MetricFamily>& metrics) const = 0;
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/detail/builder.h" // IWYU pragma: export
|
||||
#include "prometheus/detail/ckms_quantiles.h"
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/detail/time_window_quantiles.h"
|
||||
#include "prometheus/metric_type.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A summary metric samples observations over a sliding window of time.
|
||||
///
|
||||
/// This class represents the metric type summary:
|
||||
/// https://prometheus.io/docs/instrumenting/writing_clientlibs/#summary
|
||||
///
|
||||
/// A summary provides a total count of observations and a sum of all observed
|
||||
/// values. In contrast to a histogram metric it also calculates configurable
|
||||
/// Phi-quantiles over a sliding window of time.
|
||||
///
|
||||
/// The essential difference between summaries and histograms is that summaries
|
||||
/// calculate streaming Phi-quantiles on the client side and expose them
|
||||
/// directly, while histograms expose bucketed observation counts and the
|
||||
/// calculation of quantiles from the buckets of a histogram happens on the
|
||||
/// server side:
|
||||
/// https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile.
|
||||
///
|
||||
/// Note that Phi designates the probability density function of the standard
|
||||
/// Gaussian distribution.
|
||||
///
|
||||
/// See https://prometheus.io/docs/practices/histograms/ for detailed
|
||||
/// explanations of Phi-quantiles, summary usage, and differences to histograms.
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
class PROMETHEUS_CPP_CORE_EXPORT Summary {
|
||||
public:
|
||||
using Quantiles = std::vector<detail::CKMSQuantiles::Quantile>;
|
||||
|
||||
static const MetricType metric_type{MetricType::Summary};
|
||||
|
||||
/// \brief Create a summary metric.
|
||||
///
|
||||
/// \param quantiles A list of 'targeted' Phi-quantiles. A targeted
|
||||
/// Phi-quantile is specified in the form of a Phi-quantile and tolerated
|
||||
/// error. For example a Quantile{0.5, 0.1} means that the median (= 50th
|
||||
/// percentile) should be returned with 10 percent error or a Quantile{0.2,
|
||||
/// 0.05} means the 20th percentile with 5 percent tolerated error. Note that
|
||||
/// percentiles and quantiles are the same concept, except percentiles are
|
||||
/// expressed as percentages. The Phi-quantile must be in the interval [0, 1].
|
||||
/// Note that a lower tolerated error for a Phi-quantile results in higher
|
||||
/// usage of resources (memory and cpu) to calculate the summary.
|
||||
///
|
||||
/// The Phi-quantiles are calculated over a sliding window of time. The
|
||||
/// sliding window of time is configured by max_age and age_buckets.
|
||||
///
|
||||
/// \param max_age Set the duration of the time window, i.e., how long
|
||||
/// observations are kept before they are discarded. The default value is 60
|
||||
/// seconds.
|
||||
///
|
||||
/// \param age_buckets Set the number of buckets of the time window. It
|
||||
/// determines the number of buckets used to exclude observations that are
|
||||
/// older than max_age from the summary, e.g., if max_age is 60 seconds and
|
||||
/// age_buckets is 5, buckets will be switched every 12 seconds. The value is
|
||||
/// a trade-off between resources (memory and cpu for maintaining the bucket)
|
||||
/// and how smooth the time window is moved. With only one age bucket it
|
||||
/// effectively results in a complete reset of the summary each time max_age
|
||||
/// has passed. The default value is 5.
|
||||
explicit Summary(const Quantiles& quantiles,
|
||||
std::chrono::milliseconds max_age = std::chrono::seconds{60},
|
||||
int age_buckets = 5);
|
||||
|
||||
/// \copydoc Summary::Summary(const Quantiles&,std::chrono::milliseconds,int)
|
||||
explicit Summary(Quantiles&& quantiles,
|
||||
std::chrono::milliseconds max_age = std::chrono::seconds{60},
|
||||
int age_buckets = 5);
|
||||
|
||||
/// \brief Observe the given amount.
|
||||
void Observe(double value);
|
||||
|
||||
/// \brief Get the current value of the summary.
|
||||
///
|
||||
/// Collect is called by the Registry when collecting metrics.
|
||||
ClientMetric Collect() const;
|
||||
|
||||
private:
|
||||
Quantiles quantiles_;
|
||||
mutable std::mutex mutex_;
|
||||
std::uint64_t count_{};
|
||||
double sum_{};
|
||||
detail::TimeWindowQuantiles quantile_values_;
|
||||
};
|
||||
|
||||
/// \brief Return a builder to configure and register a Summary metric.
|
||||
///
|
||||
/// @copydetails Family<>::Family()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& summary_family = prometheus::BuildSummary()
|
||||
/// .Name("some_name")
|
||||
/// .Help("Additional description.")
|
||||
/// .Labels({{"key", "value"}})
|
||||
/// .Register(*registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Labels(const Labels&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Summary metric register it with
|
||||
/// Register(Registry&).
|
||||
PROMETHEUS_CPP_CORE_EXPORT detail::Builder<Summary> BuildSummary();
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/metric_family.h"
|
||||
#include "prometheus/serializer.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
class PROMETHEUS_CPP_CORE_EXPORT TextSerializer : public Serializer {
|
||||
public:
|
||||
using Serializer::Serialize;
|
||||
void Serialize(std::ostream& out,
|
||||
const std::vector<MetricFamily>& metrics) const override;
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,85 @@
|
||||
|
||||
#include "prometheus/check_names.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
namespace {
|
||||
bool isLocaleIndependentAlphaNumeric(char c) {
|
||||
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
|
||||
('A' <= c && c <= 'Z');
|
||||
}
|
||||
|
||||
bool isLocaleIndependentDigit(char c) { return '0' <= c && c <= '9'; }
|
||||
|
||||
bool nameStartsValid(const std::string& name) {
|
||||
// must not be empty
|
||||
if (name.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// must not start with a digit
|
||||
if (isLocaleIndependentDigit(name.front())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// must not start with "__"
|
||||
auto reserved_for_internal_purposes = name.compare(0, 2, "__") == 0;
|
||||
if (reserved_for_internal_purposes) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
/// \brief Check if the metric name is valid
|
||||
///
|
||||
/// The metric name regex is "[a-zA-Z_:][a-zA-Z0-9_:]*"
|
||||
///
|
||||
/// \see https://prometheus.io/docs/concepts/data_model/
|
||||
///
|
||||
/// \param name metric name
|
||||
/// \return true is valid, false otherwise
|
||||
bool CheckMetricName(const std::string& name) {
|
||||
if (!nameStartsValid(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto validMetricCharacters = [](char c) {
|
||||
return isLocaleIndependentAlphaNumeric(c) || c == '_' || c == ':';
|
||||
};
|
||||
|
||||
auto mismatch =
|
||||
std::find_if_not(std::begin(name), std::end(name), validMetricCharacters);
|
||||
return mismatch == std::end(name);
|
||||
}
|
||||
|
||||
/// \brief Check if the label name is valid
|
||||
///
|
||||
/// The label name regex is "[a-zA-Z_][a-zA-Z0-9_]*"
|
||||
///
|
||||
/// \see https://prometheus.io/docs/concepts/data_model/
|
||||
///
|
||||
/// \param name label name
|
||||
/// \param type metric type
|
||||
/// \return true is valid, false otherwise
|
||||
bool CheckLabelName(const std::string& name, MetricType type) {
|
||||
if (!nameStartsValid(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto validLabelCharacters = [](char c) {
|
||||
return isLocaleIndependentAlphaNumeric(c) || c == '_';
|
||||
};
|
||||
|
||||
if ((type == MetricType::Histogram && name == "le") ||
|
||||
(type == MetricType::Summary && name == "quantile")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mismatch =
|
||||
std::find_if_not(std::begin(name), std::end(name), validLabelCharacters);
|
||||
return mismatch == std::end(name);
|
||||
}
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "prometheus/counter.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
void Counter::Increment() { gauge_.Increment(); }
|
||||
|
||||
void Counter::Increment(const double val) {
|
||||
if (val < 0.0) {
|
||||
return;
|
||||
}
|
||||
gauge_.Increment(val);
|
||||
}
|
||||
|
||||
double Counter::Value() const { return gauge_.Value(); }
|
||||
|
||||
ClientMetric Counter::Collect() const {
|
||||
ClientMetric metric;
|
||||
metric.counter.value = Value();
|
||||
return metric;
|
||||
}
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "prometheus/detail/builder.h"
|
||||
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/detail/core_export.h"
|
||||
#include "prometheus/gauge.h"
|
||||
#include "prometheus/histogram.h"
|
||||
#include "prometheus/info.h"
|
||||
#include "prometheus/registry.h"
|
||||
#include "prometheus/summary.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
Builder<T>& Builder<T>::Labels(const ::prometheus::Labels& labels) {
|
||||
labels_ = labels;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Builder<T>& Builder<T>::Name(const std::string& name) {
|
||||
name_ = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Builder<T>& Builder<T>::Help(const std::string& help) {
|
||||
help_ = help;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Family<T>& Builder<T>::Register(Registry& registry) {
|
||||
return registry.Add<T>(name_, help_, labels_);
|
||||
}
|
||||
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Builder<Counter>;
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Builder<Gauge>;
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Builder<Histogram>;
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Builder<Info>;
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Builder<Summary>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
detail::Builder<Counter> BuildCounter() { return {}; }
|
||||
detail::Builder<Gauge> BuildGauge() { return {}; }
|
||||
detail::Builder<Histogram> BuildHistogram() { return {}; }
|
||||
detail::Builder<Info> BuildInfo() { return {}; }
|
||||
detail::Builder<Summary> BuildSummary() { return {}; }
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,153 @@
|
||||
#include "prometheus/detail/ckms_quantiles.h" // IWYU pragma: export
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
namespace prometheus {
|
||||
namespace detail {
|
||||
|
||||
CKMSQuantiles::Quantile::Quantile(double quantile, double error)
|
||||
: quantile(quantile),
|
||||
error(error),
|
||||
u(2.0 * error / (1.0 - quantile)),
|
||||
v(2.0 * error / quantile) {}
|
||||
|
||||
CKMSQuantiles::Item::Item(double value, int lower_delta, int delta)
|
||||
: value(value), g(lower_delta), delta(delta) {}
|
||||
|
||||
CKMSQuantiles::CKMSQuantiles(const std::vector<Quantile>& quantiles)
|
||||
: quantiles_(quantiles), count_(0), buffer_{}, buffer_count_(0) {}
|
||||
|
||||
void CKMSQuantiles::insert(double value) {
|
||||
buffer_[buffer_count_] = value;
|
||||
++buffer_count_;
|
||||
|
||||
if (buffer_count_ == buffer_.size()) {
|
||||
insertBatch();
|
||||
compress();
|
||||
}
|
||||
}
|
||||
|
||||
double CKMSQuantiles::get(double q) {
|
||||
insertBatch();
|
||||
compress();
|
||||
|
||||
if (sample_.empty()) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
int rankMin = 0;
|
||||
const auto desired = static_cast<int>(q * count_);
|
||||
const auto bound = desired + (allowableError(desired) / 2);
|
||||
|
||||
auto it = sample_.begin();
|
||||
decltype(it) prev;
|
||||
auto cur = it++;
|
||||
|
||||
while (it != sample_.end()) {
|
||||
prev = cur;
|
||||
cur = it++;
|
||||
|
||||
rankMin += prev->g;
|
||||
|
||||
if (rankMin + cur->g + cur->delta > bound) {
|
||||
return prev->value;
|
||||
}
|
||||
}
|
||||
|
||||
return sample_.back().value;
|
||||
}
|
||||
|
||||
void CKMSQuantiles::reset() {
|
||||
count_ = 0;
|
||||
sample_.clear();
|
||||
buffer_count_ = 0;
|
||||
}
|
||||
|
||||
double CKMSQuantiles::allowableError(int rank) {
|
||||
auto size = sample_.size();
|
||||
double minError = size + 1;
|
||||
|
||||
for (const auto& q : quantiles_.get()) {
|
||||
double error;
|
||||
if (rank <= q.quantile * size) {
|
||||
error = q.u * (size - rank);
|
||||
} else {
|
||||
error = q.v * rank;
|
||||
}
|
||||
if (error < minError) {
|
||||
minError = error;
|
||||
}
|
||||
}
|
||||
|
||||
return minError;
|
||||
}
|
||||
|
||||
bool CKMSQuantiles::insertBatch() {
|
||||
if (buffer_count_ == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::sort(buffer_.begin(), buffer_.begin() + buffer_count_);
|
||||
|
||||
std::size_t start = 0;
|
||||
if (sample_.empty()) {
|
||||
sample_.emplace_back(buffer_[0], 1, 0);
|
||||
++start;
|
||||
++count_;
|
||||
}
|
||||
|
||||
std::size_t idx = 0;
|
||||
std::size_t item = idx++;
|
||||
|
||||
for (std::size_t i = start; i < buffer_count_; ++i) {
|
||||
double v = buffer_[i];
|
||||
while (idx < sample_.size() && sample_[item].value < v) {
|
||||
item = idx++;
|
||||
}
|
||||
|
||||
if (sample_[item].value > v) {
|
||||
--idx;
|
||||
}
|
||||
|
||||
int delta;
|
||||
if (idx - 1 == 0 || idx + 1 == sample_.size()) {
|
||||
delta = 0;
|
||||
} else {
|
||||
delta = static_cast<int>(std::floor(allowableError(idx + 1))) + 1;
|
||||
}
|
||||
|
||||
sample_.emplace(sample_.begin() + idx, v, 1, delta);
|
||||
count_++;
|
||||
item = idx++;
|
||||
}
|
||||
|
||||
buffer_count_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CKMSQuantiles::compress() {
|
||||
if (sample_.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t idx = 0;
|
||||
std::size_t prev;
|
||||
std::size_t next = idx++;
|
||||
|
||||
while (idx < sample_.size()) {
|
||||
prev = next;
|
||||
next = idx++;
|
||||
|
||||
if (sample_[prev].g + sample_[next].g + sample_[next].delta <=
|
||||
allowableError(idx - 1)) {
|
||||
sample_[next].g += sample_[prev].g;
|
||||
sample_.erase(sample_.begin() + prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// \brief Combine a hash value with nothing.
|
||||
/// It's the boundary condition of this serial functions.
|
||||
///
|
||||
/// \param seed Not effect.
|
||||
inline void hash_combine(std::size_t*) {}
|
||||
|
||||
/// \brief Combine the given hash value with another object.
|
||||
///
|
||||
/// \param seed The given hash value. It's a input/output parameter.
|
||||
/// \param value The object that will be combined with the given hash value.
|
||||
template <typename T>
|
||||
inline void hash_combine(std::size_t* seed, const T& value) {
|
||||
*seed ^= std::hash<T>{}(value) + 0x9e3779b9 + (*seed << 6) + (*seed >> 2);
|
||||
}
|
||||
|
||||
/// \brief Combine the given hash value with another objects. It's a recursion。
|
||||
///
|
||||
/// \param seed The give hash value. It's a input/output parameter.
|
||||
/// \param value The object that will be combined with the given hash value.
|
||||
/// \param args The objects that will be combined with the given hash value.
|
||||
template <typename T, typename... Types>
|
||||
inline void hash_combine(std::size_t* seed, const T& value,
|
||||
const Types&... args) {
|
||||
hash_combine(seed, value);
|
||||
hash_combine(seed, args...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "prometheus/detail/time_window_quantiles.h" // IWYU pragma: export
|
||||
|
||||
#include <memory>
|
||||
#include <ratio>
|
||||
|
||||
namespace prometheus {
|
||||
namespace detail {
|
||||
|
||||
TimeWindowQuantiles::TimeWindowQuantiles(
|
||||
const std::vector<CKMSQuantiles::Quantile>& quantiles,
|
||||
const Clock::duration max_age, const int age_buckets)
|
||||
: quantiles_(quantiles),
|
||||
ckms_quantiles_(age_buckets, CKMSQuantiles(quantiles_)),
|
||||
current_bucket_(0),
|
||||
last_rotation_(Clock::now()),
|
||||
rotation_interval_(max_age / age_buckets) {}
|
||||
|
||||
double TimeWindowQuantiles::get(double q) const {
|
||||
CKMSQuantiles& current_bucket = rotate();
|
||||
return current_bucket.get(q);
|
||||
}
|
||||
|
||||
void TimeWindowQuantiles::insert(double value) {
|
||||
rotate();
|
||||
for (auto& bucket : ckms_quantiles_) {
|
||||
bucket.insert(value);
|
||||
}
|
||||
}
|
||||
|
||||
CKMSQuantiles& TimeWindowQuantiles::rotate() const {
|
||||
auto delta = Clock::now() - last_rotation_;
|
||||
while (delta > rotation_interval_) {
|
||||
ckms_quantiles_[current_bucket_].reset();
|
||||
|
||||
if (++current_bucket_ >= ckms_quantiles_.size()) {
|
||||
current_bucket_ = 0;
|
||||
}
|
||||
|
||||
delta -= rotation_interval_;
|
||||
last_rotation_ += rotation_interval_;
|
||||
}
|
||||
return ckms_quantiles_[current_bucket_];
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "prometheus/detail/utils.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
namespace detail {
|
||||
|
||||
std::size_t LabelHasher::operator()(const Labels& labels) const {
|
||||
std::size_t seed = 0;
|
||||
for (auto& label : labels) {
|
||||
hash_combine(&seed, label.first, label.second);
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,131 @@
|
||||
#include "prometheus/family.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "prometheus/check_names.h"
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/gauge.h"
|
||||
#include "prometheus/histogram.h"
|
||||
#include "prometheus/info.h"
|
||||
#include "prometheus/summary.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
template <typename T>
|
||||
Family<T>::Family(const std::string& name, const std::string& help,
|
||||
const Labels& constant_labels)
|
||||
: name_(name), help_(help), constant_labels_(constant_labels) {
|
||||
if (!CheckMetricName(name_)) {
|
||||
throw std::invalid_argument("Invalid metric name");
|
||||
}
|
||||
for (auto& label_pair : constant_labels_) {
|
||||
auto& label_name = label_pair.first;
|
||||
if (!CheckLabelName(label_name, T::metric_type)) {
|
||||
throw std::invalid_argument("Invalid label name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& Family<T>::Add(const Labels& labels, std::unique_ptr<T> object) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
auto insert_result =
|
||||
metrics_.insert(std::make_pair(labels, std::move(object)));
|
||||
|
||||
if (insert_result.second) {
|
||||
// insertion took place, retroactively check for unlikely issues
|
||||
for (auto& label_pair : labels) {
|
||||
const auto& label_name = label_pair.first;
|
||||
if (!CheckLabelName(label_name, T::metric_type)) {
|
||||
metrics_.erase(insert_result.first);
|
||||
throw std::invalid_argument("Invalid label name");
|
||||
}
|
||||
if (constant_labels_.count(label_name)) {
|
||||
metrics_.erase(insert_result.first);
|
||||
throw std::invalid_argument("Duplicate label name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& stored_object = insert_result.first->second;
|
||||
assert(stored_object);
|
||||
return *stored_object;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Family<T>::Remove(T* metric) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
for (auto it = metrics_.begin(); it != metrics_.end(); ++it) {
|
||||
if (it->second.get() == metric) {
|
||||
metrics_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Family<T>::Has(const Labels& labels) const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
return metrics_.count(labels) != 0u;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const std::string& Family<T>::GetName() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const Labels& Family<T>::GetConstantLabels() const {
|
||||
return constant_labels_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<MetricFamily> Family<T>::Collect() const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
if (metrics_.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto family = MetricFamily{};
|
||||
family.name = name_;
|
||||
family.help = help_;
|
||||
family.type = T::metric_type;
|
||||
family.metric.reserve(metrics_.size());
|
||||
for (const auto& m : metrics_) {
|
||||
family.metric.push_back(std::move(CollectMetric(m.first, m.second.get())));
|
||||
}
|
||||
return {family};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ClientMetric Family<T>::CollectMetric(const Labels& metric_labels,
|
||||
T* metric) const {
|
||||
auto collected = metric->Collect();
|
||||
collected.label.reserve(constant_labels_.size() + metric_labels.size());
|
||||
const auto add_label =
|
||||
[&collected](const std::pair<std::string, std::string>& label_pair) {
|
||||
auto label = ClientMetric::Label{};
|
||||
label.name = label_pair.first;
|
||||
label.value = label_pair.second;
|
||||
collected.label.push_back(std::move(label));
|
||||
};
|
||||
std::for_each(constant_labels_.cbegin(), constant_labels_.cend(), add_label);
|
||||
std::for_each(metric_labels.cbegin(), metric_labels.cend(), add_label);
|
||||
return collected;
|
||||
}
|
||||
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Family<Counter>;
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Family<Gauge>;
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Family<Histogram>;
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Family<Info>;
|
||||
template class PROMETHEUS_CPP_CORE_EXPORT Family<Summary>;
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "prometheus/gauge.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
Gauge::Gauge(const double value) : value_{value} {}
|
||||
|
||||
void Gauge::Increment() { Increment(1.0); }
|
||||
|
||||
void Gauge::Increment(const double value) { Change(value); }
|
||||
|
||||
void Gauge::Decrement() { Decrement(1.0); }
|
||||
|
||||
void Gauge::Decrement(const double value) { Change(-1.0 * value); }
|
||||
|
||||
void Gauge::Set(const double value) { value_.store(value); }
|
||||
|
||||
void Gauge::Change(const double value) {
|
||||
// C++ 20 will add std::atomic::fetch_add support for floating point types
|
||||
auto current = value_.load();
|
||||
while (!value_.compare_exchange_weak(current, current + value)) {
|
||||
// intentionally empty block
|
||||
}
|
||||
}
|
||||
|
||||
void Gauge::SetToCurrentTime() {
|
||||
const auto time = std::time(nullptr);
|
||||
Set(static_cast<double>(time));
|
||||
}
|
||||
|
||||
double Gauge::Value() const { return value_; }
|
||||
|
||||
ClientMetric Gauge::Collect() const {
|
||||
ClientMetric metric;
|
||||
metric.gauge.value = Value();
|
||||
return metric;
|
||||
}
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,89 @@
|
||||
#include "prometheus/histogram.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
namespace {
|
||||
|
||||
template <class ForwardIterator>
|
||||
bool is_strict_sorted(ForwardIterator first, ForwardIterator last) {
|
||||
return std::adjacent_find(first, last,
|
||||
std::greater_equal<typename std::iterator_traits<
|
||||
ForwardIterator>::value_type>()) == last;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Histogram::Histogram(const BucketBoundaries& buckets)
|
||||
: bucket_boundaries_{buckets}, bucket_counts_{buckets.size() + 1} {
|
||||
if (!is_strict_sorted(begin(bucket_boundaries_), end(bucket_boundaries_))) {
|
||||
throw std::invalid_argument("Bucket Boundaries must be strictly sorted");
|
||||
}
|
||||
}
|
||||
|
||||
Histogram::Histogram(BucketBoundaries&& buckets)
|
||||
: bucket_boundaries_{std::move(buckets)},
|
||||
bucket_counts_{bucket_boundaries_.size() + 1} {
|
||||
if (!is_strict_sorted(begin(bucket_boundaries_), end(bucket_boundaries_))) {
|
||||
throw std::invalid_argument("Bucket Boundaries must be strictly sorted");
|
||||
}
|
||||
}
|
||||
|
||||
void Histogram::Observe(const double value) {
|
||||
const auto bucket_index = static_cast<std::size_t>(
|
||||
std::distance(bucket_boundaries_.begin(),
|
||||
std::lower_bound(bucket_boundaries_.begin(),
|
||||
bucket_boundaries_.end(), value)));
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
sum_.Increment(value);
|
||||
bucket_counts_[bucket_index].Increment();
|
||||
}
|
||||
|
||||
void Histogram::ObserveMultiple(const std::vector<double>& bucket_increments,
|
||||
const double sum_of_values) {
|
||||
if (bucket_increments.size() != bucket_counts_.size()) {
|
||||
throw std::length_error(
|
||||
"The size of bucket_increments was not equal to"
|
||||
"the number of buckets in the histogram.");
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
sum_.Increment(sum_of_values);
|
||||
|
||||
for (std::size_t i{0}; i < bucket_counts_.size(); ++i) {
|
||||
bucket_counts_[i].Increment(bucket_increments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ClientMetric Histogram::Collect() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto metric = ClientMetric{};
|
||||
|
||||
auto cumulative_count = 0ULL;
|
||||
metric.histogram.bucket.reserve(bucket_counts_.size());
|
||||
for (std::size_t i{0}; i < bucket_counts_.size(); ++i) {
|
||||
cumulative_count += bucket_counts_[i].Value();
|
||||
auto bucket = ClientMetric::Bucket{};
|
||||
bucket.cumulative_count = cumulative_count;
|
||||
bucket.upper_bound = (i == bucket_boundaries_.size()
|
||||
? std::numeric_limits<double>::infinity()
|
||||
: bucket_boundaries_[i]);
|
||||
metric.histogram.bucket.push_back(std::move(bucket));
|
||||
}
|
||||
metric.histogram.sample_count = cumulative_count;
|
||||
metric.histogram.sample_sum = sum_.Value();
|
||||
|
||||
return metric;
|
||||
}
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "prometheus/info.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
ClientMetric Info::Collect() const {
|
||||
ClientMetric metric;
|
||||
metric.info.value = 1;
|
||||
return metric;
|
||||
}
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,203 @@
|
||||
#include "prometheus/registry.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/gauge.h"
|
||||
#include "prometheus/histogram.h"
|
||||
#include "prometheus/info.h"
|
||||
#include "prometheus/summary.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
void CollectAll(std::vector<MetricFamily>& results, const T& families) {
|
||||
for (auto&& collectable : families) {
|
||||
auto metrics = collectable->Collect();
|
||||
results.insert(results.end(), std::make_move_iterator(metrics.begin()),
|
||||
std::make_move_iterator(metrics.end()));
|
||||
}
|
||||
}
|
||||
|
||||
bool FamilyNameExists(const std::string& /* name */) { return false; }
|
||||
|
||||
template <typename T, typename... Args>
|
||||
bool FamilyNameExists(const std::string& name, const T& families,
|
||||
Args&&... args) {
|
||||
auto sameName = [&name](const typename T::value_type& entry) {
|
||||
return name == entry->GetName();
|
||||
};
|
||||
auto exists = std::find_if(std::begin(families), std::end(families),
|
||||
sameName) != std::end(families);
|
||||
return exists || FamilyNameExists(name, args...);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Registry::Registry(InsertBehavior insert_behavior)
|
||||
: insert_behavior_{insert_behavior} {}
|
||||
|
||||
Registry::~Registry() = default;
|
||||
|
||||
std::vector<MetricFamily> Registry::Collect() const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
auto results = std::vector<MetricFamily>{};
|
||||
|
||||
CollectAll(results, counters_);
|
||||
CollectAll(results, gauges_);
|
||||
CollectAll(results, histograms_);
|
||||
CollectAll(results, infos_);
|
||||
CollectAll(results, summaries_);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<std::unique_ptr<Family<Counter>>>& Registry::GetFamilies() {
|
||||
return counters_;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<std::unique_ptr<Family<Gauge>>>& Registry::GetFamilies() {
|
||||
return gauges_;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<std::unique_ptr<Family<Histogram>>>& Registry::GetFamilies() {
|
||||
return histograms_;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<std::unique_ptr<Family<Info>>>& Registry::GetFamilies() {
|
||||
return infos_;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<std::unique_ptr<Family<Summary>>>& Registry::GetFamilies() {
|
||||
return summaries_;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Registry::NameExistsInOtherType<Counter>(const std::string& name) const {
|
||||
return FamilyNameExists(name, gauges_, histograms_, infos_, summaries_);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Registry::NameExistsInOtherType<Gauge>(const std::string& name) const {
|
||||
return FamilyNameExists(name, counters_, histograms_, infos_, summaries_);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Registry::NameExistsInOtherType<Histogram>(const std::string& name) const {
|
||||
return FamilyNameExists(name, counters_, gauges_, infos_, summaries_);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Registry::NameExistsInOtherType<Info>(const std::string& name) const {
|
||||
return FamilyNameExists(name, counters_, gauges_, histograms_, summaries_);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Registry::NameExistsInOtherType<Summary>(const std::string& name) const {
|
||||
return FamilyNameExists(name, counters_, gauges_, histograms_, infos_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Family<T>& Registry::Add(const std::string& name, const std::string& help,
|
||||
const Labels& labels) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
if (NameExistsInOtherType<T>(name)) {
|
||||
throw std::invalid_argument(
|
||||
"Family name already exists with different type");
|
||||
}
|
||||
|
||||
auto& families = GetFamilies<T>();
|
||||
|
||||
if (insert_behavior_ == InsertBehavior::Merge) {
|
||||
auto same_name_and_labels =
|
||||
[&name, &labels](const std::unique_ptr<Family<T>>& family) {
|
||||
return std::tie(name, labels) ==
|
||||
std::tie(family->GetName(), family->GetConstantLabels());
|
||||
};
|
||||
|
||||
auto it =
|
||||
std::find_if(families.begin(), families.end(), same_name_and_labels);
|
||||
if (it != families.end()) {
|
||||
return **it;
|
||||
}
|
||||
}
|
||||
|
||||
auto same_name = [&name](const std::unique_ptr<Family<T>>& family) {
|
||||
return name == family->GetName();
|
||||
};
|
||||
|
||||
auto it = std::find_if(families.begin(), families.end(), same_name);
|
||||
if (it != families.end()) {
|
||||
throw std::invalid_argument("Family name already exists");
|
||||
}
|
||||
|
||||
auto family = std::make_unique<Family<T>>(name, help, labels);
|
||||
auto& ref = *family;
|
||||
families.push_back(std::move(family));
|
||||
return ref;
|
||||
}
|
||||
|
||||
template Family<Counter>& Registry::Add(const std::string& name,
|
||||
const std::string& help,
|
||||
const Labels& labels);
|
||||
|
||||
template Family<Gauge>& Registry::Add(const std::string& name,
|
||||
const std::string& help,
|
||||
const Labels& labels);
|
||||
|
||||
template Family<Info>& Registry::Add(const std::string& name,
|
||||
const std::string& help,
|
||||
const Labels& labels);
|
||||
|
||||
template Family<Summary>& Registry::Add(const std::string& name,
|
||||
const std::string& help,
|
||||
const Labels& labels);
|
||||
|
||||
template Family<Histogram>& Registry::Add(const std::string& name,
|
||||
const std::string& help,
|
||||
const Labels& labels);
|
||||
|
||||
template <typename T>
|
||||
bool Registry::Remove(const Family<T>& family) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
auto& families = GetFamilies<T>();
|
||||
auto same_family = [&family](const std::unique_ptr<Family<T>>& in) {
|
||||
return &family == in.get();
|
||||
};
|
||||
|
||||
auto it = std::find_if(families.begin(), families.end(), same_family);
|
||||
if (it == families.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
families.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool PROMETHEUS_CPP_CORE_EXPORT
|
||||
Registry::Remove(const Family<Counter>& family);
|
||||
|
||||
template bool PROMETHEUS_CPP_CORE_EXPORT
|
||||
Registry::Remove(const Family<Gauge>& family);
|
||||
|
||||
template bool PROMETHEUS_CPP_CORE_EXPORT
|
||||
Registry::Remove(const Family<Summary>& family);
|
||||
|
||||
template bool PROMETHEUS_CPP_CORE_EXPORT
|
||||
Registry::Remove(const Family<Histogram>& family);
|
||||
|
||||
template bool PROMETHEUS_CPP_CORE_EXPORT
|
||||
Registry::Remove(const Family<Info>& family);
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "prometheus/serializer.h"
|
||||
|
||||
#include <sstream> // IWYU pragma: keep
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
std::string Serializer::Serialize(
|
||||
const std::vector<MetricFamily>& metrics) const {
|
||||
std::ostringstream ss;
|
||||
Serialize(ss, metrics);
|
||||
return ss.str();
|
||||
}
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "prometheus/summary.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
Summary::Summary(const Quantiles& quantiles,
|
||||
const std::chrono::milliseconds max_age, const int age_buckets)
|
||||
: quantiles_{quantiles},
|
||||
quantile_values_{quantiles_, max_age, age_buckets} {}
|
||||
|
||||
Summary::Summary(Quantiles&& quantiles, const std::chrono::milliseconds max_age,
|
||||
const int age_buckets)
|
||||
: quantiles_{std::move(quantiles)},
|
||||
quantile_values_{quantiles_, max_age, age_buckets} {}
|
||||
|
||||
void Summary::Observe(const double value) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
count_ += 1;
|
||||
sum_ += value;
|
||||
quantile_values_.insert(value);
|
||||
}
|
||||
|
||||
ClientMetric Summary::Collect() const {
|
||||
auto metric = ClientMetric{};
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
metric.summary.quantile.reserve(quantiles_.size());
|
||||
for (const auto& quantile : quantiles_) {
|
||||
auto metricQuantile = ClientMetric::Quantile{};
|
||||
metricQuantile.quantile = quantile.quantile;
|
||||
metricQuantile.value = quantile_values_.get(quantile.quantile);
|
||||
metric.summary.quantile.push_back(std::move(metricQuantile));
|
||||
}
|
||||
metric.summary.sample_count = count_;
|
||||
metric.summary.sample_sum = sum_;
|
||||
|
||||
return metric;
|
||||
}
|
||||
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,219 @@
|
||||
#include "prometheus/text_serializer.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/metric_family.h"
|
||||
#include "prometheus/metric_type.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
namespace {
|
||||
|
||||
// Write a double as a string, with proper formatting for infinity and NaN
|
||||
void WriteValue(std::ostream& out, double value) {
|
||||
if (std::isnan(value)) {
|
||||
out << "Nan";
|
||||
} else if (std::isinf(value)) {
|
||||
out << (value < 0 ? "-Inf" : "+Inf");
|
||||
} else {
|
||||
out << value;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteValue(std::ostream& out, const std::string& value) {
|
||||
for (auto c : value) {
|
||||
switch (c) {
|
||||
case '\n':
|
||||
out << '\\' << 'n';
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
out << '\\' << c;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
out << '\\' << c;
|
||||
break;
|
||||
|
||||
default:
|
||||
out << c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write a line header: metric name and labels
|
||||
template <typename T = std::string>
|
||||
void WriteHead(std::ostream& out, const MetricFamily& family,
|
||||
const ClientMetric& metric, const std::string& suffix = "",
|
||||
const std::string& extraLabelName = "",
|
||||
const T& extraLabelValue = T()) {
|
||||
out << family.name << suffix;
|
||||
if (!metric.label.empty() || !extraLabelName.empty()) {
|
||||
out << "{";
|
||||
const char* prefix = "";
|
||||
|
||||
for (auto& lp : metric.label) {
|
||||
out << prefix << lp.name << "=\"";
|
||||
WriteValue(out, lp.value);
|
||||
out << "\"";
|
||||
prefix = ",";
|
||||
}
|
||||
if (!extraLabelName.empty()) {
|
||||
out << prefix << extraLabelName << "=\"";
|
||||
WriteValue(out, extraLabelValue);
|
||||
out << "\"";
|
||||
}
|
||||
out << "}";
|
||||
}
|
||||
out << " ";
|
||||
}
|
||||
|
||||
// Write a line trailer: timestamp
|
||||
void WriteTail(std::ostream& out, const ClientMetric& metric) {
|
||||
if (metric.timestamp_ms != 0) {
|
||||
out << " " << metric.timestamp_ms;
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
void SerializeCounter(std::ostream& out, const MetricFamily& family,
|
||||
const ClientMetric& metric) {
|
||||
WriteHead(out, family, metric);
|
||||
WriteValue(out, metric.counter.value);
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
|
||||
void SerializeGauge(std::ostream& out, const MetricFamily& family,
|
||||
const ClientMetric& metric) {
|
||||
WriteHead(out, family, metric);
|
||||
WriteValue(out, metric.gauge.value);
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
|
||||
void SerializeInfo(std::ostream& out, const MetricFamily& family,
|
||||
const ClientMetric& metric) {
|
||||
WriteHead(out, family, metric, "_info");
|
||||
WriteValue(out, metric.info.value);
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
|
||||
void SerializeSummary(std::ostream& out, const MetricFamily& family,
|
||||
const ClientMetric& metric) {
|
||||
auto& sum = metric.summary;
|
||||
WriteHead(out, family, metric, "_count");
|
||||
out << sum.sample_count;
|
||||
WriteTail(out, metric);
|
||||
|
||||
WriteHead(out, family, metric, "_sum");
|
||||
WriteValue(out, sum.sample_sum);
|
||||
WriteTail(out, metric);
|
||||
|
||||
for (auto& q : sum.quantile) {
|
||||
WriteHead(out, family, metric, "", "quantile", q.quantile);
|
||||
WriteValue(out, q.value);
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeUntyped(std::ostream& out, const MetricFamily& family,
|
||||
const ClientMetric& metric) {
|
||||
WriteHead(out, family, metric);
|
||||
WriteValue(out, metric.untyped.value);
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
|
||||
void SerializeHistogram(std::ostream& out, const MetricFamily& family,
|
||||
const ClientMetric& metric) {
|
||||
auto& hist = metric.histogram;
|
||||
WriteHead(out, family, metric, "_count");
|
||||
out << hist.sample_count;
|
||||
WriteTail(out, metric);
|
||||
|
||||
WriteHead(out, family, metric, "_sum");
|
||||
WriteValue(out, hist.sample_sum);
|
||||
WriteTail(out, metric);
|
||||
|
||||
double last = -std::numeric_limits<double>::infinity();
|
||||
for (auto& b : hist.bucket) {
|
||||
WriteHead(out, family, metric, "_bucket", "le", b.upper_bound);
|
||||
last = b.upper_bound;
|
||||
out << b.cumulative_count;
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
|
||||
if (last != std::numeric_limits<double>::infinity()) {
|
||||
WriteHead(out, family, metric, "_bucket", "le", "+Inf");
|
||||
out << hist.sample_count;
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeFamily(std::ostream& out, const MetricFamily& family) {
|
||||
if (!family.help.empty()) {
|
||||
out << "# HELP " << family.name << " " << family.help << "\n";
|
||||
}
|
||||
switch (family.type) {
|
||||
case MetricType::Counter:
|
||||
out << "# TYPE " << family.name << " counter\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeCounter(out, family, metric);
|
||||
}
|
||||
break;
|
||||
case MetricType::Gauge:
|
||||
out << "# TYPE " << family.name << " gauge\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeGauge(out, family, metric);
|
||||
}
|
||||
break;
|
||||
// info is not handled by prometheus, we use gauge as workaround
|
||||
// (https://github.com/OpenObservability/OpenMetrics/blob/98ae26c87b1c3bcf937909a880b32c8be643cc9b/specification/OpenMetrics.md#info-1)
|
||||
case MetricType::Info:
|
||||
out << "# TYPE " << family.name << " gauge\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeInfo(out, family, metric);
|
||||
}
|
||||
break;
|
||||
case MetricType::Summary:
|
||||
out << "# TYPE " << family.name << " summary\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeSummary(out, family, metric);
|
||||
}
|
||||
break;
|
||||
case MetricType::Untyped:
|
||||
out << "# TYPE " << family.name << " untyped\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeUntyped(out, family, metric);
|
||||
}
|
||||
break;
|
||||
case MetricType::Histogram:
|
||||
out << "# TYPE " << family.name << " histogram\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeHistogram(out, family, metric);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void TextSerializer::Serialize(std::ostream& out,
|
||||
const std::vector<MetricFamily>& metrics) const {
|
||||
auto saved_locale = out.getloc();
|
||||
auto saved_precision = out.precision();
|
||||
|
||||
out.imbue(std::locale::classic());
|
||||
out.precision(std::numeric_limits<double>::max_digits10 - 1);
|
||||
|
||||
for (auto& family : metrics) {
|
||||
SerializeFamily(out, family);
|
||||
}
|
||||
|
||||
out.imbue(saved_locale);
|
||||
out.precision(saved_precision);
|
||||
}
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,13 @@
|
||||
cc_test(
|
||||
name = "unit",
|
||||
srcs = glob([
|
||||
"*.cc",
|
||||
"*.h",
|
||||
]),
|
||||
copts = ["-Iexternal/googletest/include"],
|
||||
linkstatic = True,
|
||||
deps = [
|
||||
"//core",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
add_executable(prometheus_core_test
|
||||
builder_test.cc
|
||||
check_label_name_test.cc
|
||||
check_metric_name_test.cc
|
||||
counter_test.cc
|
||||
family_test.cc
|
||||
gauge_test.cc
|
||||
histogram_test.cc
|
||||
registry_test.cc
|
||||
serializer_test.cc
|
||||
summary_test.cc
|
||||
text_serializer_test.cc
|
||||
utils_test.cc
|
||||
)
|
||||
|
||||
target_link_libraries(prometheus_core_test
|
||||
PRIVATE
|
||||
${PROJECT_NAME}::core
|
||||
GTest::gmock_main
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME prometheus_core_test
|
||||
COMMAND prometheus_core_test
|
||||
)
|
||||
@@ -0,0 +1,116 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/gauge.h"
|
||||
#include "prometheus/histogram.h"
|
||||
#include "prometheus/info.h"
|
||||
#include "prometheus/labels.h"
|
||||
#include "prometheus/registry.h"
|
||||
#include "prometheus/summary.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
class BuilderTest : public testing::Test {
|
||||
protected:
|
||||
std::vector<ClientMetric::Label> getExpectedLabels() {
|
||||
std::vector<ClientMetric::Label> labels;
|
||||
|
||||
auto gen = [](const std::pair<const std::string, std::string>& p) {
|
||||
return ClientMetric::Label{p.first, p.second};
|
||||
};
|
||||
|
||||
std::transform(std::begin(const_labels), std::end(const_labels),
|
||||
std::back_inserter(labels), gen);
|
||||
std::transform(std::begin(more_labels), std::end(more_labels),
|
||||
std::back_inserter(labels), gen);
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
void verifyCollectedLabels() {
|
||||
const auto collected = registry.Collect();
|
||||
|
||||
ASSERT_EQ(1U, collected.size());
|
||||
EXPECT_EQ(name, collected.at(0).name);
|
||||
EXPECT_EQ(help, collected.at(0).help);
|
||||
ASSERT_EQ(1U, collected.at(0).metric.size());
|
||||
|
||||
EXPECT_THAT(collected.at(0).metric.at(0).label,
|
||||
testing::UnorderedElementsAreArray(expected_labels));
|
||||
}
|
||||
|
||||
Registry registry;
|
||||
|
||||
const std::string name = "some_name";
|
||||
const std::string help = "Additional description.";
|
||||
const Labels const_labels = {{"key", "value"}};
|
||||
const Labels more_labels = {{"name", "test"}};
|
||||
const std::vector<ClientMetric::Label> expected_labels = getExpectedLabels();
|
||||
};
|
||||
|
||||
TEST_F(BuilderTest, build_counter) {
|
||||
auto& family = BuildCounter()
|
||||
.Name(name)
|
||||
.Help(help)
|
||||
.Labels(const_labels)
|
||||
.Register(registry);
|
||||
family.Add(more_labels);
|
||||
|
||||
verifyCollectedLabels();
|
||||
}
|
||||
|
||||
TEST_F(BuilderTest, build_gauge) {
|
||||
auto& family = BuildGauge()
|
||||
.Name(name)
|
||||
.Help(help)
|
||||
.Labels(const_labels)
|
||||
.Register(registry);
|
||||
family.Add(more_labels);
|
||||
|
||||
verifyCollectedLabels();
|
||||
}
|
||||
|
||||
TEST_F(BuilderTest, build_histogram) {
|
||||
auto& family = BuildHistogram()
|
||||
.Name(name)
|
||||
.Help(help)
|
||||
.Labels(const_labels)
|
||||
.Register(registry);
|
||||
family.Add(more_labels, Histogram::BucketBoundaries{1, 2});
|
||||
|
||||
verifyCollectedLabels();
|
||||
}
|
||||
|
||||
TEST_F(BuilderTest, build_info) {
|
||||
auto& family =
|
||||
BuildInfo().Name(name).Help(help).Labels(const_labels).Register(registry);
|
||||
family.Add(more_labels);
|
||||
|
||||
verifyCollectedLabels();
|
||||
}
|
||||
|
||||
TEST_F(BuilderTest, build_summary) {
|
||||
auto& family = BuildSummary()
|
||||
.Name(name)
|
||||
.Help(help)
|
||||
.Labels(const_labels)
|
||||
.Register(registry);
|
||||
family.Add(more_labels, Summary::Quantiles{});
|
||||
|
||||
verifyCollectedLabels();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,50 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "prometheus/check_names.h"
|
||||
#include "prometheus/metric_type.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
class CheckLabelNameTest : public testing::TestWithParam<MetricType> {
|
||||
protected:
|
||||
bool CheckLabelName(const std::string& name) {
|
||||
return ::prometheus::CheckLabelName(name, GetParam());
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(CheckLabelNameTest, empty_label_name) {
|
||||
EXPECT_FALSE(CheckLabelName(""));
|
||||
}
|
||||
TEST_P(CheckLabelNameTest, invalid_label_name) {
|
||||
EXPECT_FALSE(CheckLabelName("log-level"));
|
||||
}
|
||||
TEST_P(CheckLabelNameTest, leading_invalid_label_name) {
|
||||
EXPECT_FALSE(CheckLabelName("-abcd"));
|
||||
}
|
||||
TEST_P(CheckLabelNameTest, trailing_invalid_label_name) {
|
||||
EXPECT_FALSE(CheckLabelName("abcd-"));
|
||||
}
|
||||
TEST_P(CheckLabelNameTest, good_label_name) {
|
||||
EXPECT_TRUE(CheckLabelName("type"));
|
||||
}
|
||||
TEST_P(CheckLabelNameTest, reserved_label_name) {
|
||||
EXPECT_FALSE(CheckLabelName("__some_reserved_label"));
|
||||
}
|
||||
TEST_P(CheckLabelNameTest, reject_le_for_histogram) {
|
||||
EXPECT_EQ(GetParam() != MetricType::Histogram, CheckLabelName("le"));
|
||||
}
|
||||
TEST_P(CheckLabelNameTest, reject_quantile_for_histogram) {
|
||||
EXPECT_EQ(GetParam() != MetricType::Summary, CheckLabelName("quantile"));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(AllMetricTypes, CheckLabelNameTest,
|
||||
testing::Values(MetricType::Counter, MetricType::Gauge,
|
||||
MetricType::Histogram,
|
||||
MetricType::Info, MetricType::Summary,
|
||||
MetricType::Untyped));
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "prometheus/check_names.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
TEST(CheckMetricNameTest, empty_metric_name) {
|
||||
EXPECT_FALSE(CheckMetricName(""));
|
||||
}
|
||||
TEST(CheckMetricNameTest, good_metric_name) {
|
||||
EXPECT_TRUE(CheckMetricName("prometheus_notifications_total"));
|
||||
}
|
||||
TEST(CheckMetricNameTest, reserved_metric_name) {
|
||||
EXPECT_FALSE(CheckMetricName("__some_reserved_metric"));
|
||||
}
|
||||
TEST(CheckMetricNameTest, malformed_metric_name) {
|
||||
EXPECT_FALSE(CheckMetricName("fa mi ly with space in name or |"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "prometheus/counter.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
TEST(CounterTest, initialize_with_zero) {
|
||||
Counter counter;
|
||||
EXPECT_EQ(counter.Value(), 0);
|
||||
}
|
||||
|
||||
TEST(CounterTest, inc) {
|
||||
Counter counter;
|
||||
counter.Increment();
|
||||
EXPECT_EQ(counter.Value(), 1.0);
|
||||
}
|
||||
|
||||
TEST(CounterTest, inc_number) {
|
||||
Counter counter;
|
||||
counter.Increment(4);
|
||||
EXPECT_EQ(counter.Value(), 4.0);
|
||||
}
|
||||
|
||||
TEST(CounterTest, inc_multiple) {
|
||||
Counter counter;
|
||||
counter.Increment();
|
||||
counter.Increment();
|
||||
counter.Increment(5);
|
||||
EXPECT_EQ(counter.Value(), 7.0);
|
||||
}
|
||||
|
||||
TEST(CounterTest, inc_negative_value) {
|
||||
Counter counter;
|
||||
counter.Increment(5.0);
|
||||
counter.Increment(-5.0);
|
||||
EXPECT_EQ(counter.Value(), 5.0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,143 @@
|
||||
#include "prometheus/family.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/histogram.h"
|
||||
#include "prometheus/labels.h"
|
||||
#include "prometheus/summary.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
TEST(FamilyTest, labels) {
|
||||
auto const_label = ClientMetric::Label{"component", "test"};
|
||||
auto dynamic_label = ClientMetric::Label{"status", "200"};
|
||||
|
||||
Family<Counter> family{"total_requests",
|
||||
"Counts all requests",
|
||||
{{const_label.name, const_label.value}}};
|
||||
family.Add({{dynamic_label.name, dynamic_label.value}});
|
||||
auto collected = family.Collect();
|
||||
ASSERT_GE(collected.size(), 1U);
|
||||
ASSERT_GE(collected.at(0).metric.size(), 1U);
|
||||
EXPECT_THAT(collected.at(0).metric.at(0).label,
|
||||
::testing::ElementsAre(const_label, dynamic_label));
|
||||
}
|
||||
|
||||
TEST(FamilyTest, reject_same_label_keys) {
|
||||
auto labels = Labels{{"component", "test"}};
|
||||
|
||||
Family<Counter> family{"total_requests", "Counts all requests", labels};
|
||||
EXPECT_ANY_THROW(family.Add(labels));
|
||||
}
|
||||
|
||||
TEST(FamilyTest, counter_value) {
|
||||
Family<Counter> family{"total_requests", "Counts all requests", {}};
|
||||
auto& counter = family.Add({});
|
||||
counter.Increment();
|
||||
auto collected = family.Collect();
|
||||
ASSERT_GE(collected.size(), 1U);
|
||||
ASSERT_GE(collected[0].metric.size(), 1U);
|
||||
EXPECT_EQ(1, collected[0].metric.at(0).counter.value);
|
||||
}
|
||||
|
||||
TEST(FamilyTest, remove) {
|
||||
Family<Counter> family{"total_requests", "Counts all requests", {}};
|
||||
auto& counter1 = family.Add({{"name", "counter1"}});
|
||||
family.Add({{"name", "counter2"}});
|
||||
family.Remove(&counter1);
|
||||
auto collected = family.Collect();
|
||||
ASSERT_GE(collected.size(), 1U);
|
||||
EXPECT_EQ(collected[0].metric.size(), 1U);
|
||||
}
|
||||
|
||||
TEST(FamilyTest, removeUnknownMetricMustNotCrash) {
|
||||
Family<Counter> family{"total_requests", "Counts all requests", {}};
|
||||
family.Remove(nullptr);
|
||||
}
|
||||
|
||||
TEST(FamilyTest, Histogram) {
|
||||
Family<Histogram> family{"request_latency", "Latency Histogram", {}};
|
||||
auto& histogram1 = family.Add({{"name", "histogram1"}},
|
||||
Histogram::BucketBoundaries{0, 1, 2});
|
||||
histogram1.Observe(0);
|
||||
auto collected = family.Collect();
|
||||
ASSERT_EQ(collected.size(), 1U);
|
||||
ASSERT_GE(collected[0].metric.size(), 1U);
|
||||
EXPECT_EQ(1U, collected[0].metric.at(0).histogram.sample_count);
|
||||
}
|
||||
|
||||
TEST(FamilyTest, add_twice) {
|
||||
Family<Counter> family{"total_requests", "Counts all requests", {}};
|
||||
auto& counter = family.Add({{"name", "counter1"}});
|
||||
auto& counter1 = family.Add({{"name", "counter1"}});
|
||||
ASSERT_EQ(&counter, &counter1);
|
||||
}
|
||||
|
||||
TEST(FamilyTest, throw_on_invalid_metric_name) {
|
||||
auto create_family_with_invalid_name = []() {
|
||||
return std::make_unique<Family<Counter>>("", "empty name", Labels{});
|
||||
};
|
||||
EXPECT_ANY_THROW(create_family_with_invalid_name());
|
||||
}
|
||||
|
||||
TEST(FamilyTest, throw_on_invalid_constant_label_name) {
|
||||
auto create_family_with_invalid_labels = []() {
|
||||
return std::make_unique<Family<Counter>>("total_requests",
|
||||
"Counts all requests",
|
||||
Labels{{"__invalid", "counter1"}});
|
||||
};
|
||||
EXPECT_ANY_THROW(create_family_with_invalid_labels());
|
||||
}
|
||||
|
||||
TEST(FamilyTest, should_throw_on_invalid_labels) {
|
||||
Family<Counter> family{"total_requests", "Counts all requests", {}};
|
||||
auto add_metric_with_invalid_label_name = [&family]() {
|
||||
family.Add({{"__invalid", "counter1"}});
|
||||
};
|
||||
EXPECT_ANY_THROW(add_metric_with_invalid_label_name());
|
||||
}
|
||||
|
||||
TEST(FamilyTest, should_not_collect_empty_metrics) {
|
||||
Family<Counter> family{"total_requests", "Counts all requests", {}};
|
||||
auto collected = family.Collect();
|
||||
EXPECT_TRUE(collected.empty());
|
||||
}
|
||||
|
||||
TEST(FamilyTest, query_family_if_metric_already_exists) {
|
||||
Family<Counter> family{"total_requests", "Counts all requests", {}};
|
||||
family.Add({{"name", "counter1"}});
|
||||
EXPECT_TRUE(family.Has({{"name", "counter1"}}));
|
||||
EXPECT_FALSE(family.Has({{"name", "counter2"}}));
|
||||
}
|
||||
|
||||
TEST(FamilyTest, reject_histogram_with_constant_le_label) {
|
||||
auto labels = Labels{{"le", "test"}};
|
||||
EXPECT_ANY_THROW(std::make_unique<Family<Histogram>>("name", "help", labels));
|
||||
}
|
||||
|
||||
TEST(FamilyTest, reject_histogram_with_le_label) {
|
||||
Family<Histogram> family{"name", "help", {}};
|
||||
auto labels = Labels{{"le", "test"}};
|
||||
EXPECT_ANY_THROW(family.Add(labels, Histogram::BucketBoundaries{0, 1, 2}));
|
||||
}
|
||||
|
||||
TEST(FamilyTest, reject_summary_with_constant_quantile_label) {
|
||||
auto labels = Labels{{"quantile", "test"}};
|
||||
EXPECT_ANY_THROW(std::make_unique<Family<Summary>>("name", "help", labels));
|
||||
}
|
||||
|
||||
TEST(FamilyTest, reject_summary_with_quantile_label) {
|
||||
Family<Summary> family{"name", "help", {}};
|
||||
auto labels = Labels{{"quantile", "test"}};
|
||||
auto quantiles = Summary::Quantiles{{0.5, 0.05}};
|
||||
EXPECT_ANY_THROW(family.Add(labels, quantiles));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "prometheus/gauge.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
TEST(GaugeTest, initialize_with_zero) {
|
||||
Gauge gauge;
|
||||
EXPECT_EQ(gauge.Value(), 0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, inc) {
|
||||
Gauge gauge;
|
||||
gauge.Increment();
|
||||
EXPECT_EQ(gauge.Value(), 1.0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, inc_number) {
|
||||
Gauge gauge;
|
||||
gauge.Increment(4);
|
||||
EXPECT_EQ(gauge.Value(), 4.0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, inc_multiple) {
|
||||
Gauge gauge;
|
||||
gauge.Increment();
|
||||
gauge.Increment();
|
||||
gauge.Increment(5);
|
||||
EXPECT_EQ(gauge.Value(), 7.0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, inc_negative_value) {
|
||||
Gauge gauge;
|
||||
gauge.Increment(-1.0);
|
||||
EXPECT_EQ(gauge.Value(), -1.0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, dec) {
|
||||
Gauge gauge;
|
||||
gauge.Set(5.0);
|
||||
gauge.Decrement();
|
||||
EXPECT_EQ(gauge.Value(), 4.0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, dec_negative_value) {
|
||||
Gauge gauge;
|
||||
gauge.Decrement(-1.0);
|
||||
EXPECT_EQ(gauge.Value(), 1.0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, dec_number) {
|
||||
Gauge gauge;
|
||||
gauge.Set(5.0);
|
||||
gauge.Decrement(3.0);
|
||||
EXPECT_EQ(gauge.Value(), 2.0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, set) {
|
||||
Gauge gauge;
|
||||
gauge.Set(3.0);
|
||||
EXPECT_EQ(gauge.Value(), 3.0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, set_multiple) {
|
||||
Gauge gauge;
|
||||
gauge.Set(3.0);
|
||||
gauge.Set(8.0);
|
||||
gauge.Set(1.0);
|
||||
EXPECT_EQ(gauge.Value(), 1.0);
|
||||
}
|
||||
|
||||
TEST(GaugeTest, set_to_current_time) {
|
||||
Gauge gauge;
|
||||
gauge.SetToCurrentTime();
|
||||
EXPECT_GT(gauge.Value(), 0.0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,130 @@
|
||||
#include "prometheus/histogram.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
TEST(HistogramTest, initialize_with_zero) {
|
||||
Histogram histogram{{}};
|
||||
auto metric = histogram.Collect();
|
||||
auto h = metric.histogram;
|
||||
EXPECT_EQ(h.sample_count, 0U);
|
||||
EXPECT_EQ(h.sample_sum, 0);
|
||||
}
|
||||
|
||||
TEST(HistogramTest, sample_count) {
|
||||
Histogram histogram{{1}};
|
||||
histogram.Observe(0);
|
||||
histogram.Observe(200);
|
||||
auto metric = histogram.Collect();
|
||||
auto h = metric.histogram;
|
||||
EXPECT_EQ(h.sample_count, 2U);
|
||||
}
|
||||
|
||||
TEST(HistogramTest, sample_sum) {
|
||||
Histogram histogram{{1}};
|
||||
histogram.Observe(0);
|
||||
histogram.Observe(1);
|
||||
histogram.Observe(101);
|
||||
auto metric = histogram.Collect();
|
||||
auto h = metric.histogram;
|
||||
EXPECT_EQ(h.sample_sum, 102);
|
||||
}
|
||||
|
||||
TEST(HistogramTest, bucket_size) {
|
||||
Histogram histogram{{1, 2}};
|
||||
auto metric = histogram.Collect();
|
||||
auto h = metric.histogram;
|
||||
EXPECT_EQ(h.bucket.size(), 3U);
|
||||
}
|
||||
|
||||
TEST(HistogramTest, bucket_bounds) {
|
||||
Histogram histogram{{1, 2}};
|
||||
auto metric = histogram.Collect();
|
||||
auto h = metric.histogram;
|
||||
EXPECT_EQ(h.bucket.at(0).upper_bound, 1);
|
||||
EXPECT_EQ(h.bucket.at(1).upper_bound, 2);
|
||||
EXPECT_EQ(h.bucket.at(2).upper_bound,
|
||||
std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
TEST(HistogramTest, reject_unsorted_bucket_bounds) {
|
||||
EXPECT_ANY_THROW(Histogram({2, 1}));
|
||||
}
|
||||
|
||||
TEST(HistogramTest, reject_non_incrementing_bucket_bounds) {
|
||||
EXPECT_ANY_THROW(Histogram({1, 2, 2, 3}));
|
||||
}
|
||||
|
||||
TEST(HistogramTest, bucket_counts_not_reset_by_collection) {
|
||||
Histogram histogram{{1, 2}};
|
||||
histogram.Observe(1.5);
|
||||
histogram.Collect();
|
||||
histogram.Observe(1.5);
|
||||
auto metric = histogram.Collect();
|
||||
auto h = metric.histogram;
|
||||
ASSERT_EQ(h.bucket.size(), 3U);
|
||||
EXPECT_EQ(h.bucket.at(1).cumulative_count, 2U);
|
||||
}
|
||||
|
||||
TEST(HistogramTest, cumulative_bucket_count) {
|
||||
Histogram histogram{{1, 2}};
|
||||
histogram.Observe(0);
|
||||
histogram.Observe(0.5);
|
||||
histogram.Observe(1);
|
||||
histogram.Observe(1.5);
|
||||
histogram.Observe(1.5);
|
||||
histogram.Observe(2);
|
||||
histogram.Observe(3);
|
||||
auto metric = histogram.Collect();
|
||||
auto h = metric.histogram;
|
||||
ASSERT_EQ(h.bucket.size(), 3U);
|
||||
EXPECT_EQ(h.bucket.at(0).cumulative_count, 3U);
|
||||
EXPECT_EQ(h.bucket.at(1).cumulative_count, 6U);
|
||||
EXPECT_EQ(h.bucket.at(2).cumulative_count, 7U);
|
||||
}
|
||||
|
||||
TEST(HistogramTest, observe_multiple_test_bucket_counts) {
|
||||
Histogram histogram{{1, 2}};
|
||||
histogram.ObserveMultiple({5, 9, 3}, 20);
|
||||
histogram.ObserveMultiple({0, 20, 6}, 34);
|
||||
auto metric = histogram.Collect();
|
||||
auto h = metric.histogram;
|
||||
ASSERT_EQ(h.bucket.size(), 3U);
|
||||
EXPECT_EQ(h.bucket.at(0).cumulative_count, 5U);
|
||||
EXPECT_EQ(h.bucket.at(1).cumulative_count, 34U);
|
||||
EXPECT_EQ(h.bucket.at(2).cumulative_count, 43U);
|
||||
}
|
||||
|
||||
TEST(HistogramTest, observe_multiple_test_total_sum) {
|
||||
Histogram histogram{{1, 2}};
|
||||
histogram.ObserveMultiple({5, 9, 3}, 20);
|
||||
histogram.ObserveMultiple({0, 20, 6}, 34);
|
||||
auto metric = histogram.Collect();
|
||||
auto h = metric.histogram;
|
||||
EXPECT_EQ(h.sample_count, 43U);
|
||||
EXPECT_EQ(h.sample_sum, 54);
|
||||
}
|
||||
|
||||
TEST(HistogramTest, observe_multiple_test_length_error) {
|
||||
Histogram histogram{{1, 2}};
|
||||
// 2 bucket boundaries means there are 3 buckets, so giving just 2 bucket
|
||||
// increments should result in a length_error.
|
||||
ASSERT_THROW(histogram.ObserveMultiple({5, 9}, 20), std::length_error);
|
||||
}
|
||||
|
||||
TEST(HistogramTest, sum_can_go_down) {
|
||||
Histogram histogram{{1}};
|
||||
auto metric1 = histogram.Collect();
|
||||
histogram.Observe(-10);
|
||||
auto metric2 = histogram.Collect();
|
||||
EXPECT_LT(metric2.histogram.sample_sum, metric1.histogram.sample_sum);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <locale>
|
||||
|
||||
class RAIILocale {
|
||||
public:
|
||||
explicit RAIILocale(const char* name) {
|
||||
std::locale::global(std::locale(name));
|
||||
}
|
||||
|
||||
~RAIILocale() { std::locale::global(savedLocale_); }
|
||||
|
||||
RAIILocale(const RAIILocale&) = delete;
|
||||
RAIILocale(RAIILocale&&) = delete;
|
||||
RAIILocale& operator=(const RAIILocale&) = delete;
|
||||
RAIILocale& operator=(RAIILocale&&) = delete;
|
||||
|
||||
private:
|
||||
const std::locale savedLocale_ = std::locale{};
|
||||
};
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "prometheus/registry.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/gauge.h"
|
||||
#include "prometheus/histogram.h"
|
||||
#include "prometheus/info.h"
|
||||
#include "prometheus/summary.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
TEST(RegistryTest, collect_single_metric_family) {
|
||||
Registry registry{};
|
||||
auto& counter_family =
|
||||
BuildCounter().Name("test").Help("a test").Register(registry);
|
||||
counter_family.Add({{"name", "counter1"}});
|
||||
counter_family.Add({{"name", "counter2"}});
|
||||
auto collected = registry.Collect();
|
||||
ASSERT_EQ(collected.size(), 1U);
|
||||
EXPECT_EQ(collected[0].name, "test");
|
||||
EXPECT_EQ(collected[0].help, "a test");
|
||||
ASSERT_EQ(collected[0].metric.size(), 2U);
|
||||
ASSERT_EQ(collected[0].metric.at(0).label.size(), 1U);
|
||||
EXPECT_EQ(collected[0].metric.at(0).label.at(0).name, "name");
|
||||
ASSERT_EQ(collected[0].metric.at(1).label.size(), 1U);
|
||||
EXPECT_EQ(collected[0].metric.at(1).label.at(0).name, "name");
|
||||
}
|
||||
|
||||
TEST(RegistryTest, build_histogram_family) {
|
||||
Registry registry{};
|
||||
auto& histogram_family =
|
||||
BuildHistogram().Name("hist").Help("Test Histogram").Register(registry);
|
||||
auto& histogram = histogram_family.Add({{"name", "test_histogram_1"}},
|
||||
Histogram::BucketBoundaries{0, 1, 2});
|
||||
histogram.Observe(1.1);
|
||||
auto collected = registry.Collect();
|
||||
ASSERT_EQ(collected.size(), 1U);
|
||||
}
|
||||
|
||||
TEST(RegistryTest, unable_to_remove_family) {
|
||||
Family<Counter> family{"name", "help", {}};
|
||||
Registry registry{};
|
||||
EXPECT_FALSE(registry.Remove(family));
|
||||
}
|
||||
|
||||
TEST(RegistryTest, remove_and_readd_family) {
|
||||
Registry registry{Registry::InsertBehavior::Throw};
|
||||
|
||||
auto& counter = BuildCounter().Name("name").Register(registry);
|
||||
EXPECT_TRUE(registry.Remove(counter));
|
||||
EXPECT_NO_THROW(BuildCounter().Name("name").Register(registry));
|
||||
}
|
||||
|
||||
TEST(RegistryTest, reject_different_type_than_counter) {
|
||||
const auto same_name = std::string{"same_name"};
|
||||
Registry registry{};
|
||||
|
||||
EXPECT_NO_THROW(BuildCounter().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildGauge().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildHistogram().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildInfo().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildSummary().Name(same_name).Register(registry));
|
||||
}
|
||||
|
||||
TEST(RegistryTest, reject_different_type_than_gauge) {
|
||||
const auto same_name = std::string{"same_name"};
|
||||
Registry registry{};
|
||||
|
||||
EXPECT_NO_THROW(BuildGauge().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildCounter().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildHistogram().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildInfo().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildSummary().Name(same_name).Register(registry));
|
||||
}
|
||||
|
||||
TEST(RegistryTest, reject_different_type_than_histogram) {
|
||||
const auto same_name = std::string{"same_name"};
|
||||
Registry registry{};
|
||||
|
||||
EXPECT_NO_THROW(BuildHistogram().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildInfo().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildCounter().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildGauge().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildSummary().Name(same_name).Register(registry));
|
||||
}
|
||||
|
||||
TEST(RegistryTest, reject_different_type_than_info) {
|
||||
const auto same_name = std::string{"same_name"};
|
||||
Registry registry{};
|
||||
|
||||
EXPECT_NO_THROW(BuildInfo().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildCounter().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildGauge().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildSummary().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildHistogram().Name(same_name).Register(registry));
|
||||
}
|
||||
|
||||
TEST(RegistryTest, reject_different_type_than_summary) {
|
||||
const auto same_name = std::string{"same_name"};
|
||||
Registry registry{};
|
||||
|
||||
EXPECT_NO_THROW(BuildSummary().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildCounter().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildGauge().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildHistogram().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildInfo().Name(same_name).Register(registry));
|
||||
}
|
||||
|
||||
TEST(RegistryTest, throw_for_same_family_name) {
|
||||
const auto same_name = std::string{"same_name"};
|
||||
Registry registry{Registry::InsertBehavior::Throw};
|
||||
|
||||
EXPECT_NO_THROW(BuildCounter().Name(same_name).Register(registry));
|
||||
EXPECT_ANY_THROW(BuildCounter().Name(same_name).Register(registry));
|
||||
}
|
||||
|
||||
TEST(RegistryTest, merge_same_families) {
|
||||
Registry registry{Registry::InsertBehavior::Merge};
|
||||
|
||||
std::size_t loops = 4;
|
||||
|
||||
while (loops-- > 0) {
|
||||
BuildCounter()
|
||||
.Name("counter")
|
||||
.Help("Test Counter")
|
||||
.Register(registry)
|
||||
.Add({{"name", "test_counter"}});
|
||||
}
|
||||
|
||||
auto collected = registry.Collect();
|
||||
EXPECT_EQ(1U, collected.size());
|
||||
}
|
||||
|
||||
TEST(RegistryTest, do_not_merge_families_with_different_labels) {
|
||||
Registry registry{Registry::InsertBehavior::Merge};
|
||||
|
||||
EXPECT_NO_THROW(BuildCounter()
|
||||
.Name("counter")
|
||||
.Help("Test Counter")
|
||||
.Labels({{"a", "A"}})
|
||||
.Register(registry));
|
||||
|
||||
EXPECT_ANY_THROW(BuildCounter()
|
||||
.Name("counter")
|
||||
.Help("Test Counter")
|
||||
.Labels({{"b", "B"}})
|
||||
.Register(registry));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,75 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/metric_family.h"
|
||||
#include "prometheus/text_serializer.h"
|
||||
#include "raii_locale.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
class SerializerTest : public testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
Family<Counter> family{"requests_total", "", {}};
|
||||
auto& counter = family.Add({});
|
||||
counter.Increment();
|
||||
|
||||
collected = family.Collect();
|
||||
}
|
||||
|
||||
std::vector<MetricFamily> collected;
|
||||
TextSerializer textSerializer;
|
||||
};
|
||||
|
||||
#ifndef _WIN32
|
||||
// This test expects a working German locale to test that floating
|
||||
// point numbers do not use , but . as a delimiter.
|
||||
//
|
||||
// On Debian systems they can be generated by "locale-gen de_DE.UTF-8"
|
||||
TEST_F(SerializerTest, shouldSerializeLocaleIndependent) {
|
||||
std::unique_ptr<RAIILocale> localeWithCommaDecimalSeparator;
|
||||
|
||||
// ignore missing locale and skip test if setup fails
|
||||
try {
|
||||
localeWithCommaDecimalSeparator =
|
||||
std::make_unique<RAIILocale>("de_DE.UTF-8");
|
||||
} catch (std::runtime_error&) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
const auto serialized = textSerializer.Serialize(collected);
|
||||
EXPECT_THAT(serialized, testing::HasSubstr(" 1\n"));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(SerializerTest, shouldRestoreStreamState) {
|
||||
std::ostringstream os;
|
||||
|
||||
// save stream state
|
||||
auto saved_flags = os.flags();
|
||||
auto saved_precision = os.precision();
|
||||
auto saved_width = os.width();
|
||||
auto saved_fill = os.fill();
|
||||
auto saved_locale = os.getloc();
|
||||
|
||||
// serialize
|
||||
textSerializer.Serialize(os, collected);
|
||||
|
||||
// check for expected flags
|
||||
EXPECT_EQ(os.flags(), saved_flags);
|
||||
EXPECT_EQ(os.precision(), saved_precision);
|
||||
EXPECT_EQ(os.width(), saved_width);
|
||||
EXPECT_EQ(os.fill(), saved_fill);
|
||||
EXPECT_EQ(os.getloc(), saved_locale);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
@@ -0,0 +1,106 @@
|
||||
#include "prometheus/summary.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace prometheus {
|
||||
namespace {
|
||||
|
||||
TEST(SummaryTest, initialize_with_zero) {
|
||||
Summary summary{Summary::Quantiles{}};
|
||||
auto metric = summary.Collect();
|
||||
auto s = metric.summary;
|
||||
EXPECT_EQ(s.sample_count, 0U);
|
||||
EXPECT_EQ(s.sample_sum, 0);
|
||||
}
|
||||
|
||||
TEST(SummaryTest, sample_count) {
|
||||
Summary summary{Summary::Quantiles{{0.5, 0.05}}};
|
||||
summary.Observe(0);
|
||||
summary.Observe(200);
|
||||
auto metric = summary.Collect();
|
||||
auto s = metric.summary;
|
||||
EXPECT_EQ(s.sample_count, 2U);
|
||||
}
|
||||
|
||||
TEST(SummaryTest, sample_sum) {
|
||||
Summary summary{Summary::Quantiles{{0.5, 0.05}}};
|
||||
summary.Observe(0);
|
||||
summary.Observe(1);
|
||||
summary.Observe(101);
|
||||
auto metric = summary.Collect();
|
||||
auto s = metric.summary;
|
||||
EXPECT_EQ(s.sample_sum, 102);
|
||||
}
|
||||
|
||||
TEST(SummaryTest, quantile_size) {
|
||||
Summary summary{Summary::Quantiles{{0.5, 0.05}, {0.90, 0.01}}};
|
||||
auto metric = summary.Collect();
|
||||
auto s = metric.summary;
|
||||
EXPECT_EQ(s.quantile.size(), 2U);
|
||||
}
|
||||
|
||||
TEST(SummaryTest, quantile_bounds) {
|
||||
Summary summary{Summary::Quantiles{{0.5, 0.05}, {0.90, 0.01}, {0.99, 0.001}}};
|
||||
auto metric = summary.Collect();
|
||||
auto s = metric.summary;
|
||||
ASSERT_EQ(s.quantile.size(), 3U);
|
||||
EXPECT_DOUBLE_EQ(s.quantile.at(0).quantile, 0.5);
|
||||
EXPECT_DOUBLE_EQ(s.quantile.at(1).quantile, 0.9);
|
||||
EXPECT_DOUBLE_EQ(s.quantile.at(2).quantile, 0.99);
|
||||
}
|
||||
|
||||
TEST(SummaryTest, quantile_values) {
|
||||
static const int SAMPLES = 100000;
|
||||
|
||||
Summary summary{Summary::Quantiles{{0.5, 0.05}, {0.9, 0.01}, {0.99, 0.001}},
|
||||
std::chrono::hours{1}}; // prevent rotation on slow CPUs
|
||||
for (int i = 1; i <= SAMPLES; ++i) summary.Observe(i);
|
||||
|
||||
auto metric = summary.Collect();
|
||||
auto s = metric.summary;
|
||||
ASSERT_EQ(s.quantile.size(), 3U);
|
||||
|
||||
EXPECT_NEAR(s.quantile.at(0).value, 0.5 * SAMPLES, 0.05 * SAMPLES);
|
||||
EXPECT_NEAR(s.quantile.at(1).value, 0.9 * SAMPLES, 0.01 * SAMPLES);
|
||||
EXPECT_NEAR(s.quantile.at(2).value, 0.99 * SAMPLES, 0.001 * SAMPLES);
|
||||
}
|
||||
|
||||
TEST(SummaryTest, max_age) {
|
||||
Summary summary{Summary::Quantiles{{0.99, 0.001}}, std::chrono::seconds(1),
|
||||
2};
|
||||
summary.Observe(8.0);
|
||||
|
||||
const auto test_value = [&summary](double ref) {
|
||||
auto metric = summary.Collect();
|
||||
auto s = metric.summary;
|
||||
ASSERT_EQ(s.quantile.size(), 1U);
|
||||
|
||||
if (std::isnan(ref))
|
||||
EXPECT_TRUE(std::isnan(s.quantile.at(0).value));
|
||||
else
|
||||
EXPECT_DOUBLE_EQ(s.quantile.at(0).value, ref);
|
||||
};
|
||||
|
||||
test_value(8.0);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(600));
|
||||
test_value(8.0);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(600));
|
||||
test_value(std::numeric_limits<double>::quiet_NaN());
|
||||
}
|
||||
|
||||
TEST(SummaryTest, construction_with_dynamic_quantile_vector) {
|
||||
auto quantiles = Summary::Quantiles{{0.99, 0.001}};
|
||||
quantiles.push_back({0.5, 0.05});
|
||||
|
||||
Summary summary{quantiles, std::chrono::seconds(1), 2};
|
||||
summary.Observe(8.0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace prometheus
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user