// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include "platform/impl/logging.h" #include "platform/impl/logging_test.h" #include "util/trace_logging.h" namespace openscreen { namespace { int g_log_fd = STDERR_FILENO; LogLevel g_log_level = LogLevel::kWarning; std::vector* g_log_messages_for_test = nullptr; std::ostream& operator<<(std::ostream& os, const LogLevel& level) { const char* level_string = ""; switch (level) { case LogLevel::kVerbose: level_string = "VERBOSE"; break; case LogLevel::kInfo: level_string = "INFO"; break; case LogLevel::kWarning: level_string = "WARNING"; break; case LogLevel::kError: level_string = "ERROR"; break; case LogLevel::kFatal: level_string = "FATAL"; break; } os << level_string; return os; } } // namespace void SetLogFifoOrDie(const char* filename) { if (g_log_fd != STDERR_FILENO) { close(g_log_fd); g_log_fd = STDERR_FILENO; } // Note: The use of OSP_CHECK/OSP_LOG_* here will log to stderr. struct stat st = {}; int open_result = -1; if (stat(filename, &st) == -1 && errno == ENOENT) { if (mkfifo(filename, 0644) == 0) { open_result = open(filename, O_WRONLY); OSP_CHECK_NE(open_result, -1) << "open(" << filename << ") failed: " << strerror(errno); } else { OSP_LOG_FATAL << "mkfifo(" << filename << ") failed: " << strerror(errno); } } else if (S_ISFIFO(st.st_mode)) { open_result = open(filename, O_WRONLY); OSP_CHECK_NE(open_result, -1) << "open(" << filename << ") failed: " << strerror(errno); } else { OSP_LOG_FATAL << "not a FIFO special file: " << filename; } // Direct all logging to the opened FIFO file. g_log_fd = open_result; } void SetLogLevel(LogLevel level) { g_log_level = level; } LogLevel GetLogLevel() { return g_log_level; } bool IsLoggingOn(LogLevel level, const char* file) { // Possible future enhancement: Use glob patterns passed on the command-line // to use a different logging level for certain files, like in Chromium. return level >= g_log_level; } void LogWithLevel(LogLevel level, const char* file, int line, std::stringstream message) { if (level < g_log_level) return; std::stringstream ss; ss << "[" << level << ":" << file << "(" << line << "):T" << std::hex << TRACE_CURRENT_ID << "] " << message.rdbuf() << '\n'; const auto ss_str = ss.str(); const auto bytes_written = write(g_log_fd, ss_str.c_str(), ss_str.size()); OSP_DCHECK(bytes_written); if (g_log_messages_for_test) { g_log_messages_for_test->push_back(ss_str); } } [[noreturn]] void Break() { // Generally this will just resolve to an abort anyways, but gives the // compiler a chance to peform a more appropriate, target specific trap // as appropriate. #if defined(_DEBUG) __builtin_trap(); #else std::abort(); #endif } void SetLogBufferForTest(std::vector* messages) { g_log_messages_for_test = messages; } } // namespace openscreen