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