/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team * Copyright (©) 1992,2007 Frank Tore Johansen * * Deliantra is free software: you can redistribute it and/or modify it under * the terms of the Affero GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the Affero GNU General Public License * and the GNU General Public License along with this program. If not, see * . * * The authors can be reached via e-mail to */ #include #include #include #include #include #include "global.h" #include "dynbuf.h" #include "util.h" struct logline { struct timeval tv; char *buf; // includes PREFIX_LEN garbage bytes int len; int flags; }; static SMUTEX(mutex); static SCOND(cond); static int logfd = STDERR_FILENO; static int logsync = 1; static std::vector > queue; void set_logfd (int fd) { SMUTEX_LOCK (mutex); logfd = fd < 0 ? STDERR_FILENO : fd; SMUTEX_UNLOCK (mutex); } #define PREFIX_LEN sizeof ("0000-00-00 00:00:00.0000+") - 1 static void log_sync (logline &line) { struct tm lt; char pfx [PREFIX_LEN]; localtime_r (&line.tv.tv_sec, <); sprintf (pfx, "%04d-%02d-%02d %02d:%02d:%02d.%04d", lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec, (int)(line.tv.tv_usec / 100) ); pfx [PREFIX_LEN - 1] = line.flags & logSync ? '=' : ' '; struct iovec iov [2]; iov [0].iov_base = pfx; iov [0].iov_len = PREFIX_LEN; char *buf = line.buf; while (char *end = strchr (buf, '\n')) { iov [1].iov_base = buf; iov [1].iov_len = end - buf + 1; writev (STDERR_FILENO, iov, 2); if (logfd != STDERR_FILENO) writev (logfd, iov, 2); buf = end + 1; if (buf == line.buf + line.len) break; pfx [PREFIX_LEN - 1] = '+'; } sfree (line.buf, line.len); } static void * logthread_proc (void *arg) { int idx = 0; for (;;) { logline line; SMUTEX_LOCK (mutex); while (queue.empty ()) SCOND_WAIT (cond, mutex); line = queue [idx++]; // this algorithm could result in an ever-increasing vector // size if we log faster than we can write, but if that happens // we have bigger problems. if (idx == queue.size ()) { queue.clear (); idx = 0; } SMUTEX_UNLOCK (mutex); log_sync (line); } } void log_cleanup () { logsync = 1; for (;;) { int done; SMUTEX_LOCK (mutex); done = queue.empty (); SMUTEX_UNLOCK (mutex); if (done) break; usleep (10000); } } static void af_child () { logsync = 1; } static struct logthread : thread { logthread () { pthread_atfork (0, 0, af_child); start (logthread_proc); logsync = 0; } } logthread; void LOG (int flags, const char *format, ...) { int level = flags & 15; if (level > settings.debug) return; logline line; gettimeofday (&line.tv, 0); static dynbuf_text buf; va_list ap; va_start (ap, format); buf.vprintf (format, ap); va_end (ap); buf << '\n'; line.buf = buf.linearise (); line.len = buf.size (); if (line.buf [line.len - 2] == '\n') --line.len; line.buf = salloc (line.len, line.buf); buf.clear (); if (logsync) flags |= logSync; if (flags & logBacktrace) { line.buf [line.len - 1] = 0; log_backtrace (line.buf); line.buf [line.len - 1] = '\n'; } line.flags = flags; if (line.flags & logSync) log_sync (line); else { SMUTEX_LOCK (mutex); queue.push_back (line); SMUTEX_UNLOCK (mutex); SCOND_SIGNAL (cond); } }