root/src/utils/noit_log.c

Revision 88a71780101cbf23034aa0cb840f9f0368fda2dd, 12.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

fixes #126

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2005-2009, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *    * Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *    * Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials provided
14  *      with the distribution.
15  *    * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *      of its contributors may be used to endorse or promote products
17  *      derived from this software without specific prior written
18  *      permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #define DEFAULT_JLOG_SUBSCRIBER "stratcon"
34
35 #include "noit_defines.h"
36 #include <stdio.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <sys/time.h>
40 #include <assert.h>
41
42 #include "utils/noit_log.h"
43 #include "utils/noit_hash.h"
44 #include "jlog/jlog.h"
45
46 static noit_hash_table noit_loggers = NOIT_HASH_EMPTY;
47 static noit_hash_table noit_logops = NOIT_HASH_EMPTY;
48 noit_log_stream_t noit_stderr = NULL;
49 noit_log_stream_t noit_error = NULL;
50 noit_log_stream_t noit_debug = NULL;
51
52 static int
53 posix_logio_open(noit_log_stream_t ls) {
54   int fd;
55   ls->mode = 0664;
56   fd = open(ls->path, O_CREAT|O_WRONLY|O_APPEND, ls->mode);
57   if(fd < 0) {
58     ls->op_ctx = NULL;
59     return -1;
60   }
61   ls->op_ctx = (void *)fd;
62   return 0;
63 }
64 static int
65 posix_logio_reopen(noit_log_stream_t ls) {
66   if(ls->path) {
67     int newfd, oldfd;
68     oldfd = (int)ls->op_ctx;
69     newfd = open(ls->path, O_CREAT|O_WRONLY|O_APPEND, ls->mode);
70     if(newfd >= 0) {
71       ls->op_ctx = (void *)newfd;
72       if(oldfd >= 0) close(oldfd);
73       return 0;
74     }
75   }
76   return -1;
77 }
78 static int
79 posix_logio_write(noit_log_stream_t ls, const void *buf, size_t len) {
80   int fd;
81   fd = (int)ls->op_ctx;
82   if(fd < 0) return -1;
83   return write(fd, buf, len);
84 }
85 static int
86 posix_logio_close(noit_log_stream_t ls) {
87   int fd;
88   fd = (int)ls->op_ctx;
89   return close(fd);
90 }
91 static size_t
92 posix_logio_size(noit_log_stream_t ls) {
93   int fd;
94   struct stat sb;
95   fd = (int)ls->op_ctx;
96   if(fstat(fd, &sb) == 0) {
97     return (size_t)sb.st_size;
98   }
99   return -1;
100 }
101 static logops_t posix_logio_ops = {
102   posix_logio_open,
103   posix_logio_reopen,
104   posix_logio_write,
105   posix_logio_close,
106   posix_logio_size
107 };
108
109 static int
110 jlog_logio_reopen(noit_log_stream_t ls) {
111   char **subs;
112   int i;
113   /* reopening only has the effect of removing temporary subscriptions */
114   /* (they start with ~ in our hair-brained model */
115
116   if(jlog_ctx_list_subscribers(ls->op_ctx, &subs) == -1) {
117     noitL(noit_error, "Cannot list subscribers: %s\n",
118           jlog_ctx_err_string(ls->op_ctx));
119     return 0;
120   }
121
122   for(i=0;subs[i];i++)
123     if(subs[i][0] == '~')
124       if(jlog_ctx_remove_subscriber(ls->op_ctx, subs[i]) == -1)
125         noitL(noit_error, "Cannot remove subscriber '%s': %s\n",
126               subs[i], jlog_ctx_err_string(ls->op_ctx));
127
128   jlog_ctx_list_subscribers_dispose(ls->op_ctx, subs);
129   return 0;
130 }
131 static int
132 jlog_logio_open(noit_log_stream_t ls) {
133   char path[PATH_MAX], *sub;
134   jlog_ctx *log = NULL;
135   if(!ls->path) return -1;
136   strlcpy(path, ls->path, sizeof(path));
137   sub = strchr(path, '(');
138   if(sub) {
139     char *esub = strchr(sub, ')');
140     if(esub) {
141       *esub = '\0';
142       *sub = '\0';
143       sub += 1;
144     }
145   }
146   log = jlog_new(path);
147   if(!log) return -1;
148   /* Open the writer. */
149   if(jlog_ctx_open_writer(log)) {
150     /* If that fails, we'll give one attempt at initiailizing it. */
151     /* But, since we attempted to open it as a writer, it is tainted. */
152     /* path: close, new, init, close, new, writer, add subscriber */
153     jlog_ctx_close(log);
154     log = jlog_new(path);
155     if(jlog_ctx_init(log)) {
156       noitL(noit_error, "Cannot init jlog writer: %s\n",
157             jlog_ctx_err_string(log));
158       jlog_ctx_close(log);
159       return -1;
160     }
161     /* After it is initialized, we can try to reopen it as a writer. */
162     jlog_ctx_close(log);
163     log = jlog_new(path);
164     if(jlog_ctx_open_writer(log)) {
165       noitL(noit_error, "Cannot open jlog writer: %s\n",
166             jlog_ctx_err_string(log));
167       jlog_ctx_close(log);
168       return -1;
169     }
170     /* The first time we open after an init, we should add the subscriber. */
171     if(sub)
172       jlog_ctx_add_subscriber(log, sub, JLOG_BEGIN);
173     else
174       jlog_ctx_add_subscriber(log, DEFAULT_JLOG_SUBSCRIBER, JLOG_BEGIN);
175   }
176   ls->op_ctx = log;
177   /* We do this to clean things up */
178   jlog_logio_reopen(ls);
179   return 0;
180 }
181 static int
182 jlog_logio_write(noit_log_stream_t ls, const void *buf, size_t len) {
183   if(!ls->op_ctx) return -1;
184   if(jlog_ctx_write((jlog_ctx *)ls->op_ctx, buf, len) != 0)
185     return -1;
186   return len;
187 }
188 static int
189 jlog_logio_close(noit_log_stream_t ls) {
190   if(ls->op_ctx) {
191     jlog_ctx_close((jlog_ctx *)ls->op_ctx);
192     ls->op_ctx = NULL;
193   }
194   return 0;
195 }
196 static size_t
197 jlog_logio_size(noit_log_stream_t ls) {
198   if(!ls->op_ctx) return -1;
199   return jlog_raw_size((jlog_ctx *)ls->op_ctx);
200 }
201 static logops_t jlog_logio_ops = {
202   jlog_logio_open,
203   jlog_logio_reopen,
204   jlog_logio_write,
205   jlog_logio_close,
206   jlog_logio_size
207 };
208
209 void
210 noit_log_init() {
211   noit_hash_init(&noit_loggers);
212   noit_hash_init(&noit_logops);
213   noit_register_logops("file", &posix_logio_ops);
214   noit_register_logops("jlog", &jlog_logio_ops);
215   noit_stderr = noit_log_stream_new_on_fd("stderr", 2, NULL);
216   noit_error = noit_log_stream_new("error", NULL, NULL, NULL, NULL);
217   noit_debug = noit_log_stream_new("debug", NULL, NULL, NULL, NULL);
218 }
219
220 void
221 noit_register_logops(const char *name, logops_t *ops) {
222   noit_hash_store(&noit_logops, strdup(name), strlen(name), ops);
223 }
224
225 noit_log_stream_t
226 noit_log_stream_new_on_fd(const char *name, int fd, noit_hash_table *config) {
227   noit_log_stream_t ls;
228   ls = calloc(1, sizeof(*ls));
229   ls->name = strdup(name);
230   ls->ops = &posix_logio_ops;
231   ls->op_ctx = (void *)fd;
232   ls->enabled = 1;
233   ls->config = config;
234   /* This double strdup of ls->name is needed, look for the next one
235    * for an explanation.
236    */
237   if(noit_hash_store(&noit_loggers,
238                      strdup(ls->name), strlen(ls->name), ls) == 0) {
239     free(ls->name);
240     free(ls);
241     return NULL;
242   }
243   return ls;
244 }
245
246 noit_log_stream_t
247 noit_log_stream_new_on_file(const char *path, noit_hash_table *config) {
248   return noit_log_stream_new(path, "file", path, NULL, config);
249 }
250
251 noit_log_stream_t
252 noit_log_stream_new(const char *name, const char *type, const char *path,
253                     void *ctx, noit_hash_table *config) {
254   noit_log_stream_t ls, saved;
255   struct _noit_log_stream tmpbuf;
256   ls = calloc(1, sizeof(*ls));
257   ls->name = strdup(name);
258   ls->path = path ? strdup(path) : NULL;
259   ls->type = type ? strdup(type) : NULL;
260   ls->enabled = 1;
261   ls->config = config;
262   if(!type)
263     ls->ops = NULL;
264   else if(!noit_hash_retrieve(&noit_logops, type, strlen(type),
265                               (void **)&ls->ops))
266     goto freebail;
267  
268   if(ls->ops && ls->ops->openop(ls)) goto freebail;
269
270   saved = noit_log_stream_find(name);
271   if(saved) {
272     memcpy(&tmpbuf, saved, sizeof(*saved));
273     memcpy(saved, ls, sizeof(*saved));
274     memcpy(ls, &tmpbuf, sizeof(*saved));
275     noit_log_stream_free(ls);
276     ls = saved;
277   }
278   else
279     /* We strdup the name *again*.  We'going to kansas city shuffle the
280      * ls later (see memcpy above).  However, if don't strdup, then the
281      * noit_log_stream_free up there will sweep our key right our from
282      * under us.
283      */
284     if(noit_hash_store(&noit_loggers,
285                        strdup(ls->name), strlen(ls->name), ls) == 0)
286       goto freebail;
287
288   /* This is for things that don't open on paths */
289   if(ctx) ls->op_ctx = ctx;
290   return ls;
291
292  freebail:
293   fprintf(stderr, "Failed to instantiate logger(%s,%s,%s)\n",
294           name, type ? type : "[null]", path ? path : "[null]");
295   free(ls->name);
296   if(ls->path) free(ls->path);
297   if(ls->type) free(ls->type);
298   free(ls);
299   return NULL;
300 }
301
302 noit_log_stream_t
303 noit_log_stream_find(const char *name) {
304   void *vls;
305   if(noit_hash_retrieve(&noit_loggers, name, strlen(name), &vls)) {
306     return (noit_log_stream_t)vls;
307   }
308   return NULL;
309 }
310
311 void
312 noit_log_stream_remove(const char *name) {
313   noit_hash_delete(&noit_loggers, name, strlen(name), NULL, NULL);
314 }
315
316 void
317 noit_log_stream_add_stream(noit_log_stream_t ls, noit_log_stream_t outlet) {
318   struct _noit_log_stream_outlet_list *newnode;
319   newnode = calloc(1, sizeof(*newnode));
320   newnode->outlet = outlet;
321   newnode->next = ls->outlets;
322   ls->outlets = newnode;
323 }
324
325 noit_log_stream_t
326 noit_log_stream_remove_stream(noit_log_stream_t ls, const char *name) {
327   noit_log_stream_t outlet;
328   struct _noit_log_stream_outlet_list *node, *tmp;
329   if(!ls->outlets) return NULL;
330   if(!strcmp(ls->outlets->outlet->name, name)) {
331     node = ls->outlets;
332     ls->outlets = node->next;
333     outlet = node->outlet;
334     free(node);
335     return outlet;
336   }
337   for(node = ls->outlets; node->next; node = node->next) {
338     if(!strcmp(node->next->outlet->name, name)) {
339       /* splice */
340       tmp = node->next;
341       node->next = tmp->next;
342       /* pluck */
343       outlet = tmp->outlet;
344       /* shed */
345       free(tmp);
346       /* return */
347       return outlet;
348     }
349   }
350   return NULL;
351 }
352
353 void noit_log_stream_reopen(noit_log_stream_t ls) {
354   struct _noit_log_stream_outlet_list *node;
355   if(ls->ops) ls->ops->reopenop(ls);
356   for(node = ls->outlets; node; node = node->next) {
357     noit_log_stream_reopen(node->outlet);
358   }
359 }
360
361 void
362 noit_log_stream_close(noit_log_stream_t ls) {
363   if(ls->ops) ls->ops->closeop(ls);
364 }
365
366 size_t
367 noit_log_stream_size(noit_log_stream_t ls) {
368   if(ls->ops && ls->ops->sizeop) return ls->ops->sizeop(ls);
369   return -1;
370 }
371
372 void
373 noit_log_stream_free(noit_log_stream_t ls) {
374   if(ls) {
375     struct _noit_log_stream_outlet_list *node;
376     if(ls->name) free(ls->name);
377     if(ls->path) free(ls->path);
378     while(ls->outlets) {
379       node = ls->outlets->next;
380       free(ls->outlets);
381       ls->outlets = node;
382     }
383     if(ls->config) {
384       noit_hash_destroy(ls->config, free, free);
385       free(ls->config);
386     }
387     free(ls);
388   }
389 }
390
391 static int
392 noit_log_line(noit_log_stream_t ls, char *buffer, size_t len) {
393   int rv = 0;
394   struct _noit_log_stream_outlet_list *node;
395   if(ls->ops)
396     rv = ls->ops->writeop(ls, buffer, len); /* Not much one can do about errors */
397   for(node = ls->outlets; node; node = node->next) {
398     int srv = 0;
399     srv = noit_log_line(node->outlet, buffer, len);
400     if(srv) rv = srv;
401   }
402   return rv;
403 }
404 int
405 noit_vlog(noit_log_stream_t ls, struct timeval *now,
406           const char *file, int line,
407           const char *format, va_list arg) {
408   int rv = 0, allocd = 0;
409   char buffer[4096], *dynbuff = NULL;
410   char fbuf[1024];
411 #ifdef va_copy
412   va_list copy;
413 #endif
414
415   if(ls->enabled) {
416     int len;
417     if(ls->debug) {
418       struct tm _tm, *tm = &_tm;
419       char tbuf[32];
420       time_t s = (time_t)now->tv_sec;
421       tm = localtime_r(&s, &_tm);
422       strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", tm);
423       snprintf(fbuf, sizeof(fbuf), "[%s.%06d %s:%d] %s",
424                tbuf, (int)now->tv_usec, file, line, format);
425       format = fbuf;
426     }
427 #ifdef va_copy
428     va_copy(copy, arg);
429     len = vsnprintf(buffer, sizeof(buffer), format, copy);
430     va_end(copy);
431 #else
432     len = vsnprintf(buffer, sizeof(buffer), format, arg);
433 #endif
434     if(len > sizeof(buffer)) {
435       allocd = sizeof(buffer);
436       while(len > allocd) { /* guaranteed true the first time */
437         while(len > allocd) allocd <<= 2;
438         if(dynbuff) free(dynbuff);
439         dynbuff = malloc(allocd);
440         assert(dynbuff);
441 #ifdef va_copy
442         va_copy(copy, arg);
443         len = vsnprintf(dynbuff, allocd, format, copy);
444         va_end(copy);
445 #else
446         len = vsnprintf(dynbuff, allocd, format, arg);
447 #endif
448       }
449       rv = noit_log_line(ls, dynbuff, len);
450       free(dynbuff);
451     }
452     else {
453       rv = noit_log_line(ls, buffer, len);
454     }
455     if(rv == len) return 0;
456     return -1;
457   }
458   return 0;
459 }
460
461 int
462 noit_log(noit_log_stream_t ls, struct timeval *now,
463          const char *file, int line, const char *format, ...) {
464   int rv;
465   va_list arg;
466   va_start(arg, format);
467   rv = noit_vlog(ls, now, file, line, format, arg);
468   va_end(arg);
469   return rv;
470 }
471
Note: See TracBrowser for help on using the browser.