
Logger C++ library
v1.2.7
Table of contents
- Overview
- Versions
- Library files
- Logger class description
- Example
- Build and connect to your project
- Portability
Overview
Logger C++ library provides logging functions: printing in terminal and(or) printing in file. File Logger.h contains declaration of class Logger which provides logging functions. Logger class provides thread-safe methods to print information to the file or to the terminal. The user can create multiple Logger class objects and the library will provide thread-safe output to the terminal and thread-safe writing to the file (all data will be written to the file in sequence). The Logger class also provides color output to the terminal. When writing the log to a text file, the Logger class controls the size of the file as well as the total size of the files in the directory and deletes old files if the directory size is exceeded. It uses C++17 standard and doesn’t have third-party dependencies.
Logger is general-purpose software: it is not tied to any sector or application, and the developer of a program decides what the program does with it.
Versions
Table 1 - Library versions.
| Version | Release date | What’s new |
|---|---|---|
| 1.0.0 | 13.02.2023 | First version. |
| 1.1.0 | 24.03.2023 | - Added new print option DISABLE. - Example updated. - Documentation updated. |
| 1.2.0 | 08.05.2023 | - Added macro to print file name. |
| 1.2.1 | 20.06.2023 | - Required CMake version updated to 3.13. |
| 1.2.2 | 06.07.2023 | - Documentation updated. - Example updated. - Repository made public. |
| 1.2.3 | 11.08.2023 | - Code clean up. - Updated example. Fixed the same name for log folder as name of executable file. |
| 1.2.4 | 20.03.2024 | - Documentation updated. |
| 1.2.5 | 17.05.2024 | - Documentation updated. |
| 1.2.6 | 20.03.2025 | - Timestamp microsec writing fixed. |
| 1.2.7 | 15.10.2026 | - Fixed data races of the log parameters and of the log file name (setSaveLogParams(…) and print(…) called from different threads). - Fixed use of the non-thread-safe localtime function. - setSaveLogParams(…) does not throw exceptions: it returns FALSE if the folder can not be created or read, and the internal mutex is released on errors. The previous parameters stay in use if FALSE is returned. - setSaveLogParams(…) returns FALSE if maxFolderSizeMb or maxFileSizeMb is less than 1. - Folder size is calculated with 64-bit integers and compared in bytes. Sub folders and symbolic links are not counted and not removed. - Size limit of the log file is kept if several files are created within one second (suffix _1, _2 … is added to the file name). - Microseconds of the time stamp are calculated correctly on Windows. Date and microseconds are taken from the same time point. - A message with zero characters is written to the console completely. - ColourPrint class owns its stream, copy and move operations are deleted, the destructor does not throw exceptions. - Example stops by itself and checks the result of setSaveLogParams(…). - Compiler warnings fixed, compiler and linker hardening options added to CMake files. - Documentation updated. |
Library files
The library is supplied only by source code. The user is given a set of files in the form of a CMake project (repository). The repository structure is shown below:
CMakeLists.txt ---------- Main CMake file of the library.
src --------------------- Folder with library source code.
CMakeLists.txt ------ CMake file of the library.
Logger.cpp ---------- C++ implementation file.
Logger.h ------------ Library main header file.
LoggerVersion.h ----- Header file with library version.
LoggerVersion.h.in -- Service CMake file to generate version file.
example ----------------- Folder for example application.
CMakeLists.txt ------ CMake file of the example.
main.cpp ------------ Source C++ file of the example.
static ------------------ Folder with the image used by this document.
Logger class description
Logger class declaration
Logger.h file contains Logger class declaration. Class declaration:
class Logger
{
public:
/// Get string of current version of library.
static std::string getVersion();
/// Set saving params.
static bool setSaveLogParams(std::string folder,
std::string filePrefix,
int maxFolderSizeMb,
int maxFileSizeMb);
/// Print message through the operator "<<".
ColourPrint print(PrintColor color, PrintFlag flags = PrintFlag::CONSOLE);
};
getVersion method
The getVersion() method returns string of current version of Logger class. Method declaration:
static std::string getVersion();
Method can be used without Logger class instance. Example:
std::cout << "Logger class version: " << rapidpixel::Logger::getVersion() << std::endl;
Console output:
Logger class version: 1.2.7
setSaveLogParams method
The setSaveLogParams(…) static method intended to set global parameters for writing logs into text files. The method sets parameters for all objects of the Logger class. If the method is called in more than one place in the program, the last parameters will be applied to all objects of the Logger class. If the method is not called, all objects of the Logger class will only be able to output information to the console. The method is used without Logger class instance and is thread-safe. The method creates the log folder if it does not exist (the parent folder must exist), removes the oldest files of the folder if the folder size is larger than maxFolderSizeMb and selects the name of the log file. Method declaration:
static bool setSaveLogParams(std::string folder, std::string filePrefix, int maxFolderSizeMb, int maxFileSizeMb);
| Parameter | Description |
|---|---|
| folder | Path to folder. Must not be empty. Example: /home/pi/Log. The folder must be used for log files only: the library deletes the oldest files of any type from the folder. |
| filePrefix | File prefix. The library creates files with names: PREFIX_DATE_TIME.txt. Example: LOG_2023.03.24_15.55.59.txt. If a file with the same name already exists when the library creates a new file (several files within one second), the suffix _1, _2 … is added to the name before the extension. |
| maxFolderSizeMb | Maximum folder size, MB. Must be >= 1. The library controls size of all regular files in folder (sub folders are not touched). If overall size of files in folder > maxFolderSizeMb the library deletes oldest files in folder until the overall size of files <= maxFolderSizeMb. |
| maxFileSizeMb | Maximum file size, MB. Must be >= 1. Library creates files and controls their size. If size of file >= maxFileSizeMb the library creates new file. The size is checked before a message is printed, so a file can be larger than the limit by the size of the last message. |
The folder and the file prefix are used as is: they must come from a trusted configuration, not from untrusted input.
Returns: TRUE if parameters are set or FALSE if not. FALSE is returned if folder is empty, maxFolderSizeMb or maxFileSizeMb is less than 1, or the folder can not be created or read. If FALSE is returned the previous parameters are not changed. The method does not throw exceptions.
Example:
const std::string folder = "LoggerExample";
const std::string filePrefix = "LOG";
const int maxFolderSizeMb = 3;
const int maxFileSizeMb = 1;
if (!rapidpixel::Logger::setSaveLogParams(
folder, filePrefix, maxFolderSizeMb, maxFileSizeMb))
{
std::cout << "Can't set log parameters" << std::endl;
}
print method
The print(…) method intended to print data in console and(or) file. The method provides the same interface as std::cout method and supports all std::cout formats (stream manipulators like std::hex or std::setw can be used, std::endl can not be the first operand: add it after a message). The method allows the user to define the colour of the output to the console and the direction of the output (console, file or file and console). When the library prints in file it adds additional information about local date and time at the beginning of each record (e.g.: 2023-03-24 15:56:52.127716 Something to print). The library does not add a line break: add std::endl or ‘\n’ at the end of a message to move to a new line. The message is printed when the object returned by the method is destroyed (at the end of the expression). Messages are written to the file only after setSaveLogParams(…) returned TRUE. The message is written as is: the library does not filter control characters. Errors of the file system and write errors are not reported: the message is dropped. The method is thread-safe: messages of different threads are not mixed. Method declaration:
ColourPrint print(PrintColor color, PrintFlag flags = PrintFlag::CONSOLE);
| Parameter | Description |
|---|---|
| color | Print color. Colors defined in PrintColor enum. |
| flags | Print flag to define print direction: console, file, console and file or disabled. Print flags defines in PrintFlag enum. |
Returns: object of the ColourPrint class which prints the message added by the operator “«” when the object is destroyed. The object can not be copied or moved. The method throws std::bad_alloc if there is not enough memory.
PrintColor enum
PrintColor enum declared in Logger.h file and defines print colors. Enum declaration:
enum class PrintColor
{
NORMAL,
RED,
GREEN,
YELLOW,
BLUE,
MAGENTA,
CYAN,
WHITE
};
PrintFlag enum
PrintFlag enum defines output options: print to console, print to file, print to file and console or disable printing. Enum declaration:
enum class PrintFlag
{
CONSOLE = 1,
FILE = 2,
CONSOLE_AND_FILE = CONSOLE | FILE,
DISABLE
};
Example
Sample application creates two print threads. The threads and the main thread print information to file, console or both. The application stops by itself after 5 seconds.
#include <atomic>
#include <chrono>
#include <iostream>
#include <string>
#include <thread>
#include "Logger.h"
namespace
{
/// Run time of the example, seconds. The example stops by itself.
constexpr int RUN_TIME_SEC{5};
/// Stop flag of the print threads.
std::atomic<bool> g_stop{false};
/// Print thread function.
void printThreadFunction(rapidpixel::PrintColor colour,
rapidpixel::PrintFlag flag)
{
// Init local logger.
rapidpixel::Logger log;
// Thread function.
int counter = 0;
while (!g_stop)
{
// Print something in console and(or) file.
log.print(colour, flag) << "Print thread output " << counter++ <<
std::endl;
}
}
}
// Entry point.
int main()
{
std::cout << "=================================================" <<
std::endl;
std::cout << "Logger v" << rapidpixel::Logger::getVersion() <<
" example" << std::endl;
std::cout << "=================================================" <<
std::endl;
std::cout << std::endl;
// Init logger.
rapidpixel::Logger log;
// Set Logger global parameters to write log in files.
const std::string folder = "Logs";
const std::string filePrefix = "LOG";
const int maxFolderSizeMb = 3;
const int maxFileSizeMb = 1;
if (!rapidpixel::Logger::setSaveLogParams(
folder, filePrefix, maxFolderSizeMb, maxFileSizeMb))
{
std::cout << "Can't set log parameters" << std::endl;
return 1;
}
// Run print threads.
std::thread printThread1(&printThreadFunction, rapidpixel::PrintColor::GREEN,
rapidpixel::PrintFlag::CONSOLE_AND_FILE);
std::thread printThread2(&printThreadFunction, rapidpixel::PrintColor::BLUE,
rapidpixel::PrintFlag::CONSOLE);
// Main print loop.
int counter = 0;
const auto endTime = std::chrono::steady_clock::now() +
std::chrono::seconds(RUN_TIME_SEC);
while (std::chrono::steady_clock::now() < endTime)
{
// Print something in console and file.
log.print(rapidpixel::PrintColor::RED,
rapidpixel::PrintFlag::CONSOLE_AND_FILE) <<
"Main thread output " << counter++ << std::endl;
// Print something in console only.
log.print(rapidpixel::PrintColor::CYAN,
rapidpixel::PrintFlag::CONSOLE) <<
"[" << __LOGFILENAME__ << "][" << __LINE__ << "] " <<
"Main thread output " << counter++ << std::endl;
}
// Stop print threads.
g_stop = true;
printThread1.join();
printThread2.join();
return 0;
}
Build and connect to your project
Build stand alone
The Logger repository does not depend on other repositories: it contains everything that is needed to build the library and the example. Typical commands to build Logger library and the example:
git clone https://github.com/ConstantRobotics-Ltd/Logger.git
cd Logger
mkdir build
cd build
cmake ..
make
On Windows use cmake –build . –config Release instead of make.
CMake options
Table 2 - CMake options of the repository. In the options below ${PARENT} is the prefix defined by the top-level project (empty if Logger is the top-level project).
| Option | Default | Description |
|---|---|---|
| ${PARENT}_LOGGER | ON | Build the library. |
| ${PARENT}_LOGGER_EXAMPLE | ON (stand alone), OFF (included in other project) | Build the example application. |
| ${PARENT}_SUBMODULE_CACHE_OVERWRITE | ON | Forced rewriting of the options above in the CMake cache. The top-level project sets it to OFF when it includes Logger as a sub-repository. |
Connect to your project
The library has no dependencies: only the Logger repository has to be added to your project. If you want connect Logger library to your CMake project as source code you can make follow. For example, if your repository has structure:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
You can add repository Logger as submodule by command:
cd <your respository folder>
git submodule add https://github.com/ConstantRobotics-Ltd/Logger.git 3rdparty/Logger
In you repository folder will be created folder 3rdparty/Logger which contains files of Logger repository. Structure of your repository:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
Logger
Create CMakeLists.txt file in 3rdparty folder. CMakeLists.txt should contain:
cmake_minimum_required(VERSION 3.13)
################################################################################
## 3RD-PARTY
## dependencies for the project
################################################################################
project(3rdparty LANGUAGES CXX)
################################################################################
## SETTINGS
## basic 3rd-party settings before use
################################################################################
# To inherit the top-level architecture when the project is used as a submodule.
SET(PARENT ${PARENT}_YOUR_PROJECT_3RDPARTY)
# Disable self-overwriting of parameters inside included subdirectories.
SET(${PARENT}_SUBMODULE_CACHE_OVERWRITE OFF CACHE BOOL "" FORCE)
################################################################################
## CONFIGURATION
## 3rd-party submodules configuration
################################################################################
SET(${PARENT}_SUBMODULE_LOGGER ON CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_LOGGER)
SET(${PARENT}_LOGGER ON CACHE BOOL "" FORCE)
SET(${PARENT}_LOGGER_EXAMPLE OFF CACHE BOOL "" FORCE)
endif()
################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
if (${PARENT}_SUBMODULE_LOGGER)
add_subdirectory(Logger)
endif()
File 3rdparty/CMakeLists.txt adds folder Logger to your project and excludes example (Logger class example) from compiling. Structure of your repository:
CMakeLists.txt
src
CMakeList.txt
yourLib.h
yourLib.cpp
3rdparty
CMakeLists.txt
Logger
Next you need include folder 3rdparty in main CMakeLists.txt file of your repository. Add string at the end of your main CMakeLists.txt:
add_subdirectory(3rdparty)
Next you have to include Logger library in your src/CMakeLists.txt file:
target_link_libraries(${PROJECT_NAME} Logger)
Done!
Portability
The library uses only the C++17 standard library (including std::filesystem) and does not depend on byte order or on the CPU architecture. The library was built and its tests were run with MSVC 2022 (x64), GCC 12 and 13 and Clang 15 and 18 (x86-64), and GCC 13 (aarch64, run under qemu). The library is built with standard C++ only (compiler extensions are disabled). With GCC older than 9 a program that uses the library must also link stdc++fs (std::filesystem): the CMake file of the library does not add it, and the link_libraries(…) call for -lstdc++fs in the CMake file of the example comes after the example target is created, so it does not apply to the example. Folder and file names are passed to the file system as narrow strings (on Windows in the current ANSI code page). Time stamps in log files use the local time of the system. The __LOGFILENAME__ macro cuts the path at the last ‘/’ on Linux and at the last ‘' on other platforms.