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