diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..e30cc1c3 --- /dev/null +++ b/404.html @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + TEST G3log, an asynchronous "crash-safe" logger + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/API/index.html b/API/index.html new file mode 100644 index 00000000..5b9d845d --- /dev/null +++ b/API/index.html @@ -0,0 +1,758 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + API description - TEST G3log, an asynchronous "crash-safe" logger + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + +

introduction | detailed information | Configure & Build | API description | Custom log formatting

+

High Level Description of g3log

+

The g3log logger is an asynchronous, crash-safe logging library designed for C++ applications. It allows for logging messages to various sinks without blocking the main application thread. Below is a high-level overview of how the g3log logger works.

+

Asynchronous Logging

+

The logger operates on a separate thread, ensuring that the main application thread is not blocked by I/O operations when logging messages. This is achieved by using a background worker (LogWorker) that queues log messages and processes them asynchronously.

+

LogWorker and Sinks

+

The LogWorker is responsible for managing the logging sinks. A sink is an object that defines where and how log messages are outputted (e.g., to a file, console, or over the network). Users can add custom sinks to the LogWorker using the addSink method, which takes a unique pointer to a sink object and a function pointer to the method that will save the log message.

+

Signal Handling

+

The logger includes a signal handler for Unix-like systems that captures fatal signals (e.g., SIGSEGV, SIGABRT) and ensures that all pending log messages are flushed to the sinks before the application exits. The signal handler function (signalHandler) is registered to handle these signals and will attempt to generate a stack trace when a fatal signal is received. This stack trace is then logged, providing valuable debugging information.

+

Stack Trace Generation

+

Upon receiving a fatal signal, the signalHandler function will call stackdump to generate a stack trace. This function uses platform-specific calls to retrieve the stack frames and attempts to demangle the function names to make the stack trace more readable.

+

Log Message Formatting

+

Log messages can be formatted using either a streaming API (e.g., LOG(INFO) << "message";) or a printf-like syntax (e.g., LOGF(INFO, "message %d", value);). This provides flexibility in how messages are constructed.

+

Log Levels

+

The library supports various log levels (e.g., DEBUG, INFO, WARNING, FATAL). Users can define custom log levels or modify the existing ones. The log levels can be dynamically enabled or disabled at runtime if the G3_DYNAMIC_LOGGING preprocessor definition is set.

+

Crash Safety

+

In the event of a crash, the logger is designed to be crash-safe by catching fatal events and ensuring that all log messages are flushed to the sinks before the process exits.

+

Customization

+

The library allows for extensive customization, including adding custom log levels, creating custom sinks, and overriding the default signal handling behavior.

+

Thread Safety

+

The g3log logger is thread-safe, meaning it can be used from multiple threads without the need for additional synchronization.

+

Public Domain Software

+

The g3log code is released into the public domain, allowing users to use, modify, and distribute it freely without restrictions.

+

Logging and Fatal Events Explained

+

diagrams created with https://mermaid.live +G3Log sequence view

+

API description

+

Most of the API that you need for using g3log is described in this readme. For more API documentation and examples please continue to read the API readme. Examples of what you will find here are:

+

Logging API: LOG calls

+

LOG calls can follow streaming LOG(INFO) << "some text" or printf-like syntax LOGF(WARNING, "some number %d", 123);

+

Conditional logging is made with LOG_IF(INFO, <boolean-expression>) << " some text" or LOGF_IF(WARNING, <boolean-expression>) << " some text". Only if the expressions evaluates to true will the logging take place.

+

Example: +LOG_IF(INFO, 1 != 200) << " some text"; or LOG_IF(FATAL, SomeFunctionCall()) << " some text";

+

A call using FATAL logging level, such as the LOG_IF(FATAL,...) example above, will after logging the message at FATALlevel also kill the process. It is essentially the same as a CHECK(<boolea-expression>) << ... with the difference that the CHECK(<boolean-expression) triggers when the expression evaluates to false.

+

Contract API: CHECK calls

+

The contract API follows closely the logging API with CHECK(<boolean-expression>) << ... for streaming or (*) CHECKF(<boolean-expression>, ...); for printf-style.

+

If the <boolean-expression> evaluates to false then the the message for the failed contract will be logged in FIFO order with previously made messages. The process will then shut down after the message is sent to the sinks and the sinks have dealt with the fatal contract message.

+

CHECK_F(<boolean-expression>, ...); was the the previous API for printf-like CHECK. It is still kept for backwards compatability but is exactly the same as CHECKF

+

LOG(fATAL) or CHECK(false)

+

Fatal logging or failed `CHECK calls follows the same handling. +CHECK(false) or LOG(FATAL)

+

Logging levels

+

The default logging levels are DEBUG, INFO, WARNING and FATAL (see FATAL usage above). The logging levels are defined in loglevels.hpp.

+

For some windows framework there is a clash with the DEBUG logging level. One of the CMake Build options can be used to then change offending default level from DEBUG TO DBUG.

+

CMake option: (default OFF) cmake -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON ..

+

### disable/enabled levels at runtime + Logging levels can be disabled at runtime. The logic for this happens in + loglevels.hpp, loglevels.cpp and g3log.hpp.

+

There is a cmake option to enable the dynamic enable/disable of levels. + When the option is enabled there will be a slight runtime overhead for each LOG call when the enable/disable status is checked. For most intent and purposes this runtime overhead is negligable.

+

There is no runtime overhead for internally checking if a level is enabled//disabled if the cmake option is turned off. If the dynamic logging cmake option is turned off then all logging levels are enabled.

+

CMake option: (default OFF) cmake -DUSE_DYNAMIC_LOGGING_LEVELS=ON ..

+

### custom logging levels + Custom logging levels can be created and used. When defining a custom logging level you set the value for it as well as the text for it. You can re-use values for other levels such as INFO, WARNING etc or have your own values. Any value with equal or higher value than the FATAL value will be considered a FATAL logging level.

+

To keep in mind when adding your own custom levels. +1. If the cmake option G3_DYNAMIC_LOGGING is enabled then you must use g3::only_change_at_initialization::addLogLevel(...) to give g3log a record of your logging level and if it is an enabled or disbled logging level. +1. If the cmake G3_DYNAMIC_LOGGING is turned OFF, then giving g3log a record of your logging level with 'addLogLevel(...) is not needed since no "disbled/enabled" check will happen - all logging levels will be considered enabled.

+

Example: + ```cpp + // In CustomLoggingLevels.hpp + #include

+

// all values with a + 1 higher than their closest equivalet + // they could really have the same value as well.

+

const LEVELS FYI {DEBUG.value + 1, {"For Your Information"}}; + const LEVELS CUSTOM {INFO.value + 1, {"CUSTOM"}}; + const LEVELS SEVERE {WARNING.value +1, {"SEVERE"}}; + const LEVELS DEADLY {FATAL.value + 1, {"DEADLY"}}; + ```

+

More examples can be viwed in the unit tests.

+

Sink creation and utilization

+

The default sink for g3log is the one as used in g2log. It is a simple file sink with a limited API. The details for the default file sink can be found in filesink.hpp, filesink.cpp, filesinkhelper.ipp

+

More sinks can be found at g3sinks (log rotate, log rotate with filtering on levels)

+

A logging sink is not required to be a subclass of a specific type. The only requirement of a logging sink is that it can receive a logging message of

+

Using the default sink

+

Sink creation is defined in logworker.hpp and used in logworker.cpp. For in-depth knowlege regarding sink implementation details you can look at sinkhandle.hpp and sinkwrapper.hpp

+
  std::unique_ptr<FileSinkHandle> addDefaultLogger(
+            const std::string& log_prefix
+            , const std::string& log_directory
+            , const std::string& default_id = "g3log");
+
+

With the default id left as is (i.e. "g3log") a creation of the logger in the unit test "test_filechange" would look like this

+
  const std::string directory = "./";
+  const std::string name = "(ReplaceLogFile)";
+  auto worker = g3::LogWorker::createLogWorker();
+  auto handle = worker->addDefaultLogger(name, directory);
+
+

The resulting filename would be something like:

+
   ./(ReplaceLogFile).g3log.20160217-001406.log
+
+

Designate the sink function's log entry receving function

+

The default log formatting look can be overriden by any sink. +If the sink receiving function calls toString() then the default log formatting will be used. +If the sink receiving function calls toString(&XFunc) then the XFuncwill be used instead (see LogMessage.h/cpp for code details if it is not clear). (XFunc is a place holder for your formatting function of choice).

+

The API for the function-ptr to pass in is

+
std::string (*) (const LogMessage&)
+
+

or for short as defined in LogMessage.h

+
using LogDetailsFunc = std::string (*) (const LogMessage&);
+
+

Log format customization

+

Please seeAPI_custom_formatting.md

+

LOG flushing

+

The default file sink will flush each log entry at set intervals. The default buffer size for flushing is set to 100 entries. You can adjust this down to 1, or as high as makes sense for your system. Please see FileSink

+

Even more flushing policies and log rotations can be found at g3sinks logrotate and LogRotateWithFilters.

+

At shutdown all enqueued logs will be flushed to the sink.
+At a discovered fatal event (SIGSEGV et.al) all enqueued logs will be flushed to the sink.

+

A programmatically triggered abrupt process exit such as a call to exit(0) will of course not get the enqueued log entries flushed. Similary a bug that does not trigger a fatal signal but a process exit will also not get the enqueued log entries flushed. G3log can catch several fatal crashes and it deals well with RAII exits but magic is so far out of its' reach.

+

log sequence

+

G3log and Sink Usage Code Example

+

Example usage where a logrotate sink (g3sinks) is added. In the example it is shown how the logrotate API is called. The logrotate limit is changed from the default to instead be 10MB. The limit is changed by calling the sink handler which passes the function call through to the actual logrotate sink object.

+

+// main.cpp
+#include <g3log/g3log.hpp>
+#include <g3log/logworker.h>
+#include <g3sinks/LogRotate.h>
+#include <memory>
+
+int main(int argc, char**argv) {
+   using namespace g3;
+   std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };
+   auto sinkHandle = logworker->addSink(std::make_unique<LogRotate>(),
+                                          &LogRotate::save);
+
+   // initialize the logger before it can receive LOG calls
+   initializeLogging(logworker.get());            
+
+   // You can call in a thread safe manner public functions on the logrotate sink
+   // The call is asynchronously executed on your custom sink.
+   const int k10MBInBytes = 10 * 1024 * 1024;
+   std::future<void> received = sinkHandle->call(&LogRotate::setMaxLogSize, k10MBInBytes);
+
+   // Run the main part of the application. This can be anything of course, in this example
+   // we'll call it "RunApplication". Once this call exits we are in shutdown mode
+   RunApplication();
+
+   // If the LogWorker is initialized then at scope exit the g3::shutDownLogging() will be 
+   // called automatically. 
+   //  
+   // This is important since it protects from LOG calls from static or other entities that will go out of
+   // scope at a later time. 
+   //
+   // It can also be called manually if for some reason your setup is different then the one highlighted in
+   // this example
+   g3::shutDownLogging();
+}
+
+

Dynamic Message Sizing

+

The default build uses a fixed size buffer for formatting messages. The size of this buffer is 2048 bytes. If an incoming message results in a formatted message that is greater than 2048 bytes, it will be bound to 2048 bytes and will have the string [...truncated...] appended to the end of the bound message. There are cases where one would like to dynamically change the size at runtime. For example, when debugging payloads for a server, it may be desirable to handle larger message sizes in order to examine the whole payload. Rather than forcing the developer to rebuild the server, dynamic message sizing could be used along with a config file which defines the message size at runtime.

+

This feature supported as a CMake option:

+

CMake option: (default OFF) cmake -DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=ON ..

+

The following is an example of changing the size for the message.

+
    g3::only_change_at_initialization::setMaxMessageSize(10000);
+
+

Fatal handling

+

The default behaviour for G3log is to catch several fatal events before they force the process to exit. After catching a fatal event a stack dump is generated and all log entries, up to the point of the stack dump are together with the dump flushed to the sink(s).

+

fatal signal

+

Linux/*nix

+

The default fatal handling on Linux deals with fatal signals. At the time of writing these signals were SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM. The Linux fatal handling is handled in crashhandler.hpp and crashhandler_unix.cpp

+

A signal that commonly is associated with voluntarily process exit is SIGINT (ctrl + c) G3log does not deal with it.

+

The fatal signals can be disabled or changed/added .

+

An example of a Linux stackdump as shown in the output from the fatal example g3log-FATAL-sigsegv. + ``` + * FATAL SIGNAL RECEIVED * + "Received fatal signal: SIGSEGV(11) PID: 6571

