]> git.sesse.net Git - cubemap/blobdiff - log.cpp
Implement much better logging, with support for both files and syslog.
[cubemap] / log.cpp
diff --git a/log.cpp b/log.cpp
new file mode 100644 (file)
index 0000000..ba05a64
--- /dev/null
+++ b/log.cpp
@@ -0,0 +1,112 @@
+#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]);
+                       }
+               }
+       }
+}