Re-run include-what-you-use.
[cubemap] / log.cpp
1 #include <assert.h>
2 #include <errno.h>
3 #include <stdarg.h>
4 #include <stddef.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <syslog.h>
8 #include <time.h>
9 #include <string>
10 #include <vector>
11
12 #include "log.h"
13
14 using namespace std;
15
16 // Yes, it's a bit ugly.
17 #define SYSLOG_FAKE_FILE (static_cast<FILE *>(NULL))
18
19 bool logging_started = false;
20 std::vector<FILE *> log_destinations;
21
22 void add_log_destination_file(const std::string &filename)
23 {
24         FILE *fp = fopen(filename.c_str(), "a");
25         if (fp == NULL) {
26                 perror(filename.c_str());
27                 return;
28         }
29
30         log_destinations.push_back(fp); 
31 }
32
33 void add_log_destination_console()
34 {
35         log_destinations.push_back(stderr);
36 }
37
38 void add_log_destination_syslog()
39 {
40         openlog("cubemap", LOG_PID, LOG_DAEMON);
41         log_destinations.push_back(SYSLOG_FAKE_FILE);
42 }
43
44 void start_logging()
45 {
46         logging_started = true;
47 }
48
49 void shut_down_logging()
50 {
51         for (size_t i = 0; i < log_destinations.size(); ++i) {
52                 if (log_destinations[i] == SYSLOG_FAKE_FILE) {
53                         closelog();
54                 } else if (log_destinations[i] != stderr) {
55                         if (fclose(log_destinations[i]) != 0) {
56                                 perror("fclose");
57                         }
58                 }
59         }
60         log_destinations.clear();
61         logging_started = false;
62 }
63
64 void log(LogLevel log_level, const char *fmt, ...)
65 {
66         char formatted_msg[4096];
67         va_list ap;
68         va_start(ap, fmt);
69         vsnprintf(formatted_msg, sizeof(formatted_msg), fmt, ap);
70         va_end(ap);
71
72         time_t now = time(NULL);
73         struct tm lt;
74         struct tm *ltime = localtime_r(&now, &lt);
75         char timestamp[1024];
76         if (ltime == NULL) {
77                 strcpy(timestamp, "???");
78         } else {
79                 strftime(timestamp, sizeof(timestamp), "%a, %d %b %Y %T %z", ltime);
80         }
81
82         const char *log_level_str;
83         int syslog_level;
84
85         switch (log_level) {
86         case INFO:
87                 log_level_str = "INFO:    ";
88                 syslog_level = LOG_INFO;
89                 break;
90         case WARNING:
91                 log_level_str = "WARNING: ";
92                 syslog_level = LOG_WARNING;
93                 break;
94         case ERROR:
95                 log_level_str = "ERROR:   ";
96                 syslog_level = LOG_ERR;
97                 break;
98         default:
99                 assert(false);
100         }
101
102         // Log to stderr if logging hasn't been set up yet. Note that this means
103         // that such messages will come even if there are no “error_log” lines.
104         if (!logging_started) {
105                 fprintf(stderr, "[%s] %s%s\n", timestamp, log_level_str, formatted_msg);
106                 return;
107         }
108
109         for (size_t i = 0; i < log_destinations.size(); ++i) {
110                 if (log_destinations[i] == SYSLOG_FAKE_FILE) {
111                         syslog(syslog_level, "%s", formatted_msg);
112                 } else {
113                         int err = fprintf(log_destinations[i], "[%s] %s%s\n", timestamp, log_level_str, formatted_msg);
114                         if (err < 0) {
115                                 perror("fprintf");
116                         }
117                         if (log_destinations[i] != stderr) {
118                                 fflush(log_destinations[i]);
119                         }
120                 }
121         }
122 }
123
124 void log_perror(const char *msg)
125 {
126         char errbuf[4096];
127         log(ERROR, "%s: %s", msg, strerror_r(errno, errbuf, sizeof(errbuf)));
128 }