mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-15 10:52:30 +00:00
242 lines
10 KiB
C++
242 lines
10 KiB
C++
#include <Server/PrometheusRequestHandlerFactory.h>
|
|
|
|
#include <Server/HTTPHandlerFactory.h>
|
|
#include <Server/PrometheusMetricsWriter.h>
|
|
#include <Server/PrometheusRequestHandler.h>
|
|
#include <Server/PrometheusRequestHandlerConfig.h>
|
|
|
|
|
|
namespace DB
|
|
{
|
|
|
|
namespace ErrorCodes
|
|
{
|
|
extern const int UNKNOWN_ELEMENT_IN_CONFIG;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
/// Parses common configuration which is attached to any other configuration. The common configuration looks like this:
|
|
/// <prometheus>
|
|
/// <enable_stacktrace>true</enable_stacktrace>
|
|
/// </prometheus>
|
|
/// <keep_alive_timeout>30</keep_alive_timeout>
|
|
void parseCommonConfig(const Poco::Util::AbstractConfiguration & config, PrometheusRequestHandlerConfig & res)
|
|
{
|
|
res.is_stacktrace_enabled = config.getBool("prometheus.enable_stacktrace", true);
|
|
res.keep_alive_timeout = config.getUInt("keep_alive_timeout", DEFAULT_HTTP_KEEP_ALIVE_TIMEOUT);
|
|
}
|
|
|
|
/// Parses a configuration like this:
|
|
/// <!-- <type>expose_metrics</type> (Implied, not actually parsed) -->
|
|
/// <metrics>true</metrics>
|
|
/// <asynchronous_metrics>true</asynchronous_metrics>
|
|
/// <events>true</events>
|
|
/// <errors>true</errors>
|
|
PrometheusRequestHandlerConfig parseExposeMetricsConfig(const Poco::Util::AbstractConfiguration & config, const String & config_prefix)
|
|
{
|
|
PrometheusRequestHandlerConfig res;
|
|
res.type = PrometheusRequestHandlerConfig::Type::ExposeMetrics;
|
|
res.expose_metrics = config.getBool(config_prefix + ".metrics", true);
|
|
res.expose_asynchronous_metrics = config.getBool(config_prefix + ".asynchronous_metrics", true);
|
|
res.expose_events = config.getBool(config_prefix + ".events", true);
|
|
res.expose_errors = config.getBool(config_prefix + ".errors", true);
|
|
parseCommonConfig(config, res);
|
|
return res;
|
|
}
|
|
|
|
/// Extracts a qualified table name from the config. It can be set either as
|
|
/// <table>mydb.prometheus</table>
|
|
/// or
|
|
/// <database>mydb</database>
|
|
/// <table>prometheus</table>
|
|
QualifiedTableName parseTableNameFromConfig(const Poco::Util::AbstractConfiguration & config, const String & config_prefix)
|
|
{
|
|
QualifiedTableName res;
|
|
res.table = config.getString(config_prefix + ".table", "prometheus");
|
|
res.database = config.getString(config_prefix + ".database", "");
|
|
if (res.database.empty())
|
|
res = QualifiedTableName::parseFromString(res.table);
|
|
if (res.database.empty())
|
|
res.database = "default";
|
|
return res;
|
|
}
|
|
|
|
/// Parses a configuration like this:
|
|
/// <!-- <type>remote_write</type> (Implied, not actually parsed) -->
|
|
/// <table>db.time_series_table_name</table>
|
|
PrometheusRequestHandlerConfig parseRemoteWriteConfig(const Poco::Util::AbstractConfiguration & config, const String & config_prefix)
|
|
{
|
|
PrometheusRequestHandlerConfig res;
|
|
res.type = PrometheusRequestHandlerConfig::Type::RemoteWrite;
|
|
res.time_series_table_name = parseTableNameFromConfig(config, config_prefix);
|
|
parseCommonConfig(config, res);
|
|
return res;
|
|
}
|
|
|
|
/// Parses a configuration like this:
|
|
/// <!-- <type>remote_read</type> (Implied, not actually parsed) -->
|
|
/// <table>db.time_series_table_name</table>
|
|
PrometheusRequestHandlerConfig parseRemoteReadConfig(const Poco::Util::AbstractConfiguration & config, const String & config_prefix)
|
|
{
|
|
PrometheusRequestHandlerConfig res;
|
|
res.type = PrometheusRequestHandlerConfig::Type::RemoteRead;
|
|
res.time_series_table_name = parseTableNameFromConfig(config, config_prefix);
|
|
parseCommonConfig(config, res);
|
|
return res;
|
|
}
|
|
|
|
/// Parses a configuration like this:
|
|
/// <type>expose_metrics</type>
|
|
/// <metrics>true</metrics>
|
|
/// <asynchronous_metrics>true</asynchronous_metrics>
|
|
/// <events>true</events>
|
|
/// <errors>true</errors>
|
|
/// -OR-
|
|
/// <type>remote_write</type>
|
|
/// <table>db.time_series_table_name</table>
|
|
PrometheusRequestHandlerConfig parseHandlerConfig(const Poco::Util::AbstractConfiguration & config, const String & config_prefix)
|
|
{
|
|
String type = config.getString(config_prefix + ".type");
|
|
|
|
if (type == "expose_metrics")
|
|
return parseExposeMetricsConfig(config, config_prefix);
|
|
if (type == "remote_write")
|
|
return parseRemoteWriteConfig(config, config_prefix);
|
|
if (type == "remote_read")
|
|
return parseRemoteReadConfig(config, config_prefix);
|
|
throw Exception(
|
|
ErrorCodes::UNKNOWN_ELEMENT_IN_CONFIG, "Unknown type {} is specified in the configuration for a prometheus protocol", type);
|
|
}
|
|
|
|
/// Returns true if the protocol represented by a passed config can be handled.
|
|
bool canBeHandled(const PrometheusRequestHandlerConfig & config, bool for_keeper)
|
|
{
|
|
/// The standalone ClickHouse Keeper can only expose its metrics.
|
|
/// It can't handle other Prometheus protocols.
|
|
return !for_keeper || (config.type == PrometheusRequestHandlerConfig::Type::ExposeMetrics);
|
|
}
|
|
|
|
/// Creates a writer which serializes exposing metrics.
|
|
std::shared_ptr<PrometheusMetricsWriter> createPrometheusMetricWriter(bool for_keeper)
|
|
{
|
|
if (for_keeper)
|
|
return std::make_unique<KeeperPrometheusMetricsWriter>();
|
|
return std::make_unique<PrometheusMetricsWriter>();
|
|
}
|
|
|
|
/// Base function for making a factory for PrometheusRequestHandler. This function can return nullptr.
|
|
std::shared_ptr<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>> createPrometheusHandlerFactoryFromConfig(
|
|
IServer & server,
|
|
const AsynchronousMetrics & async_metrics,
|
|
const PrometheusRequestHandlerConfig & config,
|
|
bool for_keeper)
|
|
{
|
|
if (!canBeHandled(config, for_keeper))
|
|
return nullptr;
|
|
auto metric_writer = createPrometheusMetricWriter(for_keeper);
|
|
auto creator = [&server, &async_metrics, config, metric_writer]() -> std::unique_ptr<PrometheusRequestHandler>
|
|
{
|
|
return std::make_unique<PrometheusRequestHandler>(server, config, async_metrics, metric_writer);
|
|
};
|
|
return std::make_shared<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>>(std::move(creator));
|
|
}
|
|
|
|
/// Generic function for createPrometheusHandlerFactory() and createKeeperPrometheusHandlerFactory().
|
|
HTTPRequestHandlerFactoryPtr createPrometheusHandlerFactoryImpl(
|
|
IServer & server,
|
|
const Poco::Util::AbstractConfiguration & config,
|
|
const AsynchronousMetrics & asynchronous_metrics,
|
|
const String & name,
|
|
bool for_keeper)
|
|
{
|
|
auto factory = std::make_shared<HTTPRequestHandlerFactoryMain>(name);
|
|
|
|
if (config.has("prometheus.handlers"))
|
|
{
|
|
Strings keys;
|
|
config.keys("prometheus.handlers", keys);
|
|
for (const String & key : keys)
|
|
{
|
|
String prefix = "prometheus.handlers." + key;
|
|
auto parsed_config = parseHandlerConfig(config, prefix + ".handler");
|
|
if (auto handler = createPrometheusHandlerFactoryFromConfig(server, asynchronous_metrics, parsed_config, for_keeper))
|
|
{
|
|
handler->addFiltersFromConfig(config, prefix);
|
|
factory->addHandler(handler);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto parsed_config = parseExposeMetricsConfig(config, "prometheus");
|
|
if (auto handler = createPrometheusHandlerFactoryFromConfig(server, asynchronous_metrics, parsed_config, for_keeper))
|
|
{
|
|
String endpoint = config.getString("prometheus.endpoint", "/metrics");
|
|
handler->attachStrictPath(endpoint);
|
|
handler->allowGetAndHeadRequest();
|
|
factory->addHandler(handler);
|
|
}
|
|
}
|
|
|
|
return factory;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
HTTPRequestHandlerFactoryPtr createPrometheusHandlerFactory(
|
|
IServer & server,
|
|
const Poco::Util::AbstractConfiguration & config,
|
|
const AsynchronousMetrics & asynchronous_metrics,
|
|
const String & name)
|
|
{
|
|
return createPrometheusHandlerFactoryImpl(server, config, asynchronous_metrics, name, /* for_keeper= */ false);
|
|
}
|
|
|
|
|
|
HTTPRequestHandlerFactoryPtr createPrometheusHandlerFactoryForHTTPRule(
|
|
IServer & server,
|
|
const Poco::Util::AbstractConfiguration & config,
|
|
const String & config_prefix,
|
|
const AsynchronousMetrics & asynchronous_metrics)
|
|
{
|
|
auto parsed_config = parseExposeMetricsConfig(config, config_prefix + ".handler");
|
|
auto handler = createPrometheusHandlerFactoryFromConfig(server, asynchronous_metrics, parsed_config, /* for_keeper= */ false);
|
|
chassert(handler); /// `handler` can't be nullptr here because `for_keeper` is false.
|
|
handler->addFiltersFromConfig(config, config_prefix);
|
|
return handler;
|
|
}
|
|
|
|
|
|
HTTPRequestHandlerFactoryPtr createPrometheusHandlerFactoryForHTTPRuleDefaults(
|
|
IServer & server,
|
|
const Poco::Util::AbstractConfiguration & config,
|
|
const AsynchronousMetrics & asynchronous_metrics)
|
|
{
|
|
/// The "defaults" HTTP handler should serve the prometheus exposing metrics protocol on the http port
|
|
/// only if it isn't already served on its own port <prometheus.port> and if there is no <prometheus.handlers> section.
|
|
if (!config.has("prometheus") || config.getInt("prometheus.port", 0) || config.has("prometheus.handlers"))
|
|
return nullptr;
|
|
|
|
auto parsed_config = parseExposeMetricsConfig(config, "prometheus");
|
|
String endpoint = config.getString("prometheus.endpoint", "/metrics");
|
|
auto handler = createPrometheusHandlerFactoryFromConfig(server, asynchronous_metrics, parsed_config, /* for_keeper= */ false);
|
|
chassert(handler); /// `handler` can't be nullptr here because `for_keeper` is false.
|
|
handler->attachStrictPath(endpoint);
|
|
handler->allowGetAndHeadRequest();
|
|
return handler;
|
|
}
|
|
|
|
|
|
HTTPRequestHandlerFactoryPtr createKeeperPrometheusHandlerFactory(
|
|
IServer & server,
|
|
const Poco::Util::AbstractConfiguration & config,
|
|
const AsynchronousMetrics & asynchronous_metrics,
|
|
const String & name)
|
|
{
|
|
return createPrometheusHandlerFactoryImpl(server, config, asynchronous_metrics, name, /* for_keeper= */ true);
|
|
}
|
|
|
|
}
|