blob: e163deb6312e67a900a3868b6f245b79dbfd6fa7 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 <fbl/algorithm.h>
#include <fbl/auto_lock.h>
#include <fbl/string_buffer.h>
#include <zircon/assert.h>
#include <lib/syslog/logger.h>
#include <lib/syslog/wire_format.h>
#include <atomic>
#include "fx_logger.h"
namespace {
// This thread's koid.
// Initialized on first use.
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;
}
} // namespace
void fx_logger::ActivateFallback(int fallback_fd) {
fbl::AutoLock lock(&fallback_mutex_);
if (logger_fd_.load(std::memory_order_relaxed) != -1) {
return;
}
ZX_DEBUG_ASSERT(fallback_fd >= -1);
if (tagstr_.empty()) {
for (size_t i = 0; i < tags_.size(); i++) {
if (tagstr_.empty()) {
tagstr_ = tags_[i];
} else {
tagstr_ = fbl::String::Concat({tagstr_, ", ", tags_[i]});
}
}
}
if (fallback_fd == -1) {
fallback_fd = STDERR_FILENO;
}
// Do not change fd_to_close_ as we don't want to close fallback_fd.
// We will still close original console_fd_
logger_fd_.store(fallback_fd, std::memory_order_relaxed);
}
zx_status_t fx_logger::VLogWriteToSocket(fx_log_severity_t severity,
const char* tag, const char* msg,
va_list args, bool perform_format) {
zx_time_t time = zx_clock_get_monotonic();
fx_log_packet_t packet;
memset(&packet, 0, sizeof(packet));
constexpr size_t kDataSize = sizeof(packet.data);
packet.metadata.pid = pid_;
packet.metadata.tid = GetCurrentThreadKoid();
packet.metadata.time = time;
packet.metadata.severity = severity;
packet.metadata.dropped_logs = dropped_logs_.load();
// Write tags
size_t pos = 0;
for (size_t i = 0; i < tags_.size(); i++) {
size_t len = tags_[i].length();
ZX_DEBUG_ASSERT(len < 128);
packet.data[pos++] = static_cast<char>(len);
memcpy(packet.data + pos, tags_[i].c_str(), len);
pos += len;
}
if (tag != NULL) {
size_t len = strlen(tag);
if (len > 0) {
size_t write_len =
fbl::min(len, static_cast<size_t>(FX_LOG_MAX_TAG_LEN - 1));
ZX_DEBUG_ASSERT(write_len < 128);
packet.data[pos++] = static_cast<char>(write_len);
memcpy(packet.data + pos, tag, write_len);
pos += write_len;
}
}
packet.data[pos++] = 0;
ZX_DEBUG_ASSERT(pos < kDataSize);
// Write msg
int n = static_cast<int>(kDataSize - pos);
int count = 0;
size_t msg_pos = pos;
if (!perform_format) {
size_t write_len =
fbl::min(strlen(msg), static_cast<size_t>(n - 1));
memcpy(packet.data + pos, msg, write_len);
pos += write_len;
packet.data[pos] = 0;
count = static_cast<int>(write_len + 1);
} else {
count = vsnprintf(packet.data + pos, n, msg, args);
if (count < 0) {
return ZX_ERR_INVALID_ARGS;
}
}
if (count >= n) {
// truncated
constexpr char kEllipsis[] = "...";
constexpr size_t kEllipsisSize = sizeof(kEllipsis) - 1;
memcpy(packet.data + kDataSize - 1 - kEllipsisSize, kEllipsis,
kEllipsisSize);
count = n - 1;
}
auto size = sizeof(packet.metadata) + msg_pos + count + 1;
ZX_DEBUG_ASSERT(size <= sizeof(packet));
auto status = socket_.write(0, &packet, size, nullptr);
if (status == ZX_ERR_BAD_STATE || status == ZX_ERR_PEER_CLOSED) {
ActivateFallback(-1);
return VLogWriteToFd(logger_fd_.load(std::memory_order_relaxed),
severity, tag, packet.data + msg_pos, args, false);
}
if (status != ZX_OK) {
dropped_logs_.fetch_add(1);
}
return status;
}
zx_status_t fx_logger::VLogWriteToFd(int fd, fx_log_severity_t severity,
const char* tag, const char* msg,
va_list args, bool perform_format) {
zx_time_t time = zx_clock_get_monotonic();
constexpr char kEllipsis[] = "...";
constexpr size_t kEllipsisSize = sizeof(kEllipsis) - 1;
constexpr size_t kMaxMessageSize = 2043;
fbl::StringBuffer<kMaxMessageSize + kEllipsisSize + 1 /*\n*/> buf;
buf.AppendPrintf("[%05ld.%06ld]", time / 1000000000UL, (time / 1000UL) % 1000000UL);
buf.AppendPrintf("[%ld]", pid_);
buf.AppendPrintf("[%ld]", GetCurrentThreadKoid());
buf.Append("[");
if (!tagstr_.empty()) {
buf.Append(tagstr_);
}
if (tag != NULL) {
size_t len = strlen(tag);
if (len > 0) {
if (!tagstr_.empty()) {
buf.Append(", ");
}
buf.Append(tag,
fbl::min(len, static_cast<size_t>(FX_LOG_MAX_TAG_LEN - 1)));
}
}
buf.Append("]");
switch (severity) {
case FX_LOG_INFO:
buf.Append(" INFO");
break;
case FX_LOG_WARNING:
buf.Append(" WARNING");
break;
case FX_LOG_ERROR:
buf.Append(" ERROR");
break;
case FX_LOG_FATAL:
buf.Append(" FATAL");
break;
default:
buf.AppendPrintf(" VLOG(%d)", -severity);
}
buf.Append(": ");
if (!perform_format) {
buf.Append(msg);
} else {
buf.AppendVPrintf(msg, args);
}
if (buf.size() > kMaxMessageSize) {
buf.Resize(kMaxMessageSize);
buf.Append(kEllipsis);
}
buf.Append('\n');
ssize_t status = write(fd, buf.data(), buf.size());
if (status < 0) {
return ZX_ERR_IO;
}
return ZX_OK;
}
zx_status_t fx_logger::VLogWrite(fx_log_severity_t severity, const char* tag,
const char* msg, va_list args, bool perform_format) {
if (msg == NULL || severity > FX_LOG_FATAL) {
return ZX_ERR_INVALID_ARGS;
}
if (GetSeverity() > severity) {
return ZX_OK;
}
zx_status_t status;
int fd = logger_fd_.load(std::memory_order_relaxed);
if (fd != -1) {
status = VLogWriteToFd(fd, severity, tag, msg, args, perform_format);
} else if (socket_.is_valid()) {
status = VLogWriteToSocket(severity, tag, msg, args, perform_format);
} else {
return ZX_ERR_BAD_STATE;
}
if (severity == FX_LOG_FATAL) {
abort();
}
return status;
}
// This function is not thread safe
zx_status_t fx_logger::AddTags(const char** tags, size_t ntags) {
if (ntags > FX_LOG_MAX_TAGS) {
return ZX_ERR_INVALID_ARGS;
}
auto fd_mode = logger_fd_.load(std::memory_order_relaxed) != -1;
for (size_t i = 0; i < ntags; i++) {
auto len = strlen(tags[i]);
fbl::String str(
tags[i], len > FX_LOG_MAX_TAG_LEN - 1 ? FX_LOG_MAX_TAG_LEN - 1 : len);
if (fd_mode) {
if (tagstr_.empty()) {
tagstr_ = str;
} else {
tagstr_ = fbl::String::Concat({tagstr_, ", ", str});
}
} else {
tags_.push_back(str);
}
}
return ZX_OK;
}