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