12 #include "serverpool.h"
18 extern ServerPool *servers;
20 StatsThread::StatsThread(const string &stats_file, int stats_interval)
21 : stats_file(stats_file),
22 stats_interval(stats_interval)
26 void StatsThread::do_work()
28 while (!should_stop()) {
33 vector<ClientStats> client_stats;
34 vector<HLSZombie> hls_zombies;
35 unordered_map<string, HLSZombie> remaining_hls_zombies;
37 if (clock_gettime(CLOCK_MONOTONIC_COARSE, &now) == -1) {
38 log_perror("clock_gettime(CLOCK_MONOTONIC_COARSE)");
42 // Open a new, temporary file.
43 filename = strdup((stats_file + ".new.XXXXXX").c_str());
44 fd = mkostemp(filename, O_WRONLY | O_CLOEXEC);
55 if (unlink(filename) == -1) {
62 // Get all the HLS zombies and combine them into one map (we resolve conflicts
63 // by having an arbitrary element win; in practice, that means the lowest
65 for (HLSZombie &zombie : servers->get_hls_zombies()) {
66 const string remote_addr = zombie.remote_addr;
67 remaining_hls_zombies[move(remote_addr)] = move(zombie);
70 // Remove all zombies whose ID match an already ongoing request.
71 // (Normally, this is cleared out already when it starts,
72 // but the request could happen on a different server from the zombie,
73 // or the zombie could be deserialized.)
74 for (const ClientStats &stats : servers->get_client_stats()) {
75 if (stats.url != "-") {
76 remaining_hls_zombies.erase(stats.hls_zombie_key);
80 for (const ClientStats &stats : servers->get_client_stats()) {
81 string url = stats.url;
83 // No download going on currently; could it be waiting for more HLS fragments?
84 auto it = remaining_hls_zombies.find(stats.remote_addr);
85 if (it != remaining_hls_zombies.end()) {
87 remaining_hls_zombies.erase(it);
91 fprintf(fp, "%s %d %d %s %d %llu %llu %llu \"%s\" \"%s\"\n",
92 stats.remote_addr.c_str(),
94 0, // Used to be fwmark.
96 int(now.tv_sec - stats.connect_time.tv_sec), // Rather coarse.
97 (long long unsigned)(stats.bytes_sent),
98 (long long unsigned)(stats.bytes_lost),
99 (long long unsigned)(stats.num_loss_events),
100 stats.referer.c_str(),
101 stats.user_agent.c_str());
103 for (const auto &url_and_zombie : remaining_hls_zombies) {
104 const HLSZombie &zombie = url_and_zombie.second;
105 fprintf(fp, "%s %d %d %s %d %llu %llu %llu \"%s\" \"%s\"\n",
106 zombie.remote_addr.c_str(),
107 0, // Fake socket. (The Munin script doesn't like negative numbers.)
108 0, // Used to be fwmark.
114 zombie.referer.c_str(),
115 zombie.user_agent.c_str());
117 if (fclose(fp) == EOF) {
118 log_perror("fclose");
119 if (unlink(filename) == -1) {
120 log_perror(filename);
126 if (rename(filename, stats_file.c_str()) == -1) {
127 log_perror("rename");
128 if (unlink(filename) == -1) {
129 log_perror(filename);
135 // Wait until we are asked to quit, stats_interval timeout,
136 // or a spurious signal. (The latter will cause us to write stats
137 // too often, but that's okay.)
139 timeout_ts.tv_sec = stats_interval;
140 timeout_ts.tv_nsec = 0;
141 wait_for_wakeup(&timeout_ts);