diff --git a/CMakeLists.txt b/CMakeLists.txt index 45064ab78..85039653a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ include (GetCacheVariables) include (GNUInstallDirs) option (BUILD_SHARED_LIBS "Build shared libraries" ON) +option (BUILD_EXAMPLES "Build examples" ON) option (PRINT_UNSYMBOLIZED_STACK_TRACES "Print file offsets in traces instead of symbolizing" OFF) option (WITH_GFLAGS "Use gflags" ON) @@ -960,6 +961,11 @@ if (BUILD_TESTING) ) endif (BUILD_TESTING) +if (BUILD_EXAMPLES) + add_executable (custom_sink_example examples/custom_sink.cc) + target_link_libraries (custom_sink_example PRIVATE glog::glog) +endif (BUILD_EXAMPLES) + install (TARGETS glog EXPORT glog-targets RUNTIME DESTINATION ${_glog_CMake_BINDIR} diff --git a/docs/sinks.md b/docs/sinks.md new file mode 100644 index 000000000..66ef2975e --- /dev/null +++ b/docs/sinks.md @@ -0,0 +1,84 @@ +# Custom Sinks + +Under certain circumstances, it is useful to send the log output to a +destination other than a file, `stderr` and/or `stdout`. In case, the library +provides the `#!cpp google::LogSink` interface whose implementations can be used +to write the log output to arbitrary locations. + +## Basic Interface + +The sink interface is defined as follows: + +``` cpp +class LogSink { + public: + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const LogMessageTime& time, const char* message, + size_t message_len); +}; +``` + +The user must implement `#!cpp google::LogSink::send`, which is called by the +library every time a message is logged. + +!!! warning "Possible deadlock due to nested logging" + This method can't use `LOG()` or `CHECK()` as logging system mutex(s) are + held during this call. + +## Registering Log Sinks + +To use the custom sink and instance of the above interface implementation must +be registered using `google::AddLogSink` which expects a pointer to the +`google::LogSink` instance. To unregister use `google::RemoveLogSink`. Both +functions are thread-safe. + +!!! danger "`LogSink` ownership" + The `google::LogSink` instance must not be destroyed until the referencing + pointer is unregistered. + +## Direct Logging + +Instead of registering the sink, we can directly use to log messages. While `#! +LOG_TO_SINK(sink, severity)` allows to log both to the sink and to a global log +registry, e.g., a file, `#!cpp LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity)` +will avoid the latter. + +!!! example "Using a custom sink" + ``` cpp title="custom_sink.cc" + -8<- "examples/custom_sink.cc:33:" + ``` + + 1. `MySink` implements a custom sink that sends log messages to `std::cout`. + 2. The custom sink must be registered to for use with existing logging + macros. + 3. Once the custom sink is no longer needed we remove it from the registry. + 4. A sink does not need to be registered globally. However, then, messages + must be logged using dedicated macros. + + Running the above example as `#!bash GLOG_log_dir=. ./custom_sink_example` + will produce + +
+ + ``` title="Custom sink output" + INFO custom_sink.cc:63 logging to MySink + INFO custom_sink.cc:68 direct logging + INFO custom_sink.cc:69 direct logging but not to file (1) + ``` + +
+ + 1. This line is not present in the log file because we used + `LOG_TO_SINK_BUT_NOT_TO_LOGFILE` to log the message. + + and the corresponding log file will contain + + ``` title="Log file generated with the custom sink" + Log file created at: 2024/06/11 13:24:27 + Running on machine: pc + Running duration (h:mm:ss): 0:00:00 + Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg + I20240611 13:24:27.476620 126237946035776 custom_sink.cc:63] logging to MySink + I20240611 13:24:27.476796 126237946035776 custom_sink.cc:68] direct logging + ``` diff --git a/examples/custom_sink.cc b/examples/custom_sink.cc new file mode 100644 index 000000000..5390de8dd --- /dev/null +++ b/examples/custom_sink.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2024, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Sergiu Deitsch +// + +#include + +#include +#include +#include +#include + +namespace { + +struct MyLogSink : google::LogSink { // (1)! + void send(google::LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const google::LogMessageTime& time, const char* message, + std::size_t message_len) override { + std::cout << google::GetLogSeverityName(severity) << ' ' << base_filename + << ':' << line << ' '; + std::copy_n(message, message_len, + std::ostreambuf_iterator{std::cout}); + std::cout << '\n'; + } +}; + +} // namespace + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + + MyLogSink sink; + google::AddLogSink(&sink); // (2)! + + LOG(INFO) << "logging to MySink"; + + google::RemoveLogSink(&sink); // (3)! + + // We can directly log to a sink without registering it + LOG_TO_SINK(&sink, INFO) << "direct logging"; // (4)! + LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, INFO) + << "direct logging but not to file"; +} diff --git a/mkdocs.yml b/mkdocs.yml index 8b3640052..fd9a11449 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,7 +7,9 @@ edit_uri: edit/0.7.x/docs/ copyright: Copyright © 2024 Google Inc. & contributors - Change cookie settings markdown_extensions: - admonition + - attr_list - def_list + - md_in_html - pymdownx.details - pymdownx.highlight: anchor_linenums: true @@ -26,8 +28,9 @@ theme: name: material custom_dir: docs/overrides icon: - repo: fontawesome/brands/git-alt + annotation: material/chevron-right-circle edit: material/pencil + repo: fontawesome/brands/git-alt view: material/eye language: en features: @@ -107,6 +110,7 @@ nav: - Installation using Package Managers: packages.md - User Guide: - Logging: logging.md + - Custom Sinks: sinks.md - Failure Handler: failures.md - Log Removal: log_cleaner.md - Stripping Log Messages: log_stripping.md