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