SERVER-117298: Add prometheus-cpp third-party library (#47013)

GitOrigin-RevId: 3d3b2191b5b97327b89818a31d8b5b5b370c1659
This commit is contained in:
Mike Nugent 2026-02-23 12:41:07 -05:00 committed by MongoDB Bot
parent 4f59c2a59e
commit 5244cf0a85
62 changed files with 3535 additions and 346 deletions

View File

@ -1,6 +1,7 @@
# TODO(SERVER-81039): Remove once these can be compiled from the root directory.
src/third_party/grpc/dist
src/third_party/abseil-cpp/dist
src/third_party/prometheus-cpp
src/third_party/protobuf/dist
src/third_party/re2/dist
src/third_party/tcmalloc/dist

View File

@ -199,6 +199,12 @@ single_version_override(
version = "6.4.0",
)
bazel_dep(name = "prometheus-cpp", version = "1.3.0", repo_name = "com_github_jupp0r_prometheus_cpp")
local_path_override(
module_name = "prometheus-cpp",
path = "src/third_party/prometheus-cpp/dist",
)
bazel_dep(name = "rules_multitool", version = "0.4.0")
multitool = use_extension("@rules_multitool//multitool:extension.bzl", "multitool")

350
MODULE.bazel.lock generated
View File

@ -46,8 +46,6 @@
"https://bcr.bazel.build/modules/bzip2/1.0.8/source.json": "b64f3a2f973749cf5f6ee32b3d804af56a35a746228a7845ed5daa31c8cc8af1",
"https://bcr.bazel.build/modules/cel-spec/0.15.0/MODULE.bazel": "e1eed53d233acbdcf024b4b0bc1528116d92c29713251b5154078ab1348cb600",
"https://bcr.bazel.build/modules/cel-spec/0.15.0/source.json": "ab7dccdf21ea2261c0f809b5a5221a4d7f8b580309f285fdf1444baaca75d44a",
"https://bcr.bazel.build/modules/civetweb/1.16/MODULE.bazel": "46a38f9daeb57392e3827fce7d40926be0c802bd23cdd6bfd3a96c804de42fae",
"https://bcr.bazel.build/modules/civetweb/1.16/source.json": "ba8b9585adb8355cb51b999d57172fd05e7a762c56b8d4bac6db42c99de3beb7",
"https://bcr.bazel.build/modules/curl/8.7.1/MODULE.bazel": "088221c35a2939c555e6e47cb31a81c15f8b59f4daa8009b1e9271a502d33485",
"https://bcr.bazel.build/modules/curl/8.8.0/MODULE.bazel": "7da3b3e79b0b4ee8f8c95d640bc6ad7b430ce66ef6e9c9d2bc29b3b5ef85f6fe",
"https://bcr.bazel.build/modules/curl/8.8.0/source.json": "d7d138b6878cf38891692fee0649ace35357fd549b425614d571786f054374d4",
@ -100,9 +98,6 @@
"https://bcr.bazel.build/modules/opentracing-cpp/1.6.0/source.json": "da1cb1add160f5e5074b7272e9db6fd8f1b3336c15032cd0a653af9d2f484aed",
"https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f",
"https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29",
"https://bcr.bazel.build/modules/prometheus-cpp/1.3.0.bcr.1/MODULE.bazel": "116ad46e97c1d2aeb020fe2899a342a7e703574ce7c0faf7e4810f938c974a9a",
"https://bcr.bazel.build/modules/prometheus-cpp/1.3.0.bcr.1/source.json": "e813cce2d450708cfcb26e309c5172583a7440776edf354e83e6788c768e5cca",
"https://bcr.bazel.build/modules/prometheus-cpp/1.3.0/MODULE.bazel": "ce82e086bbc0b60267e970f6a54b2ca6d0f22d3eb6633e00e2cc2899c700f3d8",
"https://bcr.bazel.build/modules/protoc-gen-validate/1.0.4.bcr.2/MODULE.bazel": "c4bd2c850211ff5b7dadf9d2d0496c1c922fdedc303c775b01dfd3b3efc907ed",
"https://bcr.bazel.build/modules/protoc-gen-validate/1.0.4/MODULE.bazel": "b8913c154b16177990f6126d2d2477d187f9ddc568e95ee3e2d50fc65d2c494a",
"https://bcr.bazel.build/modules/protoc-gen-validate/1.2.1.bcr.1/MODULE.bazel": "4bf09676b62fa587ae07e073420a76ec8766dcce7545e5f8c68cfa8e484b5120",
@ -128,13 +123,15 @@
"https://bcr.bazel.build/modules/rules_cc/0.0.5/MODULE.bazel": "be41f87587998fe8890cd82ea4e848ed8eb799e053c224f78f3ff7fe1a1d9b74",
"https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
"https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
"https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
"https://bcr.bazel.build/modules/rules_cc/0.1.4/MODULE.bazel": "bb03a452a7527ac25a7518fb86a946ef63df860b9657d8323a0c50f8504fb0b9",
"https://bcr.bazel.build/modules/rules_cc/0.2.10/MODULE.bazel": "76e71013ff06010b5a6682751f85b60ee7b9fd89eec0dca97583919dd8c6f44e",
"https://bcr.bazel.build/modules/rules_cc/0.2.10/source.json": "e517ec6451617032750803d6f9028e849eaf0d08878d23f9e66e8415d241b4f8",
"https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc",
"https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.10.1/MODULE.bazel": "b9527010e5fef060af92b6724edb3691970a5b1f76f74b21d39f7d433641be60",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.10.1/source.json": "9300e71df0cdde0952f10afff1401fa664e9fc5d9ae6204660ba1b158d90d6a6",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.15.1/MODULE.bazel": "c2c60d26c79fda484acb95cdbec46e89d6b28b4845cb277160ce1e0c8622bb88",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.15.1/source.json": "a161811a63ba8a859086da3b7ff3ad04f2e9c255d7727b41087103fc0eb22f55",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
"https://bcr.bazel.build/modules/rules_go/0.41.0/MODULE.bazel": "55861d8e8bb0e62cbd2896f60ff303f62ffcb0eddb74ecb0e5c0cbe36fc292c8",
"https://bcr.bazel.build/modules/rules_go/0.42.0/MODULE.bazel": "8cfa875b9aa8c6fce2b2e5925e73c1388173ea3c32a0db4d2b4804b453c14270",
@ -205,6 +202,7 @@
"https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
"https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
"https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43",
"https://bcr.bazel.build/modules/rules_python/1.2.0/MODULE.bazel": "5aeeb48b2a6c19d668b48adf2b8a2b209a6310c230db0ce77450f148a89846e4",
"https://bcr.bazel.build/modules/rules_python/1.5.1/MODULE.bazel": "acfe65880942d44a69129d4c5c3122d57baaf3edf58ae5a6bd4edea114906bf5",
"https://bcr.bazel.build/modules/rules_python/1.5.2/MODULE.bazel": "5e4bbe842ffdb54dfb6ddd78de3d28151a4cc3417a685cee30c8fe4d1dc655a2",
"https://bcr.bazel.build/modules/rules_python/1.5.2/source.json": "687aa52b6da283ee45cb702bc292386fd9c717264c38c0277b4957eeafbe1904",
@ -708,346 +706,6 @@
"recordedRepoMappingEntries": []
}
},
"@@rules_foreign_cc~//foreign_cc:extensions.bzl%tools": {
"general": {
"bzlTransitiveDigest": "a7qnESofmIRYId6wwGNPJ9kvExU80KrkxL281P3+lBE=",
"usagesDigest": "hK5/SjH6eu1u+V0YHRti+lZvw7Wb4oU6Raw6P0mAfDQ=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
"rules_foreign_cc_framework_toolchain_linux": {
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
"ruleClassName": "framework_toolchain_repository",
"attributes": {
"commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:linux_commands.bzl",
"exec_compatible_with": [
"@platforms//os:linux"
]
}
},
"rules_foreign_cc_framework_toolchain_freebsd": {
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
"ruleClassName": "framework_toolchain_repository",
"attributes": {
"commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:freebsd_commands.bzl",
"exec_compatible_with": [
"@platforms//os:freebsd"
]
}
},
"rules_foreign_cc_framework_toolchain_windows": {
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
"ruleClassName": "framework_toolchain_repository",
"attributes": {
"commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:windows_commands.bzl",
"exec_compatible_with": [
"@platforms//os:windows"
]
}
},
"rules_foreign_cc_framework_toolchain_macos": {
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
"ruleClassName": "framework_toolchain_repository",
"attributes": {
"commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:macos_commands.bzl",
"exec_compatible_with": [
"@platforms//os:macos"
]
}
},
"rules_foreign_cc_framework_toolchains": {
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
"ruleClassName": "framework_toolchain_repository_hub",
"attributes": {}
},
"cmake_src": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
"sha256": "f316b40053466f9a416adf981efda41b160ca859e97f6a484b447ea299ff26aa",
"strip_prefix": "cmake-3.23.2",
"urls": [
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2.tar.gz"
]
}
},
"gnumake_src": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
"sha256": "581f4d4e872da74b3941c874215898a7d35802f03732bdccee1d4a7979105d18",
"strip_prefix": "make-4.4",
"urls": [
"https://mirror.bazel.build/ftpmirror.gnu.org/gnu/make/make-4.4.tar.gz",
"http://ftpmirror.gnu.org/gnu/make/make-4.4.tar.gz"
]
}
},
"ninja_build_src": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
"sha256": "31747ae633213f1eda3842686f83c2aa1412e0f5691d1c14dbbcc67fe7400cea",
"strip_prefix": "ninja-1.11.1",
"urls": [
"https://github.com/ninja-build/ninja/archive/v1.11.1.tar.gz"
]
}
},
"meson_src": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"build_file_content": "exports_files([\"meson.py\"])\n\nfilegroup(\n name = \"runtime\",\n srcs = glob([\"mesonbuild/**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
"strip_prefix": "meson-1.1.1",
"url": "https://github.com/mesonbuild/meson/releases/download/1.1.1/meson-1.1.1.tar.gz"
}
},
"glib_dev": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"build_file_content": "\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\ncc_import(\n name = \"glib_dev\",\n hdrs = glob([\"include/**\"]),\n shared_library = \"@glib_runtime//:bin/libglib-2.0-0.dll\",\n visibility = [\"//visibility:public\"],\n)\n ",
"sha256": "bdf18506df304d38be98a4b3f18055b8b8cca81beabecad0eece6ce95319c369",
"urls": [
"https://download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip"
]
}
},
"glib_src": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"build_file_content": "\ncc_import(\n name = \"msvc_hdr\",\n hdrs = [\"msvc_recommended_pragmas.h\"],\n visibility = [\"//visibility:public\"],\n)\n ",
"sha256": "bc96f63112823b7d6c9f06572d2ad626ddac7eb452c04d762592197f6e07898e",
"strip_prefix": "glib-2.26.1",
"urls": [
"https://download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz"
]
}
},
"glib_runtime": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"build_file_content": "\nexports_files(\n [\n \"bin/libgio-2.0-0.dll\",\n \"bin/libglib-2.0-0.dll\",\n \"bin/libgmodule-2.0-0.dll\",\n \"bin/libgobject-2.0-0.dll\",\n \"bin/libgthread-2.0-0.dll\",\n ],\n visibility = [\"//visibility:public\"],\n)\n ",
"sha256": "88d857087e86f16a9be651ee7021880b3f7ba050d34a1ed9f06113b8799cb973",
"urls": [
"https://download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip"
]
}
},
"gettext_runtime": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"build_file_content": "\ncc_import(\n name = \"gettext_runtime\",\n shared_library = \"bin/libintl-8.dll\",\n visibility = [\"//visibility:public\"],\n)\n ",
"sha256": "1f4269c0e021076d60a54e98da6f978a3195013f6de21674ba0edbc339c5b079",
"urls": [
"https://download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip"
]
}
},
"pkgconfig_src": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
"sha256": "6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591",
"strip_prefix": "pkg-config-0.29.2",
"patches": [
"@@rules_foreign_cc~//toolchains:pkgconfig-detectenv.patch",
"@@rules_foreign_cc~//toolchains:pkgconfig-makefile-vc.patch"
],
"urls": [
"https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz"
]
}
},
"bazel_skylib": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"urls": [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz"
],
"sha256": "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728"
}
},
"rules_python": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"sha256": "84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841",
"strip_prefix": "rules_python-0.23.1",
"url": "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.23.1.tar.gz"
}
},
"cmake-3.23.2-linux-aarch64": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"urls": [
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-aarch64.tar.gz"
],
"sha256": "f2654bf780b53f170bbbec44d8ac67d401d24788e590faa53036a89476efa91e",
"strip_prefix": "cmake-3.23.2-linux-aarch64",
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n"
}
},
"cmake-3.23.2-linux-x86_64": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"urls": [
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-x86_64.tar.gz"
],
"sha256": "aaced6f745b86ce853661a595bdac6c5314a60f8181b6912a0a4920acfa32708",
"strip_prefix": "cmake-3.23.2-linux-x86_64",
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n"
}
},
"cmake-3.23.2-macos-universal": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"urls": [
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-macos-universal.tar.gz"
],
"sha256": "853a0f9af148c5ef47282ffffee06c4c9f257be2635936755f39ca13c3286c88",
"strip_prefix": "cmake-3.23.2-macos-universal/CMake.app/Contents",
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n"
}
},
"cmake-3.23.2-windows-i386": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"urls": [
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-i386.zip"
],
"sha256": "6a4fcd6a2315b93cb23c93507efccacc30c449c2bf98f14d6032bb226c582e07",
"strip_prefix": "cmake-3.23.2-windows-i386",
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n)\n"
}
},
"cmake-3.23.2-windows-x86_64": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"urls": [
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-x86_64.zip"
],
"sha256": "2329387f3166b84c25091c86389fb891193967740c9bcf01e7f6d3306f7ffda0",
"strip_prefix": "cmake-3.23.2-windows-x86_64",
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n)\n"
}
},
"cmake_3.23.2_toolchains": {
"bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl",
"ruleClassName": "prebuilt_toolchains_repository",
"attributes": {
"repos": {
"cmake-3.23.2-linux-aarch64": [
"@platforms//cpu:aarch64",
"@platforms//os:linux"
],
"cmake-3.23.2-linux-x86_64": [
"@platforms//cpu:x86_64",
"@platforms//os:linux"
],
"cmake-3.23.2-macos-universal": [
"@platforms//os:macos"
],
"cmake-3.23.2-windows-i386": [
"@platforms//cpu:x86_32",
"@platforms//os:windows"
],
"cmake-3.23.2-windows-x86_64": [
"@platforms//cpu:x86_64",
"@platforms//os:windows"
]
},
"tool": "cmake"
}
},
"ninja_1.11.1_linux": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"urls": [
"https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-linux.zip"
],
"sha256": "b901ba96e486dce377f9a070ed4ef3f79deb45f4ffe2938f8e7ddc69cfb3df77",
"strip_prefix": "",
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n"
}
},
"ninja_1.11.1_mac": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"urls": [
"https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-mac.zip"
],
"sha256": "482ecb23c59ae3d4f158029112de172dd96bb0e97549c4b1ca32d8fad11f873e",
"strip_prefix": "",
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n"
}
},
"ninja_1.11.1_win": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"urls": [
"https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-win.zip"
],
"sha256": "524b344a1a9a55005eaf868d991e090ab8ce07fa109f1820d40e74642e289abc",
"strip_prefix": "",
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja.exe\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n"
}
},
"ninja_1.11.1_toolchains": {
"bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl",
"ruleClassName": "prebuilt_toolchains_repository",
"attributes": {
"repos": {
"ninja_1.11.1_linux": [
"@platforms//cpu:x86_64",
"@platforms//os:linux"
],
"ninja_1.11.1_mac": [
"@platforms//cpu:x86_64",
"@platforms//os:macos"
],
"ninja_1.11.1_win": [
"@platforms//cpu:x86_64",
"@platforms//os:windows"
]
},
"tool": "ninja"
}
}
},
"recordedRepoMappingEntries": [
[
"rules_foreign_cc~",
"bazel_tools",
"bazel_tools"
],
[
"rules_foreign_cc~",
"rules_foreign_cc",
"rules_foreign_cc~"
]
]
}
},
"@@rules_java~//java:rules_java_deps.bzl%compatibility_proxy": {
"general": {
"bzlTransitiveDigest": "kFNgYOjJGABexRi0wd2ZNysJhy5YjAZYTI7/GThoO78=",

View File

@ -2056,6 +2056,57 @@
},
"scope": "required"
},
{
"type": "library",
"bom-ref": "pkg:github/jupp0r/prometheus-cpp@1.3.0",
"supplier": {
"name": "Prometheus",
"url": [
"https://prometheus.io/"
]
},
"author": "Jupp Mueller",
"name": "prometheus-cpp",
"version": "1.3.0",
"description": "Prometheus client library for C++",
"licenses": [
{
"license": {
"id": "MIT"
}
}
],
"copyright": "Jupp Mueller, Gregor Jasny",
"purl": "pkg:github/jupp0r/prometheus-cpp@1.3.0",
"externalReferences": [
{
"url": "https://github.com/jupp0r/prometheus-cpp",
"type": "vcs"
}
],
"properties": [
{
"name": "internal:team_responsible",
"value": "Networking & Observability"
},
{
"name": "emits_persisted_data",
"value": "true"
},
{
"name": "import_script_path",
"value": "src/third_party/prometheus-cpp/scripts/import.sh"
}
],
"evidence": {
"occurrences": [
{
"location": "src/third_party/prometheus-cpp"
}
]
},
"scope": "optional"
},
{
"type": "library",
"bom-ref": "pkg:github/protocolbuffers/protobuf@v6.31.1",

View File

@ -103,6 +103,9 @@ filters:
- "private/.placeholder":
approvers:
- 10gen/devprod-build
- "prometheus-cpp":
approvers:
- 10gen/server-networking-and-observability
- "protobuf":
approvers:
- 10gen/server-networking-and-observability

View File

@ -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"},
)

