// Copyright 2013 The Flutter 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 "flutter/fml/build_config.h" #include "flutter/fml/log_level.h" #include "flutter/fml/log_settings.h" #include "flutter/fml/logging.h" #if defined(FML_OS_ANDROID) #include #elif defined(FML_OS_IOS) #include #elif defined(OS_FUCHSIA) #include #include #include #include "flutter/fml/platform/fuchsia/log_state.h" #endif namespace fml { namespace { #if !defined(OS_FUCHSIA) const char* const kLogSeverityNames[kLogNumSeverities] = { "INFO", "WARNING", "ERROR", "IMPORTANT", "FATAL"}; const char* GetNameForLogSeverity(LogSeverity severity) { if (severity >= kLogInfo && severity < kLogNumSeverities) { return kLogSeverityNames[severity]; } return "UNKNOWN"; } #endif const char* StripDots(const char* path) { while (strncmp(path, "../", 3) == 0) { path += 3; } return path; } #if defined(OS_FUCHSIA) zx_koid_t GetKoid(zx_handle_t handle) { zx_info_handle_basic_t info; zx_status_t status = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr); return status == ZX_OK ? info.koid : ZX_KOID_INVALID; } thread_local zx_koid_t tls_thread_koid{ZX_KOID_INVALID}; zx_koid_t GetCurrentThreadKoid() { if (unlikely(tls_thread_koid == ZX_KOID_INVALID)) { tls_thread_koid = GetKoid(zx_thread_self()); } ZX_DEBUG_ASSERT(tls_thread_koid != ZX_KOID_INVALID); return tls_thread_koid; } static zx_koid_t pid = GetKoid(zx_process_self()); static thread_local zx_koid_t tid = GetCurrentThreadKoid(); std::string GetProcessName(zx_handle_t handle) { char process_name[ZX_MAX_NAME_LEN]; zx_status_t status = zx_object_get_property( handle, ZX_PROP_NAME, &process_name, sizeof(process_name)); if (status != ZX_OK) { process_name[0] = '\0'; } return process_name; } static std::string process_name = GetProcessName(zx_process_self()); static const zx::socket& socket = LogState::Default().socket(); #endif } // namespace LogMessage::LogMessage(LogSeverity severity, const char* file, int line, const char* condition) : severity_(severity), file_(StripDots(file)), line_(line) { #if !defined(OS_FUCHSIA) stream_ << "["; if (severity >= kLogInfo) { stream_ << GetNameForLogSeverity(severity); } else { stream_ << "VERBOSE" << -severity; } stream_ << ":" << file_ << "(" << line_ << ")] "; #endif if (condition) { stream_ << "Check failed: " << condition << ". "; } } // static thread_local std::ostringstream* LogMessage::capture_next_log_stream_ = nullptr; namespace testing { LogCapture::LogCapture() { fml::LogMessage::CaptureNextLog(&stream_); } LogCapture::~LogCapture() { fml::LogMessage::CaptureNextLog(nullptr); } std::string LogCapture::str() const { return stream_.str(); } } // namespace testing // static void LogMessage::CaptureNextLog(std::ostringstream* stream) { LogMessage::capture_next_log_stream_ = stream; } LogMessage::~LogMessage() { #if !defined(OS_FUCHSIA) stream_ << std::endl; #endif if (capture_next_log_stream_) { *capture_next_log_stream_ << stream_.str(); capture_next_log_stream_ = nullptr; } else { #if defined(FML_OS_ANDROID) android_LogPriority priority = (severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN; switch (severity_) { case kLogImportant: case kLogInfo: priority = ANDROID_LOG_INFO; break; case kLogWarning: priority = ANDROID_LOG_WARN; break; case kLogError: priority = ANDROID_LOG_ERROR; break; case kLogFatal: priority = ANDROID_LOG_FATAL; break; } __android_log_write(priority, "flutter", stream_.str().c_str()); #elif defined(FML_OS_IOS) syslog(LOG_ALERT, "%s", stream_.str().c_str()); #elif defined(OS_FUCHSIA) FuchsiaLogSeverity severity; switch (severity_) { case kLogImportant: case kLogInfo: severity = FUCHSIA_LOG_INFO; break; case kLogWarning: severity = FUCHSIA_LOG_WARNING; break; case kLogError: severity = FUCHSIA_LOG_ERROR; break; case kLogFatal: severity = FUCHSIA_LOG_FATAL; break; default: if (severity_ < 0) { severity = FUCHSIA_LOG_DEBUG; } else { // Unknown severity. Use INFO. severity = FUCHSIA_LOG_INFO; } break; } fuchsia_syslog::LogBuffer buffer; buffer.BeginRecord(severity, std::string_view(file_), line_, std::string_view(stream_.str()), socket.borrow(), 0, pid, tid); if (!process_name.empty()) { buffer.WriteKeyValue("tag", process_name); } if (auto tags_ptr = LogState::Default().tags()) { for (auto& tag : *tags_ptr) { buffer.WriteKeyValue("tag", tag); } } buffer.FlushRecord(); #else // Don't use std::cerr here, because it may not be initialized properly yet. fprintf(stderr, "%s", stream_.str().c_str()); fflush(stderr); #endif } if (severity_ >= kLogFatal) { KillProcess(); } } int GetVlogVerbosity() { return std::max(-1, kLogInfo - GetMinLogLevel()); } bool ShouldCreateLogMessage(LogSeverity severity) { return severity >= GetMinLogLevel(); } void KillProcess() { abort(); } } // namespace fml