a92afd41e44b473d77aa34831e1184fd86f2edde
[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 "logger_types.h"
52
53 #ifndef __TIZEN__
54 static oc_log_ctx_t *logCtx = 0;
55
56 static oc_log_level LEVEL_XTABLE[] = {OC_LOG_DEBUG, OC_LOG_INFO, OC_LOG_WARNING, OC_LOG_ERROR, OC_LOG_FATAL};
57
58 #endif
59 static const uint16_t LINE_BUFFER_SIZE = (16 * 2) + 16 + 1;  // Show 16 bytes, 2 chars/byte, spaces between bytes, null termination
60
61 // Convert LogLevel to platform-specific severity level.  Store in PROGMEM on Arduino
62 #ifdef __ANDROID__
63 #ifdef ADB_SHELL
64     static const char *LEVEL[] =
65     {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"};
66
67 #else
68     static android_LogPriority LEVEL[] =
69     {ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL};
70 #endif
71 #elif defined (__linux__) || defined (__APPLE__)
72     static const char *LEVEL[] __attribute__ ((unused)) =
73     {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"};
74 #elif defined ARDUINO
75 #include <stdarg.h>
76 #include "Arduino.h"
77
78     PROGMEM const char level0[] = "DEBUG";
79     PROGMEM const char level1[] = "INFO";
80     PROGMEM const char level2[] = "WARNING";
81     PROGMEM const char level3[] = "ERROR";
82     PROGMEM const char level4[] = "FATAL";
83
84     PROGMEM const char * const LEVEL[]  = {level0, level1, level2, level3, level4};
85
86     static void OCLogString(LogLevel level, PROGMEM const char * tag, PROGMEM const char * logStr);
87 #ifdef ARDUINO_ARCH_AVR
88     //Mega2560 and other 8-bit AVR microcontrollers
89     #define GET_PROGMEM_BUFFER(buffer, addr) { strcpy_P(buffer, (char*)pgm_read_word(addr));}
90 #elif defined ARDUINO_ARCH_SAM
91     //Arduino Due and other 32-bit ARM micro-controllers
92     #define GET_PROGMEM_BUFFER(buffer, addr) { strcpy_P(buffer, (char*)pgm_read_dword(addr));}
93 #else
94     #define GET_PROGMEM_BUFFER(buffer, addr) { buffer[0] = '\0';}
95 #endif
96 #endif // __ANDROID__
97
98
99 #ifndef ARDUINO
100 #ifndef __TIZEN__
101 void OCLogConfig(oc_log_ctx_t *ctx)
102 {
103     logCtx = ctx;
104 }
105
106 void OCLogInit()
107 {
108
109 }
110
111 void OCLogShutdown()
112 {
113 #if defined(__linux__) || defined(__APPLE__)
114     if (logCtx && logCtx->destroy)
115     {
116         logCtx->destroy(logCtx);
117     }
118 #endif
119 }
120
121 /**
122  * Output a variable argument list log string with the specified priority level.
123  * Only defined for Linux and Android
124  *
125  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
126  * @param tag    - Module name
127  * @param format - variadic log string
128  */
129 void OCLogv(LogLevel level, const char * tag, const char * format, ...)
130 {
131     if (!format || !tag) {
132         return;
133     }
134     char buffer[MAX_LOG_V_BUFFER_SIZE] = {};
135     va_list args;
136     va_start(args, format);
137     vsnprintf(buffer, sizeof buffer - 1, format, args);
138     va_end(args);
139     OCLog(level, tag, buffer);
140 }
141
142 /**
143  * Output a log string with the specified priority level.
144  * Only defined for Linux and Android
145  *
146  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
147  * @param tag    - Module name
148  * @param logStr - log string
149  */
150 void OCLog(LogLevel level, const char * tag, const char * logStr)
151 {
152     if (!logStr || !tag)
153     {
154        return;
155     }
156
157    #ifdef __ANDROID__
158
159    #ifdef ADB_SHELL
160        printf("%s: %s: %s\n", LEVEL[level], tag, logStr);
161    #else
162        __android_log_write(LEVEL[level], tag, logStr);
163    #endif
164
165    #elif defined __linux__ || defined __APPLE__
166        if (logCtx && logCtx->write_level)
167        {
168            logCtx->write_level(logCtx, LEVEL_XTABLE[level], logStr);
169
170        }
171        else
172        {
173            int min = 0;
174            int sec = 0;
175            int ms = 0;
176    #if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
177            struct timespec when = { .tv_sec = 0, .tv_nsec = 0 };
178            clockid_t clk = CLOCK_REALTIME;
179    #ifdef CLOCK_REALTIME_COARSE
180            clk = CLOCK_REALTIME_COARSE;
181    #endif
182            if (!clock_gettime(clk, &when))
183            {
184                min = (when.tv_sec / 60) % 60;
185                sec = when.tv_sec % 60;
186                ms = when.tv_nsec / 1000000;
187            }
188    #else
189            struct timeval now;
190            if (!gettimeofday(&now, NULL))
191            {
192                min = (now.tv_sec / 60) % 60;
193                sec = now.tv_sec % 60;
194                ms = now.tv_usec * 1000;
195            }
196    #endif
197            printf("%02d:%02d.%03d %s: %s: %s\n", min, sec, ms, LEVEL[level], tag, logStr);
198        }
199    #endif
200    }
201
202 /**
203  * Output the contents of the specified buffer (in hex) with the specified priority level.
204  *
205  * @param level      - DEBUG, INFO, WARNING, ERROR, FATAL
206  * @param tag        - Module name
207  * @param buffer     - pointer to buffer of bytes
208  * @param bufferSize - max number of byte in buffer
209  */
210 void OCLogBuffer(LogLevel level, const char * tag, const uint8_t * buffer, uint16_t bufferSize)
211 {
212     if (!buffer || !tag || (bufferSize == 0))
213     {
214         return;
215     }
216
217     // No idea why the static initialization won't work here, it seems the compiler is convinced
218     // that this is a variable-sized object.
219     char lineBuffer[LINE_BUFFER_SIZE];
220     memset(lineBuffer, 0, sizeof lineBuffer);
221     int lineIndex = 0;
222     int i;
223     for (i = 0; i < bufferSize; i++)
224     {
225         // Format the buffer data into a line
226         snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
227         lineIndex++;
228         // Output 16 values per line
229         if (((i+1)%16) == 0)
230         {
231             OCLog(level, tag, lineBuffer);
232             memset(lineBuffer, 0, sizeof lineBuffer);
233             lineIndex = 0;
234         }
235     }
236     // Output last values in the line, if any
237     if (bufferSize % 16)
238     {
239         OCLog(level, tag, lineBuffer);
240     }
241 }
242 #endif //__TIZEN__
243 #endif //ARDUINO
244 #ifdef ARDUINO
245 /**
246  * Initialize the serial logger for Arduino
247  * Only defined for Arduino
248  */
249 void OCLogInit()
250 {
251     Serial.begin(115200);
252 }
253
254 /**
255  * Output a log string with the specified priority level.
256  * Only defined for Arduino.  Only uses PROGMEM strings
257  * for the tag parameter
258  *
259  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
260  * @param tag    - Module name
261  * @param logStr - log string
262  */
263 void OCLogString(LogLevel level, PROGMEM const char * tag, const char * logStr)
264 {
265     if (!logStr || !tag)
266     {
267       return;
268     }
269
270     char buffer[LINE_BUFFER_SIZE];
271
272     GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
273     Serial.print(buffer);
274
275     char c;
276     Serial.print(F(": "));
277     while ((c = pgm_read_byte(tag)))
278     {
279       Serial.write(c);
280       tag++;
281     }
282     Serial.print(F(": "));
283
284     Serial.println(logStr);
285 }
286
287 /**
288  * Output the contents of the specified buffer (in hex) with the specified
289  * priority level.
290  *
291  * @param level      - DEBUG, INFO, WARNING, ERROR, FATAL
292  * @param tag        - Module name
293  * @param buffer     - pointer to buffer of bytes
294  * @param bufferSize - max number of byte in buffer
295  */
296  void OCLogBuffer(LogLevel level, PROGMEM const char * tag,
297                   const uint8_t * buffer, size_t bufferSize)
298  {
299      if (!buffer || !tag || (bufferSize == 0))
300      {
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      {
308         // Format the buffer data into a line
309         snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
310         lineIndex++;
311          // Output 16 values per line
312          if (((i+1)%16) == 0)
313          {
314              OCLogString(level, tag, lineBuffer);
315              memset(lineBuffer, 0, sizeof lineBuffer);
316              lineIndex = 0;
317          }
318      }
319      // Output last values in the line, if any
320      if (bufferSize % 16)
321      {
322          OCLogString(level, tag, lineBuffer);
323      }
324  }
325
326 /**
327  * Output a log string with the specified priority level.
328  * Only defined for Arduino.  Uses PROGMEM strings
329  *
330  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
331  * @param tag    - Module name
332  * @param logStr - log string
333  */
334 void OCLog(LogLevel level, PROGMEM const char *tag, const int lineNum,
335            PROGMEM const char *logStr)
336 {
337     if (!logStr || !tag)
338     {
339         return;
340     }
341     char buffer[LINE_BUFFER_SIZE] = {0};
342     GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
343     Serial.print(buffer);
344     char c;
345     Serial.print(F(": "));
346     while ((c = pgm_read_byte(tag)))
347     {
348         Serial.write(c);
349         tag++;
350     }
351     Serial.print(F(": "));
352     Serial.print(lineNum);
353     Serial.print(F(": "));
354     while ((c = pgm_read_byte(logStr)))
355     {
356         Serial.write(c);
357         logStr++;
358     }
359     Serial.println();
360 }
361
362 /**
363  * Output a variable argument list log string with the specified priority level.
364  * Only defined for Arduino as depicted below.
365  *
366  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
367  * @param tag    - Module name
368  * @param format - variadic log string
369  */
370 void OCLogv(LogLevel level, PROGMEM const char *tag, const int lineNum,
371                 PROGMEM const char *format, ...)
372 {
373     char buffer[LINE_BUFFER_SIZE];
374     va_list ap;
375     va_start(ap, format);
376     GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
377     Serial.print(buffer);
378
379     char c;
380     Serial.print(F(": "));
381     while ((c = pgm_read_byte(tag)))
382     {
383      Serial.write(c);
384      tag++;
385      }
386     Serial.print(F(": "));
387     Serial.print(lineNum);
388     Serial.print(F(": "));
389
390 #ifdef __AVR__
391     vsnprintf_P(buffer, sizeof(buffer), format, ap);
392 #else
393     vsnprintf(buffer, sizeof(buffer), format, ap);
394 #endif
395     for (char *p = &buffer[0]; *p; p++)
396     {
397         // emulate cooked mode for newlines
398         if (*p == '\n')
399         {
400             Serial.write('\r');
401         }
402         Serial.write(*p);
403     }
404     Serial.println();
405     va_end(ap);
406 }
407 /**
408  * Output a variable argument list log string with the specified priority level.
409  * Only defined for Arduino as depicted below.
410  *
411  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
412  * @param tag    - Module name
413  * @param format - variadic log string
414  */
415 void OCLogv(LogLevel level, const char *tag, const __FlashStringHelper *format, ...)
416 {
417     char buffer[LINE_BUFFER_SIZE];
418     va_list ap;
419     va_start(ap, format);
420     // strcpy_P(buffer, (char*)pgm_read_word(&(LEVEL[level])));
421     // Serial.print(buffer);
422
423     Serial.print(LEVEL[level]);
424     // char c;
425     Serial.print(F(": "));
426
427     /*while ((c = pgm_read_byte(tag))) {
428      Serial.write(c);
429      tag++;
430      }*/
431     Serial.print(tag);
432     Serial.print(F(": "));
433
434 #ifdef __AVR__
435     vsnprintf_P(buffer, sizeof(buffer), (const char *)format, ap); // progmem for AVR
436 #else
437     vsnprintf(buffer, sizeof(buffer), (const char *)format, ap); // for the rest of the world
438 #endif
439     for (char *p = &buffer[0]; *p; p++)
440     {
441         // emulate cooked mode for newlines
442         if (*p == '\n')
443         {
444             // Serial.write('\r');
445             Serial.print('\r');
446         }
447         //Serial.write(*p);
448         Serial.print(p);
449     }
450     Serial.println();
451     va_end(ap);
452 }
453
454 #endif //ARDUINO