From: Steinar H. Gunderson Date: Thu, 8 Jun 2017 22:15:19 +0000 (+0200) Subject: Add the first beginnings of Prometheus metrics. X-Git-Tag: 1.6.1~77 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=42cd4144d95cb26e61f7c6ccc4bf4813845dc291 Add the first beginnings of Prometheus metrics. --- diff --git a/Makefile b/Makefile index 84e14df..968b731 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ OBJS += midi_mapper.o midi_mapping.pb.o # Mixer objects AUDIO_MIXER_OBJS = audio_mixer.o alsa_input.o alsa_pool.o ebu_r128_proc.o stereocompressor.o resampling_queue.o flags.o correlation_measurer.o filter.o input_mapping.o state.pb.o -OBJS += chroma_subsampler.o v210_converter.o mixer.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o httpd.o flags.o image_input.o alsa_output.o disk_space_estimator.o print_latency.o timecode_renderer.o tweaked_inputs.o $(AUDIO_MIXER_OBJS) +OBJS += chroma_subsampler.o v210_converter.o mixer.o metrics.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o httpd.o flags.o image_input.o alsa_output.o disk_space_estimator.o print_latency.o timecode_renderer.o tweaked_inputs.o $(AUDIO_MIXER_OBJS) # Streaming and encoding objects OBJS += quicksync_encoder.o x264_encoder.o x264_dynamic.o x264_speed_control.o video_encoder.o metacube2.o mux.o audio_encoder.o ffmpeg_raii.o ffmpeg_util.o diff --git a/httpd.cpp b/httpd.cpp index 858d952..4d5966b 100644 --- a/httpd.cpp +++ b/httpd.cpp @@ -13,6 +13,7 @@ #include "defs.h" #include "metacube2.h" +#include "metrics.h" struct MHD_Connection; struct MHD_Response; @@ -85,14 +86,23 @@ int HTTPD::answer_to_connection(MHD_Connection *connection, } *con_cls = stream; - // Does not strictly have to be equal to MUX_BUFFER_SIZE. - MHD_Response *response = MHD_create_response_from_callback( - (size_t)-1, MUX_BUFFER_SIZE, &HTTPD::Stream::reader_callback_thunk, stream, &HTTPD::free_stream); + MHD_Response *response; - // TODO: Content-type? - if (framing == HTTPD::Stream::FRAMING_METACUBE) { - MHD_add_response_header(response, "Content-encoding", "metacube"); + if (strcmp(url, "/metrics") == 0) { + string contents = global_metrics.serialize(); + response = MHD_create_response_from_buffer( + contents.size(), &contents[0], MHD_RESPMEM_MUST_COPY); + MHD_add_response_header(response, "Content-type", "text/plain"); + } else { + // Does not strictly have to be equal to MUX_BUFFER_SIZE. + response = MHD_create_response_from_callback( + (size_t)-1, MUX_BUFFER_SIZE, &HTTPD::Stream::reader_callback_thunk, stream, &HTTPD::free_stream); + // TODO: Content-type? + if (framing == HTTPD::Stream::FRAMING_METACUBE) { + MHD_add_response_header(response, "Content-encoding", "metacube"); + } } + int ret = MHD_queue_response(connection, MHD_HTTP_OK, response); MHD_destroy_response(response); // Only decreases the refcount; actual free is after the request is done. diff --git a/metrics.cpp b/metrics.cpp new file mode 100644 index 0000000..4755ba0 --- /dev/null +++ b/metrics.cpp @@ -0,0 +1,38 @@ +#include "metrics.h" + +#include +#include + +using namespace std; + +Metrics global_metrics; + +void Metrics::register_int_metric(const string &name, atomic *location) +{ + lock_guard lock(mu); + int_metrics.emplace(name, location); +} + +void Metrics::register_double_metric(const string &name, atomic *location) +{ + lock_guard lock(mu); + double_metrics.emplace(name, location); +} + +string Metrics::serialize() const +{ + stringstream ss; + ss.imbue(locale("C")); + ss.precision(20); + ss << scientific; + + lock_guard lock(mu); + for (const auto &key_and_value : int_metrics) { + ss << key_and_value.first.c_str() << " " << key_and_value.second->load() << "\n"; + } + for (const auto &key_and_value : double_metrics) { + ss << key_and_value.first.c_str() << " " << key_and_value.second->load() << "\n"; + } + + return ss.str(); +} diff --git a/metrics.h b/metrics.h new file mode 100644 index 0000000..6c72ad5 --- /dev/null +++ b/metrics.h @@ -0,0 +1,28 @@ +#ifndef _METRICS_H +#define _METRICS_H 1 + +// A simple global class to keep track of metrics export in Prometheus format. +// It would be better to use a more full-featured Prometheus client library for this, +// but it would introduce a dependency that is not commonly packaged in distributions, +// which makes it quite unwieldy. Thus, we'll package our own for the time being. + +#include +#include +#include +#include + +class Metrics { +public: + void register_int_metric(const std::string &name, std::atomic *location); + void register_double_metric(const std::string &name, std::atomic *location); + std::string serialize() const; + +private: + mutable std::mutex mu; + std::unordered_map *> int_metrics; + std::unordered_map *> double_metrics; +}; + +extern Metrics global_metrics; + +#endif // !defined(_METRICS_H) diff --git a/mixer.cpp b/mixer.cpp index fc56de3..3547f01 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -42,6 +42,7 @@ #include "ffmpeg_capture.h" #include "flags.h" #include "input_mapping.h" +#include "metrics.h" #include "pbo_frame_allocator.h" #include "ref_counted_gl_sync.h" #include "resampling_queue.h" @@ -388,6 +389,10 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) desired_output_card_index = global_flags.output_card; set_output_card_internal(global_flags.output_card); } + + global_metrics.register_int_metric("nageru_num_frames", &metrics_num_frames); + global_metrics.register_int_metric("nageru_dropped_frames", &metrics_dropped_frames); + global_metrics.register_double_metric("nageru_uptime", &metrics_uptime); } Mixer::~Mixer() @@ -871,6 +876,11 @@ void Mixer::thread_func() now = steady_clock::now(); double elapsed = duration(now - start).count(); + + metrics_num_frames = frame_num; + metrics_dropped_frames = stats_dropped_frames; + metrics_uptime = elapsed; + if (frame_num % 100 == 0) { printf("%d frames (%d dropped) in %.3f seconds = %.1f fps (%.1f ms/frame)", frame_num, stats_dropped_frames, elapsed, frame_num / elapsed, diff --git a/mixer.h b/mixer.h index 73dfa3b..bb317c8 100644 --- a/mixer.h +++ b/mixer.h @@ -518,6 +518,11 @@ private: std::vector mode_scanlist[MAX_VIDEO_CARDS]; unsigned mode_scanlist_index[MAX_VIDEO_CARDS]{ 0 }; std::chrono::steady_clock::time_point last_mode_scan_change[MAX_VIDEO_CARDS]; + + // Metrics. + std::atomic metrics_num_frames; + std::atomic metrics_dropped_frames; + std::atomic metrics_uptime; }; extern Mixer *global_mixer;