Statistics
| Branch: | Revision:

janus-gateway / log.c @ f82a8605

History | View | Annotate | Download (5.41 KB)

1
/*! \file     log.c
2
 * \author    Jay Ridgeway <jayridge@gmail.com>
3
 * \copyright GNU General Public License v3
4
 * \brief     Buffered logging
5
 * \details   Implementation of a simple buffered logger designed to remove
6
 * I/O wait from threads that may be sensitive to such delays. Buffers are
7
 * saved and reused to reduce allocation calls. The logger output can then
8
 * be printed to stdout and/or a log file.
9
 *
10
 * \ingroup core
11
 * \ref core
12
 */
13

    
14
#include <errno.h>
15
#include <string.h>
16

    
17
#include "log.h"
18

    
19
#define THREAD_NAME "log"
20

    
21
typedef struct janus_log_buffer janus_log_buffer;
22
struct janus_log_buffer {
23
        size_t allocated;
24
        janus_log_buffer *next;
25
        /* str is grown by allocating beyond the struct */
26
        char str[1];
27
};
28

    
29
#define INITIAL_BUFSZ                1024*2
30
#define BUFFER_STRSZ(b)                (b ? b->allocated - sizeof(*b) : 0)
31
#define BUFFER_ALLOCSZ(r)        (r + sizeof(janus_log_buffer))
32

    
33
static gboolean janus_log_console = TRUE;
34
static char *janus_log_filepath = NULL;
35
static FILE *janus_log_file = NULL;
36

    
37
static gint initialized = 0;
38
static gint stopping = 0;
39
static gint poolsz = 0;
40
static gint maxpoolsz = 32;
41
/* Buffers over this size will be freed */
42
static gsize maxbuffersz = 1024*8;
43
static GMutex lock;
44
static GCond cond;
45
static GThread *printthread = NULL;
46
static janus_log_buffer *printhead = NULL;
47
static janus_log_buffer *printtail = NULL;
48
static janus_log_buffer *bufferpool = NULL;
49

    
50

    
51
gboolean janus_log_is_stdout_enabled(void) {
52
        return janus_log_console;
53
}
54

    
55
gboolean janus_log_is_logfile_enabled(void) {
56
        return janus_log_file != NULL;
57
}
58

    
59
char *janus_log_get_logfile_path(void) {
60
        return janus_log_filepath;
61
}
62

    
63

    
64
static void janus_log_freebuffers(janus_log_buffer **list) {
65
        janus_log_buffer *b, *head = *list;
66

    
67
        while (head) {
68
                b = head;
69
                head = b->next;
70
                g_free(b);
71
        }
72
        *list = NULL;
73
}
74

    
75
static janus_log_buffer *janus_log_sizebuffer(janus_log_buffer *b, size_t requested) {
76
        size_t n = 1;
77

    
78
        if (!b || BUFFER_STRSZ(b) < requested) {
79
                while (n < BUFFER_ALLOCSZ(requested)) {
80
                        n <<= 1;
81
                }
82
                b = g_realloc(b, n);
83
                b->allocated = n;
84
        }
85
        return b;
86
}
87

    
88
static janus_log_buffer *janus_log_getbuf(void) {
89
        janus_log_buffer *b;
90

    
91
        g_mutex_lock(&lock);
92
        b = bufferpool;
93
        if (b) {
94
                bufferpool = b->next;
95
                b->next = NULL;
96
        } else {
97
                poolsz++;
98
        }
99
        g_mutex_unlock(&lock);
100
        if (b == NULL) {
101
                b = g_malloc(INITIAL_BUFSZ);
102
                b->allocated = INITIAL_BUFSZ;
103
                b->next = NULL;
104
        }
105
        return b;
106
}
107

    
108
static void *janus_log_thread(void *ctx) {
109
        janus_log_buffer *head, *b, *tofree = NULL;
110

    
111
        while (!g_atomic_int_get(&stopping)) {
112
                g_mutex_lock(&lock);
113
                if (!printhead) {
114
                        g_cond_wait(&cond, &lock);
115
                }
116
                head = printhead;
117
                printhead = printtail = NULL;
118
                g_mutex_unlock(&lock);
119

    
120
                if (head) {
121
                        for (b = head; b; b = b->next) {
122
                                if(janus_log_console)
123
                                        fputs(b->str, stdout);
124
                                if(janus_log_file)
125
                                        fputs(b->str, janus_log_file);
126
                        }
127
                        g_mutex_lock(&lock);
128
                        while (head) {
129
                                b = head;
130
                                head = b->next;
131
                                if (poolsz >= maxpoolsz || b->allocated > maxbuffersz) {
132
                                        b->next = tofree;
133
                                        tofree = b;
134
                                        poolsz--;
135
                                } else {
136
                                        b->next = bufferpool;
137
                                        bufferpool = b;
138
                                }
139
                        }
140
                        g_mutex_unlock(&lock);
141
                        if(janus_log_console)
142
                                fflush(stdout);
143
                        if(janus_log_file)
144
                                fflush(janus_log_file);
145
                        janus_log_freebuffers(&tofree);
146
                }
147
        }
148
        /* print any remaining messages, stdout flushed on exit */
149
        for (b = printhead; b; b = b->next) {
150
                if(janus_log_console)
151
                        fputs(b->str, stdout);
152
                if(janus_log_file)
153
                        fputs(b->str, janus_log_file);
154
        }
155
        if(janus_log_console)
156
                fflush(stdout);
157
        if(janus_log_file)
158
                fflush(janus_log_file);
159
        janus_log_freebuffers(&printhead);
160
        janus_log_freebuffers(&bufferpool);
161
        g_mutex_clear(&lock);
162
        g_cond_clear(&cond);
163

    
164
        if(janus_log_file)
165
                fclose(janus_log_file);
166
        janus_log_file = NULL;
167
        if(janus_log_filepath)
168
                g_free(janus_log_filepath);
169
        janus_log_filepath = NULL;
170

    
171
        return NULL;
172
}
173

    
174
void janus_vprintf(const char *format, ...) {
175
        size_t len;
176
        va_list ap, ap2;
177
        janus_log_buffer *b = janus_log_getbuf();
178

    
179
        va_start(ap, format);
180
        va_copy(ap2, ap);
181
        /* Determine buffer length */
182
        len = (size_t)vsnprintf(NULL, 0, format, ap);
183
        va_end(ap);
184
        /* Ensure the buffer can hold the message */
185
        b = janus_log_sizebuffer(b, len+1);
186
        b->str[0] = '\0';
187
        vsnprintf(b->str, len+1, format, ap2);
188
        va_end(ap2);
189

    
190
        g_mutex_lock(&lock);
191
        if (!printhead) {
192
                printhead = printtail = b;
193
        } else {
194
                printtail->next = b;
195
                printtail = b;
196
        }
197
        g_cond_signal(&cond);
198
        g_mutex_unlock(&lock);
199
}
200

    
201
int janus_log_init(gboolean console, const char *logfile) {
202
        if (g_atomic_int_get(&initialized)) {
203
                return 0;
204
        }
205
        g_atomic_int_set(&initialized, 1);
206
        g_mutex_init(&lock);
207
        g_cond_init(&cond);
208
        if(console) {
209
                /* Set stdout to block buffering, see BUFSIZ in stdio.h */
210
                setvbuf(stdout, NULL, _IOFBF, 0);
211
        }
212
        janus_log_console = console;
213
        if(logfile != NULL) {
214
                /* Open a log file for writing (and append) */
215
                janus_log_file = fopen(logfile, "awt");
216
                if(janus_log_file == NULL) {
217
                        g_print("Error opening log file %s: %s\n", logfile, strerror(errno));
218
                        return -1;
219
                }
220
                janus_log_filepath = g_strdup(logfile);
221
        }
222
        if(!janus_log_console && logfile == NULL) {
223
                g_print("WARNING: logging completely disabled!\n");
224
                g_print("         (no stdout and no logfile, this may not be what you want...)\n");
225
        }
226
        printthread = g_thread_new(THREAD_NAME, &janus_log_thread, NULL);
227
        return 0;
228
}
229

    
230
void janus_log_destroy(void) {
231
        g_atomic_int_set(&stopping, 1);
232
        g_mutex_lock(&lock);
233
        /* Signal print thread to print any remaining message */
234
        g_cond_signal(&cond);
235
        g_mutex_unlock(&lock);
236
        g_thread_join(printthread);
237
}