Back to home page

EIC code displayed by LXR

 
 

    


Warning, /EICrecon/docs/design/logging.md is written in an unsupported language. File is not indexed.

0001 # Logging
0002 
0003 EICrecon uses [spdlog](https://github.com/gabime/spdlog) library as a logging backbone. **spdlog** utilizes
0004 [fmt](https://github.com/fmtlib/fmt) - a string formatting and output library which uses python alike string formatting
0005 ([is C++20 standard](https://en.cppreference.com/w/cpp/utility/format)) and is very performant
0006 (in many cases [is faster than printf and std streams](https://github.com/fmtlib/fmt#speed-tests))
0007 
0008 
0009 ## Log levels
0010 
0011 **What log levels mean**
0012 
0013 - **trace**    - something very verbose like each hit parameter
0014 - **debug**    - all information that is relevant for an expert to debug but should not be present in production
0015 - **info**     - something that will always (almost) get into the global production log
0016 - **warning**  - something that needs attention but the overall results are likely usable
0017 - **error**    - something bad making results are probably UNUSABLE
0018 - **critical** - imminent software failure, crash and termination
0019 
0020 Global log can be controlled through:
0021 
0022 ```bash
0023 -Peicrecon:LogLevel=info   # trace, debug, info, warning, error, critical, off
0024 ```
0025 
0026 Each plugin or even factory can create its own logger, which clones default logger parameters
0027 but then could be set separately. But convention `<prefix>:LogLevel=<level>`. E.g.:
0028 
0029 ```bash
0030 -Pacts:LogLevel=debug
0031 -PTracking:CentralTrackingParticles:LogLevel=trace
0032 ```
0033 
0034 
0035 ## Basic use
0036 
0037 EICRecon has a log service that centralizes default logger configuration and helps spawn named loggers.
0038 Each unit - a plugin, factory, class, etc. can spawn its own named logger and use it:
0039 
0040 [FULL WORKING EXAMPLE](https://github.com/eic/EICrecon/blob/main/src/examples/log_example/log_example.cc)
0041 
0042 ```cpp
0043 #include <services/log/Log_service.h>
0044 class ExampleProcessor: public JEventProcessor {
0045 private:
0046     std::shared_ptr<spdlog::logger> m_log;
0047 
0048 public:
0049     /// Once in m_app lifetime function call
0050     void Init() override {
0051 
0052         auto m_app = GetApplication();
0053 
0054         // The service centralizes the use of spdlog and properly spawning logger
0055         auto log_service = m_app->GetService<Log_service>();
0056 
0057         // Loggers are spawned by name.
0058         m_log = log_service->logger("ExampleProcessor");
0059 
0060         // log things!
0061         m_log->info("Hello world! {}", 42);
0062     }
0063 
0064     /// The function is executed every event
0065     void Process(const std::shared_ptr<const JEvent>& event) override {
0066         // Will print something if 'trace' log level is set (see below)
0067         m_log->trace("Processing event #{}", event->GetEventNumber());
0068         // ...
0069     }
0070 ```
0071 
0072 
0073 
0074 ## Formatting
0075 
0076 Thanks to fmt, logger has rich text formatting. More examples and full
0077 specification is in [fmt documentation](https://github.com/fmtlib/fmt):
0078 
0079 ```cpp
0080 m_log->info("Welcome to spdlog!");
0081 // [info] Welcome to spdlog!
0082 
0083 m_log->error("Some error message with arg: {}", 1);
0084 // [error] Some error message with arg: 1
0085 
0086 m_log->warn("Easy padding in numbers like {:08d}", 12);
0087 // [warning] Easy padding in numbers like 00000012
0088 
0089 m_log->critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
0090 // [critical] Support for int: 42;  hex: 2a;  oct: 52; bin: 101010
0091 
0092 m_log->info("Support for floats number of digits {:03.2f}", 1.23456);
0093 // [info] Support for floats number of digits 1.23
0094 
0095 m_log->info("Positional args are {1} {0}...", "too", "supported");
0096 // [info] Positional args are supported too...
0097 
0098 m_log->info("{:>30}", "right aligned");
0099 // [info]                  right aligned
0100 
0101 m_log->info("Table of values:");
0102 m_log->info("{:<5} {:<7} {:>10}", "N", "val1", "val2");
0103 m_log->info("{:=^30}", "centered");  // use '=' as a fill char
0104 m_log->info("{:<5} {:<7.2f} {:>10}", 23, 3.1415, 3.1415);
0105 m_log->info("{:<5} {:<7.2f} {:>10}", 24, 2.7182, 2.7182);
0106 m_log->info("{:<5} {:<7.2f} {:>10}", 25, 1.6180, 1.6180);
0107 // [info] Table of values:
0108 // [info] N     val1          val2
0109 // [info] ===========centered===========
0110 // [info] 23    3.14        3.1415
0111 // [info] 24    2.72        2.7182
0112 // [info] 25    1.62         1.618
0113 
0114 // Compile time log levels
0115 // define SPDLOG_ACTIVE_LEVEL to desired level e.g.:
0116 //    #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
0117 SPDLOG_TRACE("Some trace message with param {}", 42);
0118 SPDLOG_DEBUG("Some debug message");
0119 ```
0120 
0121 
0122 ## User parameters
0123 
0124 In order to wire your logger level with a jana-parameter to change log level without recompilation, use:
0125 
0126 ```cpp
0127 // includes:
0128 #include <services/log/Log_service.h>
0129 #include <extensions/spdlog/SpdlogExtensions.h>
0130 
0131 // ... all from previous example
0132 void Init() override {
0133    // ...
0134 
0135    // This is a default level
0136    std::string log_level_str = "info";
0137 
0138    // Ask service locator for parameter manager. We want to get this plugin parameters.
0139    auto pm = m_app->GetJParameterManager();
0140 
0141    // Define parameter
0142    pm->SetDefaultParameter("log_example:log-level", log_level_str, "log_level: trace, debug, info, warn, err, critical, off");
0143 
0144    // At this point log_level_str is initialized with user provided flag value or the default value
0145 
0146    // convert input std::string to spdlog::level::level_enum and set logger level
0147    m_log->set_level(eicrecon::ParseLogLevel(log_level_str));
0148 }
0149 ```
0150 
0151 ## If log level <=
0152 
0153 
0154 
0155 Sometimes one needs to know current log level to calculate debugging values. Use **<=** operator to check level.
0156 It works because enum values are: trace=0, debug=1, ... critical= 5. So:
0157 
0158 ```cpp
0159   // In general one doesn't need to manually check log debug level
0160   m_log->debug("Will be printed if level is 'debug' or 'trace' ");
0161 
0162   // But sometimes one needs to know current log level to calculate debugging values
0163   // Use  <= operator to check level.
0164   // m_log->level() <= spdlog::level::debug  means 'debug' or 'trace'
0165   // m_log->level() <= spdlog::level::info   means 'info', 'debug' or 'trace' levels
0166   if(m_log->level() <= spdlog::level::debug) {
0167       int x = event->GetRunNumber()*1000000 + event->GetEventNumber()/2;
0168       m_log->debug("Calculated debug value #{}", x);
0169   }
0170 ```
0171 
0172 
0173 
0174 ## Hints
0175 
0176 
0177 
0178 ### Streamable objects
0179 
0180 Sometimes you need to print objects with custom overloaded ostr stream operator `<<`
0181 i.e. objects that knows how to print self when used with something like  ```cout<<object```
0182 Such objects are used sometimes, e.g. lib eigen matrices might be printed that way.
0183 To enable printing of such objects include the next header
0184 
0185 ```cpp
0186 // Include this to print/format streamable objects.
0187 // You don't need this header most of the time (until you see a compilation error)
0188 #include <spdlog/fmt/ostr.h>
0189 ```
0190 
0191 
0192 ### Default logger
0193 
0194 spdlog has a default logger and global functions like `spdlog::info`, `spdlog::warn` etc.
0195 
0196 ```cpp
0197 spdlog::info("Hello world from default logger! You can do this, but please don't");
0198 ```
0199 
0200 It is possible to use a default logger to print something out, but it is NOT RECOMMENDED to be used instead
0201 of a named loggers. Default logger is used to highlight something that relates to whole application execution.
0202 Otherwise, use named logger.
0203 
0204 
0205 ### Shared names
0206 
0207 By default, spdlog fails if a logger with such name exists (but one can get existing logger
0208 from registry). EICrecon Logger service simplifies and automates it with a single function `logger(name)`.
0209 This allows to use the same logger with the same name from different units if the context is the same.
0210 Imagine you want to highlight that this message belongs to "tracking" you can do:
0211 
0212 ```cpp
0213 
0214 // One class
0215 m_tracking_log = m_app->GetService<Log_service>()->logger("tracking");
0216 
0217 // Another class
0218 m_tracking_log = m_app->GetService<Log_service>()->logger("tracking");
0219 ```
0220 
0221 You can mix named loggers depending on context
0222 
0223 ```cpp
0224 
0225 // Some class INIT
0226 m_this_log = m_app->GetService<Log_service>()->logger("ExampleFactoryName");
0227 m_tracking_log = m_app->GetService<Log_service>()->logger("tracking");
0228 
0229 // Some class event PROCESSING
0230 m_this_log->trace("Something related to this class/factory/plugin");
0231 m_tracking_log->info("Something relating to tracking in general");
0232 ```
0233 
0234 
0235 ### String format
0236 
0237 It is recommended to use fmt string formatting both from performance and safety points. But it is also very convenient!
0238 fmt can be accessed through spdlog headers:
0239 
0240 ```cpp
0241 #include <spdlog/fmt/fmt.h>
0242 
0243 // code
0244 std::string hello = fmt::format("Hello world {}", 42);
0245 ```
0246 
0247 
0248 ### CMake
0249 
0250 spdlog is included by default in every plugin if `plugin_add` macro is used:
0251 
0252 ```cmake
0253 plugin_add(${PLUGIN_NAME})  # <= spdlog will be included by default
0254 ```
0255 
0256 eicrecon application also includes Log_service by default. So it should not appear on `-Pplugins` list.
0257 
0258 
0259 
0260 ## Additional links
0261 
0262 - [spdlog](https://github.com/gabime/spdlog)
0263 - [fmt](https://github.com/fmtlib/fmt)
0264 - [EICrecon logging examples](https://github.com/eic/EICrecon/blob/main/src/examples/log_example/log_example.cc)
0265 - [EICrecon factory with log example](https://github.com/eic/EICrecon/blob/main/src/algorithms/tracking/TrackerHitReconstruction_factory.cc)