You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
229 lines
7.5 KiB
229 lines
7.5 KiB
//
|
|
// Copyright (C) 2020 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "tee_logging.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
#include <android-base/threads.h>
|
|
|
|
#include "common/libs/fs/shared_buf.h"
|
|
#include "common/libs/utils/environment.h"
|
|
|
|
using android::base::GetThreadId;
|
|
using android::base::FATAL;
|
|
using android::base::LogSeverity;
|
|
using android::base::StringPrintf;
|
|
|
|
namespace cuttlefish {
|
|
|
|
static LogSeverity GuessSeverity(
|
|
const std::string& env_var, LogSeverity default_value) {
|
|
using android::base::VERBOSE;
|
|
using android::base::DEBUG;
|
|
using android::base::INFO;
|
|
using android::base::WARNING;
|
|
using android::base::ERROR;
|
|
using android::base::FATAL_WITHOUT_ABORT;
|
|
using android::base::FATAL;
|
|
std::string env_value = StringFromEnv(env_var, "");
|
|
using android::base::EqualsIgnoreCase;
|
|
if (EqualsIgnoreCase(env_value, "VERBOSE")
|
|
|| env_value == std::to_string((int) VERBOSE)) {
|
|
return VERBOSE;
|
|
} else if (EqualsIgnoreCase(env_value, "DEBUG")
|
|
|| env_value == std::to_string((int) DEBUG)) {
|
|
return DEBUG;
|
|
} else if (EqualsIgnoreCase(env_value, "INFO")
|
|
|| env_value == std::to_string((int) INFO)) {
|
|
return INFO;
|
|
} else if (EqualsIgnoreCase(env_value, "WARNING")
|
|
|| env_value == std::to_string((int) WARNING)) {
|
|
return WARNING;
|
|
} else if (EqualsIgnoreCase(env_value, "ERROR")
|
|
|| env_value == std::to_string((int) ERROR)) {
|
|
return ERROR;
|
|
} else if (EqualsIgnoreCase(env_value, "FATAL_WITHOUT_ABORT")
|
|
|| env_value == std::to_string((int) FATAL_WITHOUT_ABORT)) {
|
|
return FATAL_WITHOUT_ABORT;
|
|
} else if (EqualsIgnoreCase(env_value, "FATAL")
|
|
|| env_value == std::to_string((int) FATAL)) {
|
|
return FATAL;
|
|
} else {
|
|
return default_value;
|
|
}
|
|
}
|
|
|
|
LogSeverity ConsoleSeverity() {
|
|
return GuessSeverity("CF_CONSOLE_SEVERITY", android::base::INFO);
|
|
}
|
|
|
|
LogSeverity LogFileSeverity() {
|
|
return GuessSeverity("CF_FILE_SEVERITY", android::base::DEBUG);
|
|
}
|
|
|
|
TeeLogger::TeeLogger(const std::vector<SeverityTarget>& destinations)
|
|
: destinations_(destinations) {
|
|
}
|
|
|
|
// Copied from system/libbase/logging_splitters.h
|
|
static std::pair<int, int> CountSizeAndNewLines(const char* message) {
|
|
int size = 0;
|
|
int new_lines = 0;
|
|
while (*message != '\0') {
|
|
size++;
|
|
if (*message == '\n') {
|
|
++new_lines;
|
|
}
|
|
++message;
|
|
}
|
|
return {size, new_lines};
|
|
}
|
|
|
|
// Copied from system/libbase/logging_splitters.h
|
|
// This splits the message up line by line, by calling log_function with a pointer to the start of
|
|
// each line and the size up to the newline character. It sends size = -1 for the final line.
|
|
template <typename F, typename... Args>
|
|
static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
|
|
const char* newline = strchr(msg, '\n');
|
|
while (newline != nullptr) {
|
|
log_function(msg, newline - msg, args...);
|
|
msg = newline + 1;
|
|
newline = strchr(msg, '\n');
|
|
}
|
|
|
|
log_function(msg, -1, args...);
|
|
}
|
|
|
|
// Copied from system/libbase/logging_splitters.h
|
|
// This adds the log header to each line of message and returns it as a string intended to be
|
|
// written to stderr.
|
|
static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid,
|
|
LogSeverity severity, const char* tag, const char* file,
|
|
unsigned int line, const char* message) {
|
|
char timestamp[32];
|
|
strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
|
|
|
|
static const char log_characters[] = "VDIWEFF";
|
|
static_assert(arraysize(log_characters) - 1 == FATAL + 1,
|
|
"Mismatch in size of log_characters and values in LogSeverity");
|
|
char severity_char = log_characters[severity];
|
|
std::string line_prefix;
|
|
if (file != nullptr) {
|
|
line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr",
|
|
severity_char, timestamp, pid, tid, file, line);
|
|
} else {
|
|
line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char,
|
|
timestamp, pid, tid);
|
|
}
|
|
|
|
auto [size, new_lines] = CountSizeAndNewLines(message);
|
|
std::string output_string;
|
|
output_string.reserve(size + new_lines * line_prefix.size() + 1);
|
|
|
|
auto concat_lines = [&](const char* message, int size) {
|
|
output_string.append(line_prefix);
|
|
if (size == -1) {
|
|
output_string.append(message);
|
|
} else {
|
|
output_string.append(message, size);
|
|
}
|
|
output_string.append("\n");
|
|
};
|
|
SplitByLines(message, concat_lines);
|
|
return output_string;
|
|
}
|
|
|
|
// TODO(schuffelen): Do something less primitive.
|
|
static std::string StripColorCodes(const std::string& str) {
|
|
std::stringstream sstream;
|
|
bool in_color_code = false;
|
|
for (char c : str) {
|
|
if (c == '\033') {
|
|
in_color_code = true;
|
|
}
|
|
if (!in_color_code) {
|
|
sstream << c;
|
|
}
|
|
if (c == 'm') {
|
|
in_color_code = false;
|
|
}
|
|
}
|
|
return sstream.str();
|
|
}
|
|
|
|
void TeeLogger::operator()(
|
|
android::base::LogId,
|
|
android::base::LogSeverity severity,
|
|
const char* tag,
|
|
const char* file,
|
|
unsigned int line,
|
|
const char* message) {
|
|
for (const auto& destination : destinations_) {
|
|
std::string output_string;
|
|
if (destination.metadata_level == MetadataLevel::ONLY_MESSAGE) {
|
|
output_string = message + std::string("\n");
|
|
} else {
|
|
struct tm now;
|
|
time_t t = time(nullptr);
|
|
localtime_r(&t, &now);
|
|
output_string = StderrOutputGenerator(now, getpid(), GetThreadId(),
|
|
severity, tag, file, line, message);
|
|
}
|
|
if (severity >= destination.severity) {
|
|
if (destination.target->IsATTY()) {
|
|
WriteAll(destination.target, output_string);
|
|
} else {
|
|
WriteAll(destination.target, StripColorCodes(output_string));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static std::vector<SeverityTarget> SeverityTargetsForFiles(
|
|
const std::vector<std::string>& files) {
|
|
std::vector<SeverityTarget> log_severities;
|
|
for (const auto& file : files) {
|
|
auto log_file_fd =
|
|
SharedFD::Open(
|
|
file,
|
|
O_CREAT | O_WRONLY | O_APPEND,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
|
if (!log_file_fd->IsOpen()) {
|
|
LOG(FATAL) << "Failed to create log file: " << log_file_fd->StrError();
|
|
}
|
|
log_severities.push_back(
|
|
SeverityTarget{LogFileSeverity(), log_file_fd, MetadataLevel::FULL});
|
|
}
|
|
return log_severities;
|
|
}
|
|
|
|
TeeLogger LogToFiles(const std::vector<std::string>& files) {
|
|
return TeeLogger(SeverityTargetsForFiles(files));
|
|
}
|
|
|
|
TeeLogger LogToStderrAndFiles(const std::vector<std::string>& files) {
|
|
std::vector<SeverityTarget> log_severities = SeverityTargetsForFiles(files);
|
|
log_severities.push_back(SeverityTarget{ConsoleSeverity(),
|
|
SharedFD::Dup(/* stderr */ 2),
|
|
MetadataLevel::ONLY_MESSAGE});
|
|
return TeeLogger(log_severities);
|
|
}
|
|
|
|
} // namespace cuttlefish
|