+#include "log.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <assert.h>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+// Yes, it's a bit ugly.
+#define SYSLOG_FAKE_FILE (static_cast<FILE *>(NULL))
+
+bool logging_started = false;
+std::vector<FILE *> log_destinations;
+
+void add_log_destination_file(const std::string &filename)
+{
+ FILE *fp = fopen(filename.c_str(), "a");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return;
+ }
+
+ log_destinations.push_back(fp);
+}
+
+void add_log_destination_console()
+{
+ log_destinations.push_back(stderr);
+}
+
+void add_log_destination_syslog()
+{
+ openlog("cubemap", LOG_PID, LOG_DAEMON);
+ log_destinations.push_back(SYSLOG_FAKE_FILE);
+}
+
+void start_logging()
+{
+ logging_started = true;
+}
+
+void shut_down_logging()
+{
+ for (size_t i = 0; i < log_destinations.size(); ++i) {
+ if (log_destinations[i] == SYSLOG_FAKE_FILE) {
+ closelog();
+ } else if (log_destinations[i] != stderr) {
+ if (fclose(log_destinations[i]) != 0) {
+ perror("fclose");
+ }
+ }
+ }
+ log_destinations.clear();
+ logging_started = false;
+}
+
+void log(LogLevel log_level, const char *fmt, ...)
+{
+ char formatted_msg[4096];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(formatted_msg, sizeof(formatted_msg), fmt, ap);
+ va_end(ap);
+
+ const char *log_level_str;
+ int syslog_level;
+
+ switch (log_level) {
+ case NO_LEVEL:
+ log_level_str = "";
+ syslog_level = LOG_INFO;
+ break;
+ case INFO:
+ log_level_str = "INFO: ";
+ syslog_level = LOG_INFO;
+ break;
+ case WARNING:
+ log_level_str = "WARNING: ";
+ syslog_level = LOG_WARNING;
+ break;
+ case ERROR:
+ log_level_str = "ERROR: ";
+ syslog_level = LOG_ERR;
+ break;
+ default:
+ assert(false);
+ }
+
+ // Log to stderr if logging hasn't been set up yet. Note that this means
+ // that such messages will come even if there are no “error_log” lines.
+ if (!logging_started) {
+ fprintf(stderr, "%s%s\n", log_level_str, formatted_msg);
+ return;
+ }
+
+ for (size_t i = 0; i < log_destinations.size(); ++i) {
+ if (log_destinations[i] == SYSLOG_FAKE_FILE) {
+ syslog(syslog_level, "%s", formatted_msg);
+ } else {
+ int err = fprintf(log_destinations[i], "%s%s\n", log_level_str, formatted_msg);
+ if (err < 0) {
+ perror("fprintf");
+ }
+ if (log_destinations[i] != stderr) {
+ fflush(log_destinations[i]);
+ }
+ }
+ }
+}