+
***** SIGNAL SIGSEGV(11)
+
+******* STACKDUMP *******
+        stack dump [1]  ./g3log-FATAL-sigsegv() [0x42a500]
+        stack dump [2]  /lib/x86_64-linux-gnu/libpthread.so.0+0x10340 [0x7f83636d5340]
+
+        stack dump [3]  ./g3log-FATAL-sigsegv : example_fatal::tryToKillWithAccessingIllegalPointer(std::unique_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::default_delete<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)+0x119 [0x4107b9]
+        stack dump [4]  ./g3log-FATAL-sigsegvmain+0xdec [0x40e51c]
+        stack dump [5]  /lib/x86_64-linux-gnu/libc.so.6__libc_start_main+0xf5 [0x7f8363321ec5]
+        stack dump [6]  ./g3log-FATAL-sigsegv() [0x40ffa2]
+
+Exiting after fatal event  (FATAL_SIGNAL). Fatal type:  SIGSEGV
+Log content flushed sucessfully to sink
+
+"
+g3log g3FileSink shutdown at: 16:33:18
+
+```
+
+

Custom fatal handling - override defaults

+

By default the fatal signals are defined in https://github.com/KjellKod/g3log/tree/master/src/g3log.cpp as + SIGABRT + SIGFPE + SIGILL + SIGSEGV + SIGTERM + If you want to define your own set of fatal signals, override the default ones, then this can be done as shown in src/g3log/crashhandler.hpp + cpp + // Example when SIGTERM is skipped due to ZMQ usage + g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, + {SIGFPE, "SIGFPE"}, + {SIGILL, "SIGILL"}, + {SIGSEGV, "SIGSEGV"}});

+

Pre fatal hook

+

You can define a custom call back function that will be called before the fatal signal handling re-emits the fatal signal. See src/g3log/g3log.hpp for details. + // Example of how to enforce important shutdown cleanup even in the event of a fatal crash: + g3::setFatalPreLoggingHook([]{ cleanup(); });

+

Disable fatal handling

+

Fatal signal handling can be disabled with a CMake option: ENABLE_FATAL_SIGNALHANDLING. See Options.cmake for more details

+

PID1 Fatal Signal Recommendations

+

If you are using g3log on a PID1 process then you absolutely should provide your own signal handling (ref: issue 269) as g3log re-emits the fatal signal after it has restored the previous signal handler for that signal. PID1 processed do not shutdown the process for a normal fatal signal so the choice to exit the PID1 process after such a signal must be taken by the coder - not by g3log.

+

Windows

+

Windows fatal handling also deals with fatal signals just like Linux. In addition to fatal signals it also deals with unhandled exceptions, vectored exceptions. Windows fatal handling is handled in crashhandler.hpp, crashhandler_windows.cpp, stacktrace_windows.hpp, stacktrace_windows.cpp

+

An example of a Windows stackdump as shown in the output from the fatal example g3log-FATAL-sigsegv.

+

introduction | detailed information | Configure & Build | API description | Custom log formatting

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/API_custom_formatting/index.html b/API_custom_formatting/index.html new file mode 100644 index 00000000..f7e275f5 --- /dev/null +++ b/API_custom_formatting/index.html @@ -0,0 +1,498 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + API for custom log formatting - TEST G3log, an asynchronous "crash-safe" logger + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + +

introduction | detailed information | Configure & Build | API description | Custom log formatting

+

Custom LOG formatting

+

Overriding the Default File Sink's file header

+

The default file header can be customized in the default file sink in calling

+
   FileSink::overrideLogHeader(std::string);
+
+

Overriding the Default FileSink's log formatting

+

The default log formatting is defined in LogMessage.hpp

+
   static std::string DefaultLogDetailsToString(const LogMessage& msg);
+
+

Adding thread ID to the log formatting

+

An "all details" log formatting function is also defined - this one also adds the "calling thread's ID"

+
   static std::string FullLogDetailsToString(const LogMessage& msg);
+
+

Override default sink log formatting

+

For convenience the Default sink has a function +for doing exactly this

+
  void overrideLogDetails(LogMessage::LogDetailsFunc func);
+
+

Example code for replacing the default log formatting for "full details" formatting (it adds thread ID)

+
   auto worker = g3::LogWorker::createLogWorker();
+   auto handle= worker->addDefaultLogger(argv[0], path_to_log_file);
+   g3::initializeLogging(worker.get());
+   handle->call(&g3::FileSink::overrideLogDetails, &LogMessage::FullLogDetailsToString);
+
+

See test_message.cpp for details and testing

+

Example code for overloading the formatting of a custom sink. The log formatting function will be passed into the +LogMessage::toString(...) this will override the default log formatting

+

Example

+
namespace {
+      std::string MyCustomFormatting(const LogMessage& msg) {
+        ... how you want it ...
+      }
+    }
+
+   void MyCustomSink::ReceiveLogEntry(LogMessageMover message) {
+      std::string formatted = message.get().toString(&MyCustomFormatting) << std::flush;
+   }
+...
+...
+ auto worker = g3::LogWorker::createLogWorker();
+ auto sinkHandle = worker->addSink(std::make_unique<MyCustomSink>(),
+                                     &MyCustomSink::ReceiveLogMessage);
+ // ReceiveLogMessage(...) will used the custom formatting function "MyCustomFormatting(...)
+
+
+

introduction | detailed information | Configure & Build | API description | Custom log formatting

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 00000000..1cf13b9f Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.51d95adb.min.js b/assets/javascripts/bundle.51d95adb.min.js new file mode 100644 index 00000000..b20ec683 --- /dev/null +++ b/assets/javascripts/bundle.51d95adb.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Hi=Object.create;var xr=Object.defineProperty;var Pi=Object.getOwnPropertyDescriptor;var $i=Object.getOwnPropertyNames,kt=Object.getOwnPropertySymbols,Ii=Object.getPrototypeOf,Er=Object.prototype.hasOwnProperty,an=Object.prototype.propertyIsEnumerable;var on=(e,t,r)=>t in e?xr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Er.call(t,r)&&on(e,r,t[r]);if(kt)for(var r of kt(t))an.call(t,r)&&on(e,r,t[r]);return e};var sn=(e,t)=>{var r={};for(var n in e)Er.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&kt)for(var n of kt(e))t.indexOf(n)<0&&an.call(e,n)&&(r[n]=e[n]);return r};var Ht=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Fi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of $i(t))!Er.call(e,o)&&o!==r&&xr(e,o,{get:()=>t[o],enumerable:!(n=Pi(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Hi(Ii(e)):{},Fi(t||!e||!e.__esModule?xr(r,"default",{value:e,enumerable:!0}):r,e));var fn=Ht((wr,cn)=>{(function(e,t){typeof wr=="object"&&typeof cn!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(wr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(T){return!!(T&&T!==document&&T.nodeName!=="HTML"&&T.nodeName!=="BODY"&&"classList"in T&&"contains"in T.classList)}function f(T){var Ke=T.type,We=T.tagName;return!!(We==="INPUT"&&a[Ke]&&!T.readOnly||We==="TEXTAREA"&&!T.readOnly||T.isContentEditable)}function c(T){T.classList.contains("focus-visible")||(T.classList.add("focus-visible"),T.setAttribute("data-focus-visible-added",""))}function u(T){T.hasAttribute("data-focus-visible-added")&&(T.classList.remove("focus-visible"),T.removeAttribute("data-focus-visible-added"))}function p(T){T.metaKey||T.altKey||T.ctrlKey||(s(r.activeElement)&&c(r.activeElement),n=!0)}function m(T){n=!1}function d(T){s(T.target)&&(n||f(T.target))&&c(T.target)}function h(T){s(T.target)&&(T.target.classList.contains("focus-visible")||T.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(T.target))}function v(T){document.visibilityState==="hidden"&&(o&&(n=!0),B())}function B(){document.addEventListener("mousemove",z),document.addEventListener("mousedown",z),document.addEventListener("mouseup",z),document.addEventListener("pointermove",z),document.addEventListener("pointerdown",z),document.addEventListener("pointerup",z),document.addEventListener("touchmove",z),document.addEventListener("touchstart",z),document.addEventListener("touchend",z)}function re(){document.removeEventListener("mousemove",z),document.removeEventListener("mousedown",z),document.removeEventListener("mouseup",z),document.removeEventListener("pointermove",z),document.removeEventListener("pointerdown",z),document.removeEventListener("pointerup",z),document.removeEventListener("touchmove",z),document.removeEventListener("touchstart",z),document.removeEventListener("touchend",z)}function z(T){T.target.nodeName&&T.target.nodeName.toLowerCase()==="html"||(n=!1,re())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),B(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var un=Ht(Sr=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},a=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(re,z){d.append(z,re)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(T){throw new Error("URL unable to set base "+c+" due to "+T)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,B=!0,re=this;["append","delete","set"].forEach(function(T){var Ke=h[T];h[T]=function(){Ke.apply(h,arguments),v&&(B=!1,re.search=h.toString(),B=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var z=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==z&&(z=this.search,B&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},a=i.prototype,s=function(f){Object.defineProperty(a,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){s(f)}),Object.defineProperty(a,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(a,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr)});var Qr=Ht((Lt,Kr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Lt=="object"&&typeof Kr=="object"?Kr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Lt=="object"?Lt.ClipboardJS=r():t.ClipboardJS=r()})(Lt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return ki}});var a=i(279),s=i.n(a),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(O){return!1}}var d=function(O){var w=p()(O);return m("cut"),w},h=d;function v(j){var O=document.documentElement.getAttribute("dir")==="rtl",w=document.createElement("textarea");w.style.fontSize="12pt",w.style.border="0",w.style.padding="0",w.style.margin="0",w.style.position="absolute",w.style[O?"right":"left"]="-9999px";var k=window.pageYOffset||document.documentElement.scrollTop;return w.style.top="".concat(k,"px"),w.setAttribute("readonly",""),w.value=j,w}var B=function(O,w){var k=v(O);w.container.appendChild(k);var F=p()(k);return m("copy"),k.remove(),F},re=function(O){var w=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},k="";return typeof O=="string"?k=B(O,w):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?k=B(O.value,w):(k=p()(O),m("copy")),k},z=re;function T(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?T=function(w){return typeof w}:T=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},T(j)}var Ke=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},w=O.action,k=w===void 0?"copy":w,F=O.container,q=O.target,Le=O.text;if(k!=="copy"&&k!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&T(q)==="object"&&q.nodeType===1){if(k==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(k==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Le)return z(Le,{container:F});if(q)return k==="cut"?h(q):z(q,{container:F})},We=Ke;function Ie(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(w){return typeof w}:Ie=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},Ie(j)}function Ti(j,O){if(!(j instanceof O))throw new TypeError("Cannot call a class as a function")}function nn(j,O){for(var w=0;w0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof F.action=="function"?F.action:this.defaultAction,this.target=typeof F.target=="function"?F.target:this.defaultTarget,this.text=typeof F.text=="function"?F.text:this.defaultText,this.container=Ie(F.container)==="object"?F.container:document.body}},{key:"listenClick",value:function(F){var q=this;this.listener=c()(F,"click",function(Le){return q.onClick(Le)})}},{key:"onClick",value:function(F){var q=F.delegateTarget||F.currentTarget,Le=this.action(q)||"copy",Rt=We({action:Le,container:this.container,target:this.target(q),text:this.text(q)});this.emit(Rt?"success":"error",{action:Le,text:Rt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(F){return yr("action",F)}},{key:"defaultTarget",value:function(F){var q=yr("target",F);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(F){return yr("text",F)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(F){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return z(F,q)}},{key:"cut",value:function(F){return h(F)}},{key:"isSupported",value:function(){var F=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof F=="string"?[F]:F,Le=!!document.queryCommandSupported;return q.forEach(function(Rt){Le=Le&&!!document.queryCommandSupported(Rt)}),Le}}]),w}(s()),ki=Ri},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,f){for(;s&&s.nodeType!==o;){if(typeof s.matches=="function"&&s.matches(f))return s;s=s.parentNode}}n.exports=a},438:function(n,o,i){var a=i(828);function s(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?s.apply(null,arguments):typeof m=="function"?s.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return s(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=a(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(n,o,i){var a=i(879),s=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(h))throw new TypeError("Third argument must be a Function");if(a.node(m))return c(m,d,h);if(a.nodeList(m))return u(m,d,h);if(a.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return s(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),a=f.toString()}return a}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,a,s){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var f=this;function c(){f.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=s.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var is=/["'&<>]/;Jo.exports=as;function as(e){var t=""+e,r=is.exec(t);if(!r)return t;var n,o="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],a;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(s){a={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||s(m,d)})})}function s(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof Xe?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){s("next",m)}function u(m){s("throw",m)}function p(m,d){m(d),i.shift(),i.length&&s(i[0][0],i[0][1])}}function mn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof xe=="function"?xe(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(a){return new Promise(function(s,f){a=e[i](a),o(s,f,a.done,a.value)})}}function o(i,a,s,f){Promise.resolve(f).then(function(c){i({value:c,done:s})},a)}}function A(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var $t=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Fe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=xe(a),f=s.next();!f.done;f=s.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var u=this.initialTeardown;if(A(u))try{u()}catch(v){i=v instanceof $t?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=xe(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{dn(h)}catch(v){i=i!=null?i:[],v instanceof $t?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new $t(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)dn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Or=Fe.EMPTY;function It(e){return e instanceof Fe||e&&"closed"in e&&A(e.remove)&&A(e.add)&&A(e.unsubscribe)}function dn(e){A(e)?e():e.unsubscribe()}var Ae={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,a=o.isStopped,s=o.observers;return i||a?Or:(this.currentObservers=null,s.push(r),new Fe(function(){n.currentObservers=null,De(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,a=n.isStopped;o?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new U;return r.source=this,r},t.create=function(r,n){return new wn(r,n)},t}(U);var wn=function(e){ne(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Or},t}(E);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ne(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,a=n._infiniteTimeWindow,s=n._timestampProvider,f=n._windowTime;o||(i.push(r),!a&&i.push(s.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,a=o._buffer,s=a.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var a=r.actions;n!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Ut);var On=function(e){ne(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Wt);var we=new On(Tn);var R=new U(function(e){return e.complete()});function Dt(e){return e&&A(e.schedule)}function kr(e){return e[e.length-1]}function Qe(e){return A(kr(e))?e.pop():void 0}function Se(e){return Dt(kr(e))?e.pop():void 0}function Vt(e,t){return typeof kr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function zt(e){return A(e==null?void 0:e.then)}function Nt(e){return A(e[ft])}function qt(e){return Symbol.asyncIterator&&A(e==null?void 0:e[Symbol.asyncIterator])}function Kt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ki(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Qt=Ki();function Yt(e){return A(e==null?void 0:e[Qt])}function Gt(e){return ln(this,arguments,function(){var r,n,o,i;return Pt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,Xe(r.read())];case 3:return n=a.sent(),o=n.value,i=n.done,i?[4,Xe(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,Xe(o)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Bt(e){return A(e==null?void 0:e.getReader)}function $(e){if(e instanceof U)return e;if(e!=null){if(Nt(e))return Qi(e);if(pt(e))return Yi(e);if(zt(e))return Gi(e);if(qt(e))return _n(e);if(Yt(e))return Bi(e);if(Bt(e))return Ji(e)}throw Kt(e)}function Qi(e){return new U(function(t){var r=e[ft]();if(A(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Yi(e){return new U(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?_(function(o,i){return e(o,i,n)}):me,Oe(1),r?He(t):zn(function(){return new Xt}))}}function Nn(){for(var e=[],t=0;t=2,!0))}function fe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new E}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,f=s===void 0?!0:s;return function(c){var u,p,m,d=0,h=!1,v=!1,B=function(){p==null||p.unsubscribe(),p=void 0},re=function(){B(),u=m=void 0,h=v=!1},z=function(){var T=u;re(),T==null||T.unsubscribe()};return g(function(T,Ke){d++,!v&&!h&&B();var We=m=m!=null?m:r();Ke.add(function(){d--,d===0&&!v&&!h&&(p=jr(z,f))}),We.subscribe(Ke),!u&&d>0&&(u=new et({next:function(Ie){return We.next(Ie)},error:function(Ie){v=!0,B(),p=jr(re,o,Ie),We.error(Ie)},complete:function(){h=!0,B(),p=jr(re,a),We.complete()}}),$(T).subscribe(u))})(c)}}function jr(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function V(e,t=document){let r=se(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function se(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),N(e===_e()),Y())}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function Yn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,we),l(()=>Be(e)),N(Be(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,we),l(()=>rr(e)),N(rr(e)))}var Bn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!zr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),xa?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!zr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ya.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Jn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Zn=typeof WeakMap!="undefined"?new WeakMap:new Bn,eo=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=Ea.getInstance(),n=new Ra(t,r,this);Zn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){eo.prototype[e]=function(){var t;return(t=Zn.get(this))[e].apply(t,arguments)}});var ka=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:eo}(),to=ka;var ro=new E,Ha=I(()=>H(new to(e=>{for(let t of e)ro.next(t)}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){return Ha.pipe(S(t=>t.observe(e)),x(t=>ro.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(()=>de(e)))),N(de(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var no=new E,Pa=I(()=>H(new IntersectionObserver(e=>{for(let t of e)no.next(t)},{threshold:0}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function sr(e){return Pa.pipe(S(t=>t.observe(e)),x(t=>no.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function oo(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=de(e),o=bt(e);return r>=o.height-n.height-t}),Y())}var cr={drawer:V("[data-md-toggle=drawer]"),search:V("[data-md-toggle=search]")};function io(e){return cr[e].checked}function qe(e,t){cr[e].checked!==t&&cr[e].click()}function je(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),N(t.checked))}function $a(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ia(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(N(!1))}function ao(){let e=b(window,"keydown").pipe(_(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:io("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),_(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!$a(n,r)}return!0}),fe());return Ia().pipe(x(t=>t?R:e))}function Me(){return new URL(location.href)}function ot(e){location.href=e.href}function so(){return new E}function co(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)co(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)co(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function fo(){return location.hash.substring(1)}function uo(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Fa(){return b(window,"hashchange").pipe(l(fo),N(fo()),_(e=>e.length>0),J(1))}function po(){return Fa().pipe(l(e=>se(`[id="${e}"]`)),_(e=>typeof e!="undefined"))}function Nr(e){let t=matchMedia(e);return Zt(r=>t.addListener(()=>r(t.matches))).pipe(N(t.matches))}function lo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(N(e.matches))}function qr(e,t){return e.pipe(x(r=>r?t():R))}function ur(e,t={credentials:"same-origin"}){return ve(fetch(`${e}`,t)).pipe(ce(()=>R),x(r=>r.status!==200?Tt(()=>new Error(r.statusText)):H(r)))}function Ue(e,t){return ur(e,t).pipe(x(r=>r.json()),J(1))}function mo(e,t){let r=new DOMParser;return ur(e,t).pipe(x(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),J(1))}function pr(e){let t=M("script",{src:e});return I(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(x(()=>Tt(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),C(()=>document.head.removeChild(t)),Oe(1))))}function ho(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function bo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(ho),N(ho()))}function vo(){return{width:innerWidth,height:innerHeight}}function go(){return b(window,"resize",{passive:!0}).pipe(l(vo),N(vo()))}function yo(){return Q([bo(),go()]).pipe(l(([e,t])=>({offset:e,size:t})),J(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(X("size")),o=Q([n,r]).pipe(l(()=>Be(e)));return Q([r,t,o]).pipe(l(([{height:i},{offset:a,size:s},{x:f,y:c}])=>({offset:{x:a.x-f,y:a.y-c+i},size:s})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(a=>{let s=document.createElement("script");s.src=i,s.onload=a,document.body.appendChild(s)})),Promise.resolve())}var r=class{constructor(n){this.url=n,this.onerror=null,this.onmessage=null,this.onmessageerror=null,this.m=a=>{a.source===this.w&&(a.stopImmediatePropagation(),this.dispatchEvent(new MessageEvent("message",{data:a.data})),this.onmessage&&this.onmessage(a))},this.e=(a,s,f,c,u)=>{if(s===this.url.toString()){let p=new ErrorEvent("error",{message:a,filename:s,lineno:f,colno:c,error:u});this.dispatchEvent(p),this.onerror&&this.onerror(p)}};let o=new EventTarget;this.addEventListener=o.addEventListener.bind(o),this.removeEventListener=o.removeEventListener.bind(o),this.dispatchEvent=o.dispatchEvent.bind(o);let i=document.createElement("iframe");i.width=i.height=i.frameBorder="0",document.body.appendChild(this.iframe=i),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + +

introduction | detailed information | Configure & Build | API description | Custom log formatting

+

Configure, build, package, install and test g3log

+

Example Project with g3log

+

An example project integration of g3log, both statially and dynamically built can be found at g3log_example_integration

+

Building it standalone to try out is as easy as:

+
git clone https://github.com/KjellKod/g3log
+cd g3log
+mkdir build
+cd build
+
+

Prerequisites

+

You also need these tools to build g3log from source: +- CMake (Required)

+

g3log uses CMake as a one-stop solution for configuring, building, installing, packaging and testing on Windows, Linux and OSX.

+
    +
  • Git (Optional but Recommended)
  • +
+

When building g3log it uses git to calculate the software version from the commit history of this repository. If you don't want that, or your setup does not have access to git, or you download g3log source archive from the GitHub Releases page so that you do not have the commit history downloaded, you can instead pass in the version as part of the CMake build arguments. See this issue for more information. + cmake -DVERSION=1.3.2 ..

+

Configuration Options

+

g3log provides following CMake options (and default values):

+
$ cmake -LAH # List non-advanced cached variables. See `cmake --help` for more details.
+
+...
+
+// Fatal (fatal-crashes/contract) examples
+ADD_FATAL_EXAMPLE:BOOL=ON
+
+// g3log performance test
+ADD_G3LOG_BENCH_PERFORMANCE:BOOL=OFF
+
+// g3log unit tests
+ADD_G3LOG_UNIT_TEST:BOOL=OFF
+
+// Use DBUG logging level instead of DEBUG.
+// By default DEBUG is the debugging level
+CHANGE_G3LOG_DEBUG_TO_DBUG:BOOL=OFF
+
+
+// Windows only: Use __FUNCSIG__ instead of the default __FUNCTION__ 
+// to show LOG function location
+// WARNING: using this in heavily templated code, like boost can expand
+// the function name into massive size
+WINDOWS_FUNCSIG:BOOL=OFF
+
+
+// gcc/clang only: Use __PRETTY_FUNCTION__ instead of the default __FUNCTION__ 
+// to show LOG function location
+// WARNING: using this in heavily templated code, like boost can expand
+// the function name into massive size
+PRETTY_FUNCTION:BOOL=OFF
+
+
+// Specifies the build type on single-configuration generators.
+// Possible values are empty, Debug, Release, RelWithDebInfo, MinSizeRel, …
+CMAKE_BUILD_TYPE:STRING=
+
+// Install path prefix, prepended onto install directories.
+// This variable defaults to /usr/local on UNIX
+// and c:/Program Files/${PROJECT_NAME} on Windows.
+CMAKE_INSTALL_PREFIX:PATH=
+
+// The prefix used in the built package.
+// On Linux, if this option is not set:
+// 1) If CMAKE_INSTALL_PREFIX is given, then it will be
+//    set with the value of CMAKE_INSTALL_PREFIX by g3log.
+// 2) Otherwise, it will be set as /usr/local by g3log.
+CPACK_PACKAGING_INSTALL_PREFIX:PATH=
+
+// Enable Visual Studio break point when receiving a fatal exception.
+// In __DEBUG mode only
+DEBUG_BREAK_AT_FATAL_SIGNAL:BOOL=OFF
+
+// Vectored exception / crash handling with improved stack trace
+ENABLE_FATAL_SIGNALHANDLING:BOOL=ON
+
+// Vectored exception / crash handling with improved stack trace
+ENABLE_VECTORED_EXCEPTIONHANDLING:BOOL=ON
+
+// iOS version of library.
+G3_IOS_LIB:BOOL=OFF
+
+// Log full filename
+G3_LOG_FULL_FILENAME:BOOL=OFF
+
+// Build shared library
+G3_SHARED_LIB:BOOL=ON
+
+// Build shared runtime library MSVC
+G3_SHARED_RUNTIME:BOOL=ON
+
+// Turn ON/OFF log levels.
+// An disabled level will not push logs of that level to the sink.
+// By default dynamic logging is disabled
+USE_DYNAMIC_LOGGING_LEVELS:BOOL=OFF
+
+// Use dynamic memory for message buffer during log capturing
+USE_G3_DYNAMIC_MAX_MESSAGE_SIZE:BOOL=OFF
+
+...
+
+

For additional option context and comments please also see Options.cmake

+

If you want to leave everything as it was, then you should:

+
cmake ..
+
+

You may also specify one or more of those options listed above from the command line. +For example, on Windows:

+
cmake .. -G "Visual Studio 15 2017"
+         -DG3_SHARED_LIB=OFF
+         -DCMAKE_INSTALL_PREFIX=C:/g3log
+         -DADD_G3LOG_UNIT_TEST=ON
+         -DADD_FATAL_EXAMPLE=OFF
+
+

will use a Visual Studio 2017 solution generator, build g3log as a static library, headers and libraries will be installed to C:\g3log when installed from source, enable unit testing, but do not build fatal example.

+

MinGW users on Windows may find they should use a different generator:

+
cmake .. -G "MinGW Makefiles"
+
+

By default, headers and libraries will be installed to /usr/local on Linux when installed from build tree via make install. You may overwrite it by:

+
cmake .. -DCMAKE_INSTALL_PREFIX=/usr
+
+

This will install g3log to /usr instead of /usr/local.

+

Linux/OSX package maintainers may be interested in the CPACK_PACKAGING_INSTALL_PREFIX. For example:

+
cmake .. -DCPACK_PACKAGING_INSTALL_PREFIX=/usr/local
+
+

Build Commands

+

Once the configuration is done, you may build g3log with:

+
# Suppose you are still in the `build` directory. I won't repeat it anymore!
+cmake --build . --config Release
+
+

You may also build it with a system-specific way.

+

On Linux, OSX and MinGW:

+
make
+
+

On Windows:

+
msbuild g3log.sln /p:Configuration=Release
+
+

Windows users can also open the generated Visual Studio solution file and build it happily.

+

Installation

+

Install from source in a CMake way:

+
cmake --build . --target install
+
+

Linux users may also use:

+
sudo make install
+
+

You may also create a package first and install g3log with it. See the next section.

+

Packaging

+

A CMake way:

+
cmake --build . --config Release --target package
+
+

or

+
cpack -C Release
+
+

if the whole library has been built in the previous step. +It will generate a ZIP package on Windows, and a DEB package on Linux.

+

Linux users may also use a Linux way:

+
make package
+
+

If you want to use a different package generator, you should specify a -G option.

+

On Windows:

+
cpack -C Release -G NSIS;7Z
+
+

this will create a installable NSIS package and a 7z package.

+

Note: To use the NSIS generator, you should install NSIS first.

+

On Linux:

+
cpack -C Release -G TGZ
+
+

this will create a .tar.gz archive for you.

+

Once done, you may install or uncompress the package file to the target machine. For example, on Debian or Ubuntu:

+
sudo dpkg -i g3log-<version>-Linux.deb
+
+

will install the g3log library to CPACK_PACKAGING_INSTALL_PREFIX.

+

Testing

+

By default, tests will be built. To disable unit testing, you should turn off ADD_G3LOG_UNIT_TEST.

+

Suppose the build process has completed, then you can run the tests with:

+
ctest -C Release
+
+

or:

+
make test
+
+

for Linux users. +or for a detailed gtest output of all the tests:

+
cd build;
+../scripts/runAllTests.sh
+
+

CMake module

+

g3log comes with a CMake module. Once installed, it can be found under ${CMAKE_INSTALL_PREFIX}/lib/cmake/g3log. Users can use g3log in a CMake-based project this way:

+
find_package(g3log CONFIG REQUIRED)
+target_link_libraries(main PRIVATE g3log)
+
+

To make sure that CMake can find g3log, you also need to tell CMake where to search for it:

+
cmake .. -DCMAKE_PREFIX_PATH=<g3log's install prefix>
+
+

Build Options

+

The build options are defined in the file Options.cmake

+

build options are generated and saved to a header file. This avoid having to set the define options in the client source code

+

introduction | detailed information | Configure & Build | API description | Custom log formatting

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/codespaces/index.html b/codespaces/index.html new file mode 100644 index 00000000..73e0649d --- /dev/null +++ b/codespaces/index.html @@ -0,0 +1,439 @@ + + + + + + + + + + + + + + + + + + + + + + Codespaces - TEST G3log, an asynchronous "crash-safe" logger + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + +

Codespaces

+

You can experiment with codespaces and g3log.

+

Learn about Github Codespaces

+

For an introduction to codespaces you can check out example c++ codespace and using-github-codespaces-with-github-cli

+

Commandline codespaces Quick Reference

+
    +
  1. List all your codespaces gh codespace list
  2. +
  3. Create a new codespace gh codespace create -r OWNER/REPO_NAME [-b BRANCH]. Ref docs/github: Creating a codespace for a repository
  4. +
  5. View codebase details gh codespace view
  6. +
  7. Stop gh codespace stop -c CODESPACE-NAME
  8. +
  9. Delete gh codespace delete -c CODESPACE-NAME
  10. +
  11. Rebuild gh codespace rebuild
  12. +
  13. Rename gh codespace edit -c CODESPACE-NAME -d DISPLAY-NAME
  14. +
  15. SSH into REMOTE codespace gh codespace ssh -c CODESPACE-NAME
  16. +
  17. Open a remote codespace in CVisual Studio gh codespace code -c CODESPACE-NAME (ref: github:doc cs studio)
  18. +
  19. Copy local file to/from codespace gh codespace cp [-r] SOURCE(S) DESTINATION. Example: Copy a file from the local machine to the $HOME directory of a codespace: gh codespace cp myfile.txt remote:. Example Copy a file from a codespace to the current directory on the local machine: gh codespace cp remote:myfile.txt . (more information available here)
  20. +
+

Try g3log in a local dev container.

+

Please note that this will build g3log as if it's on a Debian Linux platform.

+
    +
  1. Clone this repository to your local filesystem.
  2. +
  3. Start Visual Studio Code. Press F1 and select the Dev Containers: Open Folder in Container... command.
  4. +
  5. Select the cloned copy of this g3log folder, wait for the container to start, and try things out! You should have debian C++ environment at hand.
  6. +
+

Example cmake configuration and build

+
Open a terminal in Visual Studio Code
+mkdir debianbuild
+cd debianbuild
+cmake -DADD_G3LOG_UNIT_TEST=ON -DADD_G3LOG_BENCH_PERFORMANCE=ON ..
+make -j 
+
+

Example runs

+
    +
  1. performance test in the container ./g3log-performance-threaded_mean 4
  2. +
  3. unit tests ctest -v
  4. +
  5. Try a fatal example with dumped stack trace ./g3log-FATAL-contract
  6. +
+

Example with Debugging.

+

Without any need to set up environment on your local machine you can also use Codespaces to debug examples, unit tests etc of g3log. +The pesky thing with VSCode, especially with cmake is to set up the launh.json. +It's a little bit easier if you open a VSCode terminal and do the cmake configuration and build there. Then the launch.json only needs to +contain information about the pecific executable.

+

Here we try out the g3log-FATAL-contract after cmake configure with -DCMAKE_BUILD_TYPE=Debug

+
{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    // Remember to build the specific part of cmake with 
+    // "cmake -DCMAKE_BUILD_TYPE=Debug" if you want to be able to debug it. 
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "(gdb) Start",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/build/g3log-FATAL-contract",
+            "MIMode": "gdb",
+            "cwd": "${workspaceFolder}/build"
+            "setupCommands": [
+                {
+                    "description": "Enable pretty-printing for gdb",
+                    "text": "-enable-pretty-printing",
+                    "ignoreFailures": true
+                },
+                {
+                    "description": "Set Disassembly Flavor to Intel",
+                    "text": "-gdb-set disassembly-flavor intel",
+                    "ignoreFailures": true
+                }
+            ]
+        }
+
+    ]
+}
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/contributing/index.html b/contributing/index.html new file mode 100644 index 00000000..c6bf945d --- /dev/null +++ b/contributing/index.html @@ -0,0 +1,470 @@ + + + + + + + + + + + + + + + + + + + + + + + + License and contribution - TEST G3log, an asynchronous "crash-safe" logger + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/event_sequence.png b/event_sequence.png new file mode 100644 index 00000000..dac693ca Binary files /dev/null and b/event_sequence.png differ diff --git a/fatal_log_sequence.png b/fatal_log_sequence.png new file mode 100644 index 00000000..f2a3eef0 Binary files /dev/null and b/fatal_log_sequence.png differ diff --git a/fatal_signal_sequence.png b/fatal_signal_sequence.png new file mode 100644 index 00000000..1f635328 Binary files /dev/null and b/fatal_signal_sequence.png differ diff --git a/g3log_usage/index.html b/g3log_usage/index.html new file mode 100644 index 00000000..4138a84c --- /dev/null +++ b/g3log_usage/index.html @@ -0,0 +1,631 @@ + + + + + + + + + + + + + + + + + + + + + + G3log usage - TEST G3log, an asynchronous "crash-safe" logger + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + +

introduction | detailed information | Configure & Build | API description | Custom log formatting

+

How to use g3log

+

G3log is an asynchronous logger with dynamic sinks

+

Example USAGE

+

Optional to use either streaming or printf-like syntax

+
LOG(INFO) << "streaming API is as easy as ABC or " << 123;
+
+LOGF(WARNING, "Printf-style syntax is also %s", "available");
+
+

What g3Log is

+
    +
  • G3log is the acting name for the third version of g2log and it stands for g3log with dynamic sinks
  • +
  • G3log is an asynchronous, "crash-safe" logger. You can read more about it here [g2log version]
  • +
  • You can choose to use the default log receiver which saves all LOG calls to file, or you can choose to use your own custom made log receiver(s), or both, or as many sinks as you need.
  • +
+

Conditional logging

+
int less = 1; int more = 2
+LOG_IF(INFO, (less<more)) <<"If [true], then this text will be logged";
+
+// or with printf-like syntax
+LOGF_IF(INFO, (less<more), "if %d<%d then this text will be logged", less,more);
+
+

Design-by-Contract

+

CHECK(false) will trigger a "fatal" message. It will be logged, and then the +application will exit.

+
CHECK(less != more); // not FATAL
+CHECK(less > more) << "CHECK(false) triggers a FATAL message";
+
+

Detailed API documentation

+

Please look at API.md for detailed API documentation

+

Benefits you get when using g3log

+
    +
  1. +

    Easy to use, clean syntax and a blazing fast logger.

    +
  2. +
  3. +

    All the slow log I/O disk access is done in a background thread. This ensures that the LOG caller can immediately continue with other tasks and do not have to wait for the LOG call to finish.

    +
  4. +
  5. +

    G3log provides logging, Design-by-Contract [#CHECK], and flush of log to file at + shutdown. Buffered logs will be written to the sink before the application shuts down.

    +
  6. +
  7. +

    It is thread safe, so using it from multiple threads is completely fine.

    +
  8. +
  9. +

    It is CRASH SAFE. It will save the made logs to the sink before it shuts down. +The logger will catch certain fatal events (Linux/OSX: signals, Windows: fatal OS exceptions and signals) , so if your application crashes due to, say a segmentation fault, SIGSEGV, it will log and save the crash and all previously buffered log entries before exiting.

    +
  10. +
  11. +

    It is cross platform. Tested and used by me or by clients on OSX, Windows, Ubuntu, CentOS

    +
  12. +
  13. +

    G3log and G2log are used worldwide in commercial products as well as hobby projects. G2log was introduced in early 2011 and is now retired.

    +
  14. +
  15. +

    The code is given for free as public domain. This gives the option to change, use, and do whatever with it, no strings attached.

    +
  16. +
  17. +

    g3log : is made to facilitate easy adding of custom log receivers. Its tested on at least the following platforms with Linux(Clang/gcc), Windows (mingw, visual studio) and OSX. My recommendation is to go with g3log if you have full C++17 support

    +
  18. +
+

C++11 support up to version: https://github.com/KjellKod/g3log/releases/tag/1.3.1).

+

C++14 support up to version: https://github.com/KjellKod/g3log/releases/tag/1.3.4

+

G3log with sinks

+

Sinks are receivers of LOG calls. G3log comes with a default sink (the same as g3log uses) that can be used to save log to file. A sink can be of any class type without restrictions as long as it can either receive a LOG message as a std::string or as a g3::LogMessageMover.

+

The std::string comes pre-formatted. The g3::LogMessageMover is a wrapped struct that contains the raw data for custom handling in your own sink.

+

A sink is owned by the g3log and is added to the logger inside a std::unique_ptr. The sink can be called though its public API through a handler which will asynchronously forward the call to the receiving sink.

+

It is crazy simple to create a custom sink. This example show what is needed to make a custom sink that is using custom log formatting but only using that +for adding color to the default log formatting. The sink forwards the colored log to cout

+
// in file Customsink.hpp
+#pragma once
+#include <string>
+#include <iostream>
+#include <g3log/logmessage.hpp>
+
+struct CustomSink {
+
+// Linux xterm color
+// http://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal
+  enum FG_Color {YELLOW = 33, RED = 31, GREEN=32, WHITE = 97};
+
+  FG_Color GetColor(const LEVELS level) const {
+     if (level.value == WARNING.value) { return YELLOW; }
+     if (level.value == DEBUG.value) { return GREEN; }
+     if (g3::internal::wasFatal(level)) { return RED; }
+
+     return WHITE;
+  }
+
+  void ReceiveLogMessage(g3::LogMessageMover logEntry) {
+     auto level = logEntry.get()._level;
+     auto color = GetColor(level);
+
+     std::cout << "\033[" << color << "m"
+       << logEntry.get().toString() << "\033[m" << std::endl;
+  }
+};
+
+// in main.cpp, main() function
+
+auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),
+                                     &CustomSink::ReceiveLogMessage);
+
+
+

Adding and Removing Sinks

+

You can safely remove and add sinks during the running of your program.

+

Keep in mind

+
    +
  • Initialization of the logger should happen before you have started any other threads that may call the logger.
  • +
  • Destruction of the logger (RAII concept) should happen AFTER shutdown of other threads that are calling the logger.
  • +
+

Adding Sinks

+
   auto sinkHandle1 = logworker->addSink(std::make_unique<CustomSink>(),
+                                          &CustomSink::ReceiveLogMessage);
+   auto sinkHandle2 = logworker->addDefaultLogger(argv[0],
+                                              path_to_log_file);
+   logworker->removeSink(std::move(sinkHandle1)); // this will in a thread-safe manner remove the sinkHandle1
+   logworker->removeAllSinks(); // this will in a thread-safe manner remove any sinks. 
+
+

More sinks can be found in the repository github.com/KjellKod/g3sinks.

+

Code Examples

+

Example usage where a custom sink is added. A function is called though the sink handler to the actual sink object.

+
// main.cpp
+#include <g3log/g3log.hpp>
+#include <g3log/logworker.hpp>
+#include <memory>
+
+#include "CustomSink.h"
+
+int main(int argc, char**argv) {
+   using namespace g3;
+   std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };
+   auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),
+                                          &CustomSink::ReceiveLogMessage);
+
+   // initialize the logger before it can receive LOG calls
+   initializeLogging(logworker.get());
+   LOG(WARNING) << "This log call, may or may not happend before"
+                << "the sinkHandle->call below";
+
+
+   // You can call in a thread safe manner public functions on your sink
+   // The call is asynchronously executed on your custom sink.
+   std::future<void> received = sinkHandle->call(&CustomSink::Foo,
+                                                 param1, param2);
+
+   // If the LogWorker is initialized then at scope exit the g3::internal::shutDownLogging() will be called.
+   // This is important since it protects from LOG calls from static or other entities that will go out of
+   // scope at a later time.
+   //
+   // It can also be called manually:
+   g3::internal::shutDownLogging();
+}
+
+
+// some_file.cpp : To show how easy it is to get the logger to work
+// in other parts of your software
+
+#include <g3log/g3log.hpp>
+
+void SomeFunction() {
+   ...
+   LOG(INFO) << "Hello World";
+}
+
+

Example usage where a the default file logger is used and a custom sink is added

+
// main.cpp
+#include <g3log/g3log.hpp>
+#include <g3log/logworker.hpp>
+#include <memory>
+
+#include "CustomSink.h"
+
+int main(int argc, char**argv) {
+   using namespace g3;
+   auto worker = LogWorker::createLogWorker();
+   auto defaultHandler = worker->addDefaultLogger(argv[0],
+                                                 path_to_log_file);
+
+   // logger is initialized
+   g3::initializeLogging(worker.get());
+
+   LOG(DEBUG) << "Make log call, then add another sink";
+
+   worker->addSink(std::make_unique<CustomSink>(),
+                                  &CustomSink::ReceiveLogMessage);
+
+   ...
+}
+
+

introduction | detailed information | Configure & Build | API description | Custom log formatting

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..34518f12 --- /dev/null +++ b/index.html @@ -0,0 +1,687 @@ + + + + + + + + + + + + + + + + + + + + + + + + TEST G3log, an asynchronous "crash-safe" logger + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + +

introduction | detailed information | Configure & Build | API description | Custom log formatting

+

Welcome to g3log

+

G3log is an asynchronous logger with three main features: +1. Intuitive LOG(...) API +2. Design-by-contract CHECK(...) functionality +3. Fatal crash handling for graceful shutdown of the logged process without loosing any log details up to the point of the crash.

+

The super quick introduction to g3log can be seen in the steps 1 - 9 below.

+

For more in-depth information please see the full usage description in g3log_usage.md. The internal API for more advanced integration with g3log can be accessed in API.md

+

1. Easy usage in files

+

Avoid deep dependency injection complexity and instead get access to the logger as easy as

+
#include <g3log/g3log.hpp>
+
+

2. Access to streaming and print_f log call syntax

+

Both streaming syntax LOG and print_f LOGF syntax are available.

+
LOGF(INFO, "Hi log %d", 123);
+LOG(INF) << "Hi log " << 123;
+
+
+

3. Conditional logging

+
LOG_IF(INFO, (1 < 2)) << "If true this message will be logged";
+LOGF_IF(INFO, (1 < 2), "If true, then this %s will be logged", "message");
+
+

4. Design-by-contract framework

+
CHECK(less != more); // not fatal
+CHECK_F(less > more, "CHECK(false) will trigger a fatal message")
+
+

5. Handling of fatal

+

By default g3log will capture fatal events such as LOG(FATAL), CHECK(false) and otherwise fatal signals such as:

+
    SIGABRT
+    SIGFPE
+    SIGILL
+    SIGSEGV
+    SIGTERM
+
+

When a fatal event happens the not-yet written log activity will be flushed to the logging sinks. Only when all logging activity up to the point of the fatal event has happend, will g3log allow the fatal event to proceed and exit the process.

+

If object symbols are available the fatal handler will attempt to push the stacktrace up to the fatal reason to the logging sink.

+

5b. Overriding and customization of fatal event handling

+

For overriding fatal error handling to use your own, or to add code hooks that you want to execute please see the API.md doc.

+

6. Default and Custom logging levels

+

The default logging levels are DEBUG, INFO, WARNING and FATAL. You can define your own logging levels or completely replace the logging levels. Ref: API.md

+

7. Log filtering

+

Log filtering is handled in g3log if dynamic logging levels are enabled +in the configuration. See the API.md for information. Log filtering can also be handled through the sink as can be seen in github/Kjellod/g3sinks

+

8. 3rd party and custom logging sinks

+

The default logging sink has no external 3rd party dependencies. For more logging sinks please see github/Kjellod/g3sinks

+
    +
  • log rotate
  • +
  • log to syslog
  • +
  • log to colored terminal output
  • +
  • log rotate with filter
  • +
+

See the API.md for more information about the simple steps to creating your own logging sink.

+

9. Log instantiation

+

With the default application name left as is (i.e. "g3log") a creation of the logger could look something like this:

+
  const std::string directory = "./";
+  const std::string name = "TestLogFile";
+  auto worker = g3::LogWorker::createLogWorker();
+  auto handle = worker->addDefaultLogger(name, directory);
+
+

The resulting filename would be something like:

+
   ./TestLogFile.g3log.20160217-001406.log
+
+

Performance

+

G3log aims to keep all background logging to sinks with as little log overhead as possible to the logging sink and with as small "worst case latency" as possible. For this reason g3log is a good logger for many systems that deal with critical tasks. Depending on platform the average logging overhead will differ. On my 2010 laptop the average call, when doing extreme performance testing, will be about ~2 us.

+

The worst case latency is kept stable with no extreme peaks, in spite of any sudden extreme pressure. I have a blog post regarding comparing worst case latency for g3log and other loggers which might be of interest. +You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/

+

Continuos Integration

+

The g3log repository is evaluating both github actions and CircleCI for executing test coverage, installation and document generation. For windows the repo is still relying on appveyor. In case you want to look into change any of these setups the following files are the ones of interest.

+
1. appveyor --> g3log/appveyor.yml
+2. circleCI --> g3log/.circleci/config.yml
+3. github actions --> g3log/.github/workflows/*.yml
+
+
+
+

Feedback

+

If you like this logger (or not) it would be nice with some feedback. That way I can improve g3log and it is always nice to hear when and how someone is using it.

+

If you have ANY questions or problems please do not hesitate in contacting me at + Hedstrom @ Kjellod. cc

+

Say Thanks

+

This logger is available for free and all of its source code is public domain. A great way of saying thanks is to send a donation. It would go a long way not only to show your support but also to boost continued development.

+

Donate

+
    +
  • $5 for a cup of coffee
  • +
  • $25 for a late evening coding with takeout
  • +
+

Cheers

+

Kjell (a.k.a. KjellKod)

+

introduction | detailed information | Configure & Build | API description | Custom log formatting

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/log_sequence.png b/log_sequence.png new file mode 100644 index 00000000..81baa1bc Binary files /dev/null and b/log_sequence.png differ diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 00000000..20924fc8 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Introduction to G3log","text":"

introduction | detailed information | Configure & Build | API description | Custom log formatting

"},{"location":"#welcome-to-g3log","title":"Welcome to g3log","text":"

G3log is an asynchronous logger with three main features: 1. Intuitive LOG(...) API 2. Design-by-contract CHECK(...) functionality 3. Fatal crash handling for graceful shutdown of the logged process without loosing any log details up to the point of the crash.

The super quick introduction to g3log can be seen in the steps 1 - 9 below.

For more in-depth information please see the full usage description in g3log_usage.md. The internal API for more advanced integration with g3log can be accessed in API.md

"},{"location":"#1-easy-usage-in-files","title":"1. Easy usage in files","text":"

Avoid deep dependency injection complexity and instead get access to the logger as easy as

#include <g3log/g3log.hpp>\n
"},{"location":"#2-access-to-streaming-and-print_f-log-call-syntax","title":"2. Access to streaming and print_f log call syntax","text":"

Both streaming syntax LOG and print_f LOGF syntax are available.

LOGF(INFO, \"Hi log %d\", 123);\nLOG(INF) << \"Hi log \" << 123;\n\n
"},{"location":"#3-conditional-logging","title":"3. Conditional logging","text":"
LOG_IF(INFO, (1 < 2)) << \"If true this message will be logged\";\nLOGF_IF(INFO, (1 < 2), \"If true, then this %s will be logged\", \"message\");\n
"},{"location":"#4-design-by-contract-framework","title":"4. Design-by-contract framework","text":"
CHECK(less != more); // not fatal\nCHECK_F(less > more, \"CHECK(false) will trigger a fatal message\")\n
"},{"location":"#5-handling-of-fatal","title":"5. Handling of fatal","text":"

By default g3log will capture fatal events such as LOG(FATAL), CHECK(false) and otherwise fatal signals such as:

    SIGABRT\n    SIGFPE\n    SIGILL\n    SIGSEGV\n    SIGTERM\n

When a fatal event happens the not-yet written log activity will be flushed to the logging sinks. Only when all logging activity up to the point of the fatal event has happend, will g3log allow the fatal event to proceed and exit the process.

If object symbols are available the fatal handler will attempt to push the stacktrace up to the fatal reason to the logging sink.

"},{"location":"#5b-overriding-and-customization-of-fatal-event-handling","title":"5b. Overriding and customization of fatal event handling","text":"

For overriding fatal error handling to use your own, or to add code hooks that you want to execute please see the API.md doc.

"},{"location":"#6-default-and-custom-logging-levels","title":"6. Default and Custom logging levels","text":"

The default logging levels are DEBUG, INFO, WARNING and FATAL. You can define your own logging levels or completely replace the logging levels. Ref: API.md

"},{"location":"#7-log-filtering","title":"7. Log filtering","text":"

Log filtering is handled in g3log if dynamic logging levels are enabled in the configuration. See the API.md for information. Log filtering can also be handled through the sink as can be seen in github/Kjellod/g3sinks

"},{"location":"#8-3rd-party-and-custom-logging-sinks","title":"8. 3rd party and custom logging sinks","text":"

The default logging sink has no external 3rd party dependencies. For more logging sinks please see github/Kjellod/g3sinks

  • log rotate
  • log to syslog
  • log to colored terminal output
  • log rotate with filter

See the API.md for more information about the simple steps to creating your own logging sink.

"},{"location":"#9-log-instantiation","title":"9. Log instantiation","text":"

With the default application name left as is (i.e. \"g3log\") a creation of the logger could look something like this:

  const std::string directory = \"./\";\n  const std::string name = \"TestLogFile\";\n  auto worker = g3::LogWorker::createLogWorker();\n  auto handle = worker->addDefaultLogger(name, directory);\n

The resulting filename would be something like:

   ./TestLogFile.g3log.20160217-001406.log\n
"},{"location":"#performance","title":"Performance","text":"

G3log aims to keep all background logging to sinks with as little log overhead as possible to the logging sink and with as small \"worst case latency\" as possible. For this reason g3log is a good logger for many systems that deal with critical tasks. Depending on platform the average logging overhead will differ. On my 2010 laptop the average call, when doing extreme performance testing, will be about ~2 us.

The worst case latency is kept stable with no extreme peaks, in spite of any sudden extreme pressure. I have a blog post regarding comparing worst case latency for g3log and other loggers which might be of interest. You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/

"},{"location":"#continuos-integration","title":"Continuos Integration","text":"

The g3log repository is evaluating both github actions and CircleCI for executing test coverage, installation and document generation. For windows the repo is still relying on appveyor. In case you want to look into change any of these setups the following files are the ones of interest.

1. appveyor --> g3log/appveyor.yml\n2. circleCI --> g3log/.circleci/config.yml\n3. github actions --> g3log/.github/workflows/*.yml\n\n\n
"},{"location":"#feedback","title":"Feedback","text":"

If you like this logger (or not) it would be nice with some feedback. That way I can improve g3log and it is always nice to hear when and how someone is using it.

If you have ANY questions or problems please do not hesitate in contacting me at Hedstrom @ Kjellod. cc

"},{"location":"#say-thanks","title":"Say Thanks","text":"

This logger is available for free and all of its source code is public domain. A great way of saying thanks is to send a donation. It would go a long way not only to show your support but also to boost continued development.

  • $5 for a cup of coffee
  • $25 for a late evening coding with takeout

Cheers

Kjell (a.k.a. KjellKod)

introduction | detailed information | Configure & Build | API description | Custom log formatting

"},{"location":"API/","title":"API description","text":"

introduction | detailed information | Configure & Build | API description | Custom log formatting

"},{"location":"API/#high-level-description-of-g3log","title":"High Level Description of g3log","text":"

The g3log logger is an asynchronous, crash-safe logging library designed for C++ applications. It allows for logging messages to various sinks without blocking the main application thread. Below is a high-level overview of how the g3log logger works.

"},{"location":"API/#asynchronous-logging","title":"Asynchronous Logging","text":"

The logger operates on a separate thread, ensuring that the main application thread is not blocked by I/O operations when logging messages. This is achieved by using a background worker (LogWorker) that queues log messages and processes them asynchronously.

"},{"location":"API/#logworker-and-sinks","title":"LogWorker and Sinks","text":"

The LogWorker is responsible for managing the logging sinks. A sink is an object that defines where and how log messages are outputted (e.g., to a file, console, or over the network). Users can add custom sinks to the LogWorker using the addSink method, which takes a unique pointer to a sink object and a function pointer to the method that will save the log message.

"},{"location":"API/#signal-handling","title":"Signal Handling","text":"

The logger includes a signal handler for Unix-like systems that captures fatal signals (e.g., SIGSEGV, SIGABRT) and ensures that all pending log messages are flushed to the sinks before the application exits. The signal handler function (signalHandler) is registered to handle these signals and will attempt to generate a stack trace when a fatal signal is received. This stack trace is then logged, providing valuable debugging information.

"},{"location":"API/#stack-trace-generation","title":"Stack Trace Generation","text":"

Upon receiving a fatal signal, the signalHandler function will call stackdump to generate a stack trace. This function uses platform-specific calls to retrieve the stack frames and attempts to demangle the function names to make the stack trace more readable.

"},{"location":"API/#log-message-formatting","title":"Log Message Formatting","text":"

Log messages can be formatted using either a streaming API (e.g., LOG(INFO) << \"message\";) or a printf-like syntax (e.g., LOGF(INFO, \"message %d\", value);). This provides flexibility in how messages are constructed.

"},{"location":"API/#log-levels","title":"Log Levels","text":"

The library supports various log levels (e.g., DEBUG, INFO, WARNING, FATAL). Users can define custom log levels or modify the existing ones. The log levels can be dynamically enabled or disabled at runtime if the G3_DYNAMIC_LOGGING preprocessor definition is set.

"},{"location":"API/#crash-safety","title":"Crash Safety","text":"

In the event of a crash, the logger is designed to be crash-safe by catching fatal events and ensuring that all log messages are flushed to the sinks before the process exits.

"},{"location":"API/#customization","title":"Customization","text":"

The library allows for extensive customization, including adding custom log levels, creating custom sinks, and overriding the default signal handling behavior.

"},{"location":"API/#thread-safety","title":"Thread Safety","text":"

The g3log logger is thread-safe, meaning it can be used from multiple threads without the need for additional synchronization.

"},{"location":"API/#public-domain-software","title":"Public Domain Software","text":"

The g3log code is released into the public domain, allowing users to use, modify, and distribute it freely without restrictions.

"},{"location":"API/#logging-and-fatal-events-explained","title":"Logging and Fatal Events Explained","text":"

diagrams created with https://mermaid.live

"},{"location":"API/#api-description","title":"API description","text":"

Most of the API that you need for using g3log is described in this readme. For more API documentation and examples please continue to read the API readme. Examples of what you will find here are:

"},{"location":"API/#logging-api-log-calls","title":"Logging API: LOG calls","text":"

LOG calls can follow streaming LOG(INFO) << \"some text\" or printf-like syntax LOGF(WARNING, \"some number %d\", 123);

Conditional logging is made with LOG_IF(INFO, <boolean-expression>) << \" some text\" or LOGF_IF(WARNING, <boolean-expression>) << \" some text\". Only if the expressions evaluates to true will the logging take place.

Example: LOG_IF(INFO, 1 != 200) << \" some text\"; or LOG_IF(FATAL, SomeFunctionCall()) << \" some text\";

A call using FATAL logging level, such as the LOG_IF(FATAL,...) example above, will after logging the message at FATALlevel also kill the process. It is essentially the same as a CHECK(<boolea-expression>) << ... with the difference that the CHECK(<boolean-expression) triggers when the expression evaluates to false.

"},{"location":"API/#contract-api-check-calls","title":"Contract API: CHECK calls","text":"

The contract API follows closely the logging API with CHECK(<boolean-expression>) << ... for streaming or (*) CHECKF(<boolean-expression>, ...); for printf-style.

If the <boolean-expression> evaluates to false then the the message for the failed contract will be logged in FIFO order with previously made messages. The process will then shut down after the message is sent to the sinks and the sinks have dealt with the fatal contract message.

CHECK_F(<boolean-expression>, ...); was the the previous API for printf-like CHECK. It is still kept for backwards compatability but is exactly the same as CHECKF

"},{"location":"API/#logfatal-or-checkfalse","title":"LOG(fATAL) or CHECK(false)","text":"

Fatal logging or failed `CHECK calls follows the same handling.

