- fixed loglevel output bug
[strongswan.git] / Source / charon / utils / logger.c
1 /**
2 * @file logger.c
3 *
4 * @brief Logger object, allows fine-controlled logging
5 *
6 */
7
8 /*
9 * Copyright (C) 2005 Jan Hutter, Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <syslog.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <time.h>
28 #include <pthread.h>
29
30 #include "logger.h"
31
32 #include "../daemon.h"
33 #include "allocator.h"
34
35 /**
36 * Maximum length of al log entry (only used for logger_s.log)
37 */
38 #define MAX_LOG 8192
39
40 /**
41 * @brief The logger object.
42 */
43 typedef struct private_logger_s private_logger_t;
44 struct private_logger_s {
45 /**
46 * Public data
47 */
48 logger_t public;
49 /**
50 * Detail-level of logger.
51 */
52 logger_level_t level;
53 /**
54 * Name of logger.
55 */
56 char *name;
57 /**
58 * File to write log output to .
59 * NULL for syslog.
60 */
61 FILE *output;
62
63 /**
64 * should a thread_id be included in the log?
65 */
66 bool log_thread_id;
67
68 /* private functions */
69 /**
70 * Logs a message to the associated log file.
71 */
72 void (*log_to_file) (private_logger_t *this, char *format, ...);
73 /**
74 * applies a prefix to string and stores it in buffer
75 *
76 * @warning: buffer must be at least MAX_LOG long
77 */
78 void (*prepend_prefix) (private_logger_t *this, logger_level_t loglevel, char *string, char *buffer);
79 };
80
81 /**
82 * Implements private_logger_t.prepend_prefix
83 */
84 static void prepend_prefix(private_logger_t *this, logger_level_t loglevel, char *string, char *buffer)
85 {
86 char log_type, log_details;
87 if (loglevel & CONTROL)
88 {
89 log_type = '~';
90 }
91 else if (loglevel & ERROR)
92 {
93 log_type = '!';
94 }
95 else if (loglevel & RAW)
96 {
97 log_type = '#';
98 }
99 else if (loglevel & PRIVATE)
100 {
101 log_type = '?';
102 }
103 else
104 {
105 log_type = '-';
106 }
107
108 if (loglevel & (ALL - MOST))
109 {
110 log_details = '3';
111 }
112 else if (loglevel & (MOST - MORE))
113 {
114 log_details = '2';
115 }
116 else if (loglevel & MORE)
117 {
118 log_details = '1';
119 }
120 else
121 {
122 log_details = '0';
123 }
124
125
126 if (this->log_thread_id)
127 {
128 snprintf(buffer, MAX_LOG, "[%c%c] [%s] @%u %s", log_type, log_details, this->name, (int)pthread_self(), string);
129 }
130 else
131 {
132 snprintf(buffer, MAX_LOG, "[%c%c] [%s] %s", log_type, log_details, this->name, string);
133 }
134
135 }
136
137 /**
138 * Implements logger_t-function log.
139 * @see logger_s.log.
140 *
141 * Yes, logg is wrong written :-).
142 */
143 static status_t logg(private_logger_t *this, logger_level_t loglevel, char *format, ...)
144 {
145 if ((this->level & loglevel) == loglevel)
146 {
147 char buffer[MAX_LOG];
148 va_list args;
149
150
151 if (this->output == NULL)
152 {
153 /* syslog */
154 this->prepend_prefix(this, loglevel, format, buffer);
155 va_start(args, format);
156 vsyslog(LOG_INFO, buffer, args);
157 va_end(args);
158 }
159 else
160 {
161 /* File output */
162 this->prepend_prefix(this, loglevel, format, buffer);
163 va_start(args, format);
164 this->log_to_file(this, buffer, args);
165 va_end(args);
166 }
167
168 }
169 return SUCCESS;
170 }
171
172 /**
173 * Implements private_logger_t-function log_to_file.
174 * @see private_logger_s.log_to_file.
175 */
176 static void log_to_file(private_logger_t *this,char *format, ...)
177 {
178 char buffer[MAX_LOG];
179 va_list args;
180 time_t current_time;
181 current_time = time(NULL);
182
183 snprintf(buffer, MAX_LOG, "%s\n", format);
184 va_start(args, format);
185 vfprintf(this->output, buffer, args);
186 va_end(args);
187 }
188
189 /**
190 * Implements logger_t-function destroy.
191 * @see logger_s.log_bytes.
192 */
193 static status_t log_bytes(private_logger_t *this, logger_level_t loglevel, char *label, char *bytes, size_t len)
194 {
195 if ((this->level & loglevel) == loglevel)
196 {
197 char buffer[MAX_LOG];
198 char *format;
199 char *buffer_pos;
200 char *bytes_pos, *bytes_roof;
201 int i;
202
203
204 format = "%s (%d bytes)";
205 this->prepend_prefix(this, loglevel, format, buffer);
206
207 if (this->output == NULL)
208 {
209 syslog(LOG_INFO, buffer, label, len);
210 }else
211 {
212 this->log_to_file(this, buffer, label, len);
213 }
214
215 bytes_pos = bytes;
216 bytes_roof = bytes + len;
217 buffer_pos = buffer;
218
219 for (i = 1; bytes_pos < bytes_roof; i++)
220 {
221 static const char hexdig[] = "0123456789ABCDEF";
222 *buffer_pos++ = hexdig[(*bytes_pos >> 4) & 0xF];
223 *buffer_pos++ = hexdig[ *bytes_pos & 0xF];
224 if ((i % 16) == 0)
225 {
226 *buffer_pos++ = '\0';
227 buffer_pos = buffer;
228 if (this->output == NULL)
229 {
230 syslog(LOG_INFO, "| %s", buffer);
231 }
232 else
233 {
234 this->log_to_file(this, "| %s", buffer);
235 }
236 }
237 else if ((i % 8) == 0)
238 {
239 *buffer_pos++ = ' ';
240 *buffer_pos++ = ' ';
241 *buffer_pos++ = ' ';
242 }
243 else if ((i % 4) == 0)
244 {
245 *buffer_pos++ = ' ';
246 *buffer_pos++ = ' ';
247 }
248 else
249 {
250 *buffer_pos++ = ' ';
251 }
252
253 bytes_pos++;
254 }
255
256 *buffer_pos++ = '\0';
257 buffer_pos = buffer;
258 if (this->output == NULL)
259 {
260 syslog(LOG_INFO, "| %s", buffer);
261 }
262 else
263 {
264 this->log_to_file(this, "| %s", buffer);
265 }
266 }
267
268 return SUCCESS;
269 }
270
271
272 /**
273 * Implements logger_t-function log_chunk.
274 * @see logger_s.log_chunk.
275 */
276 static status_t log_chunk(logger_t *this, logger_level_t loglevel, char *label, chunk_t *chunk)
277 {
278 this->log_bytes(this, loglevel, label, chunk->ptr, chunk->len);
279 return SUCCESS;
280 }
281
282
283 /**
284 * Implements logger_t-function enable_level.
285 * @see logger_s.enable_level.
286 */
287 static status_t enable_level(private_logger_t *this, logger_level_t log_level)
288 {
289 this->level |= log_level;
290 return SUCCESS;
291 }
292
293 /**
294 * Implements logger_t-function disable_level.
295 * @see logger_s.disable_level.
296 */
297 static status_t disable_level(private_logger_t *this, logger_level_t log_level)
298 {
299 this->level &= ~log_level;
300 return SUCCESS;
301 }
302
303 /**
304 * Implements logger_t-function destroy.
305 * @see logger_s.destroy.
306 */
307 static status_t destroy(private_logger_t *this)
308 {
309 allocator_free(this->name);
310 allocator_free(this);
311 return SUCCESS;
312 }
313
314 /*
315 * Described in Header
316 */
317 logger_t *logger_create(char *logger_name, logger_level_t log_level, bool log_thread_id, FILE * output)
318 {
319 private_logger_t *this = allocator_alloc_thing(private_logger_t);
320
321 if (this == NULL)
322 {
323 return NULL;
324 }
325
326 if (logger_name == NULL)
327 {
328 logger_name = "";
329 }
330
331 this->public.log = (status_t(*)(logger_t*,logger_level_t,char*,...))logg;
332 this->public.log_bytes = (status_t(*)(logger_t*, logger_level_t, char*,char*,size_t))log_bytes;
333 this->public.log_chunk = log_chunk;
334 this->public.enable_level = (status_t(*)(logger_t*,logger_level_t))enable_level;
335 this->public.disable_level = (status_t(*)(logger_t*,logger_level_t))disable_level;
336 this->public.destroy = (status_t(*)(logger_t*))destroy;
337
338 this->log_to_file = log_to_file;
339 this->prepend_prefix = prepend_prefix;
340
341 /* private variables */
342 this->level = log_level;
343 this->log_thread_id = log_thread_id;
344 this->name = allocator_alloc(strlen(logger_name) + 1);
345 if (this->name == NULL)
346 {
347 allocator_free(this);
348 return NULL;
349 }
350 strcpy(this->name,logger_name);
351 this->output = output;
352
353
354 if (output == NULL)
355 {
356 openlog(DAEMON_NAME, 0, LOG_DAEMON);
357 }
358
359 return (logger_t*)this;
360 }