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.
14 #include "ucw/threads.h"
15 #include "ucw/simple-lists.h"
24 * Use of the private fields of struct log_stream:
26 * idata file descriptor
27 * udata various flags (FF_xxx)
28 * pdata original name with strftime escapes
29 * name current name of the log file
30 * (a dynamically allocated buffer)
34 FF_FORMAT_NAME = 1, // Name contains strftime escapes
35 FF_CLOSE_FD = 2, // Close the fd with the stream
38 #define MAX_EXPAND 64 // Maximum size of expansion of strftime escapes
40 static int log_switch_nest;
43 do_log_reopen(struct log_stream *ls, const char *name)
45 int fd = ucw_open(name, O_WRONLY | O_CREAT | O_APPEND, 0666);
47 die("Unable to open log file %s: %m", name);
58 ls->name = NULL; // We have to keep this consistent, die() below can invoke logging
60 ls->name = xstrdup(name);
64 do_log_switch(struct log_stream *ls, struct tm *tm)
66 if (!(ls->udata & FF_FORMAT_NAME))
72 do_log_reopen(ls, ls->pdata);
77 int buflen = strlen(ls->pdata) + MAX_EXPAND;
82 if (!log_switch_nest) // Avoid infinite loops if we die when switching logs
85 int l = strftime(name, buflen, ls->pdata, tm);
86 if (l < 0 || l >= buflen)
87 die("Error formatting log file name: %m");
88 if (!ls->name || strcmp(name, ls->name))
90 do_log_reopen(ls, name);
99 /* Emulate the old single-file interface: close the existing log file and open a new one. */
101 log_file(const char *name)
106 struct log_stream *ls = log_new_file(name);
107 struct log_stream *def = log_stream_by_flags(0);
109 while (s = clist_head(&def->substreams))
111 struct log_stream *old = s->p;
112 log_rm_substream(def, old);
113 if (old != (struct log_stream *) &log_stream_default)
114 log_close_stream(old);
116 dup2(ls->idata, 2); // Let fd2 be an alias for the log file
117 log_add_substream(def, ls);
120 /* destructor for standard files */
122 file_close(struct log_stream *ls)
124 if ((ls->udata & FF_CLOSE_FD) && ls->idata >= 0)
129 /* handler for standard files */
131 file_handler(struct log_stream *ls, struct log_msg *m)
133 if ((ls->udata & FF_FORMAT_NAME) && m->tm)
134 do_log_switch(ls, m->tm);
136 int r = write(ls->idata, m->m, m->m_len);
137 /* FIXME: check for errors here? */
141 /* assign log to a file descriptor */
142 /* initialize with the default formatting, does NOT close the descriptor */
146 struct log_stream *ls = log_new_stream();
148 ls->msgfmt = LSFMT_DEFAULT;
149 ls->handler = file_handler;
150 ls->close = file_close;
151 ls->name = xmalloc(16);
152 snprintf(ls->name, 16, "fd%d", fd);
156 /* open() a file (append mode) */
157 /* initialize with the default formatting */
159 log_new_file(const char *path)
161 struct log_stream *ls = log_new_stream();
163 ls->pdata = (void *) path;
164 if (strchr(path, '%'))
165 ls->udata = FF_FORMAT_NAME;
166 ls->udata |= FF_CLOSE_FD;
167 ls->msgfmt = LSFMT_DEFAULT;
168 ls->handler = file_handler;
169 ls->close = file_close;
171 time_t now = time(NULL);
172 struct tm *tm = localtime(&now);
174 do_log_switch(ls, tm); // die()'s on errors
181 time_t now = time(NULL);
182 struct tm *tm = localtime(&now);
186 for (int i=0; i < log_streams_after; i++)
187 if (log_streams.ptr[i]->handler == file_handler)
188 switched |= do_log_switch(log_streams.ptr[i], tm);
193 log_switch_disable(void)
199 log_switch_enable(void)
201 ASSERT(log_switch_nest);
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]);