"},{"location":"API/#logging-levels","title":"Logging levels","text":"

The default logging levels are DEBUG, INFO, WARNING and FATAL (see FATAL usage above). The logging levels are defined in loglevels.hpp.

For some windows framework there is a clash with the DEBUG logging level. One of the CMake Build options can be used to then change offending default level from DEBUG TO DBUG.

CMake option: (default OFF) cmake -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON ..

### disable/enabled levels at runtime Logging levels can be disabled at runtime. The logic for this happens in loglevels.hpp, loglevels.cpp and g3log.hpp.

There is a cmake option to enable the dynamic enable/disable of levels. When the option is enabled there will be a slight runtime overhead for each LOG call when the enable/disable status is checked. For most intent and purposes this runtime overhead is negligable.

There is no runtime overhead for internally checking if a level is enabled//disabled if the cmake option is turned off. If the dynamic logging cmake option is turned off then all logging levels are enabled.

CMake option: (default OFF) cmake -DUSE_DYNAMIC_LOGGING_LEVELS=ON ..

### custom logging levels Custom logging levels can be created and used. When defining a custom logging level you set the value for it as well as the text for it. You can re-use values for other levels such as INFO, WARNING etc or have your own values. Any value with equal or higher value than the FATAL value will be considered a FATAL logging level.

