blob: 144388ade7865adc8ec1f00f6b67f3dcfce48f08 [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:
131 LogMessageData(const char* file, unsigned int line, LogSeverity severity,
132 int error)
133 : file_(file), line_number_(line), severity_(severity), error_(error) {
134 const char* last_slash = strrchr(file, '/');
135 file = (last_slash == nullptr) ? file : last_slash + 1;
136 }
137
138 const char* GetFile() const {
139 return file_;
140 }
141
142 unsigned int GetLineNumber() const {
143 return line_number_;
144 }
145
146 LogSeverity GetSeverity() const {
147 return severity_;
148 }
149
150 int GetError() const {
151 return error_;
152 }
153
154 std::ostream& GetBuffer() {
155 return buffer_;
156 }
157
158 std::string ToString() const {
159 return buffer_.str();
160 }
161
162 private:
163 std::ostringstream buffer_;
164 const char* const file_;
165 const unsigned int line_number_;
166 const LogSeverity severity_;
167 const int error_;
168
169 DISALLOW_COPY_AND_ASSIGN(LogMessageData);
170};
171
172LogMessage::LogMessage(const char* file, unsigned int line,
173 LogSeverity severity, int error)
174 : data_(new LogMessageData(file, line, severity, error)) {
175}
176
177LogMessage::~LogMessage() {
178 if (data_->GetSeverity() < gMinimumLogSeverity) {
179 return; // No need to format something we're not going to output.
180 }
181
182 // Finish constructing the message.
183 if (data_->GetError() != -1) {
184 data_->GetBuffer() << ": " << strerror(data_->GetError());
185 }
186 std::string msg(data_->ToString());
187
188 // Do the actual logging with the lock held.
189 {
190 std::lock_guard<std::mutex> lock(logging_lock);
191 if (msg.find('\n') == std::string::npos) {
192 LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(),
193 msg.c_str());
194 } else {
195 msg += '\n';
196 size_t i = 0;
197 while (i < msg.size()) {
198 size_t nl = msg.find('\n', i);
199 msg[nl] = '\0';
200 LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(),
201 &msg[i]);
202 i = nl + 1;
203 }
204 }
205 }
206
207 // Abort if necessary.
208 if (data_->GetSeverity() == FATAL) {
209#ifdef __ANDROID__
210 android_set_abort_message(msg.c_str());
211#endif
212 abort();
213 }
214}
215
216std::ostream& LogMessage::stream() {
217 return data_->GetBuffer();
218}
219
220#ifdef __ANDROID__
221static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
222 ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
223 ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL};
224static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1,
225 "Mismatch in size of kLogSeverityToAndroidLogPriority and values "
226 "in LogSeverity");
227#endif
228
229void LogMessage::LogLine(const char* file, unsigned int line,
230 LogSeverity log_severity, const char* message) {
231#ifdef __ANDROID__
232 const char* tag = ProgramInvocationShortName();
233 int priority = kLogSeverityToAndroidLogPriority[log_severity];
234 if (priority == ANDROID_LOG_FATAL) {
235 LOG_PRI(priority, tag, "%s:%u] %s", file, line, message);
236 } else {
237 LOG_PRI(priority, tag, "%s", message);
238 }
239#else
240 static const char* log_characters = "VDIWEF";
241 CHECK_EQ(strlen(log_characters), FATAL + 1U);
242 char severity = log_characters[log_severity];
243 fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationShortName(),
Dan Albert7dfb61d2015-03-20 13:46:28 -0700244 severity, getpid(), gettid(), file, line, message);
Dan Albert58310b42015-03-13 23:06:01 -0700245#endif
246}
247
Dan Albert58310b42015-03-13 23:06:01 -0700248ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
249 old_ = gMinimumLogSeverity;
250 gMinimumLogSeverity = level;
251}
252
253ScopedLogSeverity::~ScopedLogSeverity() {
254 gMinimumLogSeverity = old_;
255}
256
257} // namespace base
258} // namespace android