View File

@ -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.

View File

@ -0,0 +1,20 @@
module(
name = "prometheus-cpp",
version = "1.3.0",
repo_name = "com_github_jupp0r_prometheus_cpp",
)
bazel_dep(name = "boringssl", version = "")
local_path_override(
module_name = "boringssl",
path = "../boringssl_replacement",
)
bazel_dep(name = "platforms", version = "0.0.10")
bazel_dep(name = "rules_cc", version = "0.2.10")
bazel_dep(name = "rules_foreign_cc", version = "0.15.1")
bazel_dep(name = "zlib", version = "1.3.1")
local_path_override(
module_name = "zlib",
path = "../zlib",
)

View File

@ -0,0 +1,364 @@
# Prometheus Client Library for Modern C++
[![Bazel CI](https://github.com/jupp0r/prometheus-cpp/workflows/Bazel%20CI/badge.svg)](https://github.com/jupp0r/prometheus-cpp/actions?workflow=Continuous+Integration)
[![CMake CI](https://github.com/jupp0r/prometheus-cpp/workflows/CMake%20CI/badge.svg)](https://github.com/jupp0r/prometheus-cpp/actions?workflow=Continuous+Integration)
[![Coverage Status](https://coveralls.io/repos/github/jupp0r/prometheus-cpp/badge.svg?branch=master)](https://coveralls.io/github/jupp0r/prometheus-cpp?branch=master)
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.
## 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.
### With 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 .
```
### With Bazel
Install a recent [bazel](https://www.bazel.io) version with Modules support.
To build and test prometheus-cpp run:
```shell
bazel test //...
```
## 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).
### Ubuntu PPA
The Launchpad [prometheus-cpp team](https://launchpad.net/~prometheus-cpp) provides a
[PPA for stable versions](https://code.launchpad.net/~prometheus-cpp/+archive/ubuntu/prometheus-cpp-stable).
Please follow the "Adding this PPA to your system" steps to use it.
### 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
### Bazel
The Bazel Central Registry (BCR
[provides the prometheus-cpp package](https://registry.bazel.build/modules/prometheus-cpp).
Add the following to your `MODULE.bazel` file:
```python
bazel_dep(name = "prometheus-cpp", version = "1.2.4")
```
### 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 //...
```
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

View File

@ -0,0 +1,7 @@
exports_files(
glob([
"*.BUILD",
"*.tpl",
]),
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,4 @@
#pragma once
#define {BASE_NAME}_EXPORT
#define {BASE_NAME}_NO_EXPORT

View File

@ -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}"},
)

View File

@ -0,0 +1,21 @@
load("//bazel:export_header.bzl", "generate_dummy_export_header")
load("@rules_cc//cc:defs.bzl", "cc_library")
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"],
)

View File

@ -0,0 +1,12 @@
cc_binary(
name = "benchmarks",
srcs = glob([
"*.cc",
"*.h",
]),
linkstatic = True,
deps = [
"//core",
"@com_github_google_benchmark//:benchmark",
],
)

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)->UseManualTime();
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);

View File

@ -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);

View File

@ -0,0 +1,3 @@
#include <benchmark/benchmark.h>
BENCHMARK_MAIN();

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;
friend bool operator<(const Label& lhs, const Label& rhs) {
return std::tie(lhs.name, lhs.value) < std::tie(rhs.name, rhs.value);
}
friend bool operator==(const Label& lhs, const Label& rhs) {
return std::tie(lhs.name, lhs.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

View File

@ -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

View File

@ -0,0 +1,85 @@
#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 Reset the counter to 0
void Reset();
/// \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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,16 @@
#pragma once
#include <memory>
#include <utility>
namespace prometheus {
namespace detail {
// Remove as soon C++14 can be used.
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
} // namespace detail
} // namespace prometheus

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,157 @@
#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/future_std.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, detail::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

View File

@ -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

View File

@ -0,0 +1,115 @@
#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 Reset all data points collected so far.
///
/// All buckets and sum are reset to its oringal value. This is especially
/// useful if histogram is tracked elsewhere but report in prometheus system.
void Reset();
/// \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

View File

@ -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

View File

@ -0,0 +1,14 @@
#pragma once
#include <map>
#include <string>
namespace prometheus {
/// \brief Multiple labels and their value.
using Labels = std::map<std::string, std::string>;
/// \brief Single label and its value.
using Label = Labels::value_type;
} // namespace prometheus

View File

@ -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

View File

@ -0,0 +1,14 @@
#pragma once
namespace prometheus {
enum class MetricType {
Counter,
Gauge,
Summary,
Untyped,
Histogram,
Info,
};
} // namespace prometheus

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,24 @@
#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(); }
void Counter::Reset() { gauge_.Set(0); }
ClientMetric Counter::Collect() const {
ClientMetric metric;
metric.counter.value = Value();
return metric;
}
} // namespace prometheus

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,130 @@
#include "prometheus/family.h"
#include <algorithm>
#include <cassert>
#include <map>
#include <stdexcept>
#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

View File

@ -0,0 +1,45 @@
#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) {
#if __cpp_lib_atomic_float >= 201711L
value_.fetch_add(value);
#else
// Pre-C++ 20 fallback: busy loop (which might be more expansive than using
// fetch_add).
auto current = value_.load();
while (!value_.compare_exchange_weak(current, current + value)) {
// intentionally empty block
}
#endif
}
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

View File

@ -0,0 +1,97 @@
#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]);
}
}
void Histogram::Reset() {
std::lock_guard<std::mutex> lock(mutex_);
for (std::size_t i = 0; i < bucket_counts_.size(); ++i) {
bucket_counts_[i].Reset();
}
sum_.Set(0);
}
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