To keep in mind when adding your own custom levels. 1. If the cmake option G3_DYNAMIC_LOGGING is enabled then you must use g3::only_change_at_initialization::addLogLevel(...) to give g3log a record of your logging level and if it is an enabled or disbled logging level. 1. If the cmake G3_DYNAMIC_LOGGING is turned OFF, then giving g3log a record of your logging level with 'addLogLevel(...) is not needed since no \"disbled/enabled\" check will happen - all logging levels will be considered enabled.

Example: ```cpp // In CustomLoggingLevels.hpp #include

// all values with a + 1 higher than their closest equivalet // they could really have the same value as well.

const LEVELS FYI {DEBUG.value + 1, {\"For Your Information\"}}; const LEVELS CUSTOM {INFO.value + 1, {\"CUSTOM\"}}; const LEVELS SEVERE {WARNING.value +1, {\"SEVERE\"}}; const LEVELS DEADLY {FATAL.value + 1, {\"DEADLY\"}}; ```

More examples can be viwed in the unit tests.

"},{"location":"API/#sink-creation-and-utilization","title":"Sink creation and utilization","text":"

The default sink for g3log is the one as used in g2log. It is a simple file sink with a limited API. The details for the default file sink can be found in filesink.hpp, filesink.cpp, filesinkhelper.ipp

