3 * Copyright (c) 2005-2008, The Android Open Source Project
4 * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
30 typedef struct FilterInfo_t {
33 struct FilterInfo_t *p_next;
37 log_priority global_pri;
39 log_print_format format;
42 static FilterInfo * filterinfo_new(const char *tag, log_priority pri)
45 p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo));
46 p_ret->mTag = strdup(tag);
52 static void filterinfo_free(FilterInfo *p_info)
63 * Note: also accepts 0-9 priorities
64 * returns DLOG_UNKNOWN if the character is unrecognized
66 static log_priority filter_char_to_pri (char c)
72 if (c >= '0' && c <= '9') {
73 if (c >= ('0'+DLOG_SILENT)) {
76 pri = (log_priority)(c - '0');
78 } else if (c == 'v') {
80 } else if (c == 'd') {
82 } else if (c == 'i') {
84 } else if (c == 'w') {
86 } else if (c == 'e') {
88 } else if (c == 'f') {
90 } else if (c == 's') {
92 } else if (c == '*') {
101 static char filter_pri_to_char (log_priority pri)
104 case DLOG_VERBOSE: return 'V';
105 case DLOG_DEBUG: return 'D';
106 case DLOG_INFO: return 'I';
107 case DLOG_WARN: return 'W';
108 case DLOG_ERROR: return 'E';
109 case DLOG_FATAL: return 'F';
110 case DLOG_SILENT: return 'S';
118 static log_priority filter_pri_for_tag(log_format *p_format, const char *tag)
120 FilterInfo *p_curFilter;
121 // log_priority pri = DLOG_SILENT;
122 for (p_curFilter = p_format->filters; p_curFilter != NULL; p_curFilter = p_curFilter->p_next )
124 if (0 == strcmp(tag, p_curFilter->mTag))
126 if (p_curFilter->mPri == DLOG_DEFAULT) {
127 return p_format->global_pri;
129 return p_curFilter->mPri;
133 return p_format->global_pri;
137 void dump_filters(log_format *p_format)
141 for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) {
142 char cPri = filter_pri_to_char(p_fi->mPri);
143 if (p_fi->mPri == DLOG_DEFAULT) {
144 cPri = filter_pri_to_char(p_format->global_pri);
146 fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri);
149 fprintf(stderr,"*:%c\n", filter_pri_to_char(p_format->global_pri));
154 * returns 1 if this log line should be printed based on its priority
155 * and tag, and 0 if it should not
157 int log_should_print_line (log_format *p_format, const char *tag, log_priority pri)
159 return pri >= filter_pri_for_tag(p_format, tag);
162 log_format *log_format_new()
166 p_ret = calloc(1, sizeof(log_format));
168 p_ret->global_pri = DLOG_SILENT;
169 p_ret->format = FORMAT_BRIEF;
174 void log_format_free(log_format *p_format)
176 FilterInfo *p_info, *p_info_old;
178 p_info = p_format->filters;
180 while (p_info != NULL) {
182 p_info = p_info->p_next;
190 void log_set_print_format(log_format *p_format,log_print_format format)
192 p_format->format=format;
198 * Returns FORMAT_OFF on invalid string
200 log_print_format log_format_from_string(const char * formatString)
202 static log_print_format format;
204 if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
205 else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
206 else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
207 else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
208 else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
209 else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
210 else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
211 else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
212 else format = FORMAT_OFF;
218 * filterExpression: a single filter expression
221 * returns 0 on success and -1 on invalid expression
223 * Assumes single threaded execution
226 int log_add_filter_rule(log_format *p_format,
227 const char *filterExpression)
230 size_t tagNameLength;
231 log_priority pri = DLOG_DEFAULT;
233 tagNameLength = strcspn(filterExpression, ":");
235 if (tagNameLength == 0) {
239 if(filterExpression[tagNameLength] == ':') {
240 pri = filter_char_to_pri(filterExpression[tagNameLength+1]);
242 if (pri == DLOG_UNKNOWN) {
247 if(0 == strncmp("*", filterExpression, tagNameLength)) {
248 // This filter expression refers to the global filter
249 // The default level for this is DEBUG if the priority
251 if (pri == DLOG_DEFAULT) {
255 p_format->global_pri = pri;
257 // for filter expressions that don't refer to the global
258 // filter, the default is verbose if the priority is unspecified
259 if (pri == DLOG_DEFAULT) {
264 tagName = strndup(filterExpression, tagNameLength);
266 FilterInfo *p_fi = filterinfo_new(tagName, pri);
269 p_fi->p_next = p_format->filters;
270 p_format->filters = p_fi;
280 * filterString: a comma/whitespace-separated set of filter expressions
284 * returns 0 on success and -1 on invalid expression
286 * Assumes single threaded execution
290 int log_add_filter_string(log_format *p_format,
291 const char *filterString)
293 char *filterStringCopy = strdup (filterString);
294 char *p_cur = filterStringCopy;
298 // Yes, I'm using strsep
299 // FIXME : strtok is more portable than strsep
300 while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
301 // ignore whitespace-only entries
302 if(p_ret[0] != '\0') {
303 err = log_add_filter_rule(p_format, p_ret);
311 free (filterStringCopy);
314 free (filterStringCopy);
318 static inline char * strip_end(char *str)
320 char *end = str + strlen(str) - 1;
322 while (end >= str && isspace(*end))
329 * Splits a wire-format buffer into an LogEntry
330 * entry allocated by caller. Pointers will point directly into buf
332 * Returns 0 on success and -1 on invalid wire format (entry will be
333 * in unspecified state)
335 int log_process_log_buffer(struct logger_entry *buf,log_entry *entry)
339 entry->tv_sec = buf->sec;
340 entry->tv_nsec = buf->nsec;
341 entry->pid = buf->pid;
342 entry->tid = buf->tid;
344 if (buf->msg[0] < 0 || buf->msg[0] > DLOG_SILENT) { /* char can be signed too */
346 /* There is no tag in this message - which is an error, but it might
347 * happen when sombody redirects stdout/err to /dev/log_*.
349 * Pick ERROR priority as this shouldn't happen.
351 entry->priority = DLOG_ERROR;
352 entry->tag = "[NO TAG]";
353 entry->messageLen = buf->len;
354 entry->message = buf->msg;
356 entry->priority = buf->msg[0];
357 entry->tag = buf->msg + 1;
358 tag_len = strlen(entry->tag);
359 entry->messageLen = buf->len - tag_len - 3;
360 entry->message = entry->tag + tag_len + 1;
368 * Formats a log message into a buffer
370 * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
371 * If return value != defaultBuffer, caller must call free()
372 * Returns NULL on malloc error
375 char *log_format_log_line (
376 log_format *p_format,
378 size_t defaultBufferSize,
379 const log_entry *entry,
382 #if defined(HAVE_LOCALTIME_R)
387 // char headerBuf[128];
388 char prefixBuf[128], suffixBuf[128];
390 int prefixSuffixIsHeaderFooter = 0;
393 priChar = filter_pri_to_char(entry->priority);
396 * Get the current date/time in pretty form
398 * It's often useful when examining a log with "less" to jump to
399 * a specific point in the file by searching for the date/time stamp.
400 * For this reason it's very annoying to have regexp meta characters
401 * in the time stamp. Don't use forward slashes, parenthesis,
402 * brackets, asterisks, or other special chars here.
404 #if defined(HAVE_LOCALTIME_R)
405 ptm = localtime_r(&(entry->tv_sec), &tmBuf);
407 ptm = localtime(&(entry->tv_sec));
409 //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
410 strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
413 * Construct a buffer containing the log header and log message.
415 size_t prefixLen, suffixLen;
417 switch (p_format->format) {
419 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
420 "%c/%-8s: ", priChar, entry->tag);
421 strcpy(suffixBuf, "\n"); suffixLen = 1;
424 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
425 "%c(%5d) ", priChar, entry->pid);
426 suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
427 " (%s)\n", entry->tag);
430 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
431 "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
432 strcpy(suffixBuf, "\n");
438 strcpy(suffixBuf, "\n");
442 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
443 "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
444 priChar, entry->tag, entry->pid);
445 strcpy(suffixBuf, "\n");
448 case FORMAT_THREADTIME:
449 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
450 "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
451 (int)entry->pid, (int)entry->tid, priChar, entry->tag);
452 strcpy(suffixBuf, "\n");
456 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
457 "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
458 timeBuf, entry->tv_nsec / 1000000, entry->pid,
459 entry->tid, priChar, entry->tag);
460 strcpy(suffixBuf, "\n\n");
462 prefixSuffixIsHeaderFooter = 1;
466 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
467 "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
468 strcpy(suffixBuf, "\n");
472 /* snprintf has a weird return value. It returns what would have been
473 * written given a large enough buffer. In the case that the prefix is
474 * longer then our buffer(128), it messes up the calculations below
475 * possibly causing heap corruption. To avoid this we double check and
476 * set the length at the maximum (size minus null byte)
478 if(prefixLen >= sizeof(prefixBuf))
479 prefixLen = sizeof(prefixBuf) - 1;
480 if(suffixLen >= sizeof(suffixBuf))
481 suffixLen = sizeof(suffixBuf) - 1;
483 /* the following code is tragically unreadable */
491 if (prefixSuffixIsHeaderFooter) {
492 // we're just wrapping message with a header/footer
498 // The line-end finding here must match the line-end finding
499 // in for ( ... numLines...) loop below
500 while (pm < (entry->message + entry->messageLen)) {
501 if (*pm++ == '\n') numLines++;
503 // plus one line for anything not newline-terminated at the end
504 if (pm > entry->message && *(pm-1) != '\n') numLines++;
507 // this is an upper bound--newlines in message may be counted
509 bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
511 if (defaultBufferSize >= bufferSize) {
514 ret = (char *)malloc(bufferSize);
521 ret[0] = '\0'; /* to start strcat off */
526 if (prefixSuffixIsHeaderFooter) {
527 strcat(p, prefixBuf);
528 // strncat(p, prefixBuf, sizeof(prefixBuf));
530 strncat(p, entry->message, entry->messageLen);
531 p += entry->messageLen;
532 strcat(p, suffixBuf);
533 // strncat(p, suffixBuf, sizeof(suffixBuf));
536 while(pm < (entry->message + entry->messageLen)) {
537 const char *lineStart;
542 // Find the next end-of-line in message
543 while (pm < (entry->message + entry->messageLen)
544 && *pm != '\n') pm++;
545 lineLen = pm - lineStart;
547 strcat(p, prefixBuf);
548 //strncat(p, prefixBuf, sizeof(prefixBuf));
550 strncat(p, lineStart, lineLen);
552 strcat(p, suffixBuf);
553 //strncat(p, suffixBuf, sizeof(suffixBuf));
556 if (*pm == '\n') pm++;
560 if (p_outLength != NULL) {
561 *p_outLength = p - ret;
568 * Either print or do not print log line, based on filter
570 * Returns count bytes written
573 int log_print_log_line(
574 log_format *p_format,
576 const log_entry *entry)
579 char defaultBuffer[512];
580 char *outBuffer = NULL;
583 outBuffer = log_format_log_line(p_format, defaultBuffer,sizeof(defaultBuffer), entry, &totalLen);
589 ret = write(fd, outBuffer, totalLen);
590 } while (ret < 0 && errno == EINTR);
593 fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
598 if (((size_t)ret) < totalLen) {
599 fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
605 if (outBuffer != defaultBuffer) {
614 void logprint_run_tests()
618 fprintf(stderr, "tests disabled\n");
624 log_format *p_format;
626 p_format = log_format_new();
628 fprintf(stderr, "running tests\n");
632 log_add_filter_rule(p_format,"*:i");
634 assert (DLOG_INFO == filter_pri_for_tag(p_format, "random"));
635 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0);
636 log_add_filter_rule(p_format, "*");
637 assert (DLOG_DEBUG == filter_pri_for_tag(p_format, "random"));
638 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0);
639 log_add_filter_rule(p_format, "*:v");
640 assert (DLOG_VERBOSE == filter_pri_for_tag(p_format, "random"));
641 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0);
642 log_add_filter_rule(p_format, "*:i");
643 assert (DLOG_INFO == filter_pri_for_tag(p_format, "random"));
644 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0);
646 log_add_filter_rule(p_format, "random");
647 assert (DLOG_VERBOSE == filter_pri_for_tag(p_format, "random"));
648 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0);
649 log_add_filter_rule(p_format, "random:v");
650 assert (DLOG_VERBOSE == filter_pri_for_tag(p_format, "random"));
651 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0);
652 log_add_filter_rule(p_format, "random:d");
653 assert (DLOG_DEBUG == filter_pri_for_tag(p_format, "random"));
654 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0);
655 log_add_filter_rule(p_format, "random:w");
656 assert (DLOG_WARN == filter_pri_for_tag(p_format, "random"));
657 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0);
659 log_add_filter_rule(p_format, "crap:*");
660 assert (DLOG_VERBOSE== filter_pri_for_tag(p_format, "crap"));
661 assert(log_should_print_line(p_format, "crap", DLOG_VERBOSE) > 0);
663 // invalid expression
664 err = log_add_filter_rule(p_format, "random:z");
666 assert (DLOG_WARN == filter_pri_for_tag(p_format, "random"));
667 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0);
670 err = log_add_filter_string(p_format, " ");
672 assert(DLOG_WARN == filter_pri_for_tag(p_format, "random"));
674 // note trailing space
675 err = log_add_filter_string(p_format, "*:s random:d ");
677 assert(DLOG_DEBUG == filter_pri_for_tag(p_format, "random"));
679 err = log_add_filter_string(p_format, "*:s random:z");
682 log_format_free(p_format);
687 char defaultBuffer[512];
689 ret = log_formatLogLine(p_format,
690 defaultBuffer, sizeof(defaultBuffer), 0, DLOG_ERROR, 123,
691 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL);
695 fprintf(stderr, "tests complete\n");