1 //
2 // Copyright (C) 2020 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 #include "tee_logging.h"
17
18 #include <stdlib.h>
19 #include <inttypes.h>
20
21 #include <android-base/stringprintf.h>
22 #include <android-base/strings.h>
23 #include <android-base/threads.h>
24
25 #include "common/libs/fs/shared_buf.h"
26 #include "common/libs/utils/environment.h"
27
28 using android::base::GetThreadId;
29 using android::base::FATAL;
30 using android::base::LogSeverity;
31 using android::base::StringPrintf;
32
33 namespace cuttlefish {
34
GuessSeverity(const std::string & env_var,LogSeverity default_value)35 static LogSeverity GuessSeverity(
36 const std::string& env_var, LogSeverity default_value) {
37 using android::base::VERBOSE;
38 using android::base::DEBUG;
39 using android::base::INFO;
40 using android::base::WARNING;
41 using android::base::ERROR;
42 using android::base::FATAL_WITHOUT_ABORT;
43 using android::base::FATAL;
44 std::string env_value = StringFromEnv(env_var, "");
45 using android::base::EqualsIgnoreCase;
46 if (EqualsIgnoreCase(env_value, "VERBOSE")
47 || env_value == std::to_string((int) VERBOSE)) {
48 return VERBOSE;
49 } else if (EqualsIgnoreCase(env_value, "DEBUG")
50 || env_value == std::to_string((int) DEBUG)) {
51 return DEBUG;
52 } else if (EqualsIgnoreCase(env_value, "INFO")
53 || env_value == std::to_string((int) INFO)) {
54 return INFO;
55 } else if (EqualsIgnoreCase(env_value, "WARNING")
56 || env_value == std::to_string((int) WARNING)) {
57 return WARNING;
58 } else if (EqualsIgnoreCase(env_value, "ERROR")
59 || env_value == std::to_string((int) ERROR)) {
60 return ERROR;
61 } else if (EqualsIgnoreCase(env_value, "FATAL_WITHOUT_ABORT")
62 || env_value == std::to_string((int) FATAL_WITHOUT_ABORT)) {
63 return FATAL_WITHOUT_ABORT;
64 } else if (EqualsIgnoreCase(env_value, "FATAL")
65 || env_value == std::to_string((int) FATAL)) {
66 return FATAL;
67 } else {
68 return default_value;
69 }
70 }
71
ConsoleSeverity()72 LogSeverity ConsoleSeverity() {
73 return GuessSeverity("CF_CONSOLE_SEVERITY", android::base::INFO);
74 }
75
LogFileSeverity()76 LogSeverity LogFileSeverity() {
77 return GuessSeverity("CF_FILE_SEVERITY", android::base::DEBUG);
78 }
79
TeeLogger(const std::vector<SeverityTarget> & destinations)80 TeeLogger::TeeLogger(const std::vector<SeverityTarget>& destinations)
81 : destinations_(destinations) {
82 }
83
84 // Copied from system/libbase/logging_splitters.h
CountSizeAndNewLines(const char * message)85 static std::pair<int, int> CountSizeAndNewLines(const char* message) {
86 int size = 0;
87 int new_lines = 0;
88 while (*message != '\0') {
89 size++;
90 if (*message == '\n') {
91 ++new_lines;
92 }
93 ++message;
94 }
95 return {size, new_lines};
96 }
97
98 // Copied from system/libbase/logging_splitters.h
99 // This splits the message up line by line, by calling log_function with a pointer to the start of
100 // each line and the size up to the newline character. It sends size = -1 for the final line.
101 template <typename F, typename... Args>
SplitByLines(const char * msg,const F & log_function,Args &&...args)102 static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
103 const char* newline = strchr(msg, '\n');
104 while (newline != nullptr) {
105 log_function(msg, newline - msg, args...);
106 msg = newline + 1;
107 newline = strchr(msg, '\n');
108 }
109
110 log_function(msg, -1, args...);
111 }
112
113 // Copied from system/libbase/logging_splitters.h
114 // This adds the log header to each line of message and returns it as a string intended to be
115 // written to stderr.
StderrOutputGenerator(const struct tm & now,int pid,uint64_t tid,LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)116 static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid,
117 LogSeverity severity, const char* tag, const char* file,
118 unsigned int line, const char* message) {
119 char timestamp[32];
120 strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
121
122 static const char log_characters[] = "VDIWEFF";
123 static_assert(arraysize(log_characters) - 1 == FATAL + 1,
124 "Mismatch in size of log_characters and values in LogSeverity");
125 char severity_char = log_characters[severity];
126 std::string line_prefix;
127 if (file != nullptr) {
128 line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr",
129 severity_char, timestamp, pid, tid, file, line);
130 } else {
131 line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char,
132 timestamp, pid, tid);
133 }
134
135 auto [size, new_lines] = CountSizeAndNewLines(message);
136 std::string output_string;
137 output_string.reserve(size + new_lines * line_prefix.size() + 1);
138
139 auto concat_lines = [&](const char* message, int size) {
140 output_string.append(line_prefix);
141 if (size == -1) {
142 output_string.append(message);
143 } else {
144 output_string.append(message, size);
145 }
146 output_string.append("\n");
147 };
148 SplitByLines(message, concat_lines);
149 return output_string;
150 }
151
152 // TODO(schuffelen): Do something less primitive.
StripColorCodes(const std::string & str)153 static std::string StripColorCodes(const std::string& str) {
154 std::stringstream sstream;
155 bool in_color_code = false;
156 for (char c : str) {
157 if (c == '\033') {
158 in_color_code = true;
159 }
160 if (!in_color_code) {
161 sstream << c;
162 }
163 if (c == 'm') {
164 in_color_code = false;
165 }
166 }
167 return sstream.str();
168 }
169
operator ()(android::base::LogId,android::base::LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)170 void TeeLogger::operator()(
171 android::base::LogId,
172 android::base::LogSeverity severity,
173 const char* tag,
174 const char* file,
175 unsigned int line,
176 const char* message) {
177 for (const auto& destination : destinations_) {
178 std::string output_string;
179 if (destination.metadata_level == MetadataLevel::ONLY_MESSAGE) {
180 output_string = message + std::string("\n");
181 } else {
182 struct tm now;
183 time_t t = time(nullptr);
184 localtime_r(&t, &now);
185 output_string = StderrOutputGenerator(now, getpid(), GetThreadId(),
186 severity, tag, file, line, message);
187 }
188 if (severity >= destination.severity) {
189 if (destination.target->IsATTY()) {
190 WriteAll(destination.target, output_string);
191 } else {
192 WriteAll(destination.target, StripColorCodes(output_string));
193 }
194 }
195 }
196 }
197
SeverityTargetsForFiles(const std::vector<std::string> & files)198 static std::vector<SeverityTarget> SeverityTargetsForFiles(
199 const std::vector<std::string>& files) {
200 std::vector<SeverityTarget> log_severities;
201 for (const auto& file : files) {
202 auto log_file_fd =
203 SharedFD::Open(
204 file,
205 O_CREAT | O_WRONLY | O_APPEND,
206 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
207 if (!log_file_fd->IsOpen()) {
208 LOG(FATAL) << "Failed to create log file: " << log_file_fd->StrError();
209 }
210 log_severities.push_back(
211 SeverityTarget{LogFileSeverity(), log_file_fd, MetadataLevel::FULL});
212 }
213 return log_severities;
214 }
215
LogToFiles(const std::vector<std::string> & files)216 TeeLogger LogToFiles(const std::vector<std::string>& files) {
217 return TeeLogger(SeverityTargetsForFiles(files));
218 }
219
LogToStderrAndFiles(const std::vector<std::string> & files)220 TeeLogger LogToStderrAndFiles(const std::vector<std::string>& files) {
221 std::vector<SeverityTarget> log_severities = SeverityTargetsForFiles(files);
222 log_severities.push_back(SeverityTarget{ConsoleSeverity(),
223 SharedFD::Dup(/* stderr */ 2),
224 MetadataLevel::ONLY_MESSAGE});
225 return TeeLogger(log_severities);
226 }
227
228 } // namespace cuttlefish
229