More sinks can be found at g3sinks (log rotate, log rotate with filtering on levels)

A logging sink is not required to be a subclass of a specific type. The only requirement of a logging sink is that it can receive a logging message of

"},{"location":"API/#using-the-default-sink","title":"Using the default sink","text":"

Sink creation is defined in logworker.hpp and used in logworker.cpp. For in-depth knowlege regarding sink implementation details you can look at sinkhandle.hpp and sinkwrapper.hpp

  std::unique_ptr<FileSinkHandle> addDefaultLogger(\n            const std::string& log_prefix\n            , const std::string& log_directory\n            , const std::string& default_id = \"g3log\");\n

With the default id left as is (i.e. \"g3log\") a creation of the logger in the unit test \"test_filechange\" would look like this

  const std::string directory = \"./\";\n  const std::string name = \"(ReplaceLogFile)\";\n  auto worker = g3::LogWorker::createLogWorker();\n  auto handle = worker->addDefaultLogger(name, directory);\n

The resulting filename would be something like:

   ./(ReplaceLogFile).g3log.20160217-001406.log\n
"},{"location":"API/#designate-the-sink-functions-log-entry-receving-function","title":"Designate the sink function's log entry receving function","text":"

The default log formatting look can be overriden by any sink. If the sink receiving function calls toString() then the default log formatting will be used. If the sink receiving function calls toString(&XFunc) then the XFuncwill be used instead (see LogMessage.h/cpp for code details if it is not clear). (XFunc is a place holder for your formatting function of choice).

The API for the function-ptr to pass in is

std::string (*) (const LogMessage&)\n

or for short as defined in LogMessage.h

using LogDetailsFunc = std::string (*) (const LogMessage&);\n
"},{"location":"API/#log-format-customization","title":"Log format customization","text":"

Please seeAPI_custom_formatting.md

"},{"location":"API/#log-flushing","title":"LOG flushing","text":"

The default file sink will flush each log entry at set intervals. The default buffer size for flushing is set to 100 entries. You can adjust this down to 1, or as high as makes sense for your system. Please see FileSink

Even more flushing policies and log rotations can be found at g3sinks logrotate and LogRotateWithFilters.

At shutdown all enqueued logs will be flushed to the sink. At a discovered fatal event (SIGSEGV et.al) all enqueued logs will be flushed to the sink.

A programmatically triggered abrupt process exit such as a call to exit(0) will of course not get the enqueued log entries flushed. Similary a bug that does not trigger a fatal signal but a process exit will also not get the enqueued log entries flushed. G3log can catch several fatal crashes and it deals well with RAII exits but magic is so far out of its' reach.

"},{"location":"API/#g3log-and-sink-usage-code-example","title":"G3log and Sink Usage Code Example","text":"

Example usage where a logrotate sink (g3sinks) is added. In the example it is shown how the logrotate API is called. The logrotate limit is changed from the default to instead be 10MB. The limit is changed by calling the sink handler which passes the function call through to the actual logrotate sink object.

\n// main.cpp\n#include <g3log/g3log.hpp>\n#include <g3log/logworker.h>\n#include <g3sinks/LogRotate.h>\n#include <memory>\n\nint main(int argc, char**argv) {\n   using namespace g3;\n   std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };\n   auto sinkHandle = logworker->addSink(std::make_unique<LogRotate>(),\n                                          &LogRotate::save);\n\n   // initialize the logger before it can receive LOG calls\n   initializeLogging(logworker.get());            \n\n   // You can call in a thread safe manner public functions on the logrotate sink\n   // The call is asynchronously executed on your custom sink.\n   const int k10MBInBytes = 10 * 1024 * 1024;\n   std::future<void> received = sinkHandle->call(&LogRotate::setMaxLogSize, k10MBInBytes);\n\n   // Run the main part of the application. This can be anything of course, in this example\n   // we'll call it \"RunApplication\". Once this call exits we are in shutdown mode\n   RunApplication();\n\n   // If the LogWorker is initialized then at scope exit the g3::shutDownLogging() will be \n   // called automatically. \n   //  \n   // This is important since it protects from LOG calls from static or other entities that will go out of\n   // scope at a later time. \n   //\n   // It can also be called manually if for some reason your setup is different then the one highlighted in\n   // this example\n   g3::shutDownLogging();\n}\n
"},{"location":"API/#dynamic-message-sizing","title":"Dynamic Message Sizing","text":"

The default build uses a fixed size buffer for formatting messages. The size of this buffer is 2048 bytes. If an incoming message results in a formatted message that is greater than 2048 bytes, it will be bound to 2048 bytes and will have the string [...truncated...] appended to the end of the bound message. There are cases where one would like to dynamically change the size at runtime. For example, when debugging payloads for a server, it may be desirable to handle larger message sizes in order to examine the whole payload. Rather than forcing the developer to rebuild the server, dynamic message sizing could be used along with a config file which defines the message size at runtime.

This feature supported as a CMake option:

CMake option: (default OFF) cmake -DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=ON ..

The following is an example of changing the size for the message.

    g3::only_change_at_initialization::setMaxMessageSize(10000);\n
"},{"location":"API/#fatal-handling","title":"Fatal handling","text":"

The default behaviour for G3log is to catch several fatal events before they force the process to exit. After catching a fatal event a stack dump is generated and all log entries, up to the point of the stack dump are together with the dump flushed to the sink(s).

"},{"location":"API/#linuxnix","title":"Linux/*nix","text":"

The default fatal handling on Linux deals with fatal signals. At the time of writing these signals were SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM. The Linux fatal handling is handled in crashhandler.hpp and crashhandler_unix.cpp

A signal that commonly is associated with voluntarily process exit is SIGINT (ctrl + c) G3log does not deal with it.

The fatal signals can be disabled or changed/added .

An example of a Linux stackdump as shown in the output from the fatal example g3log-FATAL-sigsegv. ``` * FATAL SIGNAL RECEIVED * \"Received fatal signal: SIGSEGV(11) PID: 6571

***** SIGNAL SIGSEGV(11)\n\n******* STACKDUMP *******\n        stack dump [1]  ./g3log-FATAL-sigsegv() [0x42a500]\n        stack dump [2]  /lib/x86_64-linux-gnu/libpthread.so.0+0x10340 [0x7f83636d5340]\n\n        stack dump [3]  ./g3log-FATAL-sigsegv : example_fatal::tryToKillWithAccessingIllegalPointer(std::unique_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::default_delete<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)+0x119 [0x4107b9]\n        stack dump [4]  ./g3log-FATAL-sigsegvmain+0xdec [0x40e51c]\n        stack dump [5]  /lib/x86_64-linux-gnu/libc.so.6__libc_start_main+0xf5 [0x7f8363321ec5]\n        stack dump [6]  ./g3log-FATAL-sigsegv() [0x40ffa2]\n\nExiting after fatal event  (FATAL_SIGNAL). Fatal type:  SIGSEGV\nLog content flushed sucessfully to sink\n\n\"\ng3log g3FileSink shutdown at: 16:33:18\n\n```\n
"},{"location":"API/#custom-fatal-handling-override-defaults","title":"Custom fatal handling - override defaults","text":"

By default the fatal signals are defined in https://github.com/KjellKod/g3log/tree/master/src/g3log.cpp as SIGABRT SIGFPE SIGILL SIGSEGV SIGTERM If you want to define your own set of fatal signals, override the default ones, then this can be done as shown in src/g3log/crashhandler.hpp cpp // Example when SIGTERM is skipped due to ZMQ usage g3::overrideSetupSignals({ {SIGABRT, \"SIGABRT\"}, {SIGFPE, \"SIGFPE\"}, {SIGILL, \"SIGILL\"}, {SIGSEGV, \"SIGSEGV\"}});

"},{"location":"API/#pre-fatal-hook","title":"Pre fatal hook","text":"

You can define a custom call back function that will be called before the fatal signal handling re-emits the fatal signal. See src/g3log/g3log.hpp for details. // Example of how to enforce important shutdown cleanup even in the event of a fatal crash: g3::setFatalPreLoggingHook([]{ cleanup(); });

"},{"location":"API/#disable-fatal-handling","title":"Disable fatal handling","text":"

Fatal signal handling can be disabled with a CMake option: ENABLE_FATAL_SIGNALHANDLING. See Options.cmake for more details

"},{"location":"API/#pid1-fatal-signal-recommendations","title":"PID1 Fatal Signal Recommendations","text":"

If you are using g3log on a PID1 process then you absolutely should provide your own signal handling (ref: issue 269) as g3log re-emits the fatal signal after it has restored the previous signal handler for that signal. PID1 processed do not shutdown the process for a normal fatal signal so the choice to exit the PID1 process after such a signal must be taken by the coder - not by g3log.

"},{"location":"API/#windows","title":"Windows","text":"

Windows fatal handling also deals with fatal signals just like Linux. In addition to fatal signals it also deals with unhandled exceptions, vectored exceptions. Windows fatal handling is handled in crashhandler.hpp, crashhandler_windows.cpp, stacktrace_windows.hpp, stacktrace_windows.cpp

An example of a Windows stackdump as shown in the output from the fatal example g3log-FATAL-sigsegv.

introduction | detailed information | Configure & Build | API description | Custom log formatting

"},{"location":"API_custom_formatting/","title":"API for custom log formatting","text":"

introduction | detailed information | Configure & Build | API description | Custom log formatting

"},{"location":"API_custom_formatting/#custom-log-formatting","title":"Custom LOG formatting","text":""},{"location":"API_custom_formatting/#overriding-the-default-file-sinks-file-header","title":"Overriding the Default File Sink's file header","text":"

The default file header can be customized in the default file sink in calling

   FileSink::overrideLogHeader(std::string);\n
"},{"location":"API_custom_formatting/#overriding-the-default-filesinks-log-formatting","title":"Overriding the Default FileSink's log formatting","text":"

The default log formatting is defined in LogMessage.hpp

   static std::string DefaultLogDetailsToString(const LogMessage& msg);\n
"},{"location":"API_custom_formatting/#adding-thread-id-to-the-log-formatting","title":"Adding thread ID to the log formatting","text":"

An \"all details\" log formatting function is also defined - this one also adds the \"calling thread's ID\"

   static std::string FullLogDetailsToString(const LogMessage& msg);\n
"},{"location":"API_custom_formatting/#override-default-sink-log-formatting","title":"Override default sink log formatting","text":"

For convenience the Default sink has a function for doing exactly this

  void overrideLogDetails(LogMessage::LogDetailsFunc func);\n

Example code for replacing the default log formatting for \"full details\" formatting (it adds thread ID)

   auto worker = g3::LogWorker::createLogWorker();\n   auto handle= worker->addDefaultLogger(argv[0], path_to_log_file);\n   g3::initializeLogging(worker.get());\n   handle->call(&g3::FileSink::overrideLogDetails, &LogMessage::FullLogDetailsToString);\n

See test_message.cpp for details and testing

Example code for overloading the formatting of a custom sink. The log formatting function will be passed into the LogMessage::toString(...) this will override the default log formatting

Example

namespace {\n      std::string MyCustomFormatting(const LogMessage& msg) {\n        ... how you want it ...\n      }\n    }\n\n   void MyCustomSink::ReceiveLogEntry(LogMessageMover message) {\n      std::string formatted = message.get().toString(&MyCustomFormatting) << std::flush;\n   }\n...\n...\n auto worker = g3::LogWorker::createLogWorker();\n auto sinkHandle = worker->addSink(std::make_unique<MyCustomSink>(),\n                                     &MyCustomSink::ReceiveLogMessage);\n // ReceiveLogMessage(...) will used the custom formatting function \"MyCustomFormatting(...)\n\n

introduction | detailed information | Configure & Build | API description | Custom log formatting

"},{"location":"building/","title":"Configure, Build, Package, Install and Test","text":"

introduction | detailed information | Configure & Build | API description | Custom log formatting

"},{"location":"building/#configure-build-package-install-and-test-g3log","title":"Configure, build, package, install and test g3log","text":""},{"location":"building/#example-project-with-g3log","title":"Example Project with g3log","text":"

An example project integration of g3log, both statially and dynamically built can be found at g3log_example_integration

"},{"location":"building/#building-it-standalone-to-try-out-is-as-easy-as","title":"Building it standalone to try out is as easy as:","text":"
git clone https://github.com/KjellKod/g3log\ncd g3log\nmkdir build\ncd build\n
"},{"location":"building/#prerequisites","title":"Prerequisites","text":"

You also need these tools to build g3log from source: - CMake (Required)

g3log uses CMake as a one-stop solution for configuring, building, installing, packaging and testing on Windows, Linux and OSX.

  • Git (Optional but Recommended)

When building g3log it uses git to calculate the software version from the commit history of this repository. If you don't want that, or your setup does not have access to git, or you download g3log source archive from the GitHub Releases page so that you do not have the commit history downloaded, you can instead pass in the version as part of the CMake build arguments. See this issue for more information. cmake -DVERSION=1.3.2 ..

"},{"location":"building/#configuration-options","title":"Configuration Options","text":"

g3log provides following CMake options (and default values):

$ cmake -LAH # List non-advanced cached variables. See `cmake --help` for more details.\n\n...\n\n// Fatal (fatal-crashes/contract) examples\nADD_FATAL_EXAMPLE:BOOL=ON\n\n// g3log performance test\nADD_G3LOG_BENCH_PERFORMANCE:BOOL=OFF\n\n// g3log unit tests\nADD_G3LOG_UNIT_TEST:BOOL=OFF\n\n// Use DBUG logging level instead of DEBUG.\n// By default DEBUG is the debugging level\nCHANGE_G3LOG_DEBUG_TO_DBUG:BOOL=OFF\n\n\n// Windows only: Use __FUNCSIG__ instead of the default __FUNCTION__ \n// to show LOG function location\n// WARNING: using this in heavily templated code, like boost can expand\n// the function name into massive size\nWINDOWS_FUNCSIG:BOOL=OFF\n\n\n// gcc/clang only: Use __PRETTY_FUNCTION__ instead of the default __FUNCTION__ \n// to show LOG function location\n// WARNING: using this in heavily templated code, like boost can expand\n// the function name into massive size\nPRETTY_FUNCTION:BOOL=OFF\n\n\n// Specifies the build type on single-configuration generators.\n// Possible values are empty, Debug, Release, RelWithDebInfo, MinSizeRel, \u2026\nCMAKE_BUILD_TYPE:STRING=\n\n// Install path prefix, prepended onto install directories.\n// This variable defaults to /usr/local on UNIX\n// and c:/Program Files/${PROJECT_NAME} on Windows.\nCMAKE_INSTALL_PREFIX:PATH=\n\n// The prefix used in the built package.\n// On Linux, if this option is not set:\n// 1) If CMAKE_INSTALL_PREFIX is given, then it will be\n//    set with the value of CMAKE_INSTALL_PREFIX by g3log.\n// 2) Otherwise, it will be set as /usr/local by g3log.\nCPACK_PACKAGING_INSTALL_PREFIX:PATH=\n\n// Enable Visual Studio break point when receiving a fatal exception.\n// In __DEBUG mode only\nDEBUG_BREAK_AT_FATAL_SIGNAL:BOOL=OFF\n\n// Vectored exception / crash handling with improved stack trace\nENABLE_FATAL_SIGNALHANDLING:BOOL=ON\n\n// Vectored exception / crash handling with improved stack trace\nENABLE_VECTORED_EXCEPTIONHANDLING:BOOL=ON\n\n// iOS version of library.\nG3_IOS_LIB:BOOL=OFF\n\n// Log full filename\nG3_LOG_FULL_FILENAME:BOOL=OFF\n\n// Build shared library\nG3_SHARED_LIB:BOOL=ON\n\n// Build shared runtime library MSVC\nG3_SHARED_RUNTIME:BOOL=ON\n\n// Turn ON/OFF log levels.\n// An disabled level will not push logs of that level to the sink.\n// By default dynamic logging is disabled\nUSE_DYNAMIC_LOGGING_LEVELS:BOOL=OFF\n\n// Use dynamic memory for message buffer during log capturing\nUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE:BOOL=OFF\n\n...\n

For additional option context and comments please also see Options.cmake

If you want to leave everything as it was, then you should:

cmake ..\n

You may also specify one or more of those options listed above from the command line. For example, on Windows:

cmake .. -G \"Visual Studio 15 2017\"\n         -DG3_SHARED_LIB=OFF\n         -DCMAKE_INSTALL_PREFIX=C:/g3log\n         -DADD_G3LOG_UNIT_TEST=ON\n         -DADD_FATAL_EXAMPLE=OFF\n

will use a Visual Studio 2017 solution generator, build g3log as a static library, headers and libraries will be installed to C:\\g3log when installed from source, enable unit testing, but do not build fatal example.

MinGW users on Windows may find they should use a different generator:

cmake .. -G \"MinGW Makefiles\"\n

By default, headers and libraries will be installed to /usr/local on Linux when installed from build tree via make install. You may overwrite it by:

cmake .. -DCMAKE_INSTALL_PREFIX=/usr\n

This will install g3log to /usr instead of /usr/local.

Linux/OSX package maintainers may be interested in the CPACK_PACKAGING_INSTALL_PREFIX. For example:

cmake .. -DCPACK_PACKAGING_INSTALL_PREFIX=/usr/local\n
"},{"location":"building/#build-commands","title":"Build Commands","text":"

Once the configuration is done, you may build g3log with:

# Suppose you are still in the `build` directory. I won't repeat it anymore!\ncmake --build . --config Release\n

You may also build it with a system-specific way.

On Linux, OSX and MinGW:

make\n

On Windows:

msbuild g3log.sln /p:Configuration=Release\n

Windows users can also open the generated Visual Studio solution file and build it happily.

"},{"location":"building/#installation","title":"Installation","text":"

Install from source in a CMake way:

cmake --build . --target install\n

Linux users may also use:

sudo make install\n

You may also create a package first and install g3log with it. See the next section.

"},{"location":"building/#packaging","title":"Packaging","text":"

A CMake way:

cmake --build . --config Release --target package\n

or

cpack -C Release\n

if the whole library has been built in the previous step. It will generate a ZIP package on Windows, and a DEB package on Linux.

Linux users may also use a Linux way:

make package\n

If you want to use a different package generator, you should specify a -G option.

On Windows:

cpack -C Release -G NSIS;7Z\n

this will create a installable NSIS package and a 7z package.

Note: To use the NSIS generator, you should install NSIS first.

On Linux:

cpack -C Release -G TGZ\n

this will create a .tar.gz archive for you.

Once done, you may install or uncompress the package file to the target machine. For example, on Debian or Ubuntu:

sudo dpkg -i g3log-<version>-Linux.deb\n

will install the g3log library to CPACK_PACKAGING_INSTALL_PREFIX.

"},{"location":"building/#testing","title":"Testing","text":"

By default, tests will be built. To disable unit testing, you should turn off ADD_G3LOG_UNIT_TEST.

Suppose the build process has completed, then you can run the tests with:

ctest -C Release\n

or:

make test\n

for Linux users. or for a detailed gtest output of all the tests:

cd build;\n../scripts/runAllTests.sh\n
"},{"location":"building/#cmake-module","title":"CMake module","text":"

g3log comes with a CMake module. Once installed, it can be found under ${CMAKE_INSTALL_PREFIX}/lib/cmake/g3log. Users can use g3log in a CMake-based project this way:

find_package(g3log CONFIG REQUIRED)\ntarget_link_libraries(main PRIVATE g3log)\n

To make sure that CMake can find g3log, you also need to tell CMake where to search for it:

cmake .. -DCMAKE_PREFIX_PATH=<g3log's install prefix>\n
"},{"location":"building/#build-options","title":"Build Options","text":"

The build options are defined in the file Options.cmake

build options are generated and saved to a header file. This avoid having to set the define options in the client source code

introduction | detailed information | Configure & Build | API description | Custom log formatting

"},{"location":"codespaces/","title":"Codespaces","text":"

You can experiment with codespaces and g3log.

"},{"location":"codespaces/#learn-about-github-codespaces","title":"Learn about Github Codespaces","text":"

For an introduction to codespaces you can check out example c++ codespace and using-github-codespaces-with-github-cli

"},{"location":"codespaces/#commandline-codespaces-quick-reference","title":"Commandline codespaces Quick Reference","text":"
  1. List all your codespaces gh codespace list
  2. Create a new codespace gh codespace create -r OWNER/REPO_NAME [-b BRANCH]. Ref docs/github: Creating a codespace for a repository
  3. View codebase details gh codespace view
  4. Stop gh codespace stop -c CODESPACE-NAME
  5. Delete gh codespace delete -c CODESPACE-NAME
  6. Rebuild gh codespace rebuild
  7. Rename gh codespace edit -c CODESPACE-NAME -d DISPLAY-NAME
  8. SSH into REMOTE codespace gh codespace ssh -c CODESPACE-NAME
  9. Open a remote codespace in CVisual Studio gh codespace code -c CODESPACE-NAME (ref: github:doc cs studio)
  10. Copy local file to/from codespace gh codespace cp [-r] SOURCE(S) DESTINATION. Example: Copy a file from the local machine to the $HOME directory of a codespace: gh codespace cp myfile.txt remote:. Example Copy a file from a codespace to the current directory on the local machine: gh codespace cp remote:myfile.txt . (more information available here)
"},{"location":"codespaces/#try-g3log-in-a-local-dev-container","title":"Try g3log in a local dev container.","text":"

Please note that this will build g3log as if it's on a Debian Linux platform.

  1. Clone this repository to your local filesystem.
  2. Start Visual Studio Code. Press F1 and select the Dev Containers: Open Folder in Container... command.
  3. Select the cloned copy of this g3log folder, wait for the container to start, and try things out! You should have debian C++ environment at hand.
"},{"location":"codespaces/#example-cmake-configuration-and-build","title":"Example cmake configuration and build","text":"
Open a terminal in Visual Studio Code\nmkdir debianbuild\ncd debianbuild\ncmake -DADD_G3LOG_UNIT_TEST=ON -DADD_G3LOG_BENCH_PERFORMANCE=ON ..\nmake -j \n
"},{"location":"codespaces/#example-runs","title":"Example runs","text":"
  1. performance test in the container ./g3log-performance-threaded_mean 4
  2. unit tests ctest -v
  3. Try a fatal example with dumped stack trace ./g3log-FATAL-contract
"},{"location":"codespaces/#example-with-debugging","title":"Example with Debugging.","text":"

Without any need to set up environment on your local machine you can also use Codespaces to debug examples, unit tests etc of g3log. The pesky thing with VSCode, especially with cmake is to set up the launh.json. It's a little bit easier if you open a VSCode terminal and do the cmake configuration and build there. Then the launch.json only needs to contain information about the pecific executable.

Here we try out the g3log-FATAL-contract after cmake configure with -DCMAKE_BUILD_TYPE=Debug

{\n    // Use IntelliSense to learn about possible attributes.\n    // Hover to view descriptions of existing attributes.\n    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n    // Remember to build the specific part of cmake with \n    // \"cmake -DCMAKE_BUILD_TYPE=Debug\" if you want to be able to debug it. \n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"(gdb) Start\",\n            \"type\": \"cppdbg\",\n            \"request\": \"launch\",\n            \"program\": \"${workspaceFolder}/build/g3log-FATAL-contract\",\n            \"MIMode\": \"gdb\",\n            \"cwd\": \"${workspaceFolder}/build\"\n            \"setupCommands\": [\n                {\n                    \"description\": \"Enable pretty-printing for gdb\",\n                    \"text\": \"-enable-pretty-printing\",\n                    \"ignoreFailures\": true\n                },\n                {\n                    \"description\": \"Set Disassembly Flavor to Intel\",\n                    \"text\": \"-gdb-set disassembly-flavor intel\",\n                    \"ignoreFailures\": true\n                }\n            ]\n        }\n\n    ]\n}\n
"},{"location":"contributing/","title":"Information for contributing to g3log","text":""},{"location":"contributing/#license","title":"License","text":"

LICENSE

"},{"location":"contributing/#contributing","title":"Contributing","text":"

CONTRIBUTING.md

"},{"location":"contributing/#code-of-conduct","title":"Code of conduct","text":"

CODE_OF_CONDUCT.md

"},{"location":"contributing/#pull-request-template","title":"Pull request template","text":"

PULL_REQUEST_TEMPLATE.md

"},{"location":"g3log_usage/","title":"G3log usage","text":"

introduction | detailed information | Configure & Build | API description | Custom log formatting

"},{"location":"g3log_usage/#how-to-use-g3log","title":"How to use g3log","text":"

G3log is an asynchronous logger with dynamic sinks

"},{"location":"g3log_usage/#example-usage","title":"Example USAGE","text":""},{"location":"g3log_usage/#optional-to-use-either-streaming-or-printf-like-syntax","title":"Optional to use either streaming or printf-like syntax","text":"
LOG(INFO) << \"streaming API is as easy as ABC or \" << 123;\n\nLOGF(WARNING, \"Printf-style syntax is also %s\", \"available\");\n
"},{"location":"g3log_usage/#what-g3log-is","title":"What g3Log is","text":"
  • G3log is the acting name for the third version of g2log and it stands for g3log with dynamic sinks
  • G3log is an asynchronous, \"crash-safe\" logger. You can read more about it here [g2log version]
  • You can choose to use the default log receiver which saves all LOG calls to file, or you can choose to use your own custom made log receiver(s), or both, or as many sinks as you need.
"},{"location":"g3log_usage/#conditional-logging","title":"Conditional logging","text":"
int less = 1; int more = 2\nLOG_IF(INFO, (less<more)) <<\"If [true], then this text will be logged\";\n\n// or with printf-like syntax\nLOGF_IF(INFO, (less<more), \"if %d<%d then this text will be logged\", less,more);\n
"},{"location":"g3log_usage/#design-by-contract","title":"Design-by-Contract","text":"

CHECK(false) will trigger a \"fatal\" message. It will be logged, and then the application will exit.

CHECK(less != more); // not FATAL\nCHECK(less > more) << \"CHECK(false) triggers a FATAL message\";\n
"},{"location":"g3log_usage/#detailed-api-documentation","title":"Detailed API documentation","text":"

Please look at API.md for detailed API documentation

"},{"location":"g3log_usage/#benefits-you-get-when-using-g3log","title":"Benefits you get when using g3log","text":"
  1. Easy to use, clean syntax and a blazing fast logger.

  2. All the slow log I/O disk access is done in a background thread. This ensures that the LOG caller can immediately continue with other tasks and do not have to wait for the LOG call to finish.

  3. G3log provides logging, Design-by-Contract [#CHECK], and flush of log to file at shutdown. Buffered logs will be written to the sink before the application shuts down.

  4. It is thread safe, so using it from multiple threads is completely fine.

  5. It is CRASH SAFE. It will save the made logs to the sink before it shuts down. The logger will catch certain fatal events (Linux/OSX: signals, Windows: fatal OS exceptions and signals) , so if your application crashes due to, say a segmentation fault, SIGSEGV, it will log and save the crash and all previously buffered log entries before exiting.

  6. It is cross platform. Tested and used by me or by clients on OSX, Windows, Ubuntu, CentOS

  7. G3log and G2log are used worldwide in commercial products as well as hobby projects. G2log was introduced in early 2011 and is now retired.

  8. The code is given for free as public domain. This gives the option to change, use, and do whatever with it, no strings attached.

  9. g3log : is made to facilitate easy adding of custom log receivers. Its tested on at least the following platforms with Linux(Clang/gcc), Windows (mingw, visual studio) and OSX. My recommendation is to go with g3log if you have full C++17 support

C++11 support up to version: https://github.com/KjellKod/g3log/releases/tag/1.3.1).

C++14 support up to version: https://github.com/KjellKod/g3log/releases/tag/1.3.4

"},{"location":"g3log_usage/#g3log-with-sinks","title":"G3log with sinks","text":"

Sinks are receivers of LOG calls. G3log comes with a default sink (the same as g3log uses) that can be used to save log to file. A sink can be of any class type without restrictions as long as it can either receive a LOG message as a std::string or as a g3::LogMessageMover.

The std::string comes pre-formatted. The g3::LogMessageMover is a wrapped struct that contains the raw data for custom handling in your own sink.

A sink is owned by the g3log and is added to the logger inside a std::unique_ptr. The sink can be called though its public API through a handler which will asynchronously forward the call to the receiving sink.

It is crazy simple to create a custom sink. This example show what is needed to make a custom sink that is using custom log formatting but only using that for adding color to the default log formatting. The sink forwards the colored log to cout

// in file Customsink.hpp\n#pragma once\n#include <string>\n#include <iostream>\n#include <g3log/logmessage.hpp>\n\nstruct CustomSink {\n\n// Linux xterm color\n// http://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal\n  enum FG_Color {YELLOW = 33, RED = 31, GREEN=32, WHITE = 97};\n\n  FG_Color GetColor(const LEVELS level) const {\n     if (level.value == WARNING.value) { return YELLOW; }\n     if (level.value == DEBUG.value) { return GREEN; }\n     if (g3::internal::wasFatal(level)) { return RED; }\n\n     return WHITE;\n  }\n\n  void ReceiveLogMessage(g3::LogMessageMover logEntry) {\n     auto level = logEntry.get()._level;\n     auto color = GetColor(level);\n\n     std::cout << \"\\033[\" << color << \"m\"\n       << logEntry.get().toString() << \"\\033[m\" << std::endl;\n  }\n};\n\n// in main.cpp, main() function\n\nauto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),\n                                     &CustomSink::ReceiveLogMessage);\n\n
"},{"location":"g3log_usage/#adding-and-removing-sinks","title":"Adding and Removing Sinks","text":"

You can safely remove and add sinks during the running of your program.

Keep in mind

  • Initialization of the logger should happen before you have started any other threads that may call the logger.
  • Destruction of the logger (RAII concept) should happen AFTER shutdown of other threads that are calling the logger.

Adding Sinks

   auto sinkHandle1 = logworker->addSink(std::make_unique<CustomSink>(),\n                                          &CustomSink::ReceiveLogMessage);\n   auto sinkHandle2 = logworker->addDefaultLogger(argv[0],\n                                              path_to_log_file);\n   logworker->removeSink(std::move(sinkHandle1)); // this will in a thread-safe manner remove the sinkHandle1\n   logworker->removeAllSinks(); // this will in a thread-safe manner remove any sinks. \n

More sinks can be found in the repository github.com/KjellKod/g3sinks.

"},{"location":"g3log_usage/#code-examples","title":"Code Examples","text":"

Example usage where a custom sink is added. A function is called though the sink handler to the actual sink object.

// main.cpp\n#include <g3log/g3log.hpp>\n#include <g3log/logworker.hpp>\n#include <memory>\n\n#include \"CustomSink.h\"\n\nint main(int argc, char**argv) {\n   using namespace g3;\n   std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };\n   auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),\n                                          &CustomSink::ReceiveLogMessage);\n\n   // initialize the logger before it can receive LOG calls\n   initializeLogging(logworker.get());\n   LOG(WARNING) << \"This log call, may or may not happend before\"\n                << \"the sinkHandle->call below\";\n\n\n   // You can call in a thread safe manner public functions on your sink\n   // The call is asynchronously executed on your custom sink.\n   std::future<void> received = sinkHandle->call(&CustomSink::Foo,\n                                                 param1, param2);\n\n   // If the LogWorker is initialized then at scope exit the g3::internal::shutDownLogging() will be called.\n   // This is important since it protects from LOG calls from static or other entities that will go out of\n   // scope at a later time.\n   //\n   // It can also be called manually:\n   g3::internal::shutDownLogging();\n}\n\n\n// some_file.cpp : To show how easy it is to get the logger to work\n// in other parts of your software\n\n#include <g3log/g3log.hpp>\n\nvoid SomeFunction() {\n   ...\n   LOG(INFO) << \"Hello World\";\n}\n

Example usage where a the default file logger is used and a custom sink is added

// main.cpp\n#include <g3log/g3log.hpp>\n#include <g3log/logworker.hpp>\n#include <memory>\n\n#include \"CustomSink.h\"\n\nint main(int argc, char**argv) {\n   using namespace g3;\n   auto worker = LogWorker::createLogWorker();\n   auto defaultHandler = worker->addDefaultLogger(argv[0],\n                                                 path_to_log_file);\n\n   // logger is initialized\n   g3::initializeLogging(worker.get());\n\n   LOG(DEBUG) << \"Make log call, then add another sink\";\n\n   worker->addSink(std::make_unique<CustomSink>(),\n                                  &CustomSink::ReceiveLogMessage);\n\n   ...\n}\n

introduction | detailed information | Configure & Build | API description | Custom log formatting

"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 00000000..603cce67 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,38 @@ + + + + https://kjellkod.github.io/g3log/ + 2024-05-23 + daily + + + https://kjellkod.github.io/g3log/API/ + 2024-05-23 + daily + + + https://kjellkod.github.io/g3log/API_custom_formatting/ + 2024-05-23 + daily + + + https://kjellkod.github.io/g3log/building/ + 2024-05-23 + daily + + + https://kjellkod.github.io/g3log/codespaces/ + 2024-05-23 + daily + + + https://kjellkod.github.io/g3log/contributing/ + 2024-05-23 + daily + + + https://kjellkod.github.io/g3log/g3log_usage/ + 2024-05-23 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 00000000..53f12ea5 Binary files /dev/null and b/sitemap.xml.gz differ