2 * DEBUG: section 50 Log file handling
3 * AUTHOR: Adrian Chadd <adrian@squid-cache.org>
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 #include "comm/Loops.h"
38 #include "log/Config.h"
40 #include "log/ModDaemon.h"
42 #include "SquidConfig.h"
43 #include "SquidTime.h"
49 /* How many buffers to keep before we say we've buffered too much */
50 #define LOGFILE_MAXBUFS 128
52 /* Size of the logfile buffer */
54 * For optimal performance this should match LOGFILE_BUFSIZ in logfile-daemon.c
56 #define LOGFILE_BUFSZ 32768
58 /* How many seconds between warnings */
59 #define LOGFILE_WARN_TIME 30
61 static LOGWRITE logfile_mod_daemon_writeline
;
62 static LOGLINESTART logfile_mod_daemon_linestart
;
63 static LOGLINEEND logfile_mod_daemon_lineend
;
64 static LOGROTATE logfile_mod_daemon_rotate
;
65 static LOGFLUSH logfile_mod_daemon_flush
;
66 static LOGCLOSE logfile_mod_daemon_close
;
68 static void logfile_mod_daemon_append(Logfile
* lf
, const char *buf
, int len
);
80 typedef struct _l_daemon l_daemon_t
;
84 logfileNewBuffer(Logfile
* lf
)
86 l_daemon_t
*ll
= (l_daemon_t
*) lf
->data
;
89 debugs(50, 5, "logfileNewBuffer: " << lf
->path
<< ": new buffer");
91 b
= static_cast<logfile_buffer_t
*>(xcalloc(1, sizeof(logfile_buffer_t
)));
93 b
->buf
= static_cast<char*>(xcalloc(1, LOGFILE_BUFSZ
));
94 assert(b
->buf
!= NULL
);
95 b
->size
= LOGFILE_BUFSZ
;
98 dlinkAddTail(b
, &b
->node
, &ll
->bufs
);
103 logfileFreeBuffer(Logfile
* lf
, logfile_buffer_t
* b
)
105 l_daemon_t
*ll
= (l_daemon_t
*) lf
->data
;
107 dlinkDelete(&b
->node
, &ll
->bufs
);
114 logfileHandleWrite(int fd
, void *data
)
116 Logfile
*lf
= static_cast<Logfile
*>(data
);
117 l_daemon_t
*ll
= static_cast<l_daemon_t
*>(lf
->data
);
120 * We'll try writing the first entry until its done - if we
121 * get a partial write then we'll re-schedule until its completed.
122 * Its naive but it'll do for now.
124 if (!ll
->bufs
.head
) // abort if there is nothing pending right now.
127 logfile_buffer_t
*b
= static_cast<logfile_buffer_t
*>(ll
->bufs
.head
->data
);
129 ll
->flush_pending
= 0;
131 int ret
= FD_WRITE_METHOD(ll
->wfd
, b
->buf
+ b
->written_len
, b
->len
- b
->written_len
);
132 debugs(50, 3, lf
->path
<< ": write returned " << ret
);
134 if (ignoreErrno(errno
)) {
135 /* something temporary */
136 Comm::SetSelect(ll
->wfd
, COMM_SELECT_WRITE
, logfileHandleWrite
, lf
, 0);
137 ll
->flush_pending
= 1;
140 debugs(50, DBG_IMPORTANT
,"logfileHandleWrite: " << lf
->path
<< ": error writing (" << xstrerror() << ")");
141 /* XXX should handle this better */
142 fatal("I don't handle this error well!");
146 debugs(50, DBG_IMPORTANT
, "logfileHandleWrite: " << lf
->path
<< ": wrote 0 bytes?");
147 /* XXX should handle this better */
148 fatal("I don't handle this error well!");
150 /* ret > 0, so something was written */
151 b
->written_len
+= ret
;
152 assert(b
->written_len
<= b
->len
);
153 if (b
->written_len
== b
->len
) {
154 /* written the whole buffer! */
155 logfileFreeBuffer(lf
, b
);
158 /* Is there more to write? */
161 /* there is, so schedule more */
163 Comm::SetSelect(ll
->wfd
, COMM_SELECT_WRITE
, logfileHandleWrite
, lf
, 0);
164 ll
->flush_pending
= 1;
169 logfileQueueWrite(Logfile
* lf
)
171 l_daemon_t
*ll
= (l_daemon_t
*) lf
->data
;
172 if (ll
->flush_pending
|| ll
->bufs
.head
== NULL
) {
175 ll
->flush_pending
= 1;
177 logfile_buffer_t
*b
= static_cast<logfile_buffer_t
*>(ll
->bufs
.head
->data
);
178 if (b
->len
+ 2 <= b
->size
)
179 logfile_mod_daemon_append(lf
, "F\n", 2);
181 /* Ok, schedule a write-event */
182 Comm::SetSelect(ll
->wfd
, COMM_SELECT_WRITE
, logfileHandleWrite
, lf
, 0);
186 logfile_mod_daemon_append(Logfile
* lf
, const char *buf
, int len
)
188 l_daemon_t
*ll
= (l_daemon_t
*) lf
->data
;
192 /* Is there a buffer? If not, create one */
193 if (ll
->bufs
.head
== NULL
) {
194 logfileNewBuffer(lf
);
196 debugs(50, 3, "logfile_mod_daemon_append: " << lf
->path
<< ": appending " << len
<< " bytes");
197 /* Copy what can be copied */
199 b
= static_cast<logfile_buffer_t
*>(ll
->bufs
.tail
->data
);
200 debugs(50, 3, "logfile_mod_daemon_append: current buffer has " << b
->len
<< " of " << b
->size
<< " bytes before append");
201 s
= min(len
, (b
->size
- b
->len
));
202 memcpy(b
->buf
+ b
->len
, buf
, s
);
206 assert(b
->len
<= LOGFILE_BUFSZ
);
209 logfileNewBuffer(lf
);
215 * only schedule a flush (write) if one isn't scheduled.
218 logfileFlushEvent(void *data
)
220 Logfile
*lf
= static_cast<Logfile
*>(data
);
223 * This might work better if we keep track of when we wrote last and only
224 * schedule a write if we haven't done so in the last second or two.
226 logfileQueueWrite(lf
);
227 eventAdd("logfileFlush", logfileFlushEvent
, lf
, 1.0, 1);
233 logfile_mod_daemon_open(Logfile
* lf
, const char *path
, size_t bufsz
, int fatal_flag
)
239 lf
->f_close
= logfile_mod_daemon_close
;
240 lf
->f_linewrite
= logfile_mod_daemon_writeline
;
241 lf
->f_linestart
= logfile_mod_daemon_linestart
;
242 lf
->f_lineend
= logfile_mod_daemon_lineend
;
243 lf
->f_flush
= logfile_mod_daemon_flush
;
244 lf
->f_rotate
= logfile_mod_daemon_rotate
;
246 cbdataInternalLock(lf
); // WTF?
247 debugs(50, DBG_IMPORTANT
, "Logfile Daemon: opening log " << path
);
248 ll
= static_cast<l_daemon_t
*>(xcalloc(1, sizeof(*ll
)));
252 Ip::Address localhost
;
253 args
[0] = "(logfile-daemon)";
256 localhost
.SetLocalhost();
257 ll
->pid
= ipcCreate(IPC_STREAM
, Log::TheConfig
.logfile_daemon
, args
, "logfile-daemon", localhost
, &ll
->rfd
, &ll
->wfd
, NULL
);
259 fatal("Couldn't start logfile helper");
263 /* Queue the initial control data */
264 tmpbuf
= static_cast<char*>(xmalloc(BUFSIZ
));
265 snprintf(tmpbuf
, BUFSIZ
, "r%d\nb%d\n", Config
.Log
.rotateNumber
, Config
.onoff
.buffered_logs
);
266 logfile_mod_daemon_append(lf
, tmpbuf
, strlen(tmpbuf
));
269 /* Start the flush event */
270 eventAdd("logfileFlush", logfileFlushEvent
, lf
, 1.0, 1);
276 logfile_mod_daemon_close(Logfile
* lf
)
278 l_daemon_t
*ll
= static_cast<l_daemon_t
*>(lf
->data
);
279 debugs(50, DBG_IMPORTANT
, "Logfile Daemon: closing log " << lf
->path
);
281 if (ll
->rfd
== ll
->wfd
)
287 kill(ll
->pid
, SIGTERM
);
288 eventDelete(logfileFlushEvent
, lf
);
291 cbdataInternalUnlock(lf
); // WTF??
295 logfile_mod_daemon_rotate(Logfile
* lf
)
298 debugs(50, DBG_IMPORTANT
, "logfileRotate: " << lf
->path
);
302 logfile_mod_daemon_append(lf
, tb
, 2);
306 * This routine assumes that up to one line is written. Don't try to
307 * call this routine with more than one line or subsequent lines
308 * won't be prefixed with the command type and confuse the logging
312 logfile_mod_daemon_writeline(Logfile
* lf
, const char *buf
, size_t len
)
314 l_daemon_t
*ll
= static_cast<l_daemon_t
*>(lf
->data
);
315 /* Make sure the logfile buffer isn't too large */
316 if (ll
->nbufs
> LOGFILE_MAXBUFS
) {
317 if (ll
->last_warned
< squid_curtime
- LOGFILE_WARN_TIME
) {
318 ll
->last_warned
= squid_curtime
;
319 debugs(50, DBG_IMPORTANT
, "Logfile: " << lf
->path
<< ": queue is too large; some log messages have been lost.");
323 /* Append this data to the end buffer; create a new one if needed */
324 /* Are we eol? If so, prefix with our logfile command byte */
325 logfile_mod_daemon_append(lf
, buf
, len
);
329 logfile_mod_daemon_linestart(Logfile
* lf
)
331 l_daemon_t
*ll
= static_cast<l_daemon_t
*>(lf
->data
);
333 assert(ll
->eol
== 1);
337 logfile_mod_daemon_append(lf
, tb
, 1);
341 logfile_mod_daemon_lineend(Logfile
* lf
)
343 l_daemon_t
*ll
= static_cast<l_daemon_t
*>(lf
->data
);
345 assert(ll
->eol
== 0);
347 /* Kick a write off if the head buffer is -full- */
348 if (ll
->bufs
.head
!= NULL
) {
349 b
= static_cast<logfile_buffer_t
*>(ll
->bufs
.head
->data
);
350 if (b
->node
.next
!= NULL
|| !Config
.onoff
.buffered_logs
)
351 logfileQueueWrite(lf
);
356 logfile_mod_daemon_flush(Logfile
* lf
)
358 l_daemon_t
*ll
= static_cast<l_daemon_t
*>(lf
->data
);
359 if (commUnsetNonBlocking(ll
->wfd
)) {
360 debugs(50, DBG_IMPORTANT
, "Logfile Daemon: Couldn't set the pipe blocking for flush! You're now missing some log entries.");
363 while (ll
->bufs
.head
!= NULL
) {
364 logfileHandleWrite(ll
->wfd
, lf
);
366 if (commSetNonBlocking(ll
->wfd
)) {
367 fatalf("Logfile Daemon: %s: Couldn't set the pipe non-blocking for flush!\n", lf
->path
);