| // 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 |