root/src/utils/noit_log.c

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

refs #122, this stuff works well enough to pull dev back into trunk.
Closing the #122 branch.

flag-day(noitd,stratcond)

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