minimized prefixed on stroke logger output
[strongswan.git] / src / libstrongswan / utils / logger.c
1 /**
2 * @file logger.c
3 *
4 * @brief Implementation of logger_t.
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
33 /**
34 * Maximum length of a log entry (only used for logger_s.log).
35 */
36 #define MAX_LOG 8192
37
38 /**
39 * Maximum number of logged bytes per line
40 */
41 #define MAX_BYTES 16
42
43 typedef struct private_logger_t private_logger_t;
44
45 /**
46 * @brief Private data of a logger_t object.
47 */
48 struct private_logger_t {
49 /**
50 * Public data.
51 */
52 logger_t public;
53 /**
54 * Detail-level of logger.
55 */
56 log_level_t level;
57 /**
58 * Name of logger.
59 */
60 char *name;
61 /**
62 * File to write log output to.
63 * NULL for syslog.
64 */
65 FILE *output;
66
67 /**
68 * Should a thread_id be included in the log?
69 */
70 bool log_thread_id;
71 };
72
73 /**
74 * thread local storage for get_thread_number
75 */
76 static pthread_key_t thread_ids;
77 static void make_key(void)
78 {
79 pthread_key_create(&thread_ids, NULL);
80 }
81
82 /**
83 * Get a unique thread number for a calling thread. Since
84 * pthread_self returns large and ugly numbers, use this function
85 * for logging; these numbers are incremental starting at 1
86 */
87 static int get_thread_number(void)
88 {
89 static int current_num = 0;
90 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
91 int stored_num;
92
93 pthread_once(&key_once, make_key);
94 stored_num = (int)pthread_getspecific(thread_ids);
95 if (stored_num == 0)
96 {
97 pthread_setspecific(thread_ids, (void*)++current_num);
98 return current_num;
99 }
100 else
101 {
102 return stored_num;
103 }
104 }
105
106 /**
107 * prepend the logging prefix to string and store it in buffer
108 */
109 static void prepend_prefix(private_logger_t *this, log_level_t loglevel, const char *string, char *buffer)
110 {
111 char thread_id[3] = "";
112 char log_type, log_details;
113 char *separator = (strlen(this->name) == 0)? "" : ":";
114
115 if (loglevel & CONTROL)
116 {
117 log_type = 'C';
118 }
119 else if (loglevel & ERROR)
120 {
121 log_type = 'E';
122 }
123 else if (loglevel & RAW)
124 {
125 log_type = 'R';
126 }
127 else if (loglevel & PRIVATE)
128 {
129 log_type = 'P';
130 }
131 else if (loglevel & AUDIT)
132 {
133 log_type = 'A';
134 }
135 else
136 {
137 log_type = '-';
138 }
139
140 if (loglevel & (LEVEL3 - LEVEL2))
141 {
142 log_details = '3';
143 }
144 else if (loglevel & (LEVEL2 - LEVEL1))
145 {
146 log_details = '2';
147 }
148 else if (loglevel & LEVEL1)
149 {
150 log_details = '1';
151 }
152 else
153 {
154 log_details = '0';
155 }
156
157 if (this->log_thread_id)
158 {
159 snprintf(thread_id, sizeof(thread_id), "%02d", get_thread_number());
160 }
161 snprintf(buffer, MAX_LOG, "%s[%c%c%s%s] %s",
162 thread_id, log_type, log_details, separator, this->name, string);
163 }
164
165 /**
166 * Convert a charon-loglevel to a syslog priority
167 */
168 static int get_priority(log_level_t loglevel)
169 {
170 if (loglevel & ERROR)
171 {
172 return LOG_AUTHPRIV|LOG_ERR;
173 }
174 if (loglevel & AUDIT)
175 {
176 return LOG_AUTHPRIV|LOG_INFO;
177 }
178 return LOG_AUTHPRIV|LOG_DEBUG;
179 }
180
181 /**
182 * Implementation of logger_t.log.
183 *
184 * Yes, logg is written wrong :-).
185 */
186 static void logg(private_logger_t *this, log_level_t loglevel, const char *format, ...)
187 {
188 if ((this->level & loglevel) == loglevel)
189 {
190 char buffer[MAX_LOG];
191 va_list args;
192
193
194 if (this->output == NULL)
195 {
196 /* syslog */
197 prepend_prefix(this, loglevel, format, buffer);
198 va_start(args, format);
199 vsyslog(get_priority(loglevel), buffer, args);
200 va_end(args);
201 }
202 else
203 {
204 /* File output */
205 prepend_prefix(this, loglevel, format, buffer);
206 va_start(args, format);
207 vfprintf(this->output, buffer, args);
208 va_end(args);
209 fprintf(this->output, "\n");
210 }
211
212 }
213 }
214
215 /**
216 * Implementation of logger_t.log_bytes.
217 */
218 static void log_bytes(private_logger_t *this, log_level_t loglevel, const char *label, const char *bytes, size_t len)
219 {
220 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
221
222 if ((this->level & loglevel) == loglevel)
223 {
224 char thread_id[3] = "";
225 char buffer[MAX_LOG];
226 char ascii_buffer[MAX_BYTES+1];
227
228 char *buffer_pos = buffer;
229 const char format[] = "%s %d bytes @ %p";
230 const char *bytes_pos = bytes;
231 const char *bytes_roof = bytes + len;
232
233 int line_start = 0;
234 int i = 0;
235
236 /* since me can't do multi-line output to syslog,
237 * we must do multiple syslogs. To avoid
238 * problems in output order, lock this by a mutex.
239 */
240 pthread_mutex_lock(&mutex);
241
242 prepend_prefix(this, loglevel, format, buffer);
243
244 if (this->log_thread_id)
245 {
246 snprintf(thread_id, sizeof(thread_id), "%02d", get_thread_number());
247 }
248
249 if (this->output == NULL)
250 {
251 syslog(get_priority(loglevel), buffer, label, len, bytes);
252 }
253 else
254 {
255 fprintf(this->output, buffer, label, len, bytes);
256 fprintf(this->output, "\n");
257 }
258
259 while (bytes_pos < bytes_roof)
260 {
261 static char hexdig[] = "0123456789ABCDEF";
262
263 *buffer_pos++ = hexdig[(*bytes_pos >> 4) & 0xF];
264 *buffer_pos++ = hexdig[ *bytes_pos & 0xF];
265
266 ascii_buffer[i++] = (*bytes_pos > 31 && *bytes_pos < 127)
267 ? *bytes_pos : '.';
268
269 if (++bytes_pos == bytes_roof || i == MAX_BYTES)
270 {
271 int padding = 3 * (MAX_BYTES - i);
272
273 while (padding--)
274 {
275 *buffer_pos++ = ' ';
276 }
277 *buffer_pos++ = '\0';
278 ascii_buffer[i] = '\0';
279
280 if (this->output == NULL)
281 {
282 syslog(get_priority(loglevel), "%s[ :%5d] %s %s", thread_id, line_start, buffer, ascii_buffer);
283 }
284 else
285 {
286 fprintf(this->output, "%s[ :%5d] %s %s\n", thread_id, line_start, buffer, ascii_buffer);
287 }
288 buffer_pos = buffer;
289 line_start += MAX_BYTES;
290 i = 0;
291 }
292 else
293 {
294 *buffer_pos++ = ' ';
295 }
296 }
297 pthread_mutex_unlock(&mutex);
298 }
299 }
300
301 /**
302 * Implementation of logger_t.log_chunk.
303 */
304 static void log_chunk(logger_t *this, log_level_t loglevel, const char *label, chunk_t chunk)
305 {
306 this->log_bytes(this, loglevel, label, chunk.ptr, chunk.len);
307 }
308
309 /**
310 * Implementation of logger_t.enable_level.
311 */
312 static void enable_level(private_logger_t *this, log_level_t log_level)
313 {
314 this->level |= log_level;
315 }
316
317 /**
318 * Implementation of logger_t.disable_level.
319 */
320 static void disable_level(private_logger_t *this, log_level_t log_level)
321 {
322 this->level &= ~log_level;
323 }
324
325 /**
326 * Implementation of logger_t.set_output.
327 */
328 static void set_output(private_logger_t *this, FILE * output)
329 {
330 this->output = output;
331 }
332
333 /**
334 * Implementation of logger_t.get_level.
335 */
336 static log_level_t get_level(private_logger_t *this)
337 {
338 return this->level;
339 }
340
341 /**
342 * Implementation of logger_t.destroy.
343 */
344 static void destroy(private_logger_t *this)
345 {
346 free(this->name);
347 free(this);
348 }
349
350 /*
351 * Described in header.
352 */
353 logger_t *logger_create(char *logger_name, log_level_t log_level, bool log_thread_id, FILE * output)
354 {
355 private_logger_t *this = malloc_thing(private_logger_t);
356
357 /* public functions */
358 this->public.log = (void(*)(logger_t*,log_level_t,const char*,...))logg;
359 this->public.log_bytes = (void(*)(logger_t*, log_level_t, const char*, const char*,size_t))log_bytes;
360 this->public.log_chunk = log_chunk;
361 this->public.enable_level = (void(*)(logger_t*,log_level_t))enable_level;
362 this->public.disable_level = (void(*)(logger_t*,log_level_t))disable_level;
363 this->public.get_level = (log_level_t(*)(logger_t*))get_level;
364 this->public.set_output = (void(*)(logger_t*,FILE*))set_output;
365 this->public.destroy = (void(*)(logger_t*))destroy;
366
367 if (logger_name == NULL)
368 {
369 logger_name = "";
370 }
371
372 /* private variables */
373 this->level = log_level;
374 this->log_thread_id = log_thread_id;
375 this->name = malloc(strlen(logger_name) + 1);
376
377 strcpy(this->name,logger_name);
378 this->output = output;
379
380 return (logger_t*)this;
381 }