]> git.sesse.net Git - nageru/commitdiff
Add the first beginnings of Prometheus metrics.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 8 Jun 2017 22:15:19 +0000 (00:15 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 8 Jun 2017 22:15:19 +0000 (00:15 +0200)
Makefile
httpd.cpp
metrics.cpp [new file with mode: 0644]
metrics.h [new file with mode: 0644]
mixer.cpp
mixer.h

index 84e14df2f4c320941d19e93b6c814edb2b20c6e9..968b731c6ae522add32f9c347c91c05b0dee646a 100644 (file)
--- 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
index 858d9522c9354f1ea275cd0e859e5fef60d30636..4d5966b18e6ee0bdc9ed21f66ca98129690fbf48 100644 (file)
--- 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 (file)
index 0000000..4755ba0
--- /dev/null
@@ -0,0 +1,38 @@
+#include "metrics.h"
+
+#include <locale>
+#include <sstream>
+
+using namespace std;
+
+Metrics global_metrics;
+
+void Metrics::register_int_metric(const string &name, atomic<int64_t> *location)
+{
+       lock_guard<mutex> lock(mu);
+       int_metrics.emplace(name, location);
+}
+
+void Metrics::register_double_metric(const string &name, atomic<double> *location)
+{
+       lock_guard<mutex> lock(mu);
+       double_metrics.emplace(name, location);
+}
+
+string Metrics::serialize() const
+{
+       stringstream ss;
+       ss.imbue(locale("C"));
+       ss.precision(20);
+       ss << scientific;
+
+       lock_guard<mutex> 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 (file)
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 <atomic>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+
+class Metrics {
+public:
+       void register_int_metric(const std::string &name, std::atomic<int64_t> *location);
+       void register_double_metric(const std::string &name, std::atomic<double> *location);
+       std::string serialize() const;
+
+private:
+       mutable std::mutex mu;
+       std::unordered_map<std::string, std::atomic<int64_t> *> int_metrics;
+       std::unordered_map<std::string, std::atomic<double> *> double_metrics;
+};
+
+extern Metrics global_metrics;
+
+#endif  // !defined(_METRICS_H)
index fc56de3b3038153c85980d2e26836f74d6fb4c87..3547f01cb653ff63966aba63e94bb934982530b2 100644 (file)
--- 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<double>(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 73dfa3b53e5690a94557113a2d69fb8f9bfb1599..bb317c87ad503f8941513e7ba12682b0dbaf3f3d 100644 (file)
--- a/mixer.h
+++ b/mixer.h
@@ -518,6 +518,11 @@ private:
        std::vector<uint32_t> 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<int64_t> metrics_num_frames;
+       std::atomic<int64_t> metrics_dropped_frames;
+       std::atomic<double> metrics_uptime;
 };
 
 extern Mixer *global_mixer;