blob: d2318cbb960f3f22e02ba392edca0d50b48961b1 [file] [log] [blame]
Dan Albert58310b42015-03-13 23:06:01 -07001/*
2 * Copyright (C) 2015 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#include "base/logging.h"
18
19#include <iostream>
20#include <limits>
Dan Albert4b3f5332015-03-26 23:23:32 -070021#include <mutex>
Dan Albert58310b42015-03-13 23:06:01 -070022#include <sstream>
23#include <string>
24#include <vector>
25
26#include "base/strings.h"
Dan Albert7dfb61d2015-03-20 13:46:28 -070027#include "cutils/threads.h"
Dan Albert58310b42015-03-13 23:06:01 -070028
29// Headers for LogMessage::LogLine.
30#ifdef __ANDROID__
31#include <android/set_abort_message.h>
32#include "cutils/log.h"
33#else
34#include <sys/types.h>
35#include <unistd.h>
36#endif
37
Dan Albert58310b42015-03-13 23:06:01 -070038namespace android {
39namespace base {
40
41static std::mutex logging_lock;
42
43static LogSeverity gMinimumLogSeverity = INFO;
44static std::unique_ptr<std::string> gCmdLine;
45static std::unique_ptr<std::string> gProgramInvocationName;
46static std::unique_ptr<std::string> gProgramInvocationShortName;
47
Dan Albert58310b42015-03-13 23:06:01 -070048const char* GetCmdLine() {
49 return (gCmdLine.get() != nullptr) ? gCmdLine->c_str() : nullptr;
50}
51
52const char* ProgramInvocationName() {
53 return (gProgramInvocationName.get() != nullptr)
54 ? gProgramInvocationName->c_str()
55 : "unknown";
56}
57
58const char* ProgramInvocationShortName() {
59 return (gProgramInvocationShortName.get() != nullptr)
60 ? gProgramInvocationShortName->c_str()
61 : "unknown";
62}
63
64void InitLogging(char* argv[]) {
65 if (gCmdLine.get() != nullptr) {
66 return;
67 }
68
69 // Stash the command line for later use. We can use /proc/self/cmdline on
70 // Linux to recover this, but we don't have that luxury on the Mac, and there
71 // are a couple of argv[0] variants that are commonly used.
72 if (argv != nullptr) {
73 gCmdLine.reset(new std::string(argv[0]));
74 for (size_t i = 1; argv[i] != nullptr; ++i) {
75 gCmdLine->append(" ");
76 gCmdLine->append(argv[i]);
77 }
78 gProgramInvocationName.reset(new std::string(argv[0]));
79 const char* last_slash = strrchr(argv[0], '/');
80 gProgramInvocationShortName.reset(
81 new std::string((last_slash != nullptr) ? last_slash + 1 : argv[0]));
82 } else {
83 // TODO: fall back to /proc/self/cmdline when argv is NULL on Linux.
84 gCmdLine.reset(new std::string("<unset>"));
85 }
86 const char* tags = getenv("ANDROID_LOG_TAGS");
87 if (tags == nullptr) {
88 return;
89 }
90
Dan Albert47328c92015-03-19 13:24:26 -070091 std::vector<std::string> specs = Split(tags, " ");
Dan Albert58310b42015-03-13 23:06:01 -070092 for (size_t i = 0; i < specs.size(); ++i) {
93 // "tag-pattern:[vdiwefs]"
94 std::string spec(specs[i]);
95 if (spec.size() == 3 && StartsWith(spec, "*:")) {
96 switch (spec[2]) {
97 case 'v':
98 gMinimumLogSeverity = VERBOSE;
99 continue;
100 case 'd':
101 gMinimumLogSeverity = DEBUG;
102 continue;
103 case 'i':
104 gMinimumLogSeverity = INFO;
105 continue;
106 case 'w':
107 gMinimumLogSeverity = WARNING;
108 continue;
109 case 'e':
110 gMinimumLogSeverity = ERROR;
111 continue;
112 case 'f':
113 gMinimumLogSeverity = FATAL;
114 continue;
115 // liblog will even suppress FATAL if you say 's' for silent, but that's
116 // crazy!
117 case 's':
118 gMinimumLogSeverity = FATAL;
119 continue;
120 }
121 }
122 LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
123 << ")";
124 }
125}
126
127// This indirection greatly reduces the stack impact of having lots of
128// checks/logging in a function.
129class LogMessageData {
130 public:
Dan Albert0c055862015-03-27 11:20:14 -0700131 LogMessageData(const char* file, unsigned int line, LogId id,
132 LogSeverity severity, int error)
133 : file_(file),
134 line_number_(line),
135 id_(id),
136 severity_(severity),
137 error_(error) {
Dan Albert58310b42015-03-13 23:06:01 -0700138 const char* last_slash = strrchr(file, '/');
139 file = (last_slash == nullptr) ? file : last_slash + 1;
140 }
141
142 const char* GetFile() const {
143 return file_;
144 }
145
146 unsigned int GetLineNumber() const {
147 return line_number_;
148 }
149
150 LogSeverity GetSeverity() const {
151 return severity_;
152 }
153
Dan Albert0c055862015-03-27 11:20:14 -0700154 LogId GetId() const {
155 return id_;
156 }
157
Dan Albert58310b42015-03-13 23:06:01 -0700158 int GetError() const {
159 return error_;
160 }
161
162 std::ostream& GetBuffer() {
163 return buffer_;
164 }
165
166 std::string ToString() const {
167 return buffer_.str();
168 }
169
170 private:
171 std::ostringstream buffer_;
172 const char* const file_;
173 const unsigned int line_number_;
Dan Albert0c055862015-03-27 11:20:14 -0700174 const LogId id_;
Dan Albert58310b42015-03-13 23:06:01 -0700175 const LogSeverity severity_;
176 const int error_;
177
178 DISALLOW_COPY_AND_ASSIGN(LogMessageData);
179};
180
Dan Albert0c055862015-03-27 11:20:14 -0700181LogMessage::LogMessage(const char* file, unsigned int line, LogId id,
Dan Albert58310b42015-03-13 23:06:01 -0700182 LogSeverity severity, int error)
Dan Albert0c055862015-03-27 11:20:14 -0700183 : data_(new LogMessageData(file, line, id, severity, error)) {
Dan Albert58310b42015-03-13 23:06:01 -0700184}
185
186LogMessage::~LogMessage() {
187 if (data_->GetSeverity() < gMinimumLogSeverity) {
188 return; // No need to format something we're not going to output.
189 }
190
191 // Finish constructing the message.
192 if (data_->GetError() != -1) {
193 data_->GetBuffer() << ": " << strerror(data_->GetError());
194 }
195 std::string msg(data_->ToString());
196
197 // Do the actual logging with the lock held.
198 {
199 std::lock_guard<std::mutex> lock(logging_lock);
200 if (msg.find('\n') == std::string::npos) {
Dan Albert0c055862015-03-27 11:20:14 -0700201 LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
202 data_->GetSeverity(), msg.c_str());
Dan Albert58310b42015-03-13 23:06:01 -0700203 } else {
204 msg += '\n';
205 size_t i = 0;
206 while (i < msg.size()) {
207 size_t nl = msg.find('\n', i);
208 msg[nl] = '\0';
Dan Albert0c055862015-03-27 11:20:14 -0700209 LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
210 data_->GetSeverity(), &msg[i]);
Dan Albert58310b42015-03-13 23:06:01 -0700211 i = nl + 1;
212 }
213 }
214 }
215
216 // Abort if necessary.
217 if (data_->GetSeverity() == FATAL) {
218#ifdef __ANDROID__
219 android_set_abort_message(msg.c_str());
220#endif
221 abort();
222 }
223}
224
225std::ostream& LogMessage::stream() {
226 return data_->GetBuffer();
227}
228
229#ifdef __ANDROID__
230static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
231 ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
232 ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL};
233static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
234 "Mismatch in size of kLogSeverityToAndroidLogPriority and values "
235 "in LogSeverity");
Dan Albert0c055862015-03-27 11:20:14 -0700236
237static const log_id kLogIdToAndroidLogId[] = {LOG_ID_MAIN, LOG_ID_SYSTEM};
238static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1,
239 "Mismatch in size of kLogIdToAndroidLogId and values "
240 "in LogSeverity");
Dan Albert58310b42015-03-13 23:06:01 -0700241#endif
242
Dan Albert0c055862015-03-27 11:20:14 -0700243void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
Dan Albert58310b42015-03-13 23:06:01 -0700244 LogSeverity log_severity, const char* message) {
245#ifdef __ANDROID__
246 const char* tag = ProgramInvocationShortName();
247 int priority = kLogSeverityToAndroidLogPriority[log_severity];
Dan Albert0c055862015-03-27 11:20:14 -0700248 log_id lg_id = kLogIdToAndroidLogId[id];
Dan Albert58310b42015-03-13 23:06:01 -0700249 if (priority == ANDROID_LOG_FATAL) {
Dan Albert0c055862015-03-27 11:20:14 -0700250 __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line, message);
Dan Albert58310b42015-03-13 23:06:01 -0700251 } else {
Dan Albert0c055862015-03-27 11:20:14 -0700252 __android_log_buf_print(lg_id, priority, tag, "%s", message);
Dan Albert58310b42015-03-13 23:06:01 -0700253 }
254#else
Dan Albert0c055862015-03-27 11:20:14 -0700255 UNUSED(id);
Dan Albert58310b42015-03-13 23:06:01 -0700256 static const char* log_characters = "VDIWEF";
257 CHECK_EQ(strlen(log_characters), FATAL + 1U);
258 char severity = log_characters[log_severity];
259 fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationShortName(),
Dan Albert7dfb61d2015-03-20 13:46:28 -0700260 severity, getpid(), gettid(), file, line, message);
Dan Albert58310b42015-03-13 23:06:01 -0700261#endif
262}
263
Dan Albert58310b42015-03-13 23:06:01 -0700264ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
265 old_ = gMinimumLogSeverity;
266 gMinimumLogSeverity = level;
267}
268
269ScopedLogSeverity::~ScopedLogSeverity() {
270 gMinimumLogSeverity = old_;
271}
272
273} // namespace base
274} // namespace android