summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2013-04-14 13:13:19 +0200
committerYorhel <git@yorhel.nl>2013-04-14 13:16:16 +0200
commitdd99f9dddd89d671c0eec8be86d4eae80aa53059 (patch)
tree0fe1897b90a82a56e729f576bef1562f12f36788
parentb54959df98d29dd3aec1725c239db261d4f28d7c (diff)
Add util/logfile abstraction and -l, --log-file option
-rw-r--r--Makefile.am2
-rw-r--r--doc/globster.pod8
-rw-r--r--src/global.h1
-rw-r--r--src/main.c20
-rw-r--r--src/util/logfile.c135
-rw-r--r--src/util/logfile.h59
6 files changed, 223 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index 022c30c..3eb5677 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -109,6 +109,7 @@ globster_SOURCES=\
src/main.c\
src/util/adc.c\
src/util/base32.c\
+ src/util/logfile.c\
src/util/netstream.c\
src/util/netutil.c\
src/util/nmdc.c
@@ -133,6 +134,7 @@ noinst_HEADERS=\
src/main.h\
src/util/adc.h\
src/util/base32.h\
+ src/util/logfile.h\
src/util/netstream.h\
src/util/netutil.h\
src/util/nmdc.h
diff --git a/doc/globster.pod b/doc/globster.pod
index 66c41c2..2c8dbdf 100644
--- a/doc/globster.pod
+++ b/doc/globster.pod
@@ -37,7 +37,13 @@ Connect to the system D-Bus.
Connect to the session D-Bus (default).
-=item --log-level I<level>
+=item --log-file I<file>
+
+Write the logs to the specified file. The special values C<stdout> and
+C<stderr> can be used to log to standard output or standard error,
+respectively.
+
+=item -l, --log-level I<level>
The log level. Can be set separately for each source file and supports wildcard
matching. For example C<hub/*:3,*:2> sets the log level for all hub-related
diff --git a/src/global.h b/src/global.h
index 4bbdf36..c6ba8c2 100644
--- a/src/global.h
+++ b/src/global.h
@@ -72,6 +72,7 @@
#include <compat.h>
#include <util/adc.h>
#include <util/base32.h>
+#include <util/logfile.h>
#include <util/netstream.h>
#include <util/netutil.h>
#include <util/nmdc.h>
diff --git a/src/main.c b/src/main.c
index 68d90f5..7ba8841 100644
--- a/src/main.c
+++ b/src/main.c
@@ -35,8 +35,11 @@ main_state_t main_state;
static bool conf_system = false;
static bool conf_autoconnect = true;
+static char *conf_log_file = "stderr";
static char *conf_log_level = NULL;
static char *conf_session_dir = NULL;
+
+static logfile_t *log_file;
static ev_signal termsig, intsig;
@@ -100,8 +103,13 @@ static void log_handler(const char *file, int line, int level, const char *messa
case YLOG_TRACE: strcpy(lvl, "trace"); break;
default: sprintf(lvl, "%5d", level);
}
+
pthread_mutex_lock(&m);
- fprintf(stderr, "%s -%s- %s:%d: %s\n", tstr, lvl, file, line, message);
+ if(log_file)
+ logfile_logf(log_file, "%s -%s- %s:%d: %s\n", tstr, lvl, file, line, message);
+ else
+ fprintf(strcmp(conf_log_file, "stderr") == 0 ? stderr : stdout,
+ "%s -%s- %s:%d: %s\n", tstr, lvl, file, line, message);
pthread_mutex_unlock(&m);
}
@@ -130,6 +138,7 @@ static void print_help() {
" -h, --help This help message\n"
" --system Connect to the D-Bus system bus\n"
" --session Connect to the D-Bus session bus (default)\n"
+ " --log-file FILE Log to the given file, `stdout' or `stderr'\n"
" --log-level Set the log level\n"
" -c, --session-dir PATH Set the session directory\n"
" -n Disable autoconnect");
@@ -146,6 +155,7 @@ static void argv_parse(int argc, char **argv) {
{ 'h', 0, "-h,--help" },
{ 'Y', 0, "--system" },
{ 'S', 0, "--session" },
+ { 'l', 1, "-l,--log-file"},
{ 'L', 1, "--log-level" },
{ 'c', 1, "-c,--session-dir" },
{ 'n', 0, "-n" },
@@ -163,6 +173,7 @@ static void argv_parse(int argc, char **argv) {
exit(0);
case 'Y': conf_system = true; break;
case 'S': conf_system = false; break;
+ case 'l': conf_log_file = val; break;
case 'L': conf_log_level = val; break;
case 'c': conf_session_dir = val; break;
case 'n': conf_autoconnect = false; break;
@@ -205,6 +216,9 @@ int main(int argc, char **argv) {
argv_parse(argc, argv);
main_state = MAIN_INIT;
+
+ if(strcmp(conf_log_file, "stderr") != 0 && strcmp(conf_log_file, "stdout") != 0)
+ log_file = logfile_open(conf_log_file);
if(!conf_log_level)
conf_log_level = getenv("YLOG_LEVEL");
ylog_set_level(YLOG_DEFAULT, conf_log_level);
@@ -244,6 +258,10 @@ int main(int argc, char **argv) {
db_destroy();
yinfo("Clean shutdown");
+
+ if(log_file)
+ logfile_close(log_file);
+
return 0;
}
diff --git a/src/util/logfile.c b/src/util/logfile.c
new file mode 100644
index 0000000..3e79b1f
--- /dev/null
+++ b/src/util/logfile.c
@@ -0,0 +1,135 @@
+/* Copyright (c) 2012-2013 Yoran Heling
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <global.h>
+
+
+struct logfile_t {
+ logfile_t *next, *prev;
+ char *fn;
+ int fd;
+ struct stat st;
+};
+
+static logfile_t *logfile_list;
+
+
+/* (Re)opens the log file and checks for inode and file size changes. */
+static void logfile_checkfile(logfile_t *l) {
+ struct stat st;
+
+ if(l->fd >= 0 && (stat(l->fn, &st) < 0
+ || (l->st.st_ino != st.st_ino || l->st.st_size > st.st_size))) {
+ /* If we were unable to stat our log file, assume it has been rotated
+ * and recreate it. If our log file has either been truncated or
+ * replaced with a different file, let's reopen it as well. */
+ close(l->fd);
+ l->fd = -1;
+ }
+
+ if(l->fd >= 0) {
+ l->st = st;
+ return;
+ }
+
+ l->fd = open(l->fn, O_WRONLY|O_APPEND|O_CREAT, 0666);
+
+ /* XXX: What do we do if open() failed? */
+ if(l->fd < 0)
+ return;
+
+ /* Stat the fd in order to fill out l->st with meaningful data for the next
+ * check. */
+ if(fstat(l->fd, &st) == 0)
+ l->st = st;
+ else {
+ /* Let's... just try again later. */
+ close(l->fd);
+ l->fd = -1;
+ }
+}
+
+
+logfile_t *logfile_open(const char *fn) {
+ logfile_t *l = malloc(sizeof(logfile_t));
+ l->fn = strdup(fn);
+ l->fd = -1;
+
+ l->next = logfile_list;
+ l->prev = NULL;
+ if(logfile_list)
+ logfile_list->prev = l;
+ logfile_list = l;
+
+ logfile_checkfile(l);
+
+ return l;
+}
+
+
+void logfile_close(logfile_t *l) {
+ if(l->next)
+ l->next->prev = l->prev;
+ if(l->prev)
+ l->prev->next = l->next;
+ if(logfile_list == l)
+ logfile_list = l->next;
+
+ if(l->fd >= 0)
+ close(l->fd);
+
+ free(l->fn);
+ free(l);
+}
+
+
+void logfile_log(logfile_t *l, const char *str) {
+ logfile_checkfile(l);
+ if(l->fd < 0)
+ return;
+
+ int len = strlen(str);
+ int wr = 0;
+ int r;
+ /* XXX: What to do if write() failed? */
+ while(wr < len && (r = write(l->fd, str+wr, len-wr)) > 0)
+ wr += r;
+}
+
+
+static void logfile_reopen(logfile_t *l) {
+ if(l->fd >= 0) {
+ close(l->fd);
+ l->fd = -1;
+ }
+ logfile_checkfile(l);
+}
+
+
+void logfile_global_reopen() {
+ logfile_t *l;
+ for(l=logfile_list; l; l=l->next)
+ logfile_reopen(l);
+}
+
+
+/* vim: set noet sw=4 ts=4: */
diff --git a/src/util/logfile.h b/src/util/logfile.h
new file mode 100644
index 0000000..ca0c564
--- /dev/null
+++ b/src/util/logfile.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2012-2013 Yoran Heling
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTIL_LOGFILE_H
+#define UTIL_LOGFILE_H
+
+/* This abstraction allows logging to files. The lines are logged as they are
+ * given, without timestamp or other information. A global list of all log
+ * files is kept in order to provide a mechanism to re-open all logs (on
+ * SIGHUP, for example).
+ *
+ * These functions are not thread-safe. I/O errors are silently ignored. :-(
+ *
+ * TODO: Currently, logfile_log() flushes each line to the OS before returning.
+ * This is required if you don't want to lose lines in the case of a crash, but
+ * hinders performance if losing the very last entries is acceptable, for
+ * example with chat logs. An interface should be added to allow deferred
+ * flushing of the log entries.
+ */
+
+typedef struct logfile_t logfile_t;
+
+logfile_t *logfile_open(const char *fn);
+
+void logfile_close(logfile_t *);
+
+void logfile_log(logfile_t *, const char *);
+
+#define logfile_logf(logfile_l, ...) do {\
+ kstring_t logfile_fmt = {};\
+ ksprintf(&logfile_fmt, __VA_ARGS__);\
+ logfile_log(logfile_l, logfile_fmt.s);\
+ free(logfile_fmt.s);\
+ } while(0)
+
+void logfile_global_reopen();
+
+#endif
+/* vim: set noet sw=4 ts=4: */