da1a420c5637f58b93442a9a3b98d69b60a08c35
[platform/upstream/iotivity.git] / resource / csdk / logger / src / logger.c
1 //******************************************************************
2 //
3 // Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 // Defining _POSIX_C_SOURCE macro with 199309L (or greater) as value
22 // causes header files to expose definitions
23 // corresponding to the POSIX.1b, Real-time extensions
24 // (IEEE Std 1003.1b-1993) specification
25 //
26 // For this specific file, see use of clock_gettime,
27 // Refer to http://pubs.opengroup.org/stage7tc1/functions/clock_gettime.html
28 // and to http://man7.org/linux/man-pages/man2/clock_gettime.2.html
29 #ifndef _POSIX_C_SOURCE
30 #define _POSIX_C_SOURCE 200809L
31 #endif
32
33 // Platform check can be extended to check and/or define more, or could be
34 // moved into a config.h
35 #if !defined(__ARDUINO__) && !defined(ARDUINO)
36 #define HAVE_UNISTD_H 1
37 #endif
38
39 // Pull in _POSIX_TIMERS feature test macro to check for
40 // clock_gettime() support.
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43
44 // if we have unistd.h, we're a Unix system
45 #include <time.h>
46 #include <sys/time.h>
47 #endif
48
49 #include "logger.h"
50 #include "string.h"
51 #include "oc_logger.h"
52 #include "oc_console_logger.h"
53
54 #ifndef __TIZEN__
55 static oc_log_ctx_t *logCtx = 0;
56
57 static oc_log_level LEVEL_XTABLE[] = {OC_LOG_DEBUG, OC_LOG_INFO, OC_LOG_WARNING, OC_LOG_ERROR, OC_LOG_FATAL};
58
59 #endif
60 static const uint16_t LINE_BUFFER_SIZE = (16 * 2) + 16 + 1;  // Show 16 bytes, 2 chars/byte, spaces between bytes, null termination
61
62 // Convert LogLevel to platform-specific severity level.  Store in PROGMEM on Arduino
63 #ifdef __ANDROID__
64     static android_LogPriority LEVEL[] = {ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL};
65 #elif defined(__linux__) || defined(__APPLE__)
66     static const char * LEVEL[] __attribute__ ((unused)) = {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"};
67 #elif defined ARDUINO
68     #include <stdarg.h>
69
70     PROGMEM const char level0[] = "DEBUG";
71     PROGMEM const char level1[] = "INFO";
72     PROGMEM const char level2[] = "WARNING";
73     PROGMEM const char level3[] = "ERROR";
74     PROGMEM const char level4[] = "FATAL";
75
76     PROGMEM const char * const LEVEL[]  = {level0, level1, level2, level3, level4};
77
78     static void OCLogString(LogLevel level, PROGMEM const char * tag, PROGMEM const char * logStr);
79 #ifdef ARDUINO_ARCH_AVR
80     //Mega2560 and other 8-bit AVR microcontrollers
81     #define GET_PROGMEM_BUFFER(buffer, addr) { strcpy_P(buffer, (char*)pgm_read_word(addr));}
82 #elif defined ARDUINO_ARCH_SAM
83     //Arduino Due and other 32-bit ARM micro-controllers
84     #define GET_PROGMEM_BUFFER(buffer, addr) { strcpy_P(buffer, (char*)pgm_read_dword(addr));}
85 #else
86     #define GET_PROGMEM_BUFFER(buffer, addr) { buffer[0] = '\0';}
87 #endif
88 #endif // __ANDROID__
89
90
91 #ifndef ARDUINO
92 #ifdef __TIZEN__
93
94 int OCGetTizenLogLevel(LogLevel level)
95 {
96     switch(level)
97     {
98         case DEBUG:
99             return DLOG_DEBUG;
100         case INFO:
101             return DLOG_INFO;
102         case WARNING:
103             return DLOG_WARN;
104         case ERROR:
105             return DLOG_ERROR;
106         case FATAL:
107             /*
108              * Temp fix to resolve DLOG_FATAL runtime crash in tizen binary.
109              * TODO: Revert back to DLOG_FATAL once logging issue is fixed in
110              * Tizen binary.
111              */
112             return DLOG_ERROR;
113     }
114     return DLOG_DEBUG;
115 }
116
117 #else
118 void OCLogConfig(oc_log_ctx_t *ctx) {
119     logCtx = ctx;
120 }
121
122 void OCLogInit() {
123
124 }
125
126 void OCLogShutdown() {
127 #if defined(__linux__) || defined(__APPLE__)
128     if (logCtx && logCtx->destroy)
129     {
130         logCtx->destroy(logCtx);
131     }
132 #endif
133 }
134
135 /**
136  * Output a variable argument list log string with the specified priority level.
137  * Only defined for Linux and Android
138  *
139  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
140  * @param tag    - Module name
141  * @param format - variadic log string
142  */
143 void OCLogv(LogLevel level, const char * tag, const char * format, ...) {
144     if (!format || !tag) {
145         return;
146     }
147     char buffer[MAX_LOG_V_BUFFER_SIZE] = {};
148     va_list args;
149     va_start(args, format);
150     vsnprintf(buffer, sizeof buffer - 1, format, args);
151     va_end(args);
152     OCLog(level, tag, buffer);
153 }
154
155 static void osalGetTime(int *min,int *sec, int *ms)
156 {
157     if (min && sec && ms)
158     {
159 #if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
160         struct timespec when = {0};
161         clockid_t clk = CLOCK_REALTIME;
162 #ifdef CLOCK_REALTIME_COARSE
163         clk = CLOCK_REALTIME_COARSE;
164 #endif
165         if (!clock_gettime(clk, &when))
166         {
167             *min = (when.tv_sec / 60) % 60;
168             *sec = when.tv_sec % 60;
169             *ms = when.tv_nsec / 1000000;
170         }
171 #else
172         struct timeval now;
173         if (!gettimeofday(&now, NULL))
174         {
175             *min = (now.tv_sec / 60) % 60;
176             *sec = now.tv_sec % 60;
177             *ms = now.tv_usec * 1000;
178         }
179 #endif
180     }
181 }
182
183 /**
184  * Output a log string with the specified priority level.
185  * Only defined for Linux and Android
186  *
187  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
188  * @param tag    - Module name
189  * @param logStr - log string
190  */
191 void OCLog(LogLevel level, const char * tag, const char * logStr) {
192     if (!logStr || !tag) {
193         return;
194     }
195
196 #ifdef __ANDROID__
197     __android_log_write(LEVEL[level], tag, logStr);
198 #elif defined(__linux__) || defined(__APPLE__)
199     if (logCtx && logCtx->write_level)
200     {
201         logCtx->write_level(logCtx, LEVEL_XTABLE[level], logStr);
202     }
203     else
204     {
205
206         int min = 0;
207         int sec = 0;
208         int ms = 0;
209         osalGetTime(&min,&sec,&ms);
210
211         printf("%02d:%02d.%03d %s: %s: %s\n", min, sec, ms, LEVEL[level], tag, logStr);
212     }
213 #endif
214 }
215
216 /**
217  * Output the contents of the specified buffer (in hex) with the specified priority level.
218  *
219  * @param level      - DEBUG, INFO, WARNING, ERROR, FATAL
220  * @param tag        - Module name
221  * @param buffer     - pointer to buffer of bytes
222  * @param bufferSize - max number of byte in buffer
223  */
224 void OCLogBuffer(LogLevel level, const char * tag, const uint8_t * buffer, uint16_t bufferSize) {
225     if (!buffer || !tag || (bufferSize == 0)) {
226         return;
227     }
228
229     // No idea why the static initialization won't work here, it seems the compiler is convinced
230     // that this is a variable-sized object.
231     char lineBuffer[LINE_BUFFER_SIZE];
232     memset(lineBuffer, 0, sizeof lineBuffer);
233     int lineIndex = 0;
234     int i;
235     for (i = 0; i < bufferSize; i++) {
236         // Format the buffer data into a line
237         snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
238         lineIndex++;
239         // Output 16 values per line
240         if (((i+1)%16) == 0) {
241             OCLog(level, tag, lineBuffer);
242             memset(lineBuffer, 0, sizeof lineBuffer);
243             lineIndex = 0;
244         }
245     }
246     // Output last values in the line, if any
247     if (bufferSize % 16) {
248         OCLog(level, tag, lineBuffer);
249     }
250 }
251 #endif //__TIZEN__
252 #else
253     /**
254      * Initialize the serial logger for Arduino
255      * Only defined for Arduino
256      */
257     void OCLogInit() {
258         Serial.begin(115200);
259     }
260
261     /**
262      * Output a log string with the specified priority level.
263      * Only defined for Arduino.  Only uses PROGMEM strings
264      * for the tag parameter
265      *
266      * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
267      * @param tag    - Module name
268      * @param logStr - log string
269      */
270     void OCLogString(LogLevel level, PROGMEM const char * tag, const char * logStr) {
271         if (!logStr || !tag) {
272           return;
273         }
274
275         char buffer[LINE_BUFFER_SIZE];
276
277         GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
278         Serial.print(buffer);
279
280         char c;
281         Serial.print(F(": "));
282         while ((c = pgm_read_byte(tag))) {
283           Serial.write(c);
284           tag++;
285         }
286         Serial.print(F(": "));
287
288         Serial.println(logStr);
289     }
290
291     /**
292      * Output the contents of the specified buffer (in hex) with the specified priority level.
293      *
294      * @param level      - DEBUG, INFO, WARNING, ERROR, FATAL
295      * @param tag        - Module name
296      * @param buffer     - pointer to buffer of bytes
297      * @param bufferSize - max number of byte in buffer
298      */
299     void OCLogBuffer(LogLevel level, PROGMEM const char * tag, const uint8_t * buffer, uint16_t bufferSize) {
300         if (!buffer || !tag || (bufferSize == 0)) {
301             return;
302         }
303
304         char lineBuffer[LINE_BUFFER_SIZE] = {0};
305         uint8_t lineIndex = 0;
306         for (uint8_t i = 0; i < bufferSize; i++) {
307             // Format the buffer data into a line
308             snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
309             lineIndex++;
310             // Output 16 values per line
311             if (((i+1)%16) == 0) {
312                 OCLogString(level, tag, lineBuffer);
313                 memset(lineBuffer, 0, sizeof lineBuffer);
314                 lineIndex = 0;
315             }
316         }
317         // Output last values in the line, if any
318         if (bufferSize % 16) {
319             OCLogString(level, tag, lineBuffer);
320         }
321     }
322
323     /**
324      * Output a log string with the specified priority level.
325      * Only defined for Arduino.  Uses PROGMEM strings
326      *
327      * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
328      * @param tag    - Module name
329      * @param logStr - log string
330      */
331     void OCLog(LogLevel level, PROGMEM const char * tag, PROGMEM const char * logStr) {
332         if (!logStr || !tag) {
333           return;
334         }
335
336         char buffer[LINE_BUFFER_SIZE];
337
338         GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
339         Serial.print(buffer);
340
341         char c;
342         Serial.print(F(": "));
343         while ((c = pgm_read_byte(tag))) {
344           Serial.write(c);
345           tag++;
346         }
347         Serial.print(F(": "));
348
349         while ((c = pgm_read_byte(logStr))) {
350           Serial.write(c);
351           logStr++;
352         }
353         Serial.println();
354     }
355
356     /**
357      * Output a variable argument list log string with the specified priority level.
358      * Only defined for Arduino as depicted below.
359      *
360      * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
361      * @param tag    - Module name
362      * @param format - variadic log string
363      */
364     void OCLogv(LogLevel level, PROGMEM const char * tag, PROGMEM const char * format, ...)
365     {
366         char buffer[LINE_BUFFER_SIZE];
367         va_list ap;
368         va_start(ap, format);
369
370         GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
371         Serial.print(buffer);
372
373         char c;
374         Serial.print(F(": "));
375
376         while ((c = pgm_read_byte(tag))) {
377             Serial.write(c);
378             tag++;
379         }
380         Serial.print(F(": "));
381
382 #ifdef __AVR__
383         vsnprintf_P(buffer, sizeof(buffer), format, ap);
384 #else
385         vsnprintf(buffer, sizeof(buffer), format, ap);
386 #endif
387         for(char *p = &buffer[0]; *p; p++) // emulate cooked mode for newlines
388         {
389             if(*p == '\n')
390             {
391                 Serial.write('\r');
392             }
393             Serial.write(*p);
394         }
395         Serial.println();
396         va_end(ap);
397     }
398
399 #endif