| // 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 <algorithm> | |
| #include <cstring> | |
| #include <iostream> | |
| #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 <android/log.h> | |
| #elif defined(FML_OS_IOS) | |
| #include <syslog.h> | |
| #elif defined(OS_FUCHSIA) | |
| #include <lib/syslog/structured_backend/cpp/fuchsia_syslog.h> | |
| #include <lib/syslog/structured_backend/fuchsia_syslog.h> | |
| #include <zircon/process.h> | |
| #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 |