2 * Copyright 2006, The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
28 typedef struct FilterInfo_t {
31 struct FilterInfo_t *p_next;
35 log_priority global_pri;
37 log_print_format format;
40 static FilterInfo * filterinfo_new(const char *tag, log_priority pri)
43 p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo));
44 p_ret->mTag = strdup(tag);
50 static void filterinfo_free(FilterInfo *p_info)
61 * Note: also accepts 0-9 priorities
62 * returns DLOG_UNKNOWN if the character is unrecognized
64 static log_priority filter_char_to_pri (char c)
70 if (c >= '0' && c <= '9') {
71 if (c >= ('0'+DLOG_SILENT)) {
74 pri = (log_priority)(c - '0');
76 } else if (c == 'v') {
78 } else if (c == 'd') {
80 } else if (c == 'i') {
82 } else if (c == 'w') {
84 } else if (c == 'e') {
86 } else if (c == 'f') {
88 } else if (c == 's') {
90 } else if (c == '*') {
99 static char filter_pri_to_char (log_priority pri)
102 case DLOG_VERBOSE: return 'V';
103 case DLOG_DEBUG: return 'D';
104 case DLOG_INFO: return 'I';
105 case DLOG_WARN: return 'W';
106 case DLOG_ERROR: return 'E';
107 case DLOG_FATAL: return 'F';
108 case DLOG_SILENT: return 'S';
116 static log_priority filter_pri_for_tag(log_format *p_format, const char *tag)
118 FilterInfo *p_curFilter;
119 // log_priority pri = DLOG_SILENT;
120 for (p_curFilter = p_format->filters; p_curFilter != NULL; p_curFilter = p_curFilter->p_next )
122 if (0 == strcmp(tag, p_curFilter->mTag))
124 if (p_curFilter->mPri == DLOG_DEFAULT) {
125 return p_format->global_pri;
127 return p_curFilter->mPri;
131 return p_format->global_pri;
135 void dump_filters(log_format *p_format)
139 for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) {
140 char cPri = filter_pri_to_char(p_fi->mPri);
141 if (p_fi->mPri == DLOG_DEFAULT) {
142 cPri = filter_pri_to_char(p_format->global_pri);
144 fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri);
147 fprintf(stderr,"*:%c\n", filter_pri_to_char(p_format->global_pri));
152 * returns 1 if this log line should be printed based on its priority
153 * and tag, and 0 if it should not
155 int log_should_print_line (log_format *p_format, const char *tag, log_priority pri)
157 return pri >= filter_pri_for_tag(p_format, tag);
160 log_format *log_format_new()
164 p_ret = calloc(1, sizeof(log_format));
166 p_ret->global_pri = DLOG_SILENT;
167 p_ret->format = FORMAT_BRIEF;
172 void log_format_free(log_format *p_format)
174 FilterInfo *p_info, *p_info_old;
176 p_info = p_format->filters;
178 while (p_info != NULL) {
180 p_info = p_info->p_next;
188 void log_set_print_format(log_format *p_format,log_print_format format)
190 p_format->format=format;
196 * Returns FORMAT_OFF on invalid string
198 log_print_format log_format_from_string(const char * formatString)
200 static log_print_format format;
202 if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
203 else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
204 else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
205 else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
206 else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
207 else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
208 else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
209 else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
210 else format = FORMAT_OFF;
216 * filterExpression: a single filter expression
219 * returns 0 on success and -1 on invalid expression
221 * Assumes single threaded execution
224 int log_add_filter_rule(log_format *p_format,
225 const char *filterExpression)
228 size_t tagNameLength;
229 log_priority pri = DLOG_DEFAULT;
231 tagNameLength = strcspn(filterExpression, ":");
233 if (tagNameLength == 0) {
237 if(filterExpression[tagNameLength] == ':') {
238 pri = filter_char_to_pri(filterExpression[tagNameLength+1]);
240 if (pri == DLOG_UNKNOWN) {
245 if(0 == strncmp("*", filterExpression, tagNameLength)) {
246 // This filter expression refers to the global filter
247 // The default level for this is DEBUG if the priority
249 if (pri == DLOG_DEFAULT) {
253 p_format->global_pri = pri;
255 // for filter expressions that don't refer to the global
256 // filter, the default is verbose if the priority is unspecified
257 if (pri == DLOG_DEFAULT) {
262 tagName = strndup(filterExpression, tagNameLength);
264 FilterInfo *p_fi = filterinfo_new(tagName, pri);
267 p_fi->p_next = p_format->filters;
268 p_format->filters = p_fi;
278 * filterString: a comma/whitespace-separated set of filter expressions
282 * returns 0 on success and -1 on invalid expression
284 * Assumes single threaded execution
288 int log_add_filter_string(log_format *p_format,
289 const char *filterString)
291 char *filterStringCopy = strdup (filterString);
292 char *p_cur = filterStringCopy;
296 // Yes, I'm using strsep
297 // FIXME : strtok is more portable than strsep
298 while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
299 // ignore whitespace-only entries
300 if(p_ret[0] != '\0') {
301 err = log_add_filter_rule(p_format, p_ret);
309 free (filterStringCopy);
312 free (filterStringCopy);
316 static inline char * strip_end(char *str)
318 char *end = str + strlen(str) - 1;
320 while (end >= str && isspace(*end))
327 * Splits a wire-format buffer into an AndroidLogEntry
328 * entry allocated by caller. Pointers will point directly into buf
330 * Returns 0 on success and -1 on invalid wire format (entry will be
331 * in unspecified state)
333 int log_process_log_buffer(struct logger_entry *buf,log_entry *entry)
337 entry->tv_sec = buf->sec;
338 entry->tv_nsec = buf->nsec;
339 entry->priority = buf->msg[0];
340 entry->pid = buf->pid;
341 entry->tid = buf->tid;
342 entry->tag = buf->msg + 1;
343 tag_len = strlen(entry->tag);
344 entry->messageLen = buf->len - tag_len - 3;
345 entry->message = entry->tag + tag_len + 1;
352 * Formats a log message into a buffer
354 * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
355 * If return value != defaultBuffer, caller must call free()
356 * Returns NULL on malloc error
359 char *log_format_log_line (
360 log_format *p_format,
362 size_t defaultBufferSize,
363 const log_entry *entry,
366 #if defined(HAVE_LOCALTIME_R)
371 // char headerBuf[128];
372 char prefixBuf[128], suffixBuf[128];
374 int prefixSuffixIsHeaderFooter = 0;
377 priChar = filter_pri_to_char(entry->priority);
380 * Get the current date/time in pretty form
382 * It's often useful when examining a log with "less" to jump to
383 * a specific point in the file by searching for the date/time stamp.
384 * For this reason it's very annoying to have regexp meta characters
385 * in the time stamp. Don't use forward slashes, parenthesis,
386 * brackets, asterisks, or other special chars here.
388 #if defined(HAVE_LOCALTIME_R)
389 ptm = localtime_r(&(entry->tv_sec), &tmBuf);
391 ptm = localtime(&(entry->tv_sec));
393 //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
394 strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
397 * Construct a buffer containing the log header and log message.
399 size_t prefixLen, suffixLen;
401 switch (p_format->format) {
403 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
404 "%c/%-8s: ", priChar, entry->tag);
405 strcpy(suffixBuf, "\n"); suffixLen = 1;
408 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
409 "%c(%5d) ", priChar, entry->pid);
410 suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
411 " (%s)\n", entry->tag);
414 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
415 "%c(%5d:%5d) ", priChar, entry->pid, entry->tid);
416 strcpy(suffixBuf, "\n");
422 strcpy(suffixBuf, "\n");
426 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
427 "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
428 priChar, entry->tag, entry->pid);
429 strcpy(suffixBuf, "\n");
432 case FORMAT_THREADTIME:
433 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
434 "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
435 (int)entry->pid, (int)entry->tid, priChar, entry->tag);
436 strcpy(suffixBuf, "\n");
440 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
441 "[ %s.%03ld %5d:%5d %c/%-8s ]\n",
442 timeBuf, entry->tv_nsec / 1000000, entry->pid,
443 entry->tid, priChar, entry->tag);
444 strcpy(suffixBuf, "\n\n");
446 prefixSuffixIsHeaderFooter = 1;
450 prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
451 "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
452 strcpy(suffixBuf, "\n");
456 /* snprintf has a weird return value. It returns what would have been
457 * written given a large enough buffer. In the case that the prefix is
458 * longer then our buffer(128), it messes up the calculations below
459 * possibly causing heap corruption. To avoid this we double check and
460 * set the length at the maximum (size minus null byte)
462 if(prefixLen >= sizeof(prefixBuf))
463 prefixLen = sizeof(prefixBuf) - 1;
464 if(suffixLen >= sizeof(suffixBuf))
465 suffixLen = sizeof(suffixBuf) - 1;
467 /* the following code is tragically unreadable */
475 if (prefixSuffixIsHeaderFooter) {
476 // we're just wrapping message with a header/footer
482 // The line-end finding here must match the line-end finding
483 // in for ( ... numLines...) loop below
484 while (pm < (entry->message + entry->messageLen)) {
485 if (*pm++ == '\n') numLines++;
487 // plus one line for anything not newline-terminated at the end
488 if (pm > entry->message && *(pm-1) != '\n') numLines++;
491 // this is an upper bound--newlines in message may be counted
493 bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
495 if (defaultBufferSize >= bufferSize) {
498 ret = (char *)malloc(bufferSize);
505 ret[0] = '\0'; /* to start strcat off */
510 if (prefixSuffixIsHeaderFooter) {
511 strcat(p, prefixBuf);
512 // strncat(p, prefixBuf, sizeof(prefixBuf));
514 strncat(p, entry->message, entry->messageLen);
515 p += entry->messageLen;
516 strcat(p, suffixBuf);
517 // strncat(p, suffixBuf, sizeof(suffixBuf));
520 while(pm < (entry->message + entry->messageLen)) {
521 const char *lineStart;
526 // Find the next end-of-line in message
527 while (pm < (entry->message + entry->messageLen)
528 && *pm != '\n') pm++;
529 lineLen = pm - lineStart;
531 strcat(p, prefixBuf);
532 //strncat(p, prefixBuf, sizeof(prefixBuf));
534 strncat(p, lineStart, lineLen);
536 strcat(p, suffixBuf);
537 //strncat(p, suffixBuf, sizeof(suffixBuf));
540 if (*pm == '\n') pm++;
544 if (p_outLength != NULL) {
545 *p_outLength = p - ret;
552 * Either print or do not print log line, based on filter
554 * Returns count bytes written
557 int log_print_log_line(
558 log_format *p_format,
560 const log_entry *entry)
563 char defaultBuffer[512];
564 char *outBuffer = NULL;
567 outBuffer = log_format_log_line(p_format, defaultBuffer,sizeof(defaultBuffer), entry, &totalLen);
573 ret = write(fd, outBuffer, totalLen);
574 } while (ret < 0 && errno == EINTR);
577 fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
582 if (((size_t)ret) < totalLen) {
583 fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
589 if (outBuffer != defaultBuffer) {
598 void logprint_run_tests()
602 fprintf(stderr, "tests disabled\n");
608 log_format *p_format;
610 p_format = log_format_new();
612 fprintf(stderr, "running tests\n");
616 log_add_filter_rule(p_format,"*:i");
618 assert (DLOG_INFO == filter_pri_for_tag(p_format, "random"));
619 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0);
620 log_add_filter_rule(p_format, "*");
621 assert (DLOG_DEBUG == filter_pri_for_tag(p_format, "random"));
622 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0);
623 log_add_filter_rule(p_format, "*:v");
624 assert (DLOG_VERBOSE == filter_pri_for_tag(p_format, "random"));
625 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0);
626 log_add_filter_rule(p_format, "*:i");
627 assert (DLOG_INFO == filter_pri_for_tag(p_format, "random"));
628 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0);
630 log_add_filter_rule(p_format, "random");
631 assert (DLOG_VERBOSE == filter_pri_for_tag(p_format, "random"));
632 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0);
633 log_add_filter_rule(p_format, "random:v");
634 assert (DLOG_VERBOSE == 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, "random:d");
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, "random:w");
640 assert (DLOG_WARN == filter_pri_for_tag(p_format, "random"));
641 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0);
643 log_add_filter_rule(p_format, "crap:*");
644 assert (DLOG_VERBOSE== filter_pri_for_tag(p_format, "crap"));
645 assert(log_should_print_line(p_format, "crap", DLOG_VERBOSE) > 0);
647 // invalid expression
648 err = log_add_filter_rule(p_format, "random:z");
650 assert (DLOG_WARN == filter_pri_for_tag(p_format, "random"));
651 assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0);
654 err = log_add_filter_string(p_format, " ");
656 assert(DLOG_WARN == filter_pri_for_tag(p_format, "random"));
658 // note trailing space
659 err = log_add_filter_string(p_format, "*:s random:d ");
661 assert(DLOG_DEBUG == filter_pri_for_tag(p_format, "random"));
663 err = log_add_filter_string(p_format, "*:s random:z");
669 char defaultBuffer[512];
671 ret = log_formatLogLine(p_format,
672 defaultBuffer, sizeof(defaultBuffer), 0, DLOG_ERROR, 123,
673 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL);
677 fprintf(stderr, "tests complete\n");