| Index: mojo/services/log/cpp/lib/log_client.cc
|
| diff --git a/mojo/services/log/cpp/lib/log_client.cc b/mojo/services/log/cpp/lib/log_client.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..58b4614cda36475c2e9b57ceb9fb014c3d49e46f
|
| --- /dev/null
|
| +++ b/mojo/services/log/cpp/lib/log_client.cc
|
| @@ -0,0 +1,157 @@
|
| +// Copyright 2015 The Chromium 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 "mojo/services/log/cpp/log_client.h"
|
| +
|
| +#include <assert.h>
|
| +
|
| +#include <atomic>
|
| +#include <utility>
|
| +
|
| +#include "mojo/public/c/environment/logger.h"
|
| +#include "mojo/public/cpp/bindings/lib/message_builder.h"
|
| +#include "mojo/public/cpp/system/message_pipe.h"
|
| +#include "mojo/services/log/interfaces/entry.mojom.h"
|
| +#include "mojo/services/log/interfaces/log.mojom.h"
|
| +
|
| +namespace mojo {
|
| +namespace {
|
| +
|
| +// Forward declare for constructing |g_logclient_logger|.
|
| +void LogMessage(MojoLogLevel log_level,
|
| + const char* source_file,
|
| + uint32_t source_line,
|
| + const char* message);
|
| +MojoLogLevel GetMinimumLogLevel();
|
| +void SetMinimumLogLevel(MojoLogLevel level);
|
| +
|
| +// This interface info represents the |mojo::log::Log| service (see log.mojom).
|
| +// This doesn't need to be synchronized, since we only allow one LogClient to be
|
| +// instantiated, so it is only initialized once, and from one thread.
|
| +InterfacePtrInfo<mojo::log::Log>* g_log_interface = nullptr;
|
| +
|
| +// The minimum logging level.
|
| +std::atomic<MojoLogLevel> g_min_log_level;
|
| +
|
| +MojoLogger g_logclient_logger = {&LogMessage, &GetMinimumLogLevel,
|
| + &SetMinimumLogLevel};
|
| +
|
| +// This fallback logger is also thread-safe.
|
| +const MojoLogger* g_fallback_logger = nullptr;
|
| +
|
| +// We avoid the use of C++ bindings to do interface calls in order to be
|
| +// thread-safe (as of this writing, the bindings are not). Because the AddEntry
|
| +// method of the Log interface does not have a return type, we can easily do
|
| +// this by constructing the params for the call
|
| +// (mojo::log::Log_AddEntry_Params), framing it inside a Message using
|
| +// MessageBuilder, writing to the message pipe connecting to the log service.
|
| +void LogMessage(MojoLogLevel log_level,
|
| + const char* source_file,
|
| + uint32_t source_line,
|
| + const char* message) {
|
| + assert(g_log_interface);
|
| + if (!g_log_interface->is_valid()) {
|
| + assert(g_fallback_logger);
|
| + return g_fallback_logger->LogMessage(log_level, source_file, source_line,
|
| + message);
|
| + }
|
| +
|
| + if (log_level < g_min_log_level.load(std::memory_order_relaxed))
|
| + return;
|
| +
|
| + assert(g_log_interface->is_valid());
|
| +
|
| + mojo::log::Log_AddEntry_Params request_params;
|
| + request_params.entry = mojo::log::Entry::New();
|
| + request_params.entry->timestamp = GetTimeTicksNow();
|
| + request_params.entry->log_level = log_level;
|
| + request_params.entry->source_file = source_file;
|
| + request_params.entry->source_line = source_line;
|
| + request_params.entry->message = message;
|
| +
|
| + size_t params_size = request_params.GetSerializedSize();
|
| + MessageBuilder builder(
|
| + static_cast<uint32_t>(mojo::log::Log::MessageOrdinals::AddEntry),
|
| + params_size);
|
| +
|
| + assert(request_params.Serialize(
|
| + static_cast<void*>(builder.message()->mutable_payload()), params_size));
|
| +
|
| + auto retval = WriteMessageRaw(g_log_interface->handle().get(),
|
| + builder.message()->data(),
|
| + builder.message()->data_num_bytes(), nullptr, 0,
|
| + MOJO_WRITE_MESSAGE_FLAG_NONE);
|
| + switch (retval) {
|
| + case MOJO_RESULT_OK:
|
| + break;
|
| +
|
| + // This means that the other end of the pipe no longer exists, so it 's time
|
| + // to fall back.
|
| + case MOJO_RESULT_FAILED_PRECONDITION: {
|
| + g_log_interface->PassHandle();
|
| + return g_fallback_logger->LogMessage(log_level, source_file, source_line,
|
| + message);
|
| + }
|
| +
|
| + default:
|
| + // TODO(vardhan): What other cases are there?
|
| + break;
|
| + }
|
| +
|
| + if (log_level >= MOJO_LOG_LEVEL_FATAL)
|
| + abort();
|
| +}
|
| +
|
| +MojoLogLevel GetMinimumLogLevel() {
|
| + assert(g_log_interface);
|
| +
|
| + if (!g_log_interface->is_valid()) {
|
| + assert(g_fallback_logger);
|
| + return g_fallback_logger->GetMinimumLogLevel();
|
| + }
|
| + return g_min_log_level.load(std::memory_order_relaxed);
|
| +}
|
| +
|
| +void SetMinimumLogLevel(MojoLogLevel level) {
|
| + assert(g_log_interface);
|
| +
|
| + if (!g_log_interface->is_valid()) {
|
| + assert(g_fallback_logger);
|
| + g_fallback_logger->SetMinimumLogLevel(level);
|
| + }
|
| + g_min_log_level.store(std::min(level, MOJO_LOG_LEVEL_FATAL),
|
| + std::memory_order_relaxed);
|
| +
|
| + // Keep the fallback logger's level consistent with ours.
|
| + g_fallback_logger->SetMinimumLogLevel(level);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +namespace log {
|
| +
|
| +LogClient::LogClient(LogPtr log, const MojoLogger* fallback_logger) {
|
| + assert(!g_log_interface);
|
| + assert(log.is_bound());
|
| + assert(fallback_logger);
|
| +
|
| + g_min_log_level.store(MOJO_LOG_LEVEL_INFO, std::memory_order_relaxed);
|
| + g_log_interface = new InterfacePtrInfo<Log>(log.PassInterface());
|
| +
|
| + g_fallback_logger = fallback_logger;
|
| +}
|
| +
|
| +LogClient::~LogClient() {
|
| + g_log_interface->PassHandle();
|
| + delete g_log_interface;
|
| + g_log_interface = nullptr;
|
| +}
|
| +
|
| +// static
|
| +const MojoLogger* LogClient::GetLogger() {
|
| + return &g_logclient_logger;
|
| +}
|
| +
|
| +} // namespace log
|
| +} // namespace mojo
|
|
|