1# Plog - portable, simple and extensible C++ logging library 2Pretty powerful logging library in about 1000 lines of code [![Build Status](https://travis-ci.com/SergiusTheBest/plog.svg?branch=master)](https://travis-ci.com/SergiusTheBest/plog) [![Build status](https://ci.appveyor.com/api/projects/status/rna5gwhqjb13wovr/branch/master?svg=true)](https://ci.appveyor.com/project/SergiusTheBest/plog/branch/master) [![CircleCI](https://circleci.com/gh/SergiusTheBest/plog.svg?style=svg)](https://circleci.com/gh/SergiusTheBest/plog) [![Build Status](https://api.cirrus-ci.com/github/SergiusTheBest/plog.svg)](https://cirrus-ci.com/github/SergiusTheBest/plog) 3 4- [Introduction](#introduction) 5 - [Hello log!](#hello-log) 6 - [Features](#features) 7- [Usage](#usage) 8 - [Step 1: Adding includes](#step-1-adding-includes) 9 - [Step 2: Initialization](#step-2-initialization) 10 - [Step 3: Logging](#step-3-logging) 11 - [Basic logging macros](#basic-logging-macros) 12 - [Conditional logging macros](#conditional-logging-macros) 13 - [Logger severity checker](#logger-severity-checker) 14- [Advanced usage](#advanced-usage) 15 - [Changing severity at runtime](#changing-severity-at-runtime) 16 - [Custom initialization](#custom-initialization) 17 - [Multiple appenders](#multiple-appenders) 18 - [Multiple loggers](#multiple-loggers) 19 - [Share log instances across modules (exe, dll, so, dylib)](#share-log-instances-across-modules-exe-dll-so-dylib) 20 - [Chained loggers](#chained-loggers) 21- [Architecture](#architecture) 22 - [Overview](#overview) 23 - [Logger](#logger) 24 - [Record](#record) 25 - [Formatter](#formatter) 26 - [TxtFormatter](#txtformatter) 27 - [TxtFormatterUtcTime](#txtformatterutctime) 28 - [CsvFormatter](#csvformatter) 29 - [CsvFormatterUtcTime](#csvformatterutctime) 30 - [FuncMessageFormatter](#funcmessageformatter) 31 - [MessageOnlyFormatter](#messageonlyformatter) 32 - [Converter](#converter) 33 - [UTF8Converter](#utf8converter) 34 - [NativeEOLConverter](#nativeeolconverter) 35 - [Appender](#appender) 36 - [RollingFileAppender](#rollingfileappender) 37 - [ConsoleAppender](#consoleappender) 38 - [ColorConsoleAppender](#colorconsoleappender) 39 - [AndroidAppender](#androidappender) 40 - [EventLogAppender](#eventlogappender) 41 - [DebugOutputAppender](#debugoutputappender) 42- [Miscellaneous notes](#miscellaneous-notes) 43 - [Lazy stream evaluation](#lazy-stream-evaluation) 44 - [Stream improvements over std::ostream](#stream-improvements-over-stdostream) 45 - [Automatic 'this' pointer capture](#automatic-this-pointer-capture) 46 - [Headers to include](#headers-to-include) 47 - [Unicode](#unicode) 48 - [Wide string support](#wide-string-support) 49 - [Performance](#performance) 50 - [Printf style formatting](#printf-style-formatting) 51 - [LOG_XXX macro name clashes](#log_xxx-macro-name-clashes) 52- [Extending](#extending) 53 - [Custom data type](#custom-data-type) 54 - [Custom appender](#custom-appender) 55 - [Custom formatter](#custom-formatter) 56 - [Custom converter](#custom-converter) 57- [Samples](#samples) 58- [References](#references) 59 - [Competing C++ log libraries](#competing-c-log-libraries) 60 - [Tools and useful info](#tools-and-useful-info) 61- [License](#license) 62- [Version history](#version-history) 63 64# Introduction 65 66## Hello log! 67Plog is a C++ logging library that is designed to be as simple, small and flexible as possible. It is created as an alternative to existing large libraries and provides some unique features as [CSV log format]((#csvformatter)) and [wide string support](#wide-string-support). 68 69Here is a minimal hello log sample: 70 71```cpp 72#include <plog/Log.h> // Step1: include the headers 73#include "plog/Initializers/RollingFileInitializer.h" 74 75int main() 76{ 77 plog::init(plog::debug, "Hello.txt"); // Step2: initialize the logger 78 79 // Step3: write log messages using a special macro 80 // There are several log macros, use the macro you liked the most 81 82 PLOGD << "Hello log!"; // short macro 83 PLOG_DEBUG << "Hello log!"; // long macro 84 PLOG(plog::debug) << "Hello log!"; // function-style macro 85 86 // Also you can use LOG_XXX macro but it may clash with other logging libraries 87 LOGD << "Hello log!"; // short macro 88 LOG_DEBUG << "Hello log!"; // long macro 89 LOG(plog::debug) << "Hello log!"; // function-style macro 90 91 return 0; 92} 93``` 94 95And its output: 96 97``` 982015-05-18 23:12:43.921 DEBUG [21428] [main@13] Hello log! 992015-05-18 23:12:43.968 DEBUG [21428] [main@14] Hello log! 1002015-05-18 23:12:43.968 DEBUG [21428] [main@15] Hello log! 101``` 102 103## Features 104- Very small (slightly more than 1000 LOC) 105- Easy to use 106- Headers only 107- No 3rd-party dependencies 108- Cross-platform: Windows, Linux, FreeBSD, macOS, Android, RTEMS (gcc, clang, msvc, mingw, mingw-w64, icc, c++builder) 109- Thread and type safe 110- Formatters: [TXT](#txtformatter), [CSV](#csvformatter), [FuncMessage](#funcmessageformatter), [MessageOnly](#messageonlyformatter) 111- Appenders: [RollingFile](#rollingfileappender), [Console](#consoleappender), [ColorConsole](#colorconsoleappender), [Android](#androidappender), [EventLog](#eventlogappender), [DebugOutput](#debugoutputappender) 112- [Automatic 'this' pointer capture](#automatic-this-pointer-capture) (supported only on msvc) 113- [Lazy stream evaluation](#lazy-stream-evaluation) 114- [Unicode aware](#unicode), files are stored in UTF8 115- Doesn't require C++11 116- [Extendable](#extending) 117- No `windows.h` dependency 118- Can use UTC or local time 119- Uses modern CMake 120 121# Usage 122To start using plog you need to make 3 simple steps. 123 124## Step 1: Adding includes 125At first your project needs to know about plog. For that you have to: 126 1271. Add `plog/include` to the project include paths 1282. Add `#include <plog/Log.h>` into your cpp/h files (if you have precompiled headers it is a good place to add this include there) 129 130## Step 2: Initialization 131The next step is to initialize the [Logger](#logger). This is done by the following `plog::init` function: 132 133```cpp 134Logger& init(Severity maxSeverity, const char/wchar_t* fileName, size_t maxFileSize = 0, int maxFiles = 0); 135``` 136 137`maxSeverity` is the logger severity upper limit. All log messages have its own severity and if it is higher than the limit those messages are dropped. Plog defines the following severity levels: 138 139```cpp 140enum Severity 141{ 142 none = 0, 143 fatal = 1, 144 error = 2, 145 warning = 3, 146 info = 4, 147 debug = 5, 148 verbose = 6 149}; 150``` 151 152*Note: messages with severity level `none` will be always printed.* 153 154The log format is determined automatically by `fileName` file extension: 155 156- .csv => [CSV format](#csvformatter) 157- anyting else => [TXT format](#txtformatter) 158 159The rolling behavior is controlled by `maxFileSize` and `maxFiles` parameters: 160 161- `maxFileSize` - the maximum log file size in bytes 162- `maxFiles` - a number of log files to keep 163 164If one of them is zero then log rolling is disabled. 165 166Sample: 167 168```cpp 169plog::init(plog::warning, "c:\\logs\\log.csv", 1000000, 5); 170``` 171 172Here the logger is initialized to write all messages with up to warning severity to a file in csv format. Maximum log file size is set to 1'000'000 bytes and 5 log files are kept. 173 174*Note: see [Custom initialization](#custom-initialization) for advanced usage.* 175 176## Step 3: Logging 177Logging is performed with the help of special macros. A log message is constructed using stream output operators `<<`. Thus it is type-safe and extendable in contrast to a format string output. 178 179### Basic logging macros 180This is the most used type of logging macros. They do unconditional logging. 181 182#### Long macros: 183 184```cpp 185PLOG_VERBOSE << "verbose"; 186PLOG_DEBUG << "debug"; 187PLOG_INFO << "info"; 188PLOG_WARNING << "warning"; 189PLOG_ERROR << "error"; 190PLOG_FATAL << "fatal"; 191PLOG_NONE << "none"; 192``` 193 194#### Short macros: 195 196```cpp 197PLOGV << "verbose"; 198PLOGD << "debug"; 199PLOGI << "info"; 200PLOGW << "warning"; 201PLOGE << "error"; 202PLOGF << "fatal"; 203PLOGN << "none"; 204``` 205 206#### Function-style macros: 207 208```cpp 209PLOG(severity) << "msg"; 210``` 211 212### Conditional logging macros 213These macros are used to do a conditional logging. They accept a condition as a parameter and perform logging if the condition is true. 214 215#### Long macros: 216 217```cpp 218PLOG_VERBOSE_IF(cond) << "verbose"; 219PLOG_DEBUG_IF(cond) << "debug"; 220PLOG_INFO_IF(cond) << "info"; 221PLOG_WARNING_IF(cond) << "warning"; 222PLOG_ERROR_IF(cond) << "error"; 223PLOG_FATAL_IF(cond) << "fatal"; 224PLOG_NONE_IF(cond) << "none"; 225``` 226 227#### Short macros: 228 229```cpp 230PLOGV_IF(cond) << "verbose"; 231PLOGD_IF(cond) << "debug"; 232PLOGI_IF(cond) << "info"; 233PLOGW_IF(cond) << "warning"; 234PLOGE_IF(cond) << "error"; 235PLOGF_IF(cond) << "fatal"; 236PLOGN_IF(cond) << "none"; 237``` 238 239#### Function-style macros: 240 241```cpp 242PLOG_IF(severity, cond) << "msg"; 243``` 244 245### Logger severity checker 246In some cases there is a need to perform a group of actions depending on the current logger severity level. There is a special macro for that. It helps to minimize performance penalty when the logger is inactive. 247 248```cpp 249IF_PLOG(severity) 250``` 251 252Sample: 253 254```cpp 255IF_PLOG(plog::debug) // we want to execute the following statements only at debug severity (and higher) 256{ 257 for (int i = 0; i < vec.size(); ++i) 258 { 259 PLOGD << "vec[" << i << "]: " << vec[i]; 260 } 261} 262``` 263 264# Advanced usage 265 266## Changing severity at runtime 267It is possible to set the maximum severity not only at the logger initialization time but at any time later. There are special accessor methods: 268 269```cpp 270Severity Logger::getMaxSeverity() const; 271Logger::setMaxSeverity(Severity severity); 272``` 273 274To get the logger use `plog::get` function: 275 276```cpp 277Logger* get(); 278``` 279 280Sample: 281 282```cpp 283plog::get()->setMaxSeverity(plog::debug); 284``` 285 286## Custom initialization 287Non-typical log cases require the use of custom initialization. It is done by the following `plog::init` function: 288 289```cpp 290Logger& init(Severity maxSeverity = none, IAppender* appender = NULL); 291``` 292 293You have to construct an [Appender](#appender) parameterized with a [Formatter](#formatter) and pass it to the `plog::init` function. 294 295*Note: a lifetime of the appender should be static!* 296 297Sample: 298 299```cpp 300static plog::ConsoleAppender<plog::TxtFormatter> consoleAppender; 301plog::init(plog::debug, &consoleAppender); 302``` 303 304## Multiple appenders 305It is possible to have multiple [Appenders](#appender) within a single [Logger](#logger). In such case log message will be written to all of them. Use the following method to accomplish that: 306 307```cpp 308Logger& Logger::addAppender(IAppender* appender); 309``` 310 311Sample: 312 313```cpp 314static plog::RollingFileAppender<plog::CsvFormatter> fileAppender("MultiAppender.csv", 8000, 3); // Create the 1st appender. 315static plog::ConsoleAppender<plog::TxtFormatter> consoleAppender; // Create the 2nd appender. 316plog::init(plog::debug, &fileAppender).addAppender(&consoleAppender); // Initialize the logger with the both appenders. 317``` 318 319Here the logger is initialized in the way when log messages are written to both a file and a console. 320 321*Refer to [MultiAppender](samples/MultiAppender) for a complete sample.* 322 323## Multiple loggers 324Multiple [Loggers](#logger) can be used simultaneously each with their own separate configuration. The [Loggers](#logger) differ by their instanceId (that is implemented as a template parameter). The default instanceId is zero. Initialization is done by the appropriate template `plog::init` functions: 325 326```cpp 327Logger<instanceId>& init<instanceId>(...); 328``` 329 330To get a logger use `plog::get` function (returns `NULL` if the logger is not initialized): 331 332```cpp 333Logger<instanceId>* get<instanceId>(); 334``` 335 336All logging macros have their special versions that accept an instanceId parameter. These kind of macros have an underscore at the end: 337 338```cpp 339PLOGD_(instanceId) << "debug"; 340PLOGD_IF_(instanceId, condition) << "conditional debug"; 341IF_PLOG_(instanceId, severity) 342``` 343 344Sample: 345 346```cpp 347enum // Define log instanceIds. Default is 0 and is omitted from this enum. 348{ 349 SecondLog = 1 350}; 351 352int main() 353{ 354 plog::init(plog::debug, "MultiInstance-default.txt"); // Initialize the default logger instance. 355 plog::init<SecondLog>(plog::debug, "MultiInstance-second.txt"); // Initialize the 2nd logger instance. 356 357 // Write some messages to the default log. 358 PLOGD << "Hello default log!"; 359 360 // Write some messages to the 2nd log. 361 PLOGD_(SecondLog) << "Hello second log!"; 362 363 return 0; 364} 365``` 366 367*Refer to [MultiInstance](samples/MultiInstance) for a complete sample.* 368 369## Share log instances across modules (exe, dll, so, dylib) 370For applications that consist from several binary modules plog instances can be local (each module has its own instance) or shared (all modules use the same instance). In case of shared you have to initialize plog only in one module, other modules will reuse that instance. 371 372Sharing behavior is controlled by the following macros and is OS-dependent: 373 374|Macro|OS|Behavior| 375|--|--|--| 376|PLOG_GLOBAL|Linux/Unix|Shared| 377|PLOG_LOCAL|Linux/Unix|Local| 378|PLOG_EXPORT|Linux/Unix|n/a| 379|PLOG_IMPORT|Linux/Unix|n/a| 380|<default>|Linux/Unix|According to compiler settings| 381|PLOG_GLOBAL|Windows|n/a| 382|PLOG_LOCAL|Windows|Local| 383|PLOG_EXPORT|Windows|Shared (exports)| 384|PLOG_IMPORT|Windows|Shared (imports)| 385|<default>|Windows|Local| 386 387For sharing on Windows one module should use `PLOG_EXPORT` and others should use `PLOG_IMPORT`. Also be cafeful on Linux/Unix: if you don't specify sharing behavior it will be determined by compiler settings (`-fvisibility`). 388 389*Refer to [Shared](samples/Shared) for a complete sample.* 390 391## Chained loggers 392A [Logger](#logger) can work as an [Appender](#appender) for another [Logger](#logger). So you can chain several loggers together. This is useful for streaming log messages from a shared library to the main application binary. 393 394*Important: don't forget to specify `PLOG_LOCAL` sharing mode on Linux/Unix systems for this sample.* 395 396Sample: 397 398```cpp 399// shared library 400 401// Function that initializes the logger in the shared library. 402extern "C" void EXPORT initialize(plog::Severity severity, plog::IAppender* appender) 403{ 404 plog::init(severity, appender); // Initialize the shared library logger. 405} 406 407// Function that produces a log message. 408extern "C" void EXPORT foo() 409{ 410 PLOGI << "Hello from shared lib!"; 411} 412``` 413 414```cpp 415// main app 416 417// Functions imported form the shared library. 418extern "C" void initialize(plog::Severity severity, plog::IAppender* appender); 419extern "C" void foo(); 420 421int main() 422{ 423 plog::init(plog::debug, "ChainedApp.txt"); // Initialize the main logger. 424 425 PLOGD << "Hello from app!"; // Write a log message. 426 427 initialize(plog::debug, plog::get()); // Initialize the logger in the shared library. Note that it has its own severity. 428 foo(); // Call a function from the shared library that produces a log message. 429 430 return 0; 431} 432``` 433 434*Refer to [Chained](samples/Chained) for a complete sample.* 435 436# Architecture 437 438## Overview 439Plog is designed to be small but flexible, so it prefers templates to interface inheritance. All main entities are shown on the following UML diagram: 440 441![Plog class diagram](http://gravizo.com/svg?@startuml;class%20Logger<int%20instance>%20<<singleton>>%20{;%20%20%20%20+addAppender%28%29;%20%20%20%20+getMaxSeverity%28%29;%20%20%20%20+setMaxSeverity%28%29;%20%20%20%20+checkSeverity%28%29;%20%20%20%20-maxSeverity;%20%20%20%20-appenders;};package%20Appenders%20<<Frame>>%20{;%20%20%20%20interface%20IAppender%20{;%20%20%20%20%20%20%20%20+write%28%29;%20%20%20%20};%20%20%20%20;%20%20%20%20class%20RollingFileAppender<Formatter,%20Converter>;%20%20%20%20class%20ConsoleAppender<Formatter>;%20%20%20%20class%20ColorConsoleAppender<Formatter>;%20%20%20%20class%20AndroidAppender<Formatter>;%20%20%20%20class%20EventLogAppender<Formatter>;%20%20%20%20class%20DebugOutputAppender<Formatter>;%20%20%20%20ConsoleAppender%20<|--%20ColorConsoleAppender;%20%20%20%20IAppender%20<|-u-%20Logger;%20%20%20%20IAppender%20<|--%20RollingFileAppender;%20%20%20%20IAppender%20<|--%20ConsoleAppender;%20%20%20%20IAppender%20<|--%20AndroidAppender;%20%20%20%20IAppender%20<|--%20EventLogAppender;%20%20%20%20IAppender%20<|--%20DebugOutputAppender;%20%20%20%20;%20%20%20%20Logger%20"1"%20o--%20"0..n"%20IAppender;};package%20Formatters%20<<Frame>>%20{;%20%20%20%20class%20CsvFormatter%20{;%20%20%20%20%20%20%20%20{static}%20header%28%29;%20%20%20%20%20%20%20%20{static}%20format%28%29;%20%20%20%20};%20%20%20%20class%20TxtFormatter%20{;%20%20%20%20%20%20%20%20{static}%20header%28%29;%20%20%20%20%20%20%20%20{static}%20format%28%29;%20%20%20%20};%20%20%20%20class%20FuncMessageFormatter%20{;%20%20%20%20%20%20%20%20{static}%20header%28%29;%20%20%20%20%20%20%20%20{static}%20format%28%29;%20%20%20%20};%20%20%20%20class%20MessageOnlyFormatter%20{;%20%20%20%20%20%20%20%20{static}%20header%28%29;%20%20%20%20%20%20%20%20{static}%20format%28%29;%20%20%20%20};};package%20Converters%20<<Frame>>%20{;%20%20%20%20class%20UTF8Converter%20{;%20%20%20%20%20%20%20%20{static}%20header%28%29;%20%20%20%20%20%20%20%20{static}%20convert%28%29;%20%20%20%20};%20%20%20%20class%20NativeEOLConverter%20<NextConverter>{;%20%20%20%20%20%20%20%20{static}%20header%28%29;%20%20%20%20%20%20%20%20{static}%20convert%28%29;%20%20%20%20};};enum%20Severity%20{;%20%20%20%20none,;%20%20%20%20fatal,;%20%20%20%20error,;%20%20%20%20warning,;%20%20%20%20info,;%20%20%20%20debug,;%20%20%20%20verbose;};class%20Record%20{;%20%20%20%20+operator<<%28%29;%20%20%20%20-time;%20%20%20%20-severity;%20%20%20%20-tid;%20%20%20%20-object;%20%20%20%20-line;%20%20%20%20-file;%20%20%20%20-message;%20%20%20%20-func;};hide%20empty%20members;hide%20empty%20fields;@enduml) 442<!-- 443@startuml 444 445class Logger<int instance> <<singleton>> { 446 +addAppender(); 447 +getMaxSeverity(); 448 +setMaxSeverity(); 449 +checkSeverity(); 450 -maxSeverity; 451 -appenders; 452} 453 454package Appenders <<Frame>> { 455 interface IAppender { 456 +write(); 457 } 458 459 class RollingFileAppender<Formatter, Converter> 460 class ConsoleAppender<Formatter> 461 class ColorConsoleAppender<Formatter> 462 class AndroidAppender<Formatter> 463 class EventLogAppender<Formatter> 464 class DebugOutputAppender<Formatter> 465 466 ConsoleAppender <|-- ColorConsoleAppender 467 IAppender <|-u- Logger 468 IAppender <|-- RollingFileAppender 469 IAppender <|-- ConsoleAppender 470 IAppender <|-- AndroidAppender 471 IAppender <|-- EventLogAppender 472 IAppender <|-- DebugOutputAppender 473 474 Logger "1" o-- "0..n" IAppender 475} 476 477package Formatters <<Frame>> { 478 class CsvFormatter { 479 {static} header(); 480 {static} format(); 481 } 482 483 class TxtFormatter { 484 {static} header(); 485 {static} format(); 486 } 487 488 class FuncMessageFormatter { 489 {static} header(); 490 {static} format(); 491 } 492 493 class MessageOnlyFormatter { 494 {static} header(); 495 {static} format(); 496 } 497} 498 499package Converters <<Frame>> { 500 class UTF8Converter { 501 {static} header(); 502 {static} convert(); 503 } 504 505 class NativeEOLConverter <NextConverter>{ 506 {static} header(); 507 {static} convert(); 508 } 509} 510 511enum Severity { 512 none, 513 fatal, 514 error, 515 warning, 516 info, 517 debug, 518 verbose 519} 520 521class Record { 522 +operator<<(); 523 -time; 524 -severity; 525 -tid; 526 -object; 527 -line; 528 -file; 529 -message; 530 -func; 531} 532 533hide empty members 534hide empty fields 535@enduml 536--> 537 538There are 5 functional parts: 539 540- [Logger](#logger) - the main object, implemented as singleton 541- [Record](#record) - keeps log data: time, message, etc 542- [Appender](#appender) - represents a log data destination: file, console, etc 543- [Formatter](#formatter) - formats log data into a string 544- [Converter](#converter) - converts formatter output into a raw buffer 545 546The log data flow is shown below: 547 548![Log data flow](http://gravizo.com/g?@startuml;%28*%29%20-r->%20"PLOG%20macro";-r->%20"Record";-r->%20"Logger";-r-->%20"Appender";-d->%20"Formatter";-d->%20"Converter";-u->%20"Appender";-r->%20%28*%29;@enduml) 549<!-- 550@startuml 551(*) -r-> "PLOG macro" 552-r-> "Record" 553-r-> "Logger" 554-r-> "Appender" 555-d-> "Formatter" 556-d-> "Converter" 557-u-> "Appender" 558-r-> (*) 559@enduml 560--> 561 562## Logger 563[Logger](#logger) is a center object of the whole logging system. It is a singleton and thus it forms a known single entry point for configuration and processing log data. [Logger](#logger) can act as [Appender](#appender) for another [Logger](#logger) because it implements `IAppender` interface. Also there can be several independent loggers that are parameterized by an integer instanceId number. The default instanceId is 0. 564 565```cpp 566template<int instanceId> 567class Logger : public util::Singleton<Logger<instanceId> >, public IAppender 568{ 569public: 570 Logger(Severity maxSeverity = none); 571 572 Logger& addAppender(IAppender* appender); 573 574 Severity getMaxSeverity() const; 575 void setMaxSeverity(Severity severity); 576 bool checkSeverity(Severity severity) const; 577 578 virtual void write(const Record& record); 579 void operator+=(const Record& record); 580}; 581``` 582 583## Record 584[Record](#record) stores all log data. It includes: 585 586- time 587- severity 588- thread id 589- 'this' pointer (if a log message is written from within an object) 590- source line 591- source file name 592- function name 593- message 594 595*Note: Source file name isn't captured by default. To enable it define PLOG_CAPTURE_FILE.* 596 597Also [Record](#record) has a number of overloaded stream output operators to construct a message. 598 599```cpp 600class Record 601{ 602public: 603 Record(Severity severity, const char* func, size_t line, const char* file, const void* object); 604 605 ////////////////////////////////////////////////////////////////////////// 606 // Stream output operators 607 608 Record& operator<<(char data); 609 Record& operator<<(wchar_t data); 610 611 template<typename T> 612 Record& operator<<(const T& data); 613 614 ////////////////////////////////////////////////////////////////////////// 615 // Getters 616 617 virtual const util::Time& getTime() const; 618 virtual Severity getSeverity() const; 619 virtual unsigned int getTid() const; 620 virtual const void* getObject() const; 621 virtual size_t getLine() const; 622 virtual const util::nchar* getMessage() const; 623 virtual const char* getFunc() const; 624 virtual const char* getFile() const; 625 virtual int getInstanceId() const; 626}; 627``` 628 629*See [Stream improvements over std::ostream](#stream-improvements-over-stdostream).* 630 631*Refer to [Demo](samples/Demo) sample to see what can be written to the log stream.* 632 633## Formatter 634[Formatter](#formatter) is responsible for formatting log data from [Record](#record) into various string representations (binary forms can be used too). There is no base class for formatters, they are implemented as classes with static functions `format` and `header`: 635 636```cpp 637class Formatter 638{ 639public: 640 static util::nstring header(); 641 static util::nstring format(const Record& record); 642}; 643``` 644 645*See [How to implement a custom formatter](#custom-formatter).* 646 647### TxtFormatter 648This is a classic log format available in almost any log library. It is good for console output and it is easy to read without any tools. 649 650``` 6512014-11-11 00:29:06.245 FATAL [4460] [main@22] fatal 6522014-11-11 00:29:06.261 ERROR [4460] [main@23] error 6532014-11-11 00:29:06.261 INFO [4460] [main@24] info 6542014-11-11 00:29:06.261 WARN [4460] [main@25] warning 6552014-11-11 00:29:06.261 DEBUG [4460] [main@26] debug 6562014-11-11 00:29:06.261 INFO [4460] [main@32] This is a message with "quotes"! 6572014-11-11 00:29:06.261 DEBUG [4460] [Object::Object@8] 6582014-11-11 00:29:06.261 DEBUG [4460] [Object::~Object@13] 659``` 660 661### TxtFormatterUtcTime 662This is a variant of [TxtFormatter](#txtformatter) that uses UTC time instead of local time. 663 664### CsvFormatter 665This is the most powerful log format. It can be easily read without any tools (but slighlty harder than [TXT format](#txtformatter)) and can be heavily analyzed if it is opened with a CSV-aware tool (like Excel). One rows can be highlighted according to their cell values, another rows can be hidden, columns can be manipulated and you can even run SQL queries on log data! This is a recommended format if logs are big and require heavy analysis. Also 'this' pointer is shown so object instances can be told apart. 666 667``` 668Date;Time;Severity;TID;This;Function;Message 6692014/11/14;15:22:25.033;FATAL;4188;00000000;main@22;"fatal" 6702014/11/14;15:22:25.033;ERROR;4188;00000000;main@23;"error" 6712014/11/14;15:22:25.033;INFO;4188;00000000;main@24;"info" 6722014/11/14;15:22:25.033;WARN;4188;00000000;main@25;"warning" 6732014/11/14;15:22:25.048;DEBUG;4188;00000000;main@26;"debug" 6742014/11/14;15:22:25.048;INFO;4188;00000000;main@32;"This is a message with ""quotes""!" 6752014/11/14;15:22:25.048;DEBUG;4188;002EF4E3;Object::Object@8; 6762014/11/14;15:22:25.048;DEBUG;4188;002EF4E3;Object::~Object@13; 677``` 678 679*Note: message size is limited to 32000 chars.* 680 681### CsvFormatterUtcTime 682This is a variant of [CsvFormatter](#csvformatter) that uses UTC time instead of local time. 683 684### FuncMessageFormatter 685This format is designed to be used with appenders that provide their own timestamps (like [AndroidAppender](#androidappender) or linux syslog facility). 686 687``` 688main@22: fatal 689main@23: error 690main@24: info 691main@25: warning 692main@26: debug 693main@32: This is a message with "quotes"! 694Object::Object@8: 695Object::~Object@13: 696``` 697 698### MessageOnlyFormatter 699Use this formatter when you're interested only in a log message. 700 701``` 702fatal 703error 704info 705warning 706debug 707This is a message with "quotes"! 708``` 709 710## Converter 711[Converter](#converter) is responsible for conversion of [Formatter](#formatter) output data to a raw buffer (represented as `std::string`). It is used by [RollingFileAppender](#rollingfileappender) to perform a conversion before writing to a file. There is no base class for converters, they are implemented as classes with static functions `convert` and `header`: 712 713```cpp 714class Converter 715{ 716public: 717 static std::string header(const util::nstring& str); 718 static std::string convert(const util::nstring& str); 719}; 720``` 721 722*See [How to implement a custom converter](#custom-converter).* 723 724### UTF8Converter 725[UTF8Converter](#utf8converter) is a default converter in plog. It converts string data to UTF-8 with BOM. 726 727### NativeEOLConverter 728This converter converts `<LF>` line endings to `<CRLF>` on Windows and do nothing on everything else. As a template parameter it accepts another converter that is called next (by default [UTF8Converter](#utf8converter)). 729 730Sample: 731 732```cpp 733plog::RollingFileAppender<plog::TxtFormatter, plog::NativeEOLConverter<> > fileAppender("NativeEOL.log"); 734``` 735 736*Refer to [NativeEOL](samples/NativeEOL) for a complete sample.* 737 738## Appender 739[Appender](#appender) uses [Formatter](#formatter) and [Converter](#converter) to get a desired representation of log data and outputs (appends) it to a file/console/etc. All appenders must implement `IAppender` interface (the only interface in plog): 740 741```cpp 742class IAppender 743{ 744public: 745 virtual ~IAppender(); 746 virtual void write(const Record& record) = 0; 747}; 748``` 749 750*See [How to implement a custom appender](#custom-appender).* 751 752### RollingFileAppender 753This appender outputs log data to a file with rolling behaviour. As template parameters it accepts both [Formatter](#formatter) and [Converter](#converter). 754 755```cpp 756RollingFileAppender<Formatter, Converter>::RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0); 757``` 758 759- `fileName` - a log file name 760- `maxFileSize` - the maximum log file size in bytes 761- `maxFiles` - a number of log files to keep 762 763If `maxFileSize` or `maxFiles` is 0 then rolling behaviour is turned off. 764 765The sample file names produced by this appender: 766 767- mylog.log <== current log file (size < maxFileSize) 768- mylog.1.log <== previous log file (size >= maxFileSize) 769- mylog.2.log <== previous log file (size >= maxFileSize) 770 771Also a file name can be changed at arbitrary moment by calling `setFileName`. 772 773*Note: the lowest `maxFileSize` is 1000 bytes.* 774 775*Note: a log file is created on the first log message.* 776 777### ConsoleAppender 778This appender outputs log data to `stdout`. As a template parameter it accepts [Formatter](#formatter). 779 780```cpp 781ConsoleAppender<Formatter>::ConsoleAppender(); 782``` 783 784### ColorConsoleAppender 785This appender outputs log data to `stdout` using colors that depends on a log message severity level. As a template parameter it accepts [Formatter](#formatter). 786 787```cpp 788ColorConsoleAppender<Formatter>::ColorConsoleAppender(); 789``` 790 791### AndroidAppender 792[AndroidAppender](#androidappender) uses Android logging system to output log data. It can be viewed with [logcat](http://developer.android.com/tools/help/logcat.html) or in a log window of Android IDEs. As a template parameter this appender accepts [Formatter](#formatter) (usually [FuncMessageFormatter](#funcmessageformatter)). 793 794```cpp 795AndroidAppender<Formatter>::AndroidAppender(const char* tag); 796``` 797### EventLogAppender 798This appender outputs log data to the windows event log. It can be viewed with the windows event log viewer. As a template parameter it accepts [Formatter](#formatter). 799The constructor parameter is the event source name - typically it is the name of the application or a subcomponent of the application. It must be unique for the whole system. 800 801```cpp 802EventLogAppender<Formatter>::EventLogAppender(const wchar_t* sourceName); 803``` 804 805[EventLogAppender](#eventlogappender) must be registered in the windows registry before use (before calling the constructor). There is a helper class for that: 806 807```cpp 808bool EventLogAppenderRegistry::add(const wchar_t* sourceName, const wchar_t* logName = L"Application"); 809bool EventLogAppenderRegistry::exists(const wchar_t* sourceName, const wchar_t* logName = L"Application"); 810void EventLogAppenderRegistry::remove(const wchar_t* sourceName, const wchar_t* logName = L"Application"); 811``` 812 813Registry operations are system-wide and require administrator rights. Also they are persistent so can be performed only once (when the application is installed/uninstalled). 814 815### DebugOutputAppender 816[DebugOutputAppender](#debugoutputappender) sends log data to the debugger (works only on Windows). As a template parameter this appender accepts [Formatter](#formatter). 817 818```cpp 819DebugOutputAppender<Formatter>::DebugOutputAppender(); 820``` 821 822# Miscellaneous notes 823 824## Lazy stream evaluation 825Log messages are constructed using lazy stream evaluation. It means that if a log message will be dropped (because of its severity) then stream output operators are not executed. Thus performance penalty of unprinted log messages is negligible. 826 827```cpp 828PLOGD << /* the following statements will be executed only when the logger severity is debug or higher */ ... 829``` 830 831## Stream improvements over std::ostream 832Stream output in plog has several improvements over the standard `std::ostream`: 833 834- handles wide chars/strings: `wchar_t`, `wchar_t*`, `std::wstring` 835- handles `NULL` values for C-strings: `char*` and `wchar_t*` 836- implicitly casts objects to: `std::string` and `std::wstring` (if they have an appropriate cast operator) 837- supports `QString` and `QStringRef` (you need to include Qt headers before plog) 838- supports managed C++ `System::String^` 839 840## Automatic 'this' pointer capture 841'This' pointer is captured automatically to log data and can be printed by [CsvFormatter](#csvformatter). Unfortunately this feature is supported only on msvc 2010 and higher. It's disabled by default (due to some compatibility issues with `__if_exists` C++ extension), to enable it define `PLOG_ENABLE_GET_THIS`. 842 843## Headers to include 844The core plog functionality is provided by inclusion of `plog/Log.h` file. Extra components require inclusion of corresponding extra headers after `plog/Log.h`. 845 846Core components are: 847- [TxtFormatter](#txtformatter)/[TxtFormatterUtcTime](#txtformatterutctime) 848- [CsvFormatter](#csvformatter)/[CsvFormatterUtcTime](#csvformatterutctime) 849- [UTF8Converter](#utf8converter) 850- [NativeEOLConverter](#nativeeolconverter) 851- [RollingFileAppender](#rollingfileappender) 852 853## Unicode 854Plog is unicode aware and wide string friendly. All messages are converted to a system native char type: 855 856- `wchar_t` - on Windows 857- `char` - on all other systems 858 859Also `char` is treated as: 860 861- active code page - on Windows 862- UTF-8 - on all other systems 863 864Internally plog uses `nstring`, `nstringstream` and `nchar` ('n' for native) that are defined as: 865 866```cpp 867#ifdef _WIN32 868 typedef std::wstring nstring; 869 typedef std::wstringstream nstringstream; 870 typedef wchar_t nchar; 871#else 872 typedef std::string nstring; 873 typedef std::stringstream nstringstream; 874 typedef char nchar; 875#endif 876``` 877 878By default all log files are stored in UTF-8 with BOM thanks to [UTF8Converter](#utf8converter). 879 880## Wide string support 881 882Whether `wchar_t`, `wchar_t*`, `std::wstring` can be streamed to log messages or not is controlled by `PLOG_ENABLE_WCHAR_INPUT` macro. Set it to a non-zero value to enable wide string support. By default wide string support is enabled for Windows and disabled for all non-Windows systems. 883 884*Note: wide string support requires linking to `iconv` on macOS.* 885 886## Performance 887Plog is not using any asynchronous techniques so it may slow down your application on large volumes of log messages. 888 889Producing a single log message takes the following amount of time: 890 891|CPU|OS|Time per a log call, microsec| 892|----|----|:----:| 893|AMD Phenom II 1055T @3.5GHz|Windows 2008 R2|12| 894|AMD Phenom II 1055T @3.5GHz|Linux Mint 17.1|8| 895|Intel Core i3-3120M @2.5GHz|Windows 2012 R2|25| 896|Intel Core i5-2500K @4.2GHz|Windows 2008 R2|8| 897|Intel Atom N270 @1.6GHz|Windows 2003|68| 898 899Assume 20 microsec per a log call then 500 log calls per a second will slow down an application by 1%. It is acceptable for the most use cases. 900 901*Refer to [Performance](samples/Performance) for a complete sample.* 902 903## Printf style formatting 904Plog supports printf style formatting: 905 906```cpp 907PLOGI.printf("%d %s", 42, "test"); 908PLOGI.printf(L"%d %S", 42, "test"); // wchar_t version 909``` 910 911## LOG_XXX macro name clashes 912`LOG_XXX` macro names may be in conflict with other libraries (for example [syslog](https://linux.die.net/man/3/syslog)). In such cases you can disable `LOG_XXX` macro by defining `PLOG_OMIT_LOG_DEFINES` and use `PLOG_XXX`. 913 914*Define `PLOG_OMIT_LOG_DEFINES` before `#include <plog/Log.h>` or in the project settings!* 915 916# Extending 917Plog can be easily extended to support new: 918 919- [custom data type](#custom-data-type) 920- [custom appender](#custom-appender) 921- [custom formatter](#custom-formatter) 922- [custom converter](#custom-converter) 923 924## Custom data type 925To output a custom data type to a log message implement the following function: 926 927```cpp 928namespace plog 929{ 930 Record& operator<<(Record& record, const MyType& t); 931} 932``` 933 934*Refer to [CustomType](samples/CustomType) for a complete sample.* 935 936## Custom appender 937A custom appender must implement `IAppender` interface. Also it may accept [Formatter](#formatter) and [Converter](#converter) as template parameters however this is optional. 938 939```cpp 940namespace plog 941{ 942 template<class Formatter> 943 class MyAppender : public IAppender 944 { 945 public: 946 virtual void write(const Record& record); 947 }; 948} 949``` 950 951*Refer to [CustomAppender](samples/CustomAppender) for a complete sample.* 952 953## Custom formatter 954A formatter that is compatible with existing appenders must be a class with 2 static methods: 955 956- `header` - returns a header for a new log 957- `format` - formats [Record](#record) to a string 958 959```cpp 960namespace plog 961{ 962 class MyFormatter 963 { 964 public: 965 static util::nstring header(); 966 static util::nstring format(const Record& record); 967 }; 968} 969``` 970 971*Refer to [CustomFormatter](samples/CustomFormatter) for a complete sample.* 972 973## Custom converter 974A converter must be a class with 2 static methods: 975 976- `header` - converts a header for a new log 977- `convert` - converts log messages 978 979```cpp 980namespace plog 981{ 982 class MyConverter 983 { 984 public: 985 static std::string header(const util::nstring& str); 986 static std::string convert(const util::nstring& str); 987 }; 988} 989``` 990 991*Refer to [CustomConverter](samples/CustomConverter) for a complete sample.* 992 993# Samples 994There are a number of samples that demonstrate various aspects of using plog. They can be found in the [samples](samples) folder: 995 996|Sample|Description| 997|------|-----------| 998|[Android](samples/Android)|Shows how to use [AndroidAppender](#androidappender).| 999|[Chained](samples/Chained)|Shows how to chain a logger in a shared library with the main logger (route messages).| 1000|[ColorConsole](samples/ColorConsole)|Shows how to use [ColorConsoleAppender](#colorconsoleappender).| 1001|[CustomAppender](samples/CustomAppender)|Shows how to implement a custom appender that stores log messages in memory.| 1002|[CustomFormatter](samples/CustomFormatter)|Shows how to implement a custom formatter.| 1003|[CustomConverter](samples/CustomConverter)|Shows how to implement a custom converter that encrypts log messages.| 1004|[CustomType](samples/CustomType)|Shows how to print a custom type to the log stream.| 1005|[DebugOutput](samples/DebugOutput)|Shows how to use [DebugOutputAppender](#debugoutputappender) to write to the windows debug output.| 1006|[Demo](samples/Demo)|Demonstrates log stream abilities, prints various types of messages.| 1007|[EventLog](samples/EventLog)|Shows how to use [EventLogAppender](#eventlogappender) to write to the windows event log.| 1008|[Facilities](samples/Facilities)|Shows how to use logging per facilities via multiple logger instances (useful for big projects).| 1009|[Hello](samples/Hello)|A minimal introduction sample, shows the basic 3 steps to start using plog.| 1010|[Library](samples/Library)|Shows plog usage in static libraries.| 1011|[MultiAppender](samples/MultiAppender)|Shows how to use multiple appenders with the same logger.| 1012|[MultiInstance](samples/MultiInstance)|Shows how to use multiple logger instances, each instance has its own independent configuration.| 1013|[ObjectiveC](samples/ObjectiveC)|Shows that plog can be used in ObjectiveC++.| 1014|[Performance](samples/Performance)|Measures time per a log call.| 1015|[SetFileName](samples/SetFileName)|Shows how to change a log file name at arbitrary moment.| 1016|[Shared](samples/Shared)|Shows how to share logger instances across binary modules.| 1017|[SkipNativeEOL](samples/SkipNativeEOL)|Shows how to skip [NativeEOLConverter](#nativeeolconverter).| 1018|[UtcTime](samples/UtcTime)|Shows how to use UTC time instead of local time.| 1019 1020# References 1021 1022## Competing C++ log libraries 1023 1024- [Boost::Log](http://www.boost.org/doc/libs/release/libs/log/) 1025- [EasyLogging++](https://github.com/easylogging/easyloggingpp) 1026- [g2log](http://www.codeproject.com/Articles/288827/g-log-An-efficient-asynchronous-logger-using-Cplus) 1027- [g3log](https://github.com/KjellKod/g3log) 1028- [glog](https://code.google.com/p/google-glog/) 1029- [Log4cplus](http://sourceforge.net/projects/log4cplus/) 1030- [Log4cpp](http://log4cpp.sourceforge.net/) 1031- [Log4cxx](http://logging.apache.org/log4cxx/) 1032- [Pantheios](http://pantheios.sourceforge.net/) 1033- [spdlog](https://github.com/gabime/spdlog/) 1034- [reckless](https://github.com/mattiasflodin/reckless) 1035- [loguru](https://github.com/emilk/loguru) 1036- [blackhole](https://github.com/3Hren/blackhole) 1037 1038## Tools and useful info 1039 1040- [__if_exists Statement](https://msdn.microsoft.com/en-us/library/x7wy9xh3.aspx) 1041- [Controlling Symbol Visibility](https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/SymbolVisibility.html) 1042- [Gravizo](http://gravizo.com) 1043- [PlantUML](http://plantuml.sourceforge.net) 1044- [DocToc](https://github.com/thlorenz/doctoc) 1045- [CMake](http://www.cmake.org) 1046 1047# License 1048Plog is licensed under the [MPL version 2.0](http://mozilla.org/MPL/2.0/). You can freely use it in your commercial or opensource software. 1049 1050# Version history 1051 1052## Version 1.1.5 (21 Oct 2019) 1053- New: Use `NativeEOLConverter` by default (#145) 1054- New: Add logger `instanceId` into `Record` (#141) 1055- New: Add support for the printf style formatting (#139) 1056- New: Make `severityFromString` case-insensitive 1057- New: Define macro names with "PLOG" instead of "LOG" in order to avoid conflicts with "LOG" names defined in other packages or in system headers (#25, #129) 1058- New: Add option for building samples (ON per default) (#125, #126) 1059- New: Add CMake installer (#121, #122) 1060- New: Add support for `QStringRef` 1061- New: Modernize CMake (#106) 1062- New: Allow rollLogFiles to be called manually (#100, #103) 1063- New: Add ability to use UTC time (#101) 1064- Fix: Disable `PLOG_GET_THIS()` by default (#120, #132) 1065- Fix: Change `RegSetValueExW` prototype to match windows native declaration (void* -> BYTE*) 1066- Fix: Move `System::String^` handler to a free function (#131) 1067- Fix: Making sure we can build standalone under Windows (#123) 1068- Fix: Parse error by ReSharper (#116) 1069- Fix: Parse error by Clang Code Model in Qt Creator (#114) 1070- Fix: Printing CustomType at begin of the stream (#94) 1071- Fix: Make `RollingFileAppender` work with maxFiles set to 1 (#70) 1072- Fix: Clang-tidy nullable issue 1073 1074## Version 1.1.4 (26 Mar 2018) 1075- New: Add `-Wundef` support 1076- New: Add [RTEMS](https://www.rtems.org) support (#87) 1077- New: Add Intel C++ Compiler support (#84) 1078- New: Add FreeBSD support (#83) 1079- New: Add `-Wnon-virtual-dtor` support (#79) 1080- New: Support `ostream` operator<< on Windows as well as `wostream` (#66) 1081- Fix: Fix compilation for Android (#68) 1082- Fix: Fix compiling with CMake 2.8 1083 1084## Version 1.1.3 (09 Aug 2017) 1085- New: Introduce `LOG_ENABLE_WCHAR_INPUT` macro to control wide string support 1086- New: Add support for managed C++ `System::String^` (#63) 1087- New: Add missing macros for logging with severity NONE (#61) 1088- Fix: Unable to build [NativeEOLConverter](#nativeeolconverter)/[UTF8Converter](#utf8converter) using Visual Studio (#59) 1089- Fix: Use `WriteConsoleW` instead of global `setlocale` for writing unicode into Windows console (#58) 1090- Fix: Mention about linking to `iconv` on macOS (#55) 1091- Fix: `IF_LOG` macro didn't work for curly braces blocks 1092 1093## Version 1.1.2 (02 May 2017) 1094- New: Add [NativeEOLConverter](#nativeeolconverter) 1095- New: Add [MessageOnlyFormatter](#messageonlyformatter) 1096- New: Slightly increase log performance on Windows (about 9%). 1097 1098## Version 1.1.1 (17 Apr 2017) 1099- New: Ability to check whether event log registry entry exists (#36) 1100- Fix: Update includes (#47) 1101- Fix: Get rid of `windows.h` dependency (#45, #13) 1102- Fix: Signed unsigned assignment warning (#40) 1103- Fix: Build warning on macOS 10.12 Sierra (#39) 1104 1105## Version 1.1.0 (20 Nov 2016) 1106- Fix: Introduce binary compatible interface to `Record` (WARNING: this is not compatible with 1.0.x version in [Chained mode](#chained-loggers), so don't mix 1.1.x and 1.0.x) (#34) 1107 1108## Version 1.0.2 (19 Nov 2016) 1109- New: Default instanceId can be set via `LOG_DEFAULT_INSTANCE` (#11) 1110- New: Support for `QString` (#30) 1111- New: Support for C++Builder 1112- New: `severityFromString` function (#15) 1113- New: Capture source file name (disabled by default) (#21) 1114- New: Add [DebugOutputAppender](#debugoutputappender) (#33) 1115- New: Add [EventLogAppender](#eventlogappender) (#32) 1116- Fix: Crash on processing Obj-C function name (#12) 1117- Fix: Compatibility with [MinGW](http://www.mingw.org/) (#17) 1118- Fix: `IF_LOG_` macro in if/else leads to miss else branch (#27) 1119- Fix: Thread safety for [ConsoleAppender](#consoleappender)/[ColorConsoleAppender](#colorconsoleappender) (#18, #29) 1120- Fix: Support for stream manipulators like `std::endl` (#31) 1121- Fix: Compatibility with old Visual Studio versions 1122 1123## Version 1.0.1 (01 Nov 2015) 1124- New: Add [ColorConsoleAppender](#colorconsoleappender) 1125- Fix: Compatibility with [Mingw-w64](http://mingw-w64.org/) (#6) 1126- Fix: Log file not created if file name contains Unicode characters in Windows (#7) 1127- Fix: Flush stdout (#4) 1128- Fix: IntelliSense error: expected an identifier (#3) 1129 1130## Version 1.0.0 (19 May 2015) 1131- Initial public release 1132