| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| // This header defines the following macros: |
| // |
| // VLOG_AND_TRACE(category, vlevel) |
| // |
| // Write a log message to VLOG(vlevel) as well as the current |
| // trace event buffer as an "INSTANT" trace event type. If the |
| // given vlog level is not enabled, this will still result in a |
| // trace buffer entry. |
| // |
| // The provided 'category' should be a trace event category, which |
| // allows the users to filter which trace events to enable. |
| // For example: |
| // |
| // VLOG_AND_TRACE("my_subsystem", 1) << "This always shows up in trace buffers " |
| // << "but only shows up in the log if VLOG(1) level logging is enabled."; |
| // |
| // Most VLOG(1) level log messages are reasonable to use this macro. |
| // Note that there is slightly more overhead to this macro as opposed |
| // to just using VLOG(1). |
| // |
| // Note that, like VLOG(n), this macro avoids evaluating its arguments unless |
| // either trace recording or VLOG(n) is enabled. In the case that both are enabled, |
| // the arguments are only evaluated once. |
| // |
| // |
| // LOG_AND_TRACE(category, severity) |
| // |
| // Same as the above, but always logs at the given severity level in addition |
| // to writing to the trace buffer. |
| |
| #ifndef KUDU_DEBUG_TRACE_LOGGING_H |
| #define KUDU_DEBUG_TRACE_LOGGING_H |
| |
| #include <glog/logging.h> |
| #include <string> |
| |
| #include "kudu/gutil/macros.h" |
| #include "kudu/util/debug/trace_event.h" |
| |
| // The inner workings of these macros are a bit arcane: |
| // - We make use of the fact that a block can be embedded within a ternary expression. |
| // This allows us to determine whether the trace event is enabled before we decide |
| // to evaluate the arguments. |
| // - We have to use google::LogMessageVoidify so that we can put 'void(0)' on one side |
| // of the ternary expression and the log stream on the other. This technique is |
| // cribbed from glog/logging.h. |
| #define VLOG_AND_TRACE_INTERNAL(category, vlevel) \ |
| kudu::debug::TraceGLog(__FILE__, __LINE__, category, google::GLOG_INFO, \ |
| /* send_to_log= */VLOG_IS_ON(vlevel)).stream() |
| |
| #define VLOG_AND_TRACE(category, vlevel) \ |
| !( { \ |
| bool enabled; \ |
| TRACE_EVENT_CATEGORY_GROUP_ENABLED(category, &enabled); \ |
| enabled || VLOG_IS_ON(vlevel); \ |
| } ) ? static_cast<void>(0) : \ |
| google::LogMessageVoidify() & VLOG_AND_TRACE_INTERNAL(category, vlevel) // NOLINT(*) |
| |
| #define VLOG_AND_TRACE_WITH_PREFIX(category, vlevel) \ |
| VLOG_AND_TRACE(category, vlevel) << LogPrefix() |
| |
| #define LOG_AND_TRACE(category, severity) \ |
| kudu::debug::TraceGLog(__FILE__, __LINE__, category, \ |
| google::GLOG_ ## severity, /* send_to_log= */true).stream() |
| |
| #define LOG_AND_TRACE_WITH_PREFIX(category, severity) \ |
| LOG_AND_TRACE(category, severity) << LogPrefix() |
| |
| namespace kudu { |
| namespace debug { |
| |
| class TraceGLog { |
| public: |
| TraceGLog(const char* file, int line, const char* category, |
| google::LogSeverity severity, bool send_to_log) |
| : sink_(category), |
| google_msg_(file, line, severity, &sink_, send_to_log) { |
| } |
| |
| std::ostream& stream() { |
| return google_msg_.stream(); |
| } |
| |
| private: |
| class TraceLogSink : public google::LogSink { |
| public: |
| explicit TraceLogSink(const char* category) : category_(category) {} |
| void send(google::LogSeverity severity, const char* /*full_filename*/, |
| const char* base_filename, int line, |
| const ::google::LogMessageTime& logmsgtime, const char* message, |
| size_t message_len) override { |
| // Rather than calling TRACE_EVENT_INSTANT here, we have to do it from |
| // the destructor. This is because glog holds its internal mutex while |
| // calling send(). So, if we try to use TRACE_EVENT here, and --trace_to_console |
| // is enabled, then we'd end up calling back into glog when its lock is already |
| // held. glog isn't re-entrant, so that causes a crash. |
| // |
| // By just storing the string here, and then emitting the trace in the dtor, |
| // we defer the tracing until the google::LogMessage has destructed and the |
| // glog lock is available again. |
| str_ = ToString(severity, base_filename, line, |
| logmsgtime, message, message_len); |
| } |
| virtual ~TraceLogSink() { |
| TRACE_EVENT_INSTANT1(category_, "vlog", TRACE_EVENT_SCOPE_THREAD, |
| "msg", str_); |
| } |
| |
| private: |
| const char* const category_; |
| std::string str_; |
| }; |
| |
| TraceLogSink sink_; |
| google::LogMessage google_msg_; |
| }; |
| |
| } // namespace debug |
| } // namespace kudu |
| #endif /* KUDU_DEBUG_TRACE_LOGGING_H */ |