View File

@ -0,0 +1,11 @@
#include "prometheus/info.h"
namespace prometheus {
ClientMetric Info::Collect() const {
ClientMetric metric;
metric.info.value = 1;
return metric;
}
} // namespace prometheus

View File

@ -0,0 +1,197 @@
#include "prometheus/registry.h"
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <tuple>
#include "prometheus/counter.h"
#include "prometheus/detail/future_std.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>();
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()) {
if (insert_behavior_ == InsertBehavior::Merge) {
if ((*it)->GetConstantLabels() == labels) {
return **it;
}
throw std::invalid_argument(
"Family name already exists with different constant labels");
} else {
throw std::invalid_argument("Family name already exists");
}
}
auto family = detail::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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,8 @@
load("@rules_cc//cc:defs.bzl", "cc_library")
cc_library(
name = "util",
hdrs = glob(["include/**/*.h"]),
strip_include_prefix = "include",
visibility = ["//:__subpackages__"],
)

View File

@ -0,0 +1,140 @@
#pragma once
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <stdexcept>
#include <string>
namespace prometheus {
namespace detail {
/*
Copyright (C) 2019-2020 by Martin Vorbrodt <martin@vorbrodt.blog>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
https://github.com/mvorbrodt/blog/blob/master/src/base64.hpp
*/
static constexpr char kEncodeLookup[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static constexpr char kPadCharacter = '=';
inline std::string base64_encode(const std::string& input) {
std::string encoded;
encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4);
std::uint32_t temp{};
auto it = input.begin();
for (std::size_t i = 0; i < input.size() / 3; ++i) {
temp = static_cast<std::uint8_t>(*it++) << 16;
temp += static_cast<std::uint8_t>(*it++) << 8;
temp += static_cast<std::uint8_t>(*it++);
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]);
encoded.append(1, kEncodeLookup[(temp & 0x0000003F)]);
}
switch (input.size() % 3) {
case 1:
temp = static_cast<std::uint8_t>(*it++) << 16;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(2, kPadCharacter);
break;
case 2:
temp = static_cast<std::uint8_t>(*it++) << 16;
temp += static_cast<std::uint8_t>(*it++) << 8;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]);
encoded.append(1, kPadCharacter);
break;
}
return encoded;
}
// https://tools.ietf.org/html/rfc4648#section-5
inline std::string base64url_encode(const std::string& input) {
std::string s = base64_encode(input);
std::transform(begin(s), end(s), begin(s), [](char c) {
if (c == '+') return '-';
if (c == '/') return '_';
return c;
});
return s;
}
inline std::string base64_decode(const std::string& input) {
if (input.length() % 4) {
throw std::runtime_error("Invalid base64 length!");
}
std::size_t padding = 0;
if (!input.empty()) {
if (input[input.length() - 1] == kPadCharacter) padding++;
if (input[input.length() - 2] == kPadCharacter) padding++;
}
std::string decoded;
decoded.reserve(((input.length() / 4) * 3) - padding);
std::uint32_t temp = 0;
auto it = input.begin();
while (it < input.end()) {
for (std::size_t i = 0; i < 4; ++i) {
temp <<= 6;
if (*it >= 0x41 && *it <= 0x5A) {
temp |= *it - 0x41;
} else if (*it >= 0x61 && *it <= 0x7A) {
temp |= *it - 0x47;
} else if (*it >= 0x30 && *it <= 0x39) {
temp |= *it + 0x04;
} else if (*it == 0x2B) {
temp |= 0x3E;
} else if (*it == 0x2F) {
temp |= 0x3F;
} else if (*it == kPadCharacter) {
switch (input.end() - it) {
case 1:
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8) & 0x000000FF);
return decoded;
case 2:
decoded.push_back((temp >> 10) & 0x000000FF);
return decoded;
default:
throw std::runtime_error("Invalid padding in base64!");
}
} else {
throw std::runtime_error("Invalid character in base64!");
}
++it;
}
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8) & 0x000000FF);
decoded.push_back((temp) & 0x000000FF);
}
return decoded;
}
} // namespace detail
} // namespace prometheus

