2 * UCW Library -- Logging to Files
4 * (c) 1997--2009 Martin Mares <mj@ucw.cz>
5 * (c) 2008 Tomas Gavenciak <gavento@ucw.cz>
7 * This software may be freely distributed and used according to the terms
8 * of the GNU Lesser General Public License.
13 #include "ucw/log-internal.h"
15 #include "ucw/threads.h"
16 #include "ucw/simple-lists.h"
25 struct log_stream ls; // ls.name is the current name of the log file
28 char *orig_name; // Original name with strftime escapes
32 FF_FORMAT_NAME = 1, // Name contains strftime escapes
33 FF_CLOSE_FD = 2, // Close the fd with the stream
34 FF_FD2_FOLLOWS = 4, // Maintain stderr as a clone of this stream
37 #define MAX_EXPAND 64 // Maximum size of expansion of strftime escapes
39 static int log_switch_nest;
42 do_log_reopen(struct file_stream *fs, const char *name)
44 int fd = ucw_open(name, O_WRONLY | O_CREAT | O_APPEND, 0666);
46 die("Unable to open log file %s: %m", name);
50 if (fs->flags & FF_FD2_FOLLOWS)
55 fs->ls.name = NULL; // We have to keep the stream consistent -- die() below can invoke logging
57 fs->ls.name = xstrdup(name);
61 do_log_switch(struct file_stream *fs, struct tm *tm)
63 if (!(fs->flags & FF_FORMAT_NAME))
69 do_log_reopen(fs, fs->orig_name);
74 int buflen = strlen(fs->orig_name) + MAX_EXPAND;
79 if (!log_switch_nest) // Avoid infinite loops if we die when switching logs
82 int l = strftime(name, buflen, fs->orig_name, tm);
83 if (l < 0 || l >= buflen)
84 die("Error formatting log file name: %m");
85 if (!fs->ls.name || strcmp(name, fs->ls.name))
87 do_log_reopen(fs, name);
96 /* destructor for standard files */
98 file_close(struct log_stream *ls)
100 struct file_stream *fs = (struct file_stream *) ls;
101 if ((fs->flags & FF_CLOSE_FD) && fs->fd >= 0)
104 xfree(fs->orig_name);
107 /* handler for standard files */
109 file_handler(struct log_stream *ls, struct log_msg *m)
111 struct file_stream *fs = (struct file_stream *) ls;
112 if ((fs->flags & FF_FORMAT_NAME) && m->tm)
113 do_log_switch(fs, m->tm);
115 int r = write(fs->fd, m->m, m->m_len);
116 /* FIXME: check for errors here? */
120 /* assign log to a file descriptor */
121 /* initialize with the default formatting, does NOT close the descriptor */
125 struct log_stream *ls = log_new_stream(sizeof(struct file_stream));
126 struct file_stream *fs = (struct file_stream *) ls;
128 ls->msgfmt = LSFMT_DEFAULT;
129 ls->handler = file_handler;
130 ls->close = file_close;
131 ls->name = xmalloc(16);
132 snprintf(ls->name, 16, "fd%d", fd);
136 static struct log_stream *
137 do_log_new_file(const char *path, uns more_flags)
139 struct log_stream *ls = log_new_stream(sizeof(struct file_stream));
140 struct file_stream *fs = (struct file_stream *) ls;
142 fs->orig_name = xstrdup(path);
143 if (strchr(path, '%'))
144 fs->flags = FF_FORMAT_NAME;
145 fs->flags |= FF_CLOSE_FD | more_flags;
146 ls->msgfmt = LSFMT_DEFAULT;
147 ls->handler = file_handler;
148 ls->close = file_close;
150 time_t now = time(NULL);
151 struct tm *tm = localtime(&now);
153 do_log_switch(fs, tm); // die()'s on errors
157 /* open() a file (append mode) */
158 /* initialize with the default formatting */
160 log_new_file(const char *path)
162 return do_log_new_file(path, 0);
168 time_t now = time(NULL);
169 struct tm *tm = localtime(&now);
173 for (int i=0; i < log_streams_after; i++)
174 if (log_streams.ptr[i]->handler == file_handler)
175 switched |= do_log_switch((struct file_stream *) log_streams.ptr[i], tm);
180 log_switch_disable(void)
186 log_switch_enable(void)
188 ASSERT(log_switch_nest);
192 /* Emulate the old single-file interface: close the existing log file and open a new one. */
194 log_file(const char *name)
199 struct log_stream *ls = do_log_new_file(name, FF_FD2_FOLLOWS);
200 struct log_stream *def = log_stream_by_flags(0);
201 log_rm_substream(def, NULL);
202 log_add_substream(def, ls);
207 int main(int argc, char **argv)
210 log_file("/proc/self/fd/1");
211 // struct log_stream *ls = log_new_fd(1);
212 // struct log_stream *ls = log_new_file("/tmp/quork-%Y%m%d-%H%M%S");
213 for (int i=1; i<argc; i++)
214 msg(L_INFO, argv[i]);