summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Salyzyn2017-02-28 14:59:01 -0600
committerMark Salyzyn2017-03-08 09:17:31 -0600
commit4d99c986d9310eb5fbc29618487577d645788eec (patch)
treef765d656f2351c864f7dafa5e9e673c3f3b5ca77 /liblog/stderr_write.c
parente1bfafd241cc8a6c419e08248352f1a5eef46ae1 (diff)
downloadplatform-system-core-4d99c986d9310eb5fbc29618487577d645788eec.tar.gz
platform-system-core-4d99c986d9310eb5fbc29618487577d645788eec.tar.xz
platform-system-core-4d99c986d9310eb5fbc29618487577d645788eec.zip
liblog: add LOGGER_STDERR frontend
Standalone, this logger provides no end-to-end capability. Only provides a writer, no reader transport. All output goes, logcat-like, into the stderr stream. Output can be adjusted with environment variables ANDROID_PRINTF_LOG and ANDROID_LOG_TAGS. liblog_*.__android_log_bswrite_and_print___max print fails if a string member is truncated with "Binary log entry conversion failed" and -1. We expose the truncated content in the tests and in LOGGER_STDERR. The purpose of this transport selection is for command-line tools, providing a means to shunt the logs to be mixed in with the tool's error stream. Test: gTest liblog-unit-tests Bug: 27405083 Change-Id: If344b6e3e67df2dc86ce317cfad8af8e857727b7
Diffstat (limited to 'liblog/stderr_write.c')
-rw-r--r--liblog/stderr_write.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/liblog/stderr_write.c b/liblog/stderr_write.c
new file mode 100644
index 000000000..b73929923
--- /dev/null
+++ b/liblog/stderr_write.c
@@ -0,0 +1,219 @@
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17/*
18 * stderr write handler. Output is logcat-like, and responds to
19 * logcat's environment variables ANDROID_PRINTF_LOG and
20 * ANDROID_LOG_TAGS to filter output.
21 *
22 * This transport only provides a writer, that means that it does not
23 * provide an End-To-End capability as the logs are effectively _lost_
24 * to the stderr file stream. The purpose of this transport is to
25 * supply a means for command line tools to report their logging
26 * to the stderr stream, in line with all other activities.
27 */
28
29#include <errno.h>
30#include <stdbool.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <sys/types.h>
35#include <unistd.h>
36
37#include <log/event_tag_map.h>
38#include <log/log.h>
39#include <log/logprint.h>
40#include <log/uio.h>
41
42#include "log_portability.h"
43#include "logger.h"
44
45static int stderrOpen();
46static void stderrClose();
47static int stderrAvailable(log_id_t logId);
48static int stderrWrite(log_id_t logId, struct timespec* ts,
49 struct iovec* vec, size_t nr);
50
51struct stderrContext {
52 AndroidLogFormat* logformat;
53#if defined(__ANDROID__)
54 EventTagMap* eventTagMap;
55#endif
56};
57
58LIBLOG_HIDDEN struct android_log_transport_write stderrLoggerWrite = {
59 .node = { &stderrLoggerWrite.node, &stderrLoggerWrite.node },
60 .context.private = NULL,
61 .name = "stderr",
62 .available = stderrAvailable,
63 .open = stderrOpen,
64 .close = stderrClose,
65 .write = stderrWrite,
66};
67
68static int stderrOpen()
69{
70 struct stderrContext* ctx;
71 const char* envStr;
72 bool setFormat;
73
74 if (!stderr || (fileno(stderr) < 0)) {
75 return -EBADF;
76 }
77
78 if (stderrLoggerWrite.context.private) {
79 return fileno(stderr);
80 }
81
82 ctx = calloc(1, sizeof(struct stderrContext));
83 if (!ctx) {
84 return -ENOMEM;
85 }
86
87 ctx->logformat = android_log_format_new();
88 if (!ctx->logformat) {
89 free(ctx);
90 return -ENOMEM;
91 }
92
93 envStr = getenv("ANDROID_PRINTF_LOG");
94 setFormat = false;
95
96 if (envStr) {
97 char* formats = strdup(envStr);
98 char* sv = NULL;
99 char* arg = formats;
100 while (!!(arg = strtok_r(arg, ",:; \t\n\r\f", &sv))) {
101 AndroidLogPrintFormat format = android_log_formatFromString(arg);
102 arg = NULL;
103 if (format == FORMAT_OFF) {
104 continue;
105 }
106 if (android_log_setPrintFormat(ctx->logformat, format) <= 0) {
107 continue;
108 }
109 setFormat = true;
110 }
111 free(formats);
112 }
113 if (!setFormat) {
114 AndroidLogPrintFormat format = android_log_formatFromString(
115 "threadtime");
116 android_log_setPrintFormat(ctx->logformat, format);
117 }
118 envStr = getenv("ANDROID_LOG_TAGS");
119 if (envStr) {
120 android_log_addFilterString(ctx->logformat, envStr);
121 }
122 stderrLoggerWrite.context.private = ctx;
123
124 return fileno(stderr);
125}
126
127static void stderrClose()
128{
129 struct stderrContext* ctx = stderrLoggerWrite.context.private;
130
131 if (ctx) {
132 stderrLoggerWrite.context.private = NULL;
133 if (ctx->logformat) {
134 android_log_format_free(ctx->logformat);
135 ctx->logformat = NULL;
136 }
137#if defined(__ANDROID__)
138 if (ctx->eventTagMap) {
139 android_closeEventTagMap(ctx->eventTagMap);
140 ctx->eventTagMap = NULL;
141 }
142#endif
143 }
144}
145
146static int stderrAvailable(log_id_t logId)
147{
148 if ((logId >= LOG_ID_MAX) || (logId == LOG_ID_KERNEL)) {
149 return -EINVAL;
150 }
151 return 1;
152}
153
154static int stderrWrite(log_id_t logId, struct timespec* ts,
155 struct iovec* vec, size_t nr)
156{
157 struct log_msg log_msg;
158 AndroidLogEntry entry;
159 char binaryMsgBuf[1024];
160 int err;
161 size_t i;
162 struct stderrContext* ctx = stderrLoggerWrite.context.private;
163
164 if (!ctx) return -EBADF;
165 if (!vec || !nr) return -EINVAL;
166
167 log_msg.entry.len = 0;
168 log_msg.entry.hdr_size = sizeof(log_msg.entry);
169 log_msg.entry.pid = getpid();
170#ifdef __BIONIC__
171 log_msg.entry.tid = gettid();
172#else
173 log_msg.entry.tid = getpid();
174#endif
175 log_msg.entry.sec = ts->tv_sec;
176 log_msg.entry.nsec = ts->tv_nsec;
177 log_msg.entry.lid = logId;
178 log_msg.entry.uid = __android_log_uid();
179
180 for (i = 0; i < nr; ++i) {
181 size_t len = vec[i].iov_len;
182 if ((log_msg.entry.len + len) > LOGGER_ENTRY_MAX_PAYLOAD) {
183 len = LOGGER_ENTRY_MAX_PAYLOAD - log_msg.entry.len;
184 }
185 if (!len) continue;
186 memcpy(log_msg.entry.msg + log_msg.entry.len, vec[i].iov_base, len);
187 log_msg.entry.len += len;
188 }
189
190 if ((logId == LOG_ID_EVENTS) || (logId == LOG_ID_SECURITY)) {
191#if defined(__ANDROID__)
192 if (!ctx->eventTagMap) {
193 ctx->eventTagMap = android_openEventTagMap(NULL);
194 }
195#endif
196 err = android_log_processBinaryLogBuffer(&log_msg.entry_v1,
197 &entry,
198#if defined(__ANDROID__)
199 ctx->eventTagMap,
200#else
201 NULL,
202#endif
203 binaryMsgBuf,
204 sizeof(binaryMsgBuf));
205 } else {
206 err = android_log_processLogBuffer(&log_msg.entry_v1, &entry);
207 }
208
209 /* print known truncated data, in essence logcat --debug */
210 if ((err < 0) && !entry.message) return -EINVAL;
211
212 if (!android_log_shouldPrintLine(ctx->logformat, entry.tag, entry.priority)) {
213 return log_msg.entry.len;
214 }
215
216 err = android_log_printLogLine(ctx->logformat, fileno(stderr), &entry);
217 if (err < 0) return errno ? -errno : -EINVAL;
218 return log_msg.entry.len;
219}