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 
27 using android::base::GetThreadId;
28 using android::base::FATAL;
29 using android::base::LogSeverity;
30 using android::base::StringPrintf;
31 
32 namespace cuttlefish {
33 
GuessSeverity(const std::string & env_var,LogSeverity default_value)34 static LogSeverity GuessSeverity(
35     const std::string& env_var, LogSeverity default_value) {
36   using android::base::VERBOSE;
37   using android::base::DEBUG;
38   using android::base::INFO;
39   using android::base::WARNING;
40   using android::base::ERROR;
41   using android::base::FATAL_WITHOUT_ABORT;
42   using android::base::FATAL;
43   char* env_cstr = getenv(env_var.c_str());
44   std::string env_value(env_cstr == nullptr ? "" : env_cstr);
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::VERBOSE);
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 
operator ()(android::base::LogId,android::base::LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)152 void TeeLogger::operator()(
153     android::base::LogId,
154     android::base::LogSeverity severity,
155     const char* tag,
156     const char* file,
157     unsigned int line,
158     const char* message) {
159   struct tm now;
160   time_t t = time(nullptr);
161   localtime_r(&t, &now);
162   auto output_string =
163       StderrOutputGenerator(
164           now, getpid(), GetThreadId(), severity, tag, file, line, message);
165   for (const auto& destination : destinations_) {
166     if (severity >= destination.severity) {
167       WriteAll(destination.target, output_string);
168     }
169   }
170 }
171 
SeverityTargetsForFiles(const std::vector<std::string> & files)172 static std::vector<SeverityTarget> SeverityTargetsForFiles(
173     const std::vector<std::string>& files) {
174   std::vector<SeverityTarget> log_severities;
175   for (const auto& file : files) {
176     auto log_file_fd =
177         SharedFD::Open(
178           file,
179           O_CREAT | O_WRONLY | O_APPEND,
180           S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
181     if (!log_file_fd->IsOpen()) {
182       LOG(FATAL) << "Failed to create log file: " << log_file_fd->StrError();
183     }
184     log_severities.push_back(SeverityTarget {LogFileSeverity(), log_file_fd});
185   }
186   return log_severities;
187 }
188 
LogToFiles(const std::vector<std::string> & files)189 TeeLogger LogToFiles(const std::vector<std::string>& files) {
190   return TeeLogger(SeverityTargetsForFiles(files));
191 }
192 
LogToStderrAndFiles(const std::vector<std::string> & files)193 TeeLogger LogToStderrAndFiles(const std::vector<std::string>& files) {
194   std::vector<SeverityTarget> log_severities = SeverityTargetsForFiles(files);
195   log_severities.push_back(
196       SeverityTarget {ConsoleSeverity(), SharedFD::Dup(/* stderr */ 2)});
197   return TeeLogger(log_severities);
198 }
199 
200 } // namespace cuttlefish
201