8e21e334ac217564a4577a2f7c4426e60046c6c0
[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 #ifndef __TIZEN__
93 void OCLogConfig(oc_log_ctx_t *ctx) {
94     logCtx = ctx;
95 }
96
97 void OCLogInit() {
98
99 }
100
101 void OCLogShutdown() {
102 #if defined(__linux__) || defined(__APPLE__)
103     if (logCtx && logCtx->destroy)
104     {
105         logCtx->destroy(logCtx);
106     }
107 #endif
108 }
109
110 /**
111  * Output a variable argument list log string with the specified priority level.
112  * Only defined for Linux and Android
113  *
114  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
115  * @param tag    - Module name
116  * @param format - variadic log string
117  */
118 void OCLogv(LogLevel level, const char * tag, const char * format, ...) {
119     if (!format || !tag) {
120         return;
121     }
122     char buffer[MAX_LOG_V_BUFFER_SIZE] = {};
123     va_list args;
124     va_start(args, format);
125     vsnprintf(buffer, sizeof buffer - 1, format, args);
126     va_end(args);
127     OCLog(level, tag, buffer);
128 }
129
130 static void osalGetTime(int *min,int *sec, int *ms)
131 {
132     if (min && sec && ms)
133     {
134 #if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
135         struct timespec when = {0};
136         clockid_t clk = CLOCK_REALTIME;
137 #ifdef CLOCK_REALTIME_COARSE
138         clk = CLOCK_REALTIME_COARSE;
139 #endif
140         if (!clock_gettime(clk, &when))
141         {
142             *min = (when.tv_sec / 60) % 60;
143             *sec = when.tv_sec % 60;
144             *ms = when.tv_nsec / 1000000;
145         }
146 #else
147         struct timeval now;
148         if (!gettimeofday(&now, NULL))
149         {
150             *min = (now.tv_sec / 60) % 60;
151             *sec = now.tv_sec % 60;
152             *ms = now.tv_usec * 1000;
153         }
154 #endif
155     }
156 }
157
158 /**
159  * Output a log string with the specified priority level.
160  * Only defined for Linux and Android
161  *
162  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
163  * @param tag    - Module name
164  * @param logStr - log string
165  */
166 void OCLog(LogLevel level, const char * tag, const char * logStr) {
167     if (!logStr || !tag) {
168         return;
169     }
170
171 #ifdef __ANDROID__
172     __android_log_write(LEVEL[level], tag, logStr);
173 #elif defined(__linux__) || defined(__APPLE__)
174     if (logCtx && logCtx->write_level)
175     {
176         logCtx->write_level(logCtx, LEVEL_XTABLE[level], logStr);
177     }
178     else
179     {
180         int min = 0;
181         int sec = 0;
182         int ms = 0;
183         osalGetTime(&min,&sec,&ms);
184
185         printf("%02d:%02d.%03d %s: %s: %s\n", min, sec, ms, LEVEL[level], tag, logStr);
186     }
187 #endif
188 }
189
190 /**
191  * Output the contents of the specified buffer (in hex) with the specified priority level.
192  *
193  * @param level      - DEBUG, INFO, WARNING, ERROR, FATAL
194  * @param tag        - Module name
195  * @param buffer     - pointer to buffer of bytes
196  * @param bufferSize - max number of byte in buffer
197  */
198 void OCLogBuffer(LogLevel level, const char * tag, const uint8_t * buffer, uint16_t bufferSize) {
199     if (!buffer || !tag || (bufferSize == 0)) {
200         return;
201     }
202
203     // No idea why the static initialization won't work here, it seems the compiler is convinced
204     // that this is a variable-sized object.
205     char lineBuffer[LINE_BUFFER_SIZE];
206     memset(lineBuffer, 0, sizeof lineBuffer);
207     int lineIndex = 0;
208     int i;
209     for (i = 0; i < bufferSize; i++) {
210         // Format the buffer data into a line
211         snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
212         lineIndex++;
213         // Output 16 values per line
214         if (((i+1)%16) == 0) {
215             OCLog(level, tag, lineBuffer);
216             memset(lineBuffer, 0, sizeof lineBuffer);
217             lineIndex = 0;
218         }
219     }
220     // Output last values in the line, if any
221     if (bufferSize % 16) {
222         OCLog(level, tag, lineBuffer);
223     }
224 }
225 #endif //__TIZEN__
226 #else
227     /**
228      * Initialize the serial logger for Arduino
229      * Only defined for Arduino
230      */
231     void OCLogInit() {
232         Serial.begin(115200);
233     }
234
235     /**
236      * Output a log string with the specified priority level.
237      * Only defined for Arduino.  Only uses PROGMEM strings
238      * for the tag parameter
239      *
240      * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
241      * @param tag    - Module name
242      * @param logStr - log string
243      */
244     void OCLogString(LogLevel level, PROGMEM const char * tag, const char * logStr) {
245         if (!logStr || !tag) {
246           return;
247         }
248
249         char buffer[LINE_BUFFER_SIZE];
250
251         GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
252         Serial.print(buffer);
253
254         char c;
255         Serial.print(F(": "));
256         while ((c = pgm_read_byte(tag))) {
257           Serial.write(c);
258           tag++;
259         }
260         Serial.print(F(": "));
261
262         Serial.println(logStr);
263     }
264
265     /**
266      * Output the contents of the specified buffer (in hex) with the specified priority level.
267      *
268      * @param level      - DEBUG, INFO, WARNING, ERROR, FATAL
269      * @param tag        - Module name
270      * @param buffer     - pointer to buffer of bytes
271      * @param bufferSize - max number of byte in buffer
272      */
273     void OCLogBuffer(LogLevel level, PROGMEM const char * tag, const uint8_t * buffer, uint16_t bufferSize) {
274         if (!buffer || !tag || (bufferSize == 0)) {
275             return;
276         }
277
278         char lineBuffer[LINE_BUFFER_SIZE] = {0};
279         uint8_t lineIndex = 0;
280         for (uint8_t i = 0; i < bufferSize; i++) {
281             // Format the buffer data into a line
282             snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
283             lineIndex++;
284             // Output 16 values per line
285             if (((i+1)%16) == 0) {
286                 OCLogString(level, tag, lineBuffer);
287                 memset(lineBuffer, 0, sizeof lineBuffer);
288                 lineIndex = 0;
289             }
290         }
291         // Output last values in the line, if any
292         if (bufferSize % 16) {
293             OCLogString(level, tag, lineBuffer);
294         }
295     }
296
297     /**
298      * Output a log string with the specified priority level.
299      * Only defined for Arduino.  Uses PROGMEM strings
300      *
301      * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
302      * @param tag    - Module name
303      * @param logStr - log string
304      */
305     void OCLog(LogLevel level, PROGMEM const char * tag, PROGMEM const char * logStr) {
306         if (!logStr || !tag) {
307           return;
308         }
309
310         char buffer[LINE_BUFFER_SIZE];
311
312         GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
313         Serial.print(buffer);
314
315         char c;
316         Serial.print(F(": "));
317         while ((c = pgm_read_byte(tag))) {
318           Serial.write(c);
319           tag++;
320         }
321         Serial.print(F(": "));
322
323         while ((c = pgm_read_byte(logStr))) {
324           Serial.write(c);
325           logStr++;
326         }
327         Serial.println();
328     }
329
330     /**
331      * Output a variable argument list log string with the specified priority level.
332      * Only defined for Arduino as depicted below.
333      *
334      * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
335      * @param tag    - Module name
336      * @param format - variadic log string
337      */
338     void OCLogv(LogLevel level, PROGMEM const char * tag, PROGMEM const char * format, ...)
339     {
340         char buffer[LINE_BUFFER_SIZE];
341         va_list ap;
342         va_start(ap, format);
343
344         GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
345         Serial.print(buffer);
346
347         char c;
348         Serial.print(F(": "));
349
350         while ((c = pgm_read_byte(tag))) {
351             Serial.write(c);
352             tag++;
353         }
354         Serial.print(F(": "));
355
356 #ifdef __AVR__
357         vsnprintf_P(buffer, sizeof(buffer), format, ap);
358 #else
359         vsnprintf(buffer, sizeof(buffer), format, ap);
360 #endif
361         for(char *p = &buffer[0]; *p; p++) // emulate cooked mode for newlines
362         {
363             if(*p == '\n')
364             {
365                 Serial.write('\r');
366             }
367             Serial.write(*p);
368         }
369         Serial.println();
370         va_end(ap);
371     }
372
373 #endif