Initial commit.
[qa-tools:testrunner-lite.git] / src / log.c
1 /*
2  * This file is part of testrunner-lite
3  *
4  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
5  *
6  * Contact: Riku Halonen <riku.halonen@nokia.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  *
22  */
23
24 /* ------------------------------------------------------------------------- */
25 /* INCLUDE FILES */
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <time.h>
31 #include <sys/time.h>
32 #include <stdarg.h>
33 #include <unistd.h>
34 #include <curl/curl.h>
35 #include "testrunnerlite.h"
36 #include "log.h"
37
38 /* ------------------------------------------------------------------------- */
39 /* EXTERNAL DATA STRUCTURES */
40 /* None */
41
42 /* ------------------------------------------------------------------------- */
43 /* EXTERNAL GLOBAL VARIABLES */
44 extern struct timeval created;
45
46 /* ------------------------------------------------------------------------- */
47 /* EXTERNAL FUNCTION PROTOTYPES */
48 /* None */
49
50 /* ------------------------------------------------------------------------- */
51 /* GLOBAL VARIABLES */
52 /* None */
53
54 /* ------------------------------------------------------------------------- */
55 /* CONSTANTS */
56 /* None */
57
58
59 /* ------------------------------------------------------------------------- */
60 /* MACROS */
61 /* None */
62
63 /* ------------------------------------------------------------------------- */
64 /* LOCAL GLOBAL VARIABLES */
65 LOCAL CURL *curl;
66 LOCAL int  do_syslog = 0;
67
68 /* ------------------------------------------------------------------------- */
69 /* LOCAL CONSTANTS AND MACROS */
70
71 /* Match these to priorities in syslog.h */
72 const char *stream_names[] = {"EMERG", "ALERT", "CRITICAL", "ERROR",
73                               "WARNING", "NOTICE", "INFO", "DEBUG", "UNKNOWN" };
74
75 /* ------------------------------------------------------------------------- */
76 /* MODULE DATA STRUCTURES */
77 LOCAL int verbosity_level = 0;
78
79
80 /* ------------------------------------------------------------------------- */
81 /* LOCAL FUNCTION PROTOTYPES */
82 /* ------------------------------------------------------------------------- */
83 LOCAL char *create_msg(const char *fmt, ...);
84 /* ------------------------------------------------------------------------- */
85 LOCAL char *vcreate_msg (const char *fmt, va_list ap);
86 /* ------------------------------------------------------------------------- */
87 /* FORWARD DECLARATIONS */
88 /* None */
89
90 /* ------------------------------------------------------------------------- */
91 /* ==================== LOCAL FUNCTIONS ==================================== */
92 /* ------------------------------------------------------------------------- */
93 /** Allocate buffer and print to message to it 
94  *  @param fmt format as in printf
95  *  @return buffer with message or NULL in case of too big message
96  */
97 LOCAL char *create_msg (const char *fmt, ...)
98 {
99            int written, size = 128;
100            char *buff, *new_buff;
101            va_list ap;
102
103            if ((buff = malloc(size)) == NULL) {
104                    fprintf (stderr, "%s: %s: malloc() failed can not log %s\n",
105                             PROGNAME, __FUNCTION__, strerror (errno));
106                    return NULL;
107            }
108            while (1) {
109                va_start(ap, fmt);
110                written = vsnprintf(buff, size, fmt, ap);
111                va_end(ap);
112                if (written > -1 && written < size)
113                        return buff;
114                size = size * 2;
115                if (size > LOG_MSG_MAX_SIZE) {
116                        fprintf (stderr, 
117                                 "%s: %s: log msg max size exceeded, "
118                                 "msg omitted\n",
119                                 PROGNAME, __FUNCTION__);
120                        free(buff);
121                        return NULL;
122                }
123                if ((new_buff = realloc (buff, size)) == NULL) {
124                        fprintf (stderr, 
125                                 "%s: %s: realloc() failed can not log %s\n",
126                                 PROGNAME, __FUNCTION__, strerror (errno));
127                        free(buff);
128                        return NULL;
129                } else {
130                        buff = new_buff;
131                }
132            }
133
134            return NULL;
135 }
136 /* ------------------------------------------------------------------------- */
137 /** Allocate buffer and print to message to it 
138  *  @param fmt format as in printf
139  *  @param ap argument list
140  *  @return buffer with message or NULL in case of too big message
141  */
142 LOCAL char *vcreate_msg (const char *fmt, va_list ap)
143 {
144            int written, size = 128;
145            char *buff, *new_buff;
146
147            if ((buff = malloc(size)) == NULL) {
148                    fprintf (stderr, "%s: %s: malloc() failed can not log %s\n",
149                             PROGNAME, __FUNCTION__, strerror (errno));
150                    return NULL;
151            }
152            while (1) {
153                written = vsnprintf(buff, size, fmt, ap);
154                if (written > -1 && written < size)
155                        return buff;
156                size = size * 2;
157                if (size > LOG_MSG_MAX_SIZE) {
158                        fprintf (stderr, 
159                                 "%s: %s: log msg max size exceeded, "
160                                 "msg omitted\n",
161                                 PROGNAME, __FUNCTION__);
162                        free(buff);
163                        return NULL;
164                }
165                if ((new_buff = realloc (buff, size)) == NULL) {
166                        fprintf (stderr, 
167                                 "%s: %s: realloc() failed can not log %s\n",
168                                 PROGNAME, __FUNCTION__, strerror (errno));
169                        free(buff);
170                        return NULL;
171                } else {
172                        buff = new_buff;
173                }
174            }
175
176            return NULL;
177 }
178
179 /* ------------------------------------------------------------------------- */
180 /* ======================== FUNCTIONS ====================================== */
181 /* ------------------------------------------------------------------------- */ 
182
183 /** Prints a log message to stdout in format "[LOG_TYPE] HH:MM:SS message"
184  *  Usage is the same as in regular printf(), except the first parameter
185  *  Example: log_msg (LOG_ERR, "Error message %s\n", "Failure");
186  * @param type Log type defined in log_message_types enum
187  * @param file Source file name the log entry corresponds to
188  * @param function Function emitting the log
189  * @param lineno Line number
190  * @param format Message format
191  */
192 void log_msg(int type, const char *file, const char *function,
193              int lineno, char *format, ...) 
194 {
195         
196         const char *stream_name;
197         char timestamp[10];
198         char *msg, *post_msg, *module, *p;
199         CURLcode res;
200         struct tm *tm;
201         time_t current_time;
202         struct timeval now, diff;
203         va_list args;
204         unsigned int to_python_level[] = {40,20,10,30,0};
205
206         /* Check if message should be printed */
207         if (verbosity_level == LOG_LEVEL_SILENT || 
208             (type == LOG_DEBUG && verbosity_level != LOG_LEVEL_DEBUG)) {
209                 /* Do nothing */
210                 return;
211         } 
212         
213         /* All messages go to stdout, also errors */
214     
215         /* Log name. */
216         if (type >= 0 && type < LOG_TYPES_COUNT) {
217                 stream_name = stream_names[type];
218         } else {
219                 /* Should not happen */
220                 stream_name = stream_names[LOG_TYPES_COUNT];
221         }
222
223         /* Current timestamp */ 
224         time (&current_time);
225         tm = localtime (&current_time);
226         strftime (timestamp, sizeof (timestamp), "%H:%M:%S", tm);
227         
228         fprintf (stdout, "[%s] %s ", stream_name, timestamp);
229         if (type == LOG_DEBUG)
230                 fprintf (stdout, "%s %s() %d ", file, function, lineno);
231
232         /* Print given arguments */
233         va_start(args, format);
234         msg = vcreate_msg (format, args);
235         va_end(args);
236
237         if (!msg)
238                 return;
239
240         if (do_syslog)
241                 syslog (type, "%s", msg);
242         
243         fprintf (stdout, "%s\n", msg);
244         
245         if (!curl) {
246                 free (msg);
247                 return;
248         }
249         /* 
250          * Calculate the elapsed time since this program started
251          */
252         gettimeofday (&now, NULL);
253         timersub (&now, &created, &diff);
254         /*
255         ** module is the source file name w/o the .c
256         */
257         module = (char *)malloc (strlen (file) + 1);
258         strcpy (module, file);
259         if ((p = strchr (module, '.')))
260             *p = '\0';
261         /*
262         ** Compose http POST
263         */
264         post_msg = create_msg ("levelno=%d&"
265                                "name=testrunner-lite&"
266                                "levelname=%s&" 
267                                "module=%s&"
268                                "filename=%s&"
269                                "pathname=testrunner-lite/src&"
270                                "funcName=%s&"
271                                "lineno=%d&"
272                                "msg=%s&"
273                                "exc_info=None&"
274                                "exc_text=None&"
275                                "args=()&"
276                                "threadName=None&"
277                                "thread=0.0&"
278                                "created=%d.%d&"
279                                "process=%d&"
280                                "relativeCreated=%d.%d&"
281                                "msecs=%d.%d&"
282                                ,to_python_level[verbosity_level],
283                                stream_name,
284                                module,
285                                file,
286                                function,
287                                lineno,
288                                msg,
289                                created.tv_sec,
290                                created.tv_usec,
291                                getpid(),
292                                diff.tv_sec,
293                                diff.tv_usec,
294                                diff.tv_usec ? diff.tv_usec/1000 : 0,
295                                diff.tv_usec % 1000);
296         if (!post_msg) {
297                 free (msg);
298                 return;
299         }
300
301         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_msg);
302         
303         res = curl_easy_perform(curl);
304         if (res != CURLE_OK) {
305                 fprintf (stdout, "[ERROR] %s http logging failed: %s\n ", 
306                          timestamp, curl_easy_strerror(res));
307
308                 log_close();
309         }
310         free (msg);
311         free (post_msg);
312         free (module);
313         
314         return;
315 }
316
317 /* ------------------------------------------------------------------------- */
318 /** Sets the verbosity level
319  * @param opts testrunner lite options
320  */
321 void log_init (testrunner_lite_options *opts) {
322     
323         verbosity_level = 0;
324     
325         if (opts->log_level > 0 && opts->log_level < LOG_LEVELS_COUNT) {
326                 verbosity_level = opts->log_level;
327                 LOG_MSG (LOG_INFO, "Verbosity level set to: %d\n", 
328                          opts->log_level);
329         } else {
330                 LOG_MSG (LOG_ERR, 
331                          "Incorrect verbosity level %d, values [0..%d]\n", 
332                          opts->log_level, LOG_LEVELS_COUNT - 1);
333         }
334         if (opts->remote_logger) {
335                 curl =  curl_easy_init();
336                 curl_easy_setopt(curl, 
337                                  CURLOPT_URL, 
338                                  opts->remote_logger);
339                 if (opts->remote_logger_port)
340                         curl_easy_setopt(curl, 
341                                          CURLOPT_PORT, 
342                                          opts->remote_logger_port);
343                         
344         }
345         if (opts->syslog_output) {
346                 openlog ("testrunner-lite", 0, LOG_LOCAL1);
347                 do_syslog = 1;
348         }
349 }
350
351 /* ------------------------------------------------------------------------- */
352 /** Closes the log, flush stdout and cleanup curl if it's in use
353  */
354 void log_close () {
355         fflush (stdout);
356         if (curl) {
357                 curl_easy_cleanup(curl);
358                 curl = NULL;
359         }
360         if (do_syslog)
361                 closelog();
362 }
363
364
365 /* ================= OTHER EXPORTED FUNCTIONS ============================== */
366 /* None */
367
368 /* ------------------------------------------------------------------------- */
369 /* End of file */
370