blob: c11688970764f056a85654e9141edefa7cbac943 [file] [log] [blame]
// Copyright 2020 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 <fidl/fuchsia.logger/cpp/wire.h>
#include <lib/driver2/logger.h>
#include <lib/fdio/directory.h>
#include <cstdarg>
namespace flog = fuchsia_syslog;
namespace driver {
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;
}
bool Logger::FlushRecord(flog::LogBuffer& buffer, uint32_t dropped) {
if (!buffer.FlushRecord()) {
dropped_logs_.fetch_add(dropped, std::memory_order_relaxed);
return false;
}
return true;
}
void Logger::BeginRecord(flog::LogBuffer& buffer, FuchsiaLogSeverity severity,
cpp17::optional<cpp17::string_view> file_name, unsigned int line,
cpp17::optional<cpp17::string_view> message,
cpp17::optional<cpp17::string_view> condition, bool is_printf,
uint32_t dropped) {
static zx_koid_t pid = GetKoid(zx_process_self());
static thread_local zx_koid_t tid = GetKoid(zx_thread_self());
buffer.BeginRecord(severity, file_name, line, message, condition, is_printf, socket_.borrow(),
dropped, pid, tid);
buffer.WriteKeyValue("tag", tag_);
buffer.WriteKeyValue("tag", "driver");
}
zx::status<Logger> Logger::Create(const Namespace& ns, async_dispatcher_t* dispatcher,
std::string_view name, FuchsiaLogSeverity min_severity) {
zx::socket client_end, server_end;
zx_status_t status = zx::socket::create(ZX_SOCKET_DATAGRAM, &client_end, &server_end);
if (status != ZX_OK) {
return zx::error(status);
}
auto ns_result = ns.Connect<fuchsia_logger::LogSink>();
if (ns_result.is_error()) {
return ns_result.take_error();
}
fidl::WireSharedClient<fuchsia_logger::LogSink> log_sink(std::move(*ns_result), dispatcher);
auto sink_result = log_sink->ConnectStructured(std::move(server_end));
if (!sink_result.ok()) {
return zx::error(sink_result.status());
}
Logger logger;
logger.dropped_logs_ = 0;
logger.tag_ = name;
logger.severity_ = min_severity;
logger.socket_ = std::move(client_end);
return zx::ok(std::move(logger));
}
Logger::~Logger() = default;
Logger::Logger(Logger&& other) noexcept {
dropped_logs_.store(other.dropped_logs_);
severity_.store(other.severity_);
socket_ = std::move(other.socket_);
tag_ = std::move(other.tag_);
}
Logger& Logger::operator=(Logger&& other) noexcept {
dropped_logs_.store(other.dropped_logs_);
severity_.store(other.severity_);
socket_ = std::move(other.socket_);
tag_ = std::move(other.tag_);
return *this;
}
uint32_t Logger::GetAndResetDropped() {
return dropped_logs_.exchange(0, std::memory_order_relaxed);
}
FuchsiaLogSeverity Logger::GetSeverity() { return severity_.load(std::memory_order_relaxed); }
void Logger::SetSeverity(FuchsiaLogSeverity severity) { severity_.store(severity); }
void Logger::logf(FuchsiaLogSeverity severity, const char* tag, const char* file, int line,
const char* msg, ...) {
va_list args;
va_start(args, msg);
logvf(severity, tag, file, line, msg, args);
va_end(args);
}
namespace {
const char* StripDots(const char* path) {
while (strncmp(path, "../", 3) == 0) {
path += 3;
}
return path;
}
const char* StripPath(const char* path) {
auto p = strrchr(path, '/');
if (p) {
return p + 1;
} else {
return path;
}
}
} // namespace
static const char* StripFile(const char* file, FuchsiaLogSeverity severity) {
return severity > FUCHSIA_LOG_INFO ? StripDots(file) : StripPath(file);
}
void Logger::logvf(FuchsiaLogSeverity severity, const char* tag, const char* file, int line,
const char* msg, va_list args) {
if (!file || line <= 0) {
// We require a file and line number for printf-style logs.
return;
}
if (severity < severity_.load()) {
return;
}
uint32_t dropped = dropped_logs_.exchange(0, std::memory_order_relaxed);
constexpr size_t kFormatStringLength = 1024;
char fmt_string[kFormatStringLength];
fmt_string[kFormatStringLength - 1] = 0;
int n = kFormatStringLength;
// Format
// Number of bytes written not including null terminator
int count = 0;
count = vsnprintf(fmt_string, n, msg, args) + 1;
if (count < 0) {
// Invalid arguments -- we don't support logging empty strings
// for legacy printf-style messages.
return;
}
if (count >= n) {
// truncated
constexpr char kEllipsis[] = "...";
constexpr size_t kEllipsisSize = sizeof(kEllipsis);
snprintf(fmt_string + kFormatStringLength - 1 - kEllipsisSize, kEllipsisSize, kEllipsis);
}
// TODO(fxbug.dev/72675): Pass file/line info regardless of severity in all cases.
// This is currently only enabled for drivers.
file = StripFile(file, severity);
flog::LogBuffer buffer;
BeginRecord(buffer, severity, file, line, fmt_string, std::nullopt, this->socket_.get(), dropped);
buffer.WriteKeyValue("tag", tag_);
if (tag) {
buffer.WriteKeyValue("tag", tag);
}
FlushRecord(buffer, dropped);
}
} // namespace driver