View File

@ -0,0 +1,49 @@
diff --git b/src/third_party/prometheus-cpp/dist/MODULE.bazel a/src/third_party/prometheus-cpp/dist/MODULE.bazel
index 8bcc2f444ee..3725746ef6a 100644
--- b/src/third_party/prometheus-cpp/dist/MODULE.bazel
+++ a/src/third_party/prometheus-cpp/dist/MODULE.bazel
@@ -4,12 +4,17 @@ module(
repo_name = "com_github_jupp0r_prometheus_cpp",
)
-bazel_dep(name = "boringssl", version = "0.0.0-20240530-2db0eb3")
-bazel_dep(name = "civetweb", version = "1.16")
-bazel_dep(name = "curl", version = "8.7.1", repo_name = "com_github_curl")
+bazel_dep(name = "boringssl", version = "")
+local_path_override(
+ module_name = "boringssl",
+ path = "../boringssl_replacement",
+)
+
bazel_dep(name = "platforms", version = "0.0.10")
-bazel_dep(name = "rules_cc", version = "0.0.9")
+bazel_dep(name = "rules_cc", version = "0.2.10")
+bazel_dep(name = "rules_foreign_cc", version = "0.15.1")
bazel_dep(name = "zlib", version = "1.3.1")
-
-bazel_dep(name = "google_benchmark", version = "1.8.3", dev_dependency = True, repo_name = "com_github_google_benchmark")
-bazel_dep(name = "googletest", version = "1.12.1", dev_dependency = True, repo_name = "com_google_googletest")
+local_path_override(
+ module_name = "zlib",
+ path = "../zlib",
+)
diff --git b/src/third_party/prometheus-cpp/dist/core/BUILD.bazel a/src/third_party/prometheus-cpp/dist/core/BUILD.bazel
index a302f19572f..f4f21248361 100644
--- b/src/third_party/prometheus-cpp/dist/core/BUILD.bazel
+++ a/src/third_party/prometheus-cpp/dist/core/BUILD.bazel
@@ -1,4 +1,5 @@
load("//bazel:export_header.bzl", "generate_dummy_export_header")
+load("@rules_cc//cc:defs.bzl", "cc_library")
generate_dummy_export_header(
name = "export_header",
diff --git b/src/third_party/prometheus-cpp/dist/util/BUILD.bazel a/src/third_party/prometheus-cpp/dist/util/BUILD.bazel
index e9e8a31a378..2dcf40f08b0 100644
--- b/src/third_party/prometheus-cpp/dist/util/BUILD.bazel
+++ a/src/third_party/prometheus-cpp/dist/util/BUILD.bazel
@@ -1,3 +1,5 @@
+load("@rules_cc//cc:defs.bzl", "cc_library")
+
cc_library(
name = "util",
hdrs = glob(["include/**/*.h"]),

View File

@ -0,0 +1,62 @@
#!/bin/bash
# This script downloads and imports opentelemetry-cpp.
#
# Turn on strict error checking, like perl use 'strict'
set -xeuo pipefail
IFS=$'\n\t'
NAME="prometheus-cpp"
VERSION="1.3.0"
BRANCH="mongo/v${VERSION}"
LIB_GIT_URL="https://github.com/mongodb-forks/prometheus-cpp.git"
LIB_GIT_DIR=$(mktemp -d /tmp/import-prometheus-cpp.XXXXXX)
trap "rm -rf $LIB_GIT_DIR" EXIT
LIBDIR=$(git rev-parse --show-toplevel)/src/third_party/$NAME
DIST=${LIBDIR}/dist
git clone "$LIB_GIT_URL" $LIB_GIT_DIR
git -C $LIB_GIT_DIR checkout $BRANCH
DEST_DIR=$(git rev-parse --show-toplevel)/src/third_party/$NAME
SUBDIR_WHITELIST=(
bazel
core
util
LICENSE
BUILD.bazel
MODULE.bazel
README.md
)
for subdir in ${SUBDIR_WHITELIST[@]}
do
[[ -d $LIB_GIT_DIR/$subdir ]] && mkdir -p $DIST/$subdir
cp -Trp $LIB_GIT_DIR/$subdir $DIST/$subdir
done
## Remove all CMakelists.txt files
find $DIST/ -name "CMakeLists.txt" -delete
# Remove unneeded directories
pushd $DIST
# Test directories
rm -rf core/tests
rm -rf util/tests
# Unneeded bazel-related files
rm bazel/civetweb.BUILD
rm bazel/curl.BUILD
rm bazel/curl.bzl
rm bazel/repositories.bzl
rm bazel/zlib.BUILD
PATCHES_DIR="${LIBDIR}/patches"
git apply "${PATCHES_DIR}/0001-fix-build-and-deps-for-bazel.patch"
popd