blob: 15bfa9de368b4ec3b6f22a9c1326a0665e0ddcef [file] [log] [blame]
// 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