Update easylogging vendor (#596)

This commit is contained in:
Ben RUBSON 2020-03-03 21:53:48 +01:00 committed by GitHub
parent 4a95d700e6
commit a64397135c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 905 additions and 439 deletions

View File

@ -6,4 +6,4 @@ release.info
bin/* bin/*
logs/* logs/*
experiments/* experiments/*
CMakeLists.txt.user CMakeLists.txt.user*

View File

@ -1,11 +1,72 @@
language: cpp language: cpp
compiler: matrix:
- gcc include:
os: linux # - os: linux
dist: trusty # dist: trusty
# compiler: clang
# before_install:
# - sudo add-apt-repository ppa:kubuntu-ppa/backports -y
# - sudo apt-get -qq update
# - sudo apt-get install -y libgtest-dev valgrind cmake
# - sudo apt-get install --only-upgrade cmake
# - sudo wget https://github.com/google/googletest/archive/release-1.7.0.tar.gz
# - sudo tar xf release-1.7.0.tar.gz
# - cd googletest-release-1.7.0
# - sudo cmake -DBUILD_SHARED_LIBS=ON .
# - sudo make
# - sudo cp -a include/gtest /usr/include
# - sudo cp -a libgtest_main.so libgtest.so /usr/lib/
# - g++ -v
- os: linux
dist: trusty
compiler: gcc
env:
- COMPILER=g++-4.8
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: [g++-4.8, valgrind]
- os: linux
dist: trusty
compiler: gcc
env:
- COMPILER=g++-4.9
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: [g++-4.9, valgrind]
- os: linux
dist: trusty
compiler: gcc
env:
- COMPILER=g++-5
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: [g++-5, valgrind]
- os: linux
dist: trusty
compiler: gcc
env:
- COMPILER=g++-6
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: [g++-6, valgrind]
- os: linux
dist: trusty
compiler: gcc
env:
- COMPILER=g++-7
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: [g++-7, valgrind]
before_install: before_install:
- sudo apt-get -qq update - sudo apt-get -qq update
- sudo apt-get install -y libgtest-dev valgrind - sudo apt-get install -y libgtest-dev valgrind cmake
- sudo wget https://github.com/google/googletest/archive/release-1.7.0.tar.gz - sudo wget https://github.com/google/googletest/archive/release-1.7.0.tar.gz
- sudo tar xf release-1.7.0.tar.gz - sudo tar xf release-1.7.0.tar.gz
- cd googletest-release-1.7.0 - cd googletest-release-1.7.0

View File

@ -0,0 +1,15 @@
# Acknowledgements
This file contains list of contributors and acknowledgement of their efforts in making this library better especially by making acceptable code changes.
If we have missed your name please feel free to add it with contribution link.
| **Github User** | **Contribution** |
|--------------------------------------|----------------------------------|
| [@aparajita](https://github.com/aparajita) | [Separated out .h and .cc file](https://github.com/muflihun/easyloggingpp/pulls?q=is%3Apr+author%3Aaparajita) |
| [@adah1972](https://github.com/adah1972) | [A lot of contributions](https://github.com/muflihun/easyloggingpp/pulls?q=is%3Apr+author%3Aadah1972) |
| [@miguelmartin75](https://github.com/miguelmartin75) | [Issue #11](https://github.com/muflihun/easyloggingpp/issues/11), [PR #16](https://github.com/muflihun/easyloggingpp/pull/16) |
| [@moneromooo-monero](https://github.com/moneromooo-monero) | [A lot of contributions](https://github.com/muflihun/easyloggingpp/pulls?q=is%3Apr+author%3Amoneromooo-monero)|
| [@MonsieurNicolas](https://github.com/MonsieurNicolas) | [PR #593](https://github.com/muflihun/easyloggingpp/pull/593) |
| [@acowley](https://github.com/acowley) | [PR #593](https://github.com/muflihun/easyloggingpp/pull/577) |
| [@rggjan](https://github.com/rggjan) | [PR 561](https://github.com/muflihun/easyloggingpp/pull/561) |
| [@sgtcodfish](https://github.com/sgtcodfish) | Support for emscripten |

View File

@ -1,5 +1,74 @@
# Change Log # Change Log
## [9.96.7] - 24-11-2018
- Adds support for compiling easyloggingpp using Emscripten. This allows the library to be compiled into Javascript or WebAssembly and run in the browser while logging to the browser's Javascript console.
## [9.96.6] - 24-11-2018
- Storage constructor (indirectly) attempts to access elStorage before it's initialized (issue #660) (@Barteks2x)
- Fixed unused variable warning while build without performance logging feature (@wrgcpp)
- Updated license
## [9.96.5] - 07-09-2018
### Fixes
- Check for level enabled when using custom log message (Advanced) (issue #666)
- Ignore interruption signal crash log
## [9.96.4] - 03-04-2018
### Fixes
- Fixes seg fault with global lock (issue #580)
## [9.96.3] - 01-04-2018
### Fixes
- Demangling in GCC fixed
- `ELPP_NO_DEFAULT_LOG_FILE` now logs to null device on major platforms (windows and unix)
- Fixes unused warnings for constants
## [9.96.2] - 27-02-2018
### Updates
- Dispatcher now passes in pointer to log message instead of creating on the fly
- Introduced new constructor for `Writer` for advanced usage (see muflihun/residue)
- Use `std::unordered_map` for memory management instead of `std::map` issue #611
## [9.96.1] - 23-02-2018
### Fixes
- Two loggers writing to same file is undefined behaviour #613
## [9.96.0] - 14-02-2018
### Fixes
- Potential deadlocks in extreme edge case #609
- Respect `MaxLogFileSize` setting even when `ELPP_NO_DEFAULT_LOG_FILE` is set (@MonsieurNicolas)
- Disable log file **initially** when using `ELPP_NO_LOG_TO_FILE`, to be consistent with documentation (@rggjan)
### Updates
- `el::Storage` no longer contains locks as it should be
- Reformatted both files with `astyle`
- License text updated
### Added
- Install a pkg-config `.pc` file (@acowley)
## [9.95.4] - 10-02-2018
### Fixes
- Fix documentation (see PR#597)
- Fix buffer underflow in getBashOutput (see PR#596)
### Updates
- Added new function `Helpers::reserveCustomFormatSpecifier` (see #606)
- Made `DateTime::buildTimeInfo` public for use
## [9.95.3] - 13-10-2017
### Fixes
- Multithreading issue fixed raised from last release at log builder
## [9.95.2] - 12-06-2017
### Fixes
- Build fix for kFreeBSD as suggested in issue #563
- Fixed issue with deadlock on dispatch (see #571)
- Fixed printf like logging with thread safety (see #572)
### Updates
- Added support for AIX (thanks to @apollo13)
## [9.95.0] - 02-08-2017 ## [9.95.0] - 02-08-2017
### Added ### Added
- Added NetBSD as unix [coypoop](https://github.com/muflihun/easyloggingpp/pull/548/commits) - Added NetBSD as unix [coypoop](https://github.com/muflihun/easyloggingpp/pull/548/commits)

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12) cmake_minimum_required(VERSION 2.8.7)
project(Easyloggingpp CXX) project(Easyloggingpp CXX)
@ -26,11 +26,12 @@ option(build_static_lib "Build easyloggingpp as a static library" OFF)
option(lib_utc_datetime "Build library with UTC date/time logging" OFF) option(lib_utc_datetime "Build library with UTC date/time logging" OFF)
set(ELPP_MAJOR_VERSION "9") set(ELPP_MAJOR_VERSION "9")
set(ELPP_MINOR_VERSION "95") set(ELPP_MINOR_VERSION "96")
set(ELPP_PATCH_VERSION "0") set(ELPP_PATCH_VERSION "7")
set(ELPP_VERSION_STRING "${ELPP_MAJOR_VERSION}.${ELPP_MINOR_VERSION}.${ELPP_PATCH_VERSION}") set(ELPP_VERSION_STRING "${ELPP_MAJOR_VERSION}.${ELPP_MINOR_VERSION}.${ELPP_PATCH_VERSION}")
set(ELPP_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in") set(ELPP_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in")
set(ELPP_PKGCONFIG_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
@ -40,6 +41,11 @@ install(FILES
DESTINATION "${ELPP_INCLUDE_INSTALL_DIR}" DESTINATION "${ELPP_INCLUDE_INSTALL_DIR}"
COMPONENT dev) COMPONENT dev)
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/easyloggingpp.pc.cmakein
${CMAKE_CURRENT_BINARY_DIR}/easyloggingpp.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/easyloggingpp.pc DESTINATION "${ELPP_PKGCONFIG_INSTALL_DIR}")
if (build_static_lib) if (build_static_lib)
if (lib_utc_datetime) if (lib_utc_datetime)
add_definitions(-DELPP_UTC_DATETIME) add_definitions(-DELPP_UTC_DATETIME)
@ -47,6 +53,7 @@ if (build_static_lib)
require_cpp11() require_cpp11()
add_library(easyloggingpp STATIC src/easylogging++.cc) add_library(easyloggingpp STATIC src/easylogging++.cc)
set_property(TARGET easyloggingpp PROPERTY POSITION_INDEPENDENT_CODE ON)
install(TARGETS install(TARGETS
easyloggingpp easyloggingpp
@ -68,6 +75,10 @@ if (test)
enable_testing() enable_testing()
if (EMSCRIPTEN)
set(CMAKE_EXE_LINKER_FLAGS "-O2 -s TOTAL_MEMORY=134217728")
endif()
add_executable(easyloggingpp-unit-tests add_executable(easyloggingpp-unit-tests
src/easylogging++.cc src/easylogging++.cc
test/main.cc test/main.cc

View File

@ -1,9 +1,10 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2017 muflihun.com Copyright (c) 2012-2018 Zuhd Web Services
Copyright (c) 2012-2018 @abumusamq
https://github.com/muflihun/ https://github.com/zuhd-org/
https://muflihun.github.io https://zuhd.org
https://muflihun.com https://muflihun.com
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of

View File

@ -1,39 +1,37 @@
‫بسم الله الرَّحْمَنِ الرَّحِيمِ <p align="center">
</p>
![banner] ![banner]
> **Manual For v9.95.0** > **Manual For v9.96.7**
[![Build Status (Develop)](https://img.shields.io/travis/muflihun/easyloggingpp/develop.svg)](https://travis-ci.org/muflihun/easyloggingpp) (`develop`) [![Build Status (Master)](https://img.shields.io/travis/muflihun/easyloggingpp/master.svg)](#build-matrix)
[![Build Status (Develop)](https://img.shields.io/travis/muflihun/easyloggingpp/develop.svg)](#build-matrix)
[![Build status](https://ci.appveyor.com/api/projects/status/sfcgrehu8ypkrun3?svg=true)](https://ci.appveyor.com/project/abumusamq/easyloggingpp-node)
[![Build Status (Master)](https://img.shields.io/travis/muflihun/easyloggingpp/master.svg)](https://travis-ci.org/muflihun/easyloggingpp) (`master`) [![Node Binding Version](https://img.shields.io/github/release/muflihun/easyloggingpp.svg)](https://github.com/muflihun/easyloggingpp/releases/latest)
[![Version](https://img.shields.io/npm/v/easyloggingpp.svg)](https://www.npmjs.com/package/easyloggingpp)
[![Version](https://img.shields.io/github/release/muflihun/easyloggingpp.svg)](https://github.com/muflihun/easyloggingpp/releases/latest)
[![Canon.io](https://img.shields.io/badge/conan.io-easyloggingpp%2F9.95.0-green.svg?logo=data:image/png;base64%2CiVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAA1VBMVEUAAABhlctjlstkl8tlmMtlmMxlmcxmmcxnmsxpnMxpnM1qnc1sn85voM91oM11oc1xotB2oc56pNF6pNJ2ptJ8ptJ8ptN9ptN8p9N5qNJ9p9N9p9R8qtOBqdSAqtOAqtR%2BrNSCrNJ/rdWDrNWCsNWCsNaJs9eLs9iRvNuVvdyVv9yXwd2Zwt6axN6dxt%2Bfx%2BChyeGiyuGjyuCjyuGly%2BGlzOKmzOGozuKoz%2BKqz%2BOq0OOv1OWw1OWw1eWx1eWy1uay1%2Baz1%2Baz1%2Bez2Oe02Oe12ee22ujUGwH3AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgBQkREyOxFIh/AAAAiklEQVQI12NgAAMbOwY4sLZ2NtQ1coVKWNvoc/Eq8XDr2wB5Ig62ekza9vaOqpK2TpoMzOxaFtwqZua2Bm4makIM7OzMAjoaCqYuxooSUqJALjs7o4yVpbowvzSUy87KqSwmxQfnsrPISyFzWeWAXCkpMaBVIC4bmCsOdgiUKwh3JojLgAQ4ZCE0AMm2D29tZwe6AAAAAElFTkSuQmCC)](http://www.conan.io/source/easyloggingpp/9.95.0/memsharded/testing)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/rwDXGcnP1IoCKXrJ)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/muflihun/easyloggingpp/blob/master/LICENCE) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/muflihun/easyloggingpp/blob/master/LICENCE)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/MuflihunDotCom/25)
[![Downloads](https://img.shields.io/github/downloads/muflihun/easyloggingpp/total.svg)](https://github.com/muflihun/easyloggingpp/releases/latest) [![Downloads](https://img.shields.io/github/downloads/muflihun/easyloggingpp/total.svg)](https://github.com/muflihun/easyloggingpp/releases/latest)
[![Donate](https://muflihun.github.io/donate.png?v2)](https://www.paypal.me/zuhd/25)
### Quick Links ### Quick Links
[![download] Latest Release](https://github.com/muflihun/easyloggingpp/releases/latest) [![download] Latest Release](https://github.com/muflihun/easyloggingpp/releases/latest)
[![notes] Changelog](/CHANGELOG.md) [![notes] Changelog](/CHANGELOG.md)
[![samples] Samples](/samples) [![samples] Samples](/samples)
--- ---
### Table of Contents ### Table of Contents
<pre> <pre>
<a href="#introduction">Introduction</a> <a href="#overview">Overview</a>
<a href="#why-yet-another-library">Why yet another library</a> <a href="#why-yet-another-library">Why yet another library</a>
<a href="#features-at-a-glance">Features at a glance</a> <a href="#features-at-a-glance">Features at a glance</a>
<a href="#getting-started">Getting Started</a> <a href="#getting-started">Getting Started</a>
@ -64,7 +62,7 @@
<a href="#network-logging">Network Logging</a> <a href="#network-logging">Network Logging</a>
<a href="#verbose-logging">Verbose Logging</a> <a href="#verbose-logging">Verbose Logging</a>
<a href="#basic-1">Basic</a> <a href="#basic-1">Basic</a>
<a href="#conditional-and-occasional">Conditional and Occasional</a> <a href="#conditional-and-occasional-logging">Conditional and Occasional</a>
<a href="#verbose-level">Verbose Level</a> <a href="#verbose-level">Verbose Level</a>
<a href="#check-if-verbose-logging-is-on">Check If Verbose Logging Is On</a> <a href="#check-if-verbose-logging-is-on">Check If Verbose Logging Is On</a>
<a href="#vmodule">VModule</a> <a href="#vmodule">VModule</a>
@ -101,30 +99,33 @@
<a href="#submitting-patches">Submitting Patches</a> <a href="#submitting-patches">Submitting Patches</a>
<a href="#reporting-a-bug">Reporting a Bug</a> <a href="#reporting-a-bug">Reporting a Bug</a>
<a href="#compatibility">Compatibility</a> <a href="#compatibility">Compatibility</a>
<a href="#build-matrix">Build Matrix</a>
<a href="#licence">Licence</a> <a href="#licence">Licence</a>
<a href="#disclaimer">Disclaimer</a> <a href="#disclaimer">Disclaimer</a>
</pre> </pre>
# Introduction # Overview
Easylogging++ is single header efficient logging library for C++ applications. It is extremely powerful, highly extendable and configurable to user's requirements. It provides ability to [write your own sinks](https://github.com/muflihun/easyloggingpp/tree/master/samples/send-to-network) (referred to as `LogDispatchCallback`). Currently used by hundreds of open-source projects. Easylogging++ is single header efficient logging library for C++ applications. It is extremely powerful, highly extendable and configurable to user's requirements. It provides ability to [write your own _sinks_](/samples/send-to-network) (via featured referred as `LogDispatchCallback`). This library is currently used by [hundreds of open-source projects on github](https://github.com/search?q=%22easylogging%2B%2B.h%22&type=Code&utf8=%E2%9C%93) and other open-source source control management sites.
This manual is for Easylogging++ v9.95.0. For other versions please refer to corresponding [release](https://github.com/muflihun/easyloggingpp/releases) on github. This manual is for Easylogging++ v9.96.7. For other versions please refer to corresponding [release](https://github.com/muflihun/easyloggingpp/releases) on github.
> You may also be interested in [Residue](https://github.com/muflihun/residue/) logging server.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Why yet another library ### Why yet another library
If you are working on a small utility or large project in C++, this library can be handy. Its based on single header and only requires to link to single source file. (Originally it was header-only and was changed to use source file in [issue #445](https://github.com/muflihun/easyloggingpp/issues/445). You can still use header-only in [v9.89](https://github.com/muflihun/easyloggingpp/releases/tag/9.89)). If you are working on a small utility or large project in C++, this library can be handy. Its based on single header and only requires to link to single source file. (Originally it was header-only and was changed to use source file in [issue #445](https://github.com/muflihun/easyloggingpp/issues/445). You can still use header-only in [v9.89](https://github.com/muflihun/easyloggingpp/releases/tag/9.89)).
This library has been designed with various thoughts in mind (i.e, portability, performance, usability, features and easy to setup). This library has been designed with various thoughts in mind (i.e, portability, performance, usability, features and easy to setup).
Why yet another library? Well, answer is pretty straight forward, use it as you wrote it so you can fix issues (if any) as you go or raise them on github. In addition to that, I personally have not seen any logging library based on single-header with such a design where you can configure on the go, extend it to your needs and get fast performance. I have seen other single-header logging libraries for C++ but either they use external libraries, e.g, boost or Qt to support certain features like threading, regular expression or date etc. This library has everything built-in to prevent usage of external libraries, not that I don't like those libraries, in fact I love them, but because not all projects use these libraries, I couldn't take risk of depending on them. Why yet another library? Well, answer is pretty straight forward, use it as you wrote it so you can fix issues (if any) as you go or raise them on github. In addition to that, I personally have not seen any logging library based on single-header with such a design where you can configure on the go, extend it to your needs and get fast performance. I have seen other single-header logging libraries for C++ but either they use external libraries, e.g, boost or Qt to support certain features like threading, regular expression or date etc. This library has everything built-in to prevent usage of external libraries, not that I don't like those libraries, in fact I love them, but because not all projects use these libraries, I couldn't take risk of depending on them.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Features at a glance ### Features at a glance
Easylogging++ is feature-rich containing many features that both typical and advanced developer will require while writing a software; Easylogging++ is feature-rich containing many features that both typical and advanced developer will require while writing a software;
* [Highly configurable](#configuration) * [Highly configurable](#configuration)
* [Extendable](#log-dispatch-callback) * [Extendable](#extending-library)
* Extremely fast * Extremely fast
* [Thread](#multi-threading) and type safe * [Thread](#multi-threading) and type safe
* [Cross-platform](#compatibility) * [Cross-platform](#compatibility)
@ -141,7 +142,7 @@ Easylogging++ is feature-rich containing many features that both typical and adv
* [And many more...](#extra-features) * [And many more...](#extra-features)
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
# Getting Started # Getting Started
### Download ### Download
Download latest version from [Latest Release](https://github.com/muflihun/easyloggingpp/releases/latest) Download latest version from [Latest Release](https://github.com/muflihun/easyloggingpp/releases/latest)
@ -149,7 +150,7 @@ Download latest version from [Latest Release](https://github.com/muflihun/easylo
For other releases, please visit [releases page](https://github.com/muflihun/easyloggingpp/releases). If you application does not support C++11, please consider using [v8.91](https://github.com/muflihun/easyloggingpp/tree/v8.91). This is stable version for C++98 and C++03, just lack some features. For other releases, please visit [releases page](https://github.com/muflihun/easyloggingpp/releases). If you application does not support C++11, please consider using [v8.91](https://github.com/muflihun/easyloggingpp/tree/v8.91). This is stable version for C++98 and C++03, just lack some features.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Quick Start ### Quick Start
In order to get started with Easylogging++, you can follow three easy steps: In order to get started with Easylogging++, you can follow three easy steps:
* Download latest version * Download latest version
@ -189,12 +190,12 @@ make install
Following options are supported by Easylogging++ cmake and you can turn these options on using `-D<option>=ON` Following options are supported by Easylogging++ cmake and you can turn these options on using `-D<option>=ON`
* `lib_utc_datetime` - Defines `ELPP_UTC_DATETIME` * `lib_utc_datetime` - Defines `ELPP_UTC_DATETIME`
* `build_static_lib` - Builds static library for Easylogging++ * `build_static_lib` - Builds static library for Easylogging++
With that said, you will still need `easylogging++.cc` file in order to compile. For header only, please check [v9.89](https://github.com/muflihun/easyloggingpp/releases/tag/9.89) and lower. With that said, you will still need `easylogging++.cc` file in order to compile. For header only, please check [v9.89](https://github.com/muflihun/easyloggingpp/releases/tag/9.89) and lower.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Setting Application Arguments ### Setting Application Arguments
It is always recommended to pass application arguments to Easylogging++. Some features of Easylogging++ require you to set application arguments, e.g, verbose logging to set verbose level or vmodules (explained later). In order to do that you can use helper macro or helper class; It is always recommended to pass application arguments to Easylogging++. Some features of Easylogging++ require you to set application arguments, e.g, verbose logging to set verbose level or vmodules (explained later). In order to do that you can use helper macro or helper class;
@ -205,7 +206,7 @@ int main(int argc, char* argv[]) {
} }
``` ```
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
# Configuration # Configuration
### Level ### Level
In order to start configuring your logging library, you must understand severity levels. Easylogging++ deliberately does not use hierarchical logging in order to fully control what's enabled and what's not. That being said, there is still option to use hierarchical logging using `LoggingFlag::HierarchicalLogging`. Easylogging++ has following levels (ordered for hierarchical levels) In order to start configuring your logging library, you must understand severity levels. Easylogging++ deliberately does not use hierarchical logging in order to fully control what's enabled and what's not. That being said, there is still option to use hierarchical logging using `LoggingFlag::HierarchicalLogging`. Easylogging++ has following levels (ordered for hierarchical levels)
@ -223,7 +224,7 @@ In order to start configuring your logging library, you must understand severity
| Unknown | Only applicable to hierarchical logging and is used to turn off logging completely. | | Unknown | Only applicable to hierarchical logging and is used to turn off logging completely. |
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Configure ### Configure
Easylogging++ is easy to configure. There are three possible ways to do so, Easylogging++ is easy to configure. There are three possible ways to do so,
* Using configuration file * Using configuration file
@ -252,7 +253,7 @@ Following table contains configurations supported by configuration file.
| `Performance_Tracking` | bool | Determines whether or not performance tracking is enabled. This does not depend on logger or level. Performance tracking always uses 'performance' logger unless specified| | `Performance_Tracking` | bool | Determines whether or not performance tracking is enabled. This does not depend on logger or level. Performance tracking always uses 'performance' logger unless specified|
| `Max_Log_File_Size` | size_t | If log file size of corresponding level is >= specified size, log file will be truncated. | | `Max_Log_File_Size` | size_t | If log file size of corresponding level is >= specified size, log file will be truncated. |
| `Log_Flush_Threshold` | size_t | Specifies number of log entries to hold until we flush pending log data | | `Log_Flush_Threshold` | size_t | Specifies number of log entries to hold until we flush pending log data |
Please do not use double-quotes anywhere in comment, you might end up in unexpected behaviour. Please do not use double-quotes anywhere in comment, you might end up in unexpected behaviour.
@ -272,7 +273,7 @@ Sample Configuration File
FORMAT = "%datetime{%d/%M} %func %msg" FORMAT = "%datetime{%d/%M} %func %msg"
``` ```
##### Explanation ##### Explanation
Configuration file contents in above sample is straightforward. We start with `GLOBAL` level in order to override all the levels. Any explicitly defined subsequent level will override configuration from `GLOBAL`. For example, all the levels except for `DEBUG` have the same format, i.e, datetime and log message. For `DEBUG` level, we have only date (with day and month), source function and log message. The rest of configurations for `DEBUG` are used from `GLOBAL`. Also, notice `{%d/%M}` in `DEBUG` format above, if you do not specify date format, default format is used. Default values of date/time is `%d/%M/%Y %h:%m:%s,%g` For more information on these format specifiers, please refer to [Date/Time Format Specifier](#datetime-format-specifiers) section below Configuration file contents in above sample is straightforward. We start with `GLOBAL` level in order to override all the levels. Any explicitly defined subsequent level will override configuration from `GLOBAL`. For example, all the levels except for `DEBUG` have the same format, i.e, datetime and log message. For `DEBUG` level, we have only date (with day and month), source function and log message. The rest of configurations for `DEBUG` are used from `GLOBAL`. Also, notice `{%d/%M}` in `DEBUG` format above, if you do not specify date format, default format is used. Default values of date/time is `%d/%M/%Y %h:%m:%s,%g` For more information on these format specifiers, please refer to [Date/Time Format Specifier](#datetime-format-specifiers) section below
##### Usage ##### Usage
@ -295,7 +296,7 @@ int main(int argc, const char** argv) {
> Your configuration file can be converted to `el::Configurations` object (using constructor) that can be used where ever it is needed (like in above example). > Your configuration file can be converted to `el::Configurations` object (using constructor) that can be used where ever it is needed (like in above example).
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
#### Using el::Configurations Class #### Using el::Configurations Class
You can set configurations or reset configurations; You can set configurations or reset configurations;
```c++ ```c++
@ -307,7 +308,7 @@ int main(int argc, const char** argv) {
el::Configurations defaultConf; el::Configurations defaultConf;
defaultConf.setToDefault(); defaultConf.setToDefault();
// Values are always std::string // Values are always std::string
defaultConf.set(el::Level::Info, defaultConf.set(el::Level::Info,
el::ConfigurationType::Format, "%datetime %level %msg"); el::ConfigurationType::Format, "%datetime %level %msg");
// default logger uses default configurations // default logger uses default configurations
el::Loggers::reconfigureLogger("default", defaultConf); el::Loggers::reconfigureLogger("default", defaultConf);
@ -323,7 +324,7 @@ int main(int argc, const char** argv) {
> Configuration just needs to be set once. If you are happy with default configuration, you may use it as well. > Configuration just needs to be set once. If you are happy with default configuration, you may use it as well.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
#### Using In line Configurations #### Using In line Configurations
Inline configuration means you can set configurations in `std::string` but make sure you add all the new line characters etc. This is not recommended because it's always messy. Inline configuration means you can set configurations in `std::string` but make sure you add all the new line characters etc. This is not recommended because it's always messy.
```c++ ```c++
@ -335,12 +336,12 @@ c.parseFromText("*GLOBAL:\n FORMAT = %level %msg");
> Above code only sets Configurations object, you still need to re-configure logger/s using this configurations. > Above code only sets Configurations object, you still need to re-configure logger/s using this configurations.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Default Configurations ### Default Configurations
If you wish to have a configuration for existing and future loggers, you can use `el::Loggers::setDefaultConfigurations(el::Configurations& configurations, bool configureExistingLoggers = false)`. This is useful when you are working on fairly large scale, or using a third-party library that is already using Easylogging++. Any newly created logger will use default configurations. If you wish to configure existing loggers as well, you can set second argument to `true` (it defaults to `false`). If you wish to have a configuration for existing and future loggers, you can use `el::Loggers::setDefaultConfigurations(el::Configurations& configurations, bool configureExistingLoggers = false)`. This is useful when you are working on fairly large scale, or using a third-party library that is already using Easylogging++. Any newly created logger will use default configurations. If you wish to configure existing loggers as well, you can set second argument to `true` (it defaults to `false`).
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Global Configurations ### Global Configurations
`Level::Global` is nothing to do with global configurations, it is concept where you can register configurations for all/or some loggers and even register new loggers using configuration file. Syntax of configuration file is: `Level::Global` is nothing to do with global configurations, it is concept where you can register configurations for all/or some loggers and even register new loggers using configuration file. Syntax of configuration file is:
``` ```
@ -365,7 +366,7 @@ int main(void) {
Please note, it is not possible to register new logger using global configuration without defining its configuration. You must define at least single configuration. Other ways to register loggers are discussed in [Logging](#logging) section below. Please note, it is not possible to register new logger using global configuration without defining its configuration. You must define at least single configuration. Other ways to register loggers are discussed in [Logging](#logging) section below.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Logging Format Specifiers ### Logging Format Specifiers
You can customize format of logging using following specifiers: You can customize format of logging using following specifiers:
@ -388,10 +389,10 @@ You can customize format of logging using following specifiers:
| `%msg` | Actual log message | | `%msg` | Actual log message |
| `%` | Escape character (e.g, %%level will write %level) | | `%` | Escape character (e.g, %%level will write %level) |
* Subject to compiler's availability of certain macros, e.g, `__LINE__`, `__FILE__` etc * Subject to compiler's availability of certain macros, e.g, `__LINE__`, `__FILE__` etc
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Date/Time Format Specifiers ### Date/Time Format Specifiers
You can customize date/time format using following specifiers You can customize date/time format using following specifiers
@ -435,8 +436,8 @@ int main(void) {
``` ```
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
###Logging Flags ### Logging Flags
Form some parts of logging you can set logging flags; here are flags supported: Form some parts of logging you can set logging flags; here are flags supported:
| Flag | Description | | Flag | Description |
@ -456,6 +457,7 @@ Form some parts of logging you can set logging flags; here are flags supported:
| `CreateLoggerAutomatically (4096)` | Creates logger automatically when not available. | | `CreateLoggerAutomatically (4096)` | Creates logger automatically when not available. |
| `AutoSpacing (8192)` | Automatically adds spaces. E.g, `LOG(INFO) << "DODGE" << "THIS!";` will output "DODGE THIS!"| | `AutoSpacing (8192)` | Automatically adds spaces. E.g, `LOG(INFO) << "DODGE" << "THIS!";` will output "DODGE THIS!"|
| `FixedTimeFormat (16384)` | Applicable to performace tracking only - this prevents formatting time. E.g, `1001 ms` will be logged as is, instead of formatting it as `1.01 sec`| | `FixedTimeFormat (16384)` | Applicable to performace tracking only - this prevents formatting time. E.g, `1001 ms` will be logged as is, instead of formatting it as `1.01 sec`|
| `IgnoreSigInt (32768)` | When application crashes ignore Interruption signal |
You can set/unset these flags by using static `el::Loggers::addFlag` and `el::Loggers::removeFlag`. You can check to see if certain flag is available by using `el::Loggers::hasFlag`, all these functions take strongly-typed enum `el::LoggingFlag` You can set/unset these flags by using static `el::Loggers::addFlag` and `el::Loggers::removeFlag`. You can check to see if certain flag is available by using `el::Loggers::hasFlag`, all these functions take strongly-typed enum `el::LoggingFlag`
@ -482,7 +484,11 @@ Following table will explain all command line arguments that you may use to defi
### Configuration Macros ### Configuration Macros
Some of logging options can be set by macros, this is a thoughtful decision, for example if we have `ELPP_THREAD_SAFE` defined, all the thread-safe functionalities are enabled otherwise disabled (making sure over-head of thread-safety goes with it). To make it easy to remember and prevent possible conflicts, all the macros start with `ELPP_` Some of logging options can be set by macros, this is a thoughtful decision, for example if we have `ELPP_THREAD_SAFE` defined, all the thread-safe functionalities are enabled otherwise disabled (making sure over-head of thread-safety goes with it). To make it easy to remember and prevent possible conflicts, all the macros start with `ELPP_`
NOTE: All the macros either need to be defined before `#include "easylogging++"` - but this gets hard and unreadable if project is getting bigger so we recommend you define all these macros using `-D` option of compiler, for example in case of `g++` you will do `g++ source.cpp ... -DELPP_SYSLOG -DELPP_THREAD_SAFE ...` **NOTE:** All the macros can be defined in one of the following ways:
1. Define macros using `-D` option of compiler, for example in case of `g++` you will do `g++ source.cpp ... -DELPP_SYSLOG -DELPP_THREAD_SAFE ...` (**recommended way**)
2. Define macros inside `"easylogging++.h"` ([defining macros in other files won't work](https://github.com/muflihun/easyloggingpp/issues/590#issuecomment-346753951))
| Macro Name | Description | | Macro Name | Description |
|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| |------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
@ -503,7 +509,7 @@ NOTE: All the macros either need to be defined before `#include "easylogging++"`
| `ELPP_FORCE_ENV_VAR_FROM_BASH` | If environment variable could not be found, force using alternative bash command to find value, e.g, `whoami` for username. (DO NOT USE THIS MACRO WITH `LD_PRELOAD` FOR LIBRARIES THAT ARE ALREADY USING Easylogging++ OR YOU WILL END UP IN STACK OVERFLOW FOR PROCESSES (`popen`) (see [issue #87](https://github.com/muflihun/easyloggingpp/issues/87) for details)) | | `ELPP_FORCE_ENV_VAR_FROM_BASH` | If environment variable could not be found, force using alternative bash command to find value, e.g, `whoami` for username. (DO NOT USE THIS MACRO WITH `LD_PRELOAD` FOR LIBRARIES THAT ARE ALREADY USING Easylogging++ OR YOU WILL END UP IN STACK OVERFLOW FOR PROCESSES (`popen`) (see [issue #87](https://github.com/muflihun/easyloggingpp/issues/87) for details)) |
| `ELPP_DEFAULT_LOG_FILE` | Full filename where you want initial files to be created. You need to embed value of this macro with quotes, e.g, `-DELPP_DEFAULT_LOG_FILE='"logs/el.gtest.log"'` Note the double quotes inside single quotes, double quotes are the values for `const char*` and single quotes specifies value of macro | | `ELPP_DEFAULT_LOG_FILE` | Full filename where you want initial files to be created. You need to embed value of this macro with quotes, e.g, `-DELPP_DEFAULT_LOG_FILE='"logs/el.gtest.log"'` Note the double quotes inside single quotes, double quotes are the values for `const char*` and single quotes specifies value of macro |
| `ELPP_NO_LOG_TO_FILE` | Disable logging to file initially| | `ELPP_NO_LOG_TO_FILE` | Disable logging to file initially|
| `ELPP_NO_DEFAULT_LOG_FILE` | If you dont want to initialize library with default log file, define this macro. But be sure to configure your logger with propery log filename or you will end up getting heaps of errors when trying to log to file (and `TO_FILE` is configured to `true`) | | `ELPP_NO_DEFAULT_LOG_FILE` | If you dont want to initialize library with default log file, define this macro. This will log to null device for unix and windows. In other platforms you may get error and you will need to use `ELPP_DEFAULT_LOG_FILE`. (PR for other platform's null devices are most welcomed) |
| `ELPP_FRESH_LOG_FILE` | Never appends log file whenever log file is created (Use with care as it may cause some unexpected result for some users) | | `ELPP_FRESH_LOG_FILE` | Never appends log file whenever log file is created (Use with care as it may cause some unexpected result for some users) |
| `ELPP_DEBUG_ERRORS` | If you wish to find out internal errors raised by Easylogging++ that can be because of configuration or something else, you can enable them by defining this macro. You will get your errors on standard output i.e, terminal or command prompt. | | `ELPP_DEBUG_ERRORS` | If you wish to find out internal errors raised by Easylogging++ that can be because of configuration or something else, you can enable them by defining this macro. You will get your errors on standard output i.e, terminal or command prompt. |
| `ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS` | Forcefully disables custom format specifiers | | `ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS` | Forcefully disables custom format specifiers |
@ -515,21 +521,22 @@ NOTE: All the macros either need to be defined before `#include "easylogging++"`
| `ELPP_NO_CHECK_MACROS` | Do not define the *CHECK* macros | | `ELPP_NO_CHECK_MACROS` | Do not define the *CHECK* macros |
| `ELPP_NO_DEBUG_MACROS` | Do not define the *DEBUG* macros | | `ELPP_NO_DEBUG_MACROS` | Do not define the *DEBUG* macros |
| `ELPP_UTC_DATETIME` | Uses UTC time instead of local time (essentially uses `gmtime` instead of `localtime` and family functions) | `ELPP_UTC_DATETIME` | Uses UTC time instead of local time (essentially uses `gmtime` instead of `localtime` and family functions)
| `ELPP_NO_GLOBAL_LOCK` | Do not lock the whole storage on dispatch. This should be used with care. See [issue #580](https://github.com/muflihun/easyloggingpp/issues/580)|
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Reading Configurations ### Reading Configurations
If you wish to read configurations of certain logger, you can do so by using `typedConfigurations()` function in Logger class. If you wish to read configurations of certain logger, you can do so by using `typedConfigurations()` function in Logger class.
```c++ ```c++
el::Logger* l = el::Loggers::getLogger("default"); el::Logger* l = el::Loggers::getLogger("default");
bool enabled = l->typedConfigurations()->enabled(el::Level::Info); bool enabled = l->typedConfigurations()->enabled(el::Level::Info);
// Or to read log format/pattern // Or to read log format/pattern
std::string format = std::string format =
l->typedConfigurations()->logFormat(el::Level::Info).format(); l->typedConfigurations()->logFormat(el::Level::Info).format();
``` ```
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
# Logging # Logging
Logging in easylogging++ is done using collection of macros. This is to make it easier for user and to prevent them knowing about unnecessary greater details of how things are done. Logging in easylogging++ is done using collection of macros. This is to make it easier for user and to prevent them knowing about unnecessary greater details of how things are done.
@ -572,9 +579,9 @@ UpdateManager::UpdateManager {
> You can also write logs by using `Logger` class directly. This feature is available on compilers that support variadic templates. You can explore more by looking at `samples/STL/logger-log-functions.cpp`. > You can also write logs by using `Logger` class directly. This feature is available on compilers that support variadic templates. You can explore more by looking at `samples/STL/logger-log-functions.cpp`.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Conditional Logging ### Conditional Logging
Easylogging++ provides certain aspects of logging, one these aspects is conditional logging, i.e, log will be written only if certain condition fulfils. This comes very handy in some situations. Easylogging++ provides certain aspects of logging, one these aspects is conditional logging, i.e, log will be written only if certain condition fulfils. This comes very handy in some situations.
Helper macros end with _IF; Helper macros end with _IF;
* `LOG_IF(condition, LEVEL)` * `LOG_IF(condition, LEVEL)`
* `CLOG_IF(condition, LEVEL, logger ID)` * `CLOG_IF(condition, LEVEL, logger ID)`
@ -591,7 +598,7 @@ CLOG_IF(true, INFO, "performance") << "Always logged (performance logger)"
Same macros are available for verbose logging with `V` in the beginning, i.e, `VLOG_IF` and `CVLOG_IF`. see verbose logging section below for further information. You may have as complicated conditions as you want depending on your need. Same macros are available for verbose logging with `V` in the beginning, i.e, `VLOG_IF` and `CVLOG_IF`. see verbose logging section below for further information. You may have as complicated conditions as you want depending on your need.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Occasional Logging ### Occasional Logging
Occasional logging is another useful aspect of logging with Easylogging++. This means a log will be written if it's hit certain times or part of certain times, e.g, every 10th hit or 100th hit or 2nd hit. Occasional logging is another useful aspect of logging with Easylogging++. This means a log will be written if it's hit certain times or part of certain times, e.g, every 10th hit or 100th hit or 2nd hit.
Helper macros end with `_EVERY_N`; Helper macros end with `_EVERY_N`;
@ -624,14 +631,14 @@ for (int i = 1; i <= 100; ++i) {
> Same versions of macros are available for `DEBUG` only mode, these macros start with `D` (for debug) followed by the same name. e.g, `DLOG` to log only in debug mode (i.e, when `_DEBUG` is defined or `NDEBUG` is undefined) > Same versions of macros are available for `DEBUG` only mode, these macros start with `D` (for debug) followed by the same name. e.g, `DLOG` to log only in debug mode (i.e, when `_DEBUG` is defined or `NDEBUG` is undefined)
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### `printf` Like Logging ### `printf` Like Logging
For compilers that support C++11's variadic templates, ability to log like "printf" is available. This is done by using `Logger` class. This feature is thread and type safe (as we do not use any macros like `LOG(INFO)` etc) For compilers that support C++11's variadic templates, ability to log like "printf" is available. This is done by using `Logger` class. This feature is thread and type safe (as we do not use any macros like `LOG(INFO)` etc)
This is done in two steps: This is done in two steps:
1. Pulling registered logger using `el::Loggers::getLogger(<logger_id>);` 1. Pulling registered logger using `el::Loggers::getLogger(<logger_id>);`
2. Using one of logging functions 2. Using one of logging functions
The only difference from `printf` is that logging using these functions require `%v` for each arg (This is for type-safety); instead of custom format specifiers. You can escape this by `%%v` The only difference from `printf` is that logging using these functions require `%v` for each arg (This is for type-safety); instead of custom format specifiers. You can escape this by `%%v`
Following are various function signatures: Following are various function signatures:
@ -642,7 +649,7 @@ Following are various function signatures:
* `fatal(const char*, const T&, const Args&...)` * `fatal(const char*, const T&, const Args&...)`
* `trace(const char*, const T&, const Args&...)` * `trace(const char*, const T&, const Args&...)`
* `verbose(int vlevel, const char*, const T&, const Args&...)` * `verbose(int vlevel, const char*, const T&, const Args&...)`
#### Simple example: #### Simple example:
```c++ ```c++
@ -676,7 +683,7 @@ Verbose logging is useful in every software to record more information than usua
* `CVLOG(verbose-level, logger ID)` * `CVLOG(verbose-level, logger ID)`
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
#### Conditional and Occasional Logging #### Conditional and Occasional Logging
Verbose logging also has conditional and occasional logging aspects i.e, Verbose logging also has conditional and occasional logging aspects i.e,
* `VLOG_IF(condition, verbose-level)` * `VLOG_IF(condition, verbose-level)`
@ -690,14 +697,14 @@ Verbose logging also has conditional and occasional logging aspects i.e,
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
#### Verbose-Level #### Verbose-Level
Verbose level is level of verbosity that can have range of 1-9. Verbose level will not be active unless you either set application arguments for it. Please read through [Application Arguments](#application-arguments) section to understand more about verbose logging. Verbose level is level of verbosity that can have range of 1-9. Verbose level will not be active unless you either set application arguments for it. Please read through [Application Arguments](#application-arguments) section to understand more about verbose logging.
In order to change verbose level on the fly, please use `Loggers::setVerboseLevel(base::type::VerboseLevel)` aka `Loggers::setVerboseLevel(int)` function. (You can check current verbose level by `Loggers::verboseLevel()` In order to change verbose level on the fly, please use `Loggers::setVerboseLevel(base::type::VerboseLevel)` aka `Loggers::setVerboseLevel(int)` function. (You can check current verbose level by `Loggers::verboseLevel()`
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
#### Check If Verbose Logging Is On #### Check If Verbose Logging Is On
You can use a macro `VLOG_IS_ON(verbose-level)` to check to see if certain logging is on for source file for specified verbose level. This returns boolean that you can embed into if condition. You can use a macro `VLOG_IS_ON(verbose-level)` to check to see if certain logging is on for source file for specified verbose level. This returns boolean that you can embed into if condition.
```c++ ```c++
@ -707,7 +714,7 @@ if (VLOG_IS_ON(2)) {
``` ```
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
#### VModule #### VModule
VModule is functionality for verbose logging (as mentioned in above table) where you can specify verbosity by modules/source file. Following are some examples with explanation; Any of vmodule below starts with `-vmodule=` and `LoggingFlag::DisableVModulesExtensions` flag not set. Vmodule can completely be disabled by adding flag `LoggingFlag::DisableVModules` VModule is functionality for verbose logging (as mentioned in above table) where you can specify verbosity by modules/source file. Following are some examples with explanation; Any of vmodule below starts with `-vmodule=` and `LoggingFlag::DisableVModulesExtensions` flag not set. Vmodule can completely be disabled by adding flag `LoggingFlag::DisableVModules`
@ -728,7 +735,7 @@ Example with no `LoggingFlag::AllowVerboseIfModuleNotSpecified` flag;
In order to change vmodules on the fly (instead of via command line args) - use `Loggers::setVModules(const char*)` where `const char*` represents the modules e.g, `main=3,parser*=4` (as per above example) In order to change vmodules on the fly (instead of via command line args) - use `Loggers::setVModules(const char*)` where `const char*` represents the modules e.g, `main=3,parser*=4` (as per above example)
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Registering New Loggers ### Registering New Loggers
Loggers are unique in logger repository by ID. You can register new logger the same way as you would get logger. Using `getLogger(.., ..)` from `el::Loggers` helper class. This function takes two params, first being ID and second being boolean (optional) to whether or not to register new logger if does not already exist and returns pointer to existing (or newly created) el::Logger class. This second param is optional and defaults to true. If you set it to false and logger does not exist already, it will return nullptr. Loggers are unique in logger repository by ID. You can register new logger the same way as you would get logger. Using `getLogger(.., ..)` from `el::Loggers` helper class. This function takes two params, first being ID and second being boolean (optional) to whether or not to register new logger if does not already exist and returns pointer to existing (or newly created) el::Logger class. This second param is optional and defaults to true. If you set it to false and logger does not exist already, it will return nullptr.
@ -740,7 +747,7 @@ By default, Easylogging++ registers three loggers (+ an internal logger);
If you wish to register a new logger, say e.g, with ID `business` If you wish to register a new logger, say e.g, with ID `business`
```c++ ```c++
el::Logger* businessLogger = el::Loggers::getLogger("business"); el::Logger* businessLogger = el::Loggers::getLogger("business");
``` ```
This will register a new logger if it does not already exist otherwise it will get an existing one. But if you have passed in `false` to the second param and logger does not already exist, `businessLogger` will be nullptr. This will register a new logger if it does not already exist otherwise it will get an existing one. But if you have passed in `false` to the second param and logger does not already exist, `businessLogger` will be nullptr.
@ -763,11 +770,11 @@ Although this is a rare situation but if you wish to get list of all the logger
### Sharing Logging Repository ### Sharing Logging Repository
For advance logging, you can share your logging repositories to shared or static libraries, or even from library to application. This is rare case but a very good example is as follows; For advance logging, you can share your logging repositories to shared or static libraries, or even from library to application. This is rare case but a very good example is as follows;
Let's say we have an application that uses easylogging++ and has it's own configuration, now you are importing library that uses easylogging++ and wants to access logging repository of main application. You can do this using two ways; Let's say we have an application that uses easylogging++ and has its own configuration, now you are importing library that uses easylogging++ and wants to access logging repository of main application. You can do this using two ways;
* Instead of using `INITIALIZE_EASYLOGGINGPP` you use `SHARE_EASYLOGGINGPP(access-function-to-repository)` * Instead of using `INITIALIZE_EASYLOGGINGPP` you use `SHARE_EASYLOGGINGPP(access-function-to-repository)`
* Instead of using `INITIALIZE_EASYLOGGINGPP` you use `INITIALIZE_NULL_EASYLOGGINGPP` and then `el::Helpers::setStorage(el::base::type::StoragePointer)` * Instead of using `INITIALIZE_EASYLOGGINGPP` you use `INITIALIZE_NULL_EASYLOGGINGPP` and then `el::Helpers::setStorage(el::base::type::StoragePointer)`
After you share repository, you can reconfigure the only repository (i.e, the one that is used by application and library both), and use both to write logs. After you share repository, you can reconfigure the only repository (i.e, the one that is used by application and library both), and use both to write logs.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
@ -780,14 +787,14 @@ Some features require you to define macros (marked as prerequisite in each secti
### Performance Tracking ### Performance Tracking
Prerequisite: Define macro `ELPP_FEATURE_PERFORMANCE_TRACKING` Prerequisite: Define macro `ELPP_FEATURE_PERFORMANCE_TRACKING`
One of the most notable features of Easylogging++ is its ability to track performance of your function or block of function. One of the most notable features of Easylogging++ is its ability to track performance of your function or block of function.
Please note, this is not backward compatible as previously we had macros that user must had defined in order to track performance and I am sure many users had avoided in doing so. (Read v8.91 ReadMe for older way of doing it) Please note, this is not backward compatible as previously we had macros that user must had defined in order to track performance and I am sure many users had avoided in doing so. (Read v8.91 ReadMe for older way of doing it)
The new way of tracking performance is much easier and reliable. All you need to do is use one of two macros from where you want to start tracking. The new way of tracking performance is much easier and reliable. All you need to do is use one of two macros from where you want to start tracking.
* `TIMED_FUNC(obj-name)` * `TIMED_FUNC(obj-name)`
* `TIMED_SCOPE(obj-name, block-name)` * `TIMED_SCOPE(obj-name, block-name)`
* `TIMED_BLOCK(obj-name, block-name)` * `TIMED_BLOCK(obj-name, block-name)`
An example that just uses usleep An example that just uses usleep
```c++ ```c++
void performHeavyTask(int iter) { void performHeavyTask(int iter) {
TIMED_FUNC(timerObj); TIMED_FUNC(timerObj);
@ -801,7 +808,7 @@ void performHeavyTask(int iter) {
} }
} }
``` ```
The result of above execution for iter = 10, is as following The result of above execution for iter = 10, is as following
``` ```
06:22:31,368 INFO Executed [heavy-iter] in [10 ms] 06:22:31,368 INFO Executed [heavy-iter] in [10 ms]
@ -819,7 +826,7 @@ The result of above execution for iter = 10, is as following
In the above example, we have used both the macros. In line-2 we have `TIMED_FUNC` with object pointer name timerObj and line-7 we have TIMED_SCOPE with object pointer name `timerBlkObj` and block name `heavy-iter`. Notice how block name is thrown out to the logs with every hit. (Note: `TIMED_FUNC` is `TIMED_SCOPE` with block name = function name) In the above example, we have used both the macros. In line-2 we have `TIMED_FUNC` with object pointer name timerObj and line-7 we have TIMED_SCOPE with object pointer name `timerBlkObj` and block name `heavy-iter`. Notice how block name is thrown out to the logs with every hit. (Note: `TIMED_FUNC` is `TIMED_SCOPE` with block name = function name)
You might wonder why do we need object name. Well easylogging++ performance tracking feature takes it further and provides ability to add, what's called checkpoints. You might wonder why do we need object name. Well easylogging++ performance tracking feature takes it further and provides ability to add, what's called checkpoints.
Checkpoints have two macros: Checkpoints have two macros:
* `PERFORMANCE_CHECKPOINT(timed-block-obj-name)` * `PERFORMANCE_CHECKPOINT(timed-block-obj-name)`
* `PERFORMANCE_CHECKPOINT_WITH_ID(timed-block-obj-name, id)` * `PERFORMANCE_CHECKPOINT_WITH_ID(timed-block-obj-name, id)`
@ -894,12 +901,12 @@ Notes:
> You may be interested in [python script to parse performance logs](https://github.com/muflihun/easyloggingpp/issues/206) > You may be interested in [python script to parse performance logs](https://github.com/muflihun/easyloggingpp/issues/206)
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
#### Conditional Performance Tracking #### Conditional Performance Tracking
If you want to enable performance tracking for certain conditions only, e.g. based on a certain verbosity level, you can use the variants `TIMED_FUNC_IF` or `TIMED_SCOPE_IF`. If you want to enable performance tracking for certain conditions only, e.g. based on a certain verbosity level, you can use the variants `TIMED_FUNC_IF` or `TIMED_SCOPE_IF`.
A verbosity level example is given below A verbosity level example is given below
```c++ ```c++
void performHeavyTask(int iter) { void performHeavyTask(int iter) {
// enable performance tracking for verbosity level 4 or higher // enable performance tracking for verbosity level 4 or higher
@ -909,7 +916,7 @@ If you want to enable performance tracking for certain conditions only, e.g. bas
``` ```
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
#### Make Use of Performance Tracking Data #### Make Use of Performance Tracking Data
If you wish to capture performance tracking data right after it is finished, you can do so by extending `el::PerformanceTrackingCallback`. If you wish to capture performance tracking data right after it is finished, you can do so by extending `el::PerformanceTrackingCallback`.
@ -922,9 +929,9 @@ In order to install this handler, use `void Helpers::installPerformanceTrackingC
### Log File Rotating ### Log File Rotating
Easylogging++ has ability to roll out (or throw away / rotate) log files if they reach certain limit. You can configure this by setting `Max_Log_File_Size`. See Configuration section above. Easylogging++ has ability to roll out (or throw away / rotate) log files if they reach certain limit. You can configure this by setting `Max_Log_File_Size`. See Configuration section above.
If you are having failure in log-rollout, you may have failed to add flag i.e, `el::LoggingFlags::StrictLogFileSizeCheck`. Rollout checking happens when Easylogging++ flushes the log file, or, if you have added the flag `el::LoggingFlags::StrictLogFileSizeCheck`, at each log output.
This feature has it's own section in this reference manual because you can do stuffs with the file being thrown away. This is useful, for example if you wish to back this file up etc. This feature has its own section in this reference manual because you can do stuffs with the file being thrown away. This is useful, for example if you wish to back this file up etc.
This can be done by using `el::Helpers::installPreRollOutCallback(const PreRollOutCallback& handler)` where `PreRollOutCallback` is typedef of type `std::function<void(const char*, std::size_t)>`. Please note following if you are using this feature This can be done by using `el::Helpers::installPreRollOutCallback(const PreRollOutCallback& handler)` where `PreRollOutCallback` is typedef of type `std::function<void(const char*, std::size_t)>`. Please note following if you are using this feature
There is a [sample](/samples/STL/logrotate.cpp) available that you can use as basis. There is a [sample](/samples/STL/logrotate.cpp) available that you can use as basis.
@ -936,7 +943,7 @@ There is a [sample](/samples/STL/logrotate.cpp) available that you can use as ba
### Crash Handling ### Crash Handling
Prerequisite: Define macro `ELPP_FEATURE_CRASH_LOG` Prerequisite: Define macro `ELPP_FEATURE_CRASH_LOG`
Easylogging++ provides ability to handle unexpected crashes for GCC compilers. This is active by default and can be disabled by defining macro `ELPP_DISABLE_DEFAULT_CRASH_HANDLING`. By doing so you are telling library not to handle any crashes. Later on if you wish to handle crash yourself, you can assign crash handler of type void func(int) where int is signal caught. Easylogging++ provides ability to handle unexpected crashes for GCC compilers. This is active by default and can be disabled by defining macro `ELPP_DISABLE_DEFAULT_CRASH_HANDLING`. By doing so you are telling library not to handle any crashes. Later on if you wish to handle crash yourself, you can assign crash handler of type void func(int) where int is signal caught.
Following signals are handled; Following signals are handled;
* SIGABRT (If `ELPP_HANDLE_SIGABRT` macro is defined) * SIGABRT (If `ELPP_HANDLE_SIGABRT` macro is defined)
@ -959,7 +966,7 @@ Following are some useful macros that you can define to change the behaviour
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
#### Installing Custom Crash Handlers #### Installing Custom Crash Handlers
You can use your own crash handler by using `el::Helpers::setCrashHandler(const el::base::debug::CrashHandler::Handler&);`. You can use your own crash handler by using `el::Helpers::setCrashHandler(const el::base::debug::CrashHandler::Handler&);`.
> Make sure to abort application at the end of your crash handler using `el::Helpers::crashAbort(int)`. If you fail to do so, you will get into endless loop of crashes. > Make sure to abort application at the end of your crash handler using `el::Helpers::crashAbort(int)`. If you fail to do so, you will get into endless loop of crashes.
@ -1001,14 +1008,14 @@ Prerequisite: Define macro `ELPP_FEATURE_CRASH_LOG`
Easylogging++ supports stack trace printing for GCC compilers. You can print stack trace at anytime by calling `el::base::debug::StackTrace()`, formatting will be done automatically. Note, if you are using non-GCC compiler, you will end-up getting empty output. Easylogging++ supports stack trace printing for GCC compilers. You can print stack trace at anytime by calling `el::base::debug::StackTrace()`, formatting will be done automatically. Note, if you are using non-GCC compiler, you will end-up getting empty output.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Multi-threading ### Multi-threading
Prerequisite: Define macro `ELPP_THREAD_SAFE` Prerequisite: Define macro `ELPP_THREAD_SAFE`
Easylogging++ is thread-safe. By default thread-safety is disabled. You can enable it by defining `ELPP_THREAD_SAFE` otherwise you will see unexpected results. This is intentional to make library efficient for single threaded application. Easylogging++ is thread-safe. By default thread-safety is disabled. You can enable it by defining `ELPP_THREAD_SAFE` otherwise you will see unexpected results. This is intentional to make library efficient for single threaded application.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### CHECK Macros ### CHECK Macros
Easylogging++ supports CHECK macros, with these macros you can quickly check whether certain condition fulfills or not. If not Easylogging++ writes FATAL log, causing application to stop (unless defined macro to prevent stopping application on fatal). Easylogging++ supports CHECK macros, with these macros you can quickly check whether certain condition fulfills or not. If not Easylogging++ writes FATAL log, causing application to stop (unless defined macro to prevent stopping application on fatal).
@ -1021,7 +1028,7 @@ Easylogging++ supports CHECK macros, with these macros you can quickly check whe
| `CHECK_GT(a, b)` | Greater than e.g, `CHECK_GT(2, 1) << "How 2 is not greater than 1?";` | | `CHECK_GT(a, b)` | Greater than e.g, `CHECK_GT(2, 1) << "How 2 is not greater than 1?";` |
| `CHECK_LE(a, b)` | Less than or equal e.g, `CHECK_LE(1, 1) << "1 is not equal or less than 1";` | | `CHECK_LE(a, b)` | Less than or equal e.g, `CHECK_LE(1, 1) << "1 is not equal or less than 1";` |
| `CHECK_GE(a, b)` | Greater than or equal e.g, `CHECK_GE(1, 1) << "1 is not equal or greater than 1";` | | `CHECK_GE(a, b)` | Greater than or equal e.g, `CHECK_GE(1, 1) << "1 is not equal or greater than 1";` |
| `CHECK_NOTNULL(pointer)` | Ensures pointer is not null - if OK returns pointer e.g, `explicit MyClass(Obj* obj) : m_obj(CHECK_NOT_NULL(obj)) {}` | | `CHECK_NOTNULL(pointer)` | Ensures pointer is not null. This function does not return anything |
| `CHECK_STREQ(str1, str2)` | C-string equality (case-sensitive) e.g, `CHECK_STREQ(argv[1], "0") << "First arg cannot be 0";` | | `CHECK_STREQ(str1, str2)` | C-string equality (case-sensitive) e.g, `CHECK_STREQ(argv[1], "0") << "First arg cannot be 0";` |
| `CHECK_STRNE(str1, str2)` | C-string inequality (case-sensitive) e.g, `CHECK_STRNE(username1, username2) << "Usernames cannot be same";` | | `CHECK_STRNE(str1, str2)` | C-string inequality (case-sensitive) e.g, `CHECK_STRNE(username1, username2) << "Usernames cannot be same";` |
| `CHECK_STRCASEEQ(str1, str2)` | C-string inequality (*case-insensitive*) e.g, `CHECK_CASESTREQ(argv[1], "Z") << "First arg cannot be 'z' or 'Z'";` | | `CHECK_STRCASEEQ(str1, str2)` | C-string inequality (*case-insensitive*) e.g, `CHECK_CASESTREQ(argv[1], "Z") << "First arg cannot be 'z' or 'Z'";` |
@ -1031,7 +1038,7 @@ Easylogging++ supports CHECK macros, with these macros you can quickly check whe
> Same versions of macros are available for `DEBUG` only mode, these macros start with `D` (for debug) followed by the same name. e.g, `DCHECK` to check only in debug mode (i.e, when `_DEBUG` is defined or `NDEBUG` is undefined) > Same versions of macros are available for `DEBUG` only mode, these macros start with `D` (for debug) followed by the same name. e.g, `DCHECK` to check only in debug mode (i.e, when `_DEBUG` is defined or `NDEBUG` is undefined)
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Logging perror() ### Logging perror()
Easylogging++ supports `perror()` styled logging using `PLOG(LEVEL)`, `PLOG_IF(Condition, LEVEL)`, and `PCHECK()` using `default` logger; and for custom logger use `CPLOG(LEVEL, LoggerId)`, `CPLOG_IF(Condition, LEVEL, LoggerId)`. This will append `: log-error [errno]` in the end of log line. Easylogging++ supports `perror()` styled logging using `PLOG(LEVEL)`, `PLOG_IF(Condition, LEVEL)`, and `PCHECK()` using `default` logger; and for custom logger use `CPLOG(LEVEL, LoggerId)`, `CPLOG_IF(Condition, LEVEL, LoggerId)`. This will append `: log-error [errno]` in the end of log line.
@ -1067,7 +1074,7 @@ Syslog support for Easylogging++ only supports following levels; each level is c
Following levels are not supported and correspond to `LOG_NOTICE`: TRACE, whereas VERBOSE level is completely not supported Following levels are not supported and correspond to `LOG_NOTICE`: TRACE, whereas VERBOSE level is completely not supported
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### STL Logging ### STL Logging
Prerequisite: Define macro `ELPP_STL_LOGGING` Prerequisite: Define macro `ELPP_STL_LOGGING`
@ -1098,7 +1105,7 @@ Some C++11 specific templates are supported by further explicit macro definition
Standard manipulators are also supported, in addition std::stringstream is also supported. Standard manipulators are also supported, in addition std::stringstream is also supported.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Qt Logging ### Qt Logging
Prerequisite: Define macro `ELPP_QT_LOGGING` Prerequisite: Define macro `ELPP_QT_LOGGING`
@ -1170,6 +1177,10 @@ You may also have a look at wxWidgets sample
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Extending Library ### Extending Library
You can extend this library using various callback handlers and inheritable classes.
A perfect example of using these features is the logging server built with this library. It's called [Residue](https://github.com/muflihun/residue/) that is feature rich. In fact, you may be interested in using that instead of this library for your medium to large sized projects.
#### Logging Your Own Class #### Logging Your Own Class
You can log your own classes by extending `el::Loggable` class and implementing pure-virtual function `void log(std::ostream& os) const`. Following example shows a good way to extend a class. You can log your own classes by extending `el::Loggable` class and implementing pure-virtual function `void log(std::ostream& os) const`. Following example shows a good way to extend a class.
@ -1259,13 +1270,87 @@ This may not be practically best implementation but you get the point.
### Manually Flushing and Rolling Log Files ### Manually Flushing and Rolling Log Files
You can manually flush log files using `el::Logger::flush()` (to flush single logger with all referencing log files) or `el::Loggers::flushAll()` (to flush all log files for all levels). You can manually flush log files using `el::Logger::flush()` (to flush single logger with all referencing log files) or `el::Loggers::flushAll()` (to flush all log files for all levels).
If you have not set flag `LoggingFlag::StrictLogFileSizeCheck` for some reason, you can manually check for log files that need rolling; by using `el::Helpers::validateFileRolling(el::Logger*, const el::Level&)`. If you have not set flag `LoggingFlag::StrictLogFileSizeCheck` for some reason, you can manually check for log files that need rolling; by using `el::Helpers::validateFileRolling(el::Logger*, const el::Level&)`.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Log Dispatch Callback ### Log Dispatch Callback
If you wish to capture log message right after it is dispatched, you can do so by having a class that extends `el::LogDispatchCallback` and implement the pure-virtual functions, then install it at anytime using `el::Helpers::installLogDispatchCallback<T>(const std::string&)`. If you wish to uninstall a pre-installed handler with same ID, you can do so by using `el::Helpers::uninstallLogDispatchCallback<T>(const std::string&)` If you wish to capture log message right after it is dispatched, you can do so by having a class that extends `el::LogDispatchCallback` and implement the pure-virtual functions, then install it at anytime using `el::Helpers::installLogDispatchCallback<T>(const std::string&)`. If you wish to uninstall a pre-installed handler with same ID, you can do so by using `el::Helpers::uninstallLogDispatchCallback<T>(const std::string&)`
You can use this feature to send it to custom destinations e.g, log stash or TCP client etc.
You can also look at [send-to-network](/samples/send-to-network) sample for practical usage of this.
```c++
// samples/send-to-network/network-logger.cpp
#include "easylogging++.h"
#include <boost/asio.hpp>
INITIALIZE_EASYLOGGINGPP
class Client
{
boost::asio::io_service* io_service;
boost::asio::ip::tcp::socket socket;
public:
Client(boost::asio::io_service* svc, const std::string& host, const std::string& port)
: io_service(svc), socket(*io_service)
{
boost::asio::ip::tcp::resolver resolver(*io_service);
boost::asio::ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query(host, port));
boost::asio::connect(this->socket, endpoint);
};
void send(std::string const& message) {
socket.send(boost::asio::buffer(message));
}
};
class NetworkDispatcher : public el::LogDispatchCallback
{
public:
void updateServer(const std::string& host, int port) {
m_client = std::unique_ptr<Client>(new Client(&m_svc, host, std::to_string(port)));
}
protected:
void handle(const el::LogDispatchData* data) noexcept override {
m_data = data;
// Dispatch using default log builder of logger
dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(),
m_data->dispatchAction() == el::base::DispatchAction::NormalLog));
}
private:
const el::LogDispatchData* m_data;
boost::asio::io_service m_svc;
std::unique_ptr<Client> m_client;
void dispatch(el::base::type::string_t&& logLine) noexcept
{
m_client->send(logLine);
}
};
int main() {
el::Helpers::installLogDispatchCallback<NetworkDispatcher>("NetworkDispatcher");
// you can uninstall default one by
// el::Helpers::uninstallLogDispatchCallback<el::base::DefaultLogDispatchCallback>("DefaultLogDispatchCallback");
// Set server params
NetworkDispatcher* dispatcher = el::Helpers::logDispatchCallback<NetworkDispatcher>("NetworkDispatcher");
dispatcher->setEnabled(true);
dispatcher->updateServer("127.0.0.1", 9090);
// Start logging and normal program...
LOG(INFO) << "First network log";
// You can even use a different logger, say "network" and send using a different log pattern
}
```
> DO NOT LOG ANYTHING IN THIS HANDLER OR YOU WILL END UP IN INFINITE-LOOP > DO NOT LOG ANYTHING IN THIS HANDLER OR YOU WILL END UP IN INFINITE-LOOP
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
@ -1276,7 +1361,7 @@ If you wish to capture event of logger registration (and potentially want to rec
> DO NOT LOG ANYTHING IN THIS HANDLER > DO NOT LOG ANYTHING IN THIS HANDLER
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Asynchronous Logging ### Asynchronous Logging
Prerequisite: Define macro `ELPP_EXPERIMENTAL_ASYNC` Prerequisite: Define macro `ELPP_EXPERIMENTAL_ASYNC`
@ -1288,7 +1373,7 @@ Please note:
* You should not rely on asynchronous logging in production, this is because feature is in experiemental stages. * You should not rely on asynchronous logging in production, this is because feature is in experiemental stages.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Helper Classes ### Helper Classes
There are static helper classes available to make it easy to do stuffs; There are static helper classes available to make it easy to do stuffs;
@ -1298,18 +1383,18 @@ There are static helper classes available to make it easy to do stuffs;
You can do various cool stuffs using functions in these classes, see [this issue](https://github.com/muflihun/easyloggingpp/issues/210) for instance. You can do various cool stuffs using functions in these classes, see [this issue](https://github.com/muflihun/easyloggingpp/issues/210) for instance.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
# Contribution # Contribution
### Submitting Patches ### Submitting Patches
You can submit patches to `develop` branch and we will try and merge them. Since it's based on single header, it can be sometimes difficult to merge without having merge conflicts. You can submit patches to `develop` branch and we will try and merge them. Since it's based on single header, it can be sometimes difficult to merge without having merge conflicts.
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
### Reporting a Bug ### Reporting a Bug
If you have found a bug and wish to report it, feel free to do so at [github issue tracker](https://github.com/muflihun/easyloggingpp/issues?state=open). I will try to look at it as soon as possible. Some information should be provided to make it easy to reproduce; If you have found a bug and wish to report it, feel free to do so at [github issue tracker](https://github.com/muflihun/easyloggingpp/issues?state=open). I will try to look at it as soon as possible. Some information should be provided to make it easy to reproduce;
* Platform (OS, Compiler) * Platform (OS, Compiler)
* Log file location * Log file location
* Macros defined (on compilation) OR simple compilation * Macros defined (on compilation) OR simple compilation
* Please assign issue label. * Please assign issue label.
Try to provide as much information as possible. Any bug with no clear information will be ignored and closed. Try to provide as much information as possible. Any bug with no clear information will be ignored and closed.
@ -1322,7 +1407,7 @@ Easylogging++ requires a decent C++0x complient compiler. Some compilers known t
| ***** | Compiler/Platform | Notes | | ***** | Compiler/Platform | Notes |
|---------|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| |---------|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
|![gcc] | GCC 4.7+ | Stack trace logging. Very close to support GCC 4.6 if it had supported strong enum types casting to underlying type. It causes internal compiler error. | |![gcc] | GCC 4.6.4+ | Stack trace logging. Very close to support GCC 4.6.0 if it had supported strong enum types casting to underlying type. It causes internal compiler error. |
|![llvm] | Clang++ 3.1+ | Stack trace logging only with gcc compliant. | |![llvm] | Clang++ 3.1+ | Stack trace logging only with gcc compliant. |
|![intel] | Intel C++ 13.0+ | Workarounds to support: Use if instead of switch on strong enum type. No `final` keyword etc. Stack trace logging only with gcc compliant | |![intel] | Intel C++ 13.0+ | Workarounds to support: Use if instead of switch on strong enum type. No `final` keyword etc. Stack trace logging only with gcc compliant |
|![vcpp] | Visual C++ 11.0+ | Tested with VS2012, VS2013; Use of argument templates instead of variadic templates. CRT warnings control. No stack trace logging. | |![vcpp] | Visual C++ 11.0+ | Tested with VS2012, VS2013; Use of argument templates instead of variadic templates. CRT warnings control. No stack trace logging. |
@ -1348,6 +1433,7 @@ Operating systems that have been tested are shown in table below. Easylogging++
|![android] | Android | Tested with C4droid (g++) on Galaxy Tab 2 | |![android] | Android | Tested with C4droid (g++) on Galaxy Tab 2 |
|![raspberrypi] | RaspberryPi 7.6 | Tested with 7.6.2-1.1 (gcc version 4.9.1 (Raspbian 4.9.1-1)) by contributor | |![raspberrypi] | RaspberryPi 7.6 | Tested with 7.6.2-1.1 (gcc version 4.9.1 (Raspbian 4.9.1-1)) by contributor |
|![solaris] | Solaris i86 | Tested by contributor | |![solaris] | Solaris i86 | Tested by contributor |
|![aix] | IBM AIX | Support added by contributor |
Easylogging++ has also been tested with following C++ libraries; Easylogging++ has also been tested with following C++ libraries;
@ -1359,15 +1445,33 @@ Easylogging++ has also been tested with following C++ libraries;
|![gtkmm] | gtkmm | Tested with gtkmm 2.4 | |![gtkmm] | gtkmm | Tested with gtkmm 2.4 |
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
## Build Matrix
| Branch | Platform | Build Status |
| -------- |:------------:|:------------:|
| `develop` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `clang++` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/develop/1)](https://travis-ci.org/muflihun/easyloggingpp) |
| `develop` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `g++-4.9` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/develop/2)](https://travis-ci.org/muflihun/easyloggingpp) |
| `develop` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `g++-5` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/develop/3)](https://travis-ci.org/muflihun/easyloggingpp) |
| `develop` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `g++-6` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/develop/4)](https://travis-ci.org/muflihun/easyloggingpp) |
| `develop` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `g++-7` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/develop/5)](https://travis-ci.org/muflihun/easyloggingpp) |
| `master` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `clang++` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/master/1)](https://travis-ci.org/muflihun/easyloggingpp) |
| `master` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `g++-4.9` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/master/2)](https://travis-ci.org/muflihun/easyloggingpp) |
| `master` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `g++-5` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/master/3)](https://travis-ci.org/muflihun/easyloggingpp) |
| `master` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `g++-6` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/master/4)](https://travis-ci.org/muflihun/easyloggingpp) |
| `master` | GNU/Linux 4.4 / Ubuntu 4.8.4 64-bit / `g++-7` | [![Build Status](https://travis-matrix-badges.herokuapp.com/repos/muflihun/easyloggingpp/branches/master/5)](https://travis-ci.org/muflihun/easyloggingpp) |
[![top] Goto Top](#table-of-contents)
# Licence # Licence
``` ```
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2017 muflihun.com Copyright (c) 2012-2018 Zuhd Web Services
Copyright (c) 2012-2018 @abumusamq
https://github.com/muflihun/easyloggingpp https://github.com/zuhd-org/
https://labs.muflihun.com https://zuhd.org
https://muflihun.com https://muflihun.com
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
@ -1389,7 +1493,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
``` ```
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
# Disclaimer # Disclaimer
Icons used in this manual (in compatibility section) are solely for information readability purposes. I do not own these icons. If anyone has issues with usage of these icon, please feel free to contact me via company's email and I will look for an alternative. Company's email address is required so that I can verify the ownership, any other email address for this purpose will be ignored. Icons used in this manual (in compatibility section) are solely for information readability purposes. I do not own these icons. If anyone has issues with usage of these icon, please feel free to contact me via company's email and I will look for an alternative. Company's email address is required so that I can verify the ownership, any other email address for this purpose will be ignored.
@ -1397,39 +1501,39 @@ Icons used in this manual (in compatibility section) are solely for information
[![top] Goto Top](#table-of-contents) [![top] Goto Top](#table-of-contents)
[banner]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/banner.png?v=4 [banner]: https://muflihun.github.io/easyloggingpp/images/banner.png?v=4
[ubuntu]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/ubuntu.png?v=2 [ubuntu]: https://muflihun.github.io/easyloggingpp/images/icons/ubuntu.png?v=2
[mint]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/linux-mint.png?v=2 [mint]: https://muflihun.github.io/easyloggingpp/images/icons/linux-mint.png?v=2
[freebsd]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/free-bsd.png?v=2 [freebsd]: https://muflihun.github.io/easyloggingpp/images/icons/free-bsd.png?v=2
[sl]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/scientific-linux.png?v=2 [sl]: https://muflihun.github.io/easyloggingpp/images/icons/scientific-linux.png?v=2
[fedora]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/fedora.png?v=3 [fedora]: https://muflihun.github.io/easyloggingpp/images/icons/fedora.png?v=3
[mac]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/mac-osx.png?v=2 [mac]: https://muflihun.github.io/easyloggingpp/images/icons/mac-osx.png?v=2
[winxp]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/windowsxp.png?v=2 [winxp]: https://muflihun.github.io/easyloggingpp/images/icons/windowsxp.png?v=2
[win7]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/windows7.png?v=2 [win7]: https://muflihun.github.io/easyloggingpp/images/icons/windows7.png?v=2
[win8]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/windows8.png?v=2 [win8]: https://muflihun.github.io/easyloggingpp/images/icons/windows8.png?v=2
[win10]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/windows10.png?v=2 [win10]: https://muflihun.github.io/easyloggingpp/images/icons/windows10.png?v=2
[qt]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/qt.png?v=3 [qt]: https://muflihun.github.io/easyloggingpp/images/icons/qt.png?v=3
[boost]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/boost.png?v=3 [boost]: https://muflihun.github.io/easyloggingpp/images/icons/boost.png?v=3
[wxwidgets]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/wxwidgets.png?v=3 [wxwidgets]: https://muflihun.github.io/easyloggingpp/images/icons/wxwidgets.png?v=3
[devcpp]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/devcpp.png?v=3 [devcpp]: https://muflihun.github.io/easyloggingpp/images/icons/devcpp.png?v=3
[gtkmm]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/gtkmm.png?v=3 [gtkmm]: https://muflihun.github.io/easyloggingpp/images/icons/gtkmm.png?v=3
[tdm]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/tdm.png?v=3 [tdm]: https://muflihun.github.io/easyloggingpp/images/icons/tdm.png?v=3
[raspberrypi]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/raspberry-pi.png?v=3 [raspberrypi]: https://muflihun.github.io/easyloggingpp/images/icons/raspberry-pi.png?v=3
[solaris]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/solaris.png?v=3 [solaris]: https://muflihun.github.io/easyloggingpp/images/icons/solaris.png?v=3
[aix]: https://muflihun.github.io/easyloggingpp/images/icons/aix.png?v=4
[gcc]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/gcc.png?v=4 [gcc]: https://muflihun.github.io/easyloggingpp/images/icons/gcc.png?v=4
[mingw]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/mingw.png?v=2 [mingw]: https://muflihun.github.io/easyloggingpp/images/icons/mingw.png?v=2
[cygwin]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/cygwin.png?v=2 [cygwin]: https://muflihun.github.io/easyloggingpp/images/icons/cygwin.png?v=2
[vcpp]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/vcpp.png?v=2 [vcpp]: https://muflihun.github.io/easyloggingpp/images/icons/vcpp.png?v=2
[llvm]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/llvm.png?v=2 [llvm]: https://muflihun.github.io/easyloggingpp/images/icons/llvm.png?v=2
[intel]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/intel.png?v=2 [intel]: https://muflihun.github.io/easyloggingpp/images/icons/intel.png?v=2
[android]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/icons/android.png?v=2 [android]: https://muflihun.github.io/easyloggingpp/images/icons/android.png?v=2
[manual]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/help.png?v=3 [manual]: https://muflihun.github.io/easyloggingpp/images/help.png?v=3
[download]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/download.png?v=2 [download]: https://muflihun.github.io/easyloggingpp/images/download.png?v=2
[samples]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/sample.png?v=2 [samples]: https://muflihun.github.io/easyloggingpp/images/sample.png?v=2
[notes]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/notes.png?v=4 [notes]: https://muflihun.github.io/easyloggingpp/images/notes.png?v=4
[top]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/up.png?v=4 [top]: https://muflihun.github.io/easyloggingpp/images/up.png?v=4
[www]: https://raw.githubusercontent.com/muflihun/easyloggingpp.muflihun.com/master/images/logo-www.png?v=6 [www]: https://muflihun.github.io/easyloggingpp/images/logo-www.png?v=6

View File

@ -6,9 +6,10 @@
# If ${EASYLOGGINGPP_USE_STATIC_LIBS} is ON then static libs are searched. # If ${EASYLOGGINGPP_USE_STATIC_LIBS} is ON then static libs are searched.
# In these cases ${EASYLOGGINGPP_LIBRARY} is also defined # In these cases ${EASYLOGGINGPP_LIBRARY} is also defined
# #
# (c) 2017 Muflihun Labs # (c) 2017-2018 Zuhd Web Services
# #
# https://github.com/muflihun/easyloggingpp # https://github.com/zuhd-org/easyloggingpp
# https://zuhd.org
# https://muflihun.com # https://muflihun.com
# #

View File

@ -0,0 +1,6 @@
Name: easyloggingpp
Description: Feature-rich single header C++ logging library
Version: @ELPP_VERSION_STRING@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=@ELPP_INCLUDE_INSTALL_DIR@
Cflags: -I${includedir}

View File

@ -1,2 +1,3 @@
bin/* bin/*
logs/* logs/*
myeasylog.log

View File

@ -10,6 +10,7 @@ macro="$macro -DELPP_LOG_UNORDERED_MAP"
macro="$macro -DELPP_FEATURE_CRASH_LOG" macro="$macro -DELPP_FEATURE_CRASH_LOG"
macro="$macro -DELPP_LOGGING_FLAGS_FROM_ARG" macro="$macro -DELPP_LOGGING_FLAGS_FROM_ARG"
macro="$macro -DELPP_FEATURE_ALL" macro="$macro -DELPP_FEATURE_ALL"
macro="$macro -DELPP_NO_GLOBAL_LOCK"
# macro="$macro -DELPP_DEFAULT_LOG_FILE=\"/a/path/that/does/not/exist/f.log\"" # macro="$macro -DELPP_DEFAULT_LOG_FILE=\"/a/path/that/does/not/exist/f.log\""
if [ "$2" = "" ];then if [ "$2" = "" ];then
@ -18,7 +19,7 @@ else
COMPILER=$2 COMPILER=$2
fi fi
CXX_STD='-std=c++0x -pthread' CXX_STD='-std=c++11'
if [ "$FILE" = "" ]; then if [ "$FILE" = "" ]; then
echo "Please provide filename to compile" echo "Please provide filename to compile"
@ -27,7 +28,7 @@ fi
echo "Compiling... [$FILE]" echo "Compiling... [$FILE]"
COMPILE_LINE="$COMPILER $FILE easylogging++.cc -o bin/$FILE.bin $macro $CXX_STD -Wall -Wextra -pedantic -pedantic-errors -Werror -Wfatal-errors -Wundef -Wunused" COMPILE_LINE="$COMPILER $FILE easylogging++.cc -o bin/$FILE.bin $macro $CXX_STD -pthread -Wall -Wextra -pedantic -pedantic-errors -Werror -Wfatal-errors -Wundef -Wunused"
echo " $COMPILE_LINE" echo " $COMPILE_LINE"

View File

@ -16,9 +16,12 @@ int main(int argc, char** argv) {
el::Logger* defaultLogger = el::Loggers::getLogger("default"); el::Logger* defaultLogger = el::Loggers::getLogger("default");
LOG(INFO) << "Blah";
std::vector<int> i; std::vector<int> i;
i.push_back(1); i.push_back(1);
i.push_back(2); i.push_back(2);
defaultLogger->info("simple %v", "msg");
defaultLogger->warn("My first ultimate log message %v %v %v", 123, 222, i); defaultLogger->warn("My first ultimate log message %v %v %v", 123, 222, i);
// Escaping // Escaping
@ -27,5 +30,6 @@ int main(int argc, char** argv) {
defaultLogger->verbose(1, "test verbose"); defaultLogger->verbose(1, "test verbose");
defaultLogger->verbose(1, "test verbose with args %v", 2); defaultLogger->verbose(1, "test verbose with args %v", 2);
return 0; return 0;
} }

View File

@ -0,0 +1,39 @@
//
// This file is part of Easylogging++ samples
//
// Revision 1.0
//
#include <thread>
#include "easylogging++.h"
INITIALIZE_EASYLOGGINGPP
void def() {
for (int i = 0; i < 1000; ++i)
CLOG(INFO, "first") << "This is from first " << i;
}
void second() {
for (int i = 0; i < 1000; ++i)
CLOG(INFO, "second") << "This is from second" << i;
}
int main(int,char**){
el::Loggers::addFlag(el::LoggingFlag::CreateLoggerAutomatically);
el::Configurations confFromFile("../default-logger.conf");
el::Loggers::setDefaultConfigurations(confFromFile, true);
LOG(INFO)<<"The program has started!";
std::thread t1(def);
std::thread t2(second);
t1.join();
t2.join();
LOG(INFO) << "Shutting down.";
return 0;
}

View File

@ -0,0 +1,51 @@
//
// This file is part of Easylogging++ samples
//
// Revision 1.0
//
#include <chrono>
#include <thread>
#include <future>
#include "easylogging++.h"
INITIALIZE_EASYLOGGINGPP
void f() {
std::async(std::launch::async, [&]() {
std::cout << "[internal] inside async()" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
LOG(INFO) << "This is from async";
});
}
class MyHandler : public el::LogDispatchCallback {
public:
void handle(const el::LogDispatchData* d) {
std::cout << "Message: " << d->logMessage()->message() << " [logger: " << d->logMessage()->logger()->id() << "]" << std::endl;
if (d->logMessage()->logger()->id() != "default") {
std::cout << "[internal] calling f()" << std::endl;
f();
}
}
};
int main(int,char**){
el::Loggers::addFlag(el::LoggingFlag::CreateLoggerAutomatically);
// el::Helpers::uninstallLogDispatchCallback<el::base::DefaultLogDispatchCallback>("DefaultLogDispatchCallback");
el::Helpers::installLogDispatchCallback<MyHandler>("MyHandler");
LOG(INFO)<<"The program has started!";
CLOG(INFO, "frommain") << "THis is another";
std::thread t1([](){
LOG(INFO) << "This is from thread";
});
t1.join();
LOG(INFO) << "Shutting down.";
return 0;
}

View File

@ -1,5 +1,5 @@
* GLOBAL: * GLOBAL:
FORMAT = "%datetime | %level | %msg" FORMAT = "%datetime | %level | %logger | %msg"
FILENAME = "/tmp/logs/myeasylog-configuration.cpp.log" FILENAME = "/tmp/logs/myeasylog-configuration.cpp.log"
ENABLED = true ENABLED = true
TO_FILE = true TO_FILE = true

View File

@ -2,7 +2,7 @@
You can use this sample to send your log messages to network as requested by many users. We are not able to add it to the library as this will require some stuffs to choose what network library to use. You can use this sample to send your log messages to network as requested by many users. We are not able to add it to the library as this will require some stuffs to choose what network library to use.
This sample uses `asio` in `boost_system` This sample uses `boost::asio`
## Run ## Run
@ -13,17 +13,15 @@ mkdir bin
sh compile.sh network-logger.sh sh compile.sh network-logger.sh
``` ```
Run a server (we are using netcat on mac for test purposes) on a different terminal Run a server (we are using netcat on mac for demo) on a new tab in your terminal
``` ```
nc -l 9090 nc -k -l 9090
``` ```
Now run `./bin/network-logger` Now run `./bin/network-logger`
## More ## More
You may need `ELPP_NO_LOG_TO_FILE` and `ELPP_NO_DEFAULT_LOG_FILE` depending on your needs.
Please refer to [doc](https://github.com/muflihun/easyloggingpp#configuration-macros) for further details on macros Please refer to [doc](https://github.com/muflihun/easyloggingpp#configuration-macros) for further details on macros

View File

@ -17,7 +17,11 @@ fi
echo "Compiling... [$FILE]" echo "Compiling... [$FILE]"
COMPILE_LINE="$COMPILER $FILE easylogging++.cc -o bin/$FILE.bin $CXX_STD -std=c++11 -pthread -I /opt/local/include -L/opt/local/lib -lboost_system -Wno-deprecated-declarations" if [ ! -d "bin" ];then
mkdir bin
fi;
COMPILE_LINE="$COMPILER $FILE easylogging++.cc -o bin/$FILE.bin $CXX_STD -pthread -lboost_system"
echo " $COMPILE_LINE" echo " $COMPILE_LINE"

View File

@ -6,7 +6,7 @@
// Revision 1.0 // Revision 1.0
// @author mkhan3189 // @author mkhan3189
// //
// Compile: g++ network-logger.cpp easylogging++.cc -o bin/network-logger.cpp.bin -std=c++11 -pthread -I /opt/local/include -L/opt/local/lib -lboost_system -Wno-deprecated-declarations // Compile: sh compile.sh network-logger.cpp
// //
#include "easylogging++.h" #include "easylogging++.h"
@ -16,16 +16,16 @@
INITIALIZE_EASYLOGGINGPP INITIALIZE_EASYLOGGINGPP
// Following Client struct adapted from http://stackoverflow.com/questions/27672591/boost-asio-send-and-receive-messages class Client
struct Client
{ {
boost::asio::io_service& io_service; boost::asio::io_service* io_service;
boost::asio::ip::tcp::socket socket; boost::asio::ip::tcp::socket socket;
Client(boost::asio::io_service& svc, std::string const& host, std::string const& port) public:
: io_service(svc), socket(io_service) Client(boost::asio::io_service* svc, const std::string& host, const std::string& port)
: io_service(svc), socket(*io_service)
{ {
boost::asio::ip::tcp::resolver resolver(io_service); boost::asio::ip::tcp::resolver resolver(*io_service);
boost::asio::ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query(host, port)); boost::asio::ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query(host, port));
boost::asio::connect(this->socket, endpoint); boost::asio::connect(this->socket, endpoint);
}; };
@ -38,46 +38,39 @@ struct Client
class NetworkDispatcher : public el::LogDispatchCallback class NetworkDispatcher : public el::LogDispatchCallback
{ {
public: public:
// Setters void updateServer(const std::string& host, int port) {
void setHost(const std::string& host) { m_client = std::unique_ptr<Client>(new Client(&m_svc, host, std::to_string(port)));
m_host = host; }
}
void setPort(int port) {
m_port = port;
}
protected: protected:
void handle(const el::LogDispatchData* data) noexcept override void handle(const el::LogDispatchData* data) noexcept override {
{ m_data = data;
m_data = data; // Dispatch using default log builder of logger
// Dispatch using default log builder of logger dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(),
dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), m_data->dispatchAction() == el::base::DispatchAction::NormalLog));
m_data->dispatchAction() == el::base::DispatchAction::NormalLog));
} }
private: private:
const el::LogDispatchData* m_data; const el::LogDispatchData* m_data;
std::string m_host; boost::asio::io_service m_svc;
int m_port; std::unique_ptr<Client> m_client;
void dispatch(el::base::type::string_t&& logLine) noexcept void dispatch(el::base::type::string_t&& logLine) noexcept
{ {
boost::asio::io_service svc; m_client->send(logLine);
Client client(svc, m_host, std::to_string(m_port));
client.send(logLine);
} }
}; };
int main() { int main() {
el::Helpers::installLogDispatchCallback<NetworkDispatcher>("NetworkDispatcher"); el::Helpers::installLogDispatchCallback<NetworkDispatcher>("NetworkDispatcher");
// Set server params // you can uninstall default one by
NetworkDispatcher* dispatcher = el::Helpers::logDispatchCallback<NetworkDispatcher>("NetworkDispatcher"); // el::Helpers::uninstallLogDispatchCallback<el::base::DefaultLogDispatchCallback>("DefaultLogDispatchCallback");
dispatcher->setEnabled(true); // Set server params
dispatcher->setHost("127.0.0.1"); NetworkDispatcher* dispatcher = el::Helpers::logDispatchCallback<NetworkDispatcher>("NetworkDispatcher");
dispatcher->setPort(9090); dispatcher->setEnabled(true);
dispatcher->updateServer("127.0.0.1", 9090);
// Start logging and normal program...
LOG(INFO) << "First network log"; // Start logging and normal program...
LOG(INFO) << "First network log";
// You can even use a different logger, say "network" and send using a different log pattern
} // You can even use a different logger, say "network" and send using a different log pattern
}

View File

@ -1,16 +1,16 @@
// //
// Bismillah ar-Rahmaan ar-Raheem // Bismillah ar-Rahmaan ar-Raheem
// //
// Easylogging++ v9.95.0 // Easylogging++ v9.96.7
// Cross-platform logging library for C++ applications // Cross-platform logging library for C++ applications
// //
// Copyright (c) 2017 muflihun.com // Copyright (c) 2012-2018 Zuhd Web Services
// Copyright (c) 2012-2018 @abumusamq
// //
// This library is released under the MIT Licence. // This library is released under the MIT Licence.
// http://labs.muflihun.com/easyloggingpp/licence.php // https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE
// //
// https://github.com/muflihun/easyloggingpp // https://zuhd.org
// https://muflihun.github.io/easyloggingpp
// http://muflihun.com // http://muflihun.com
// //
@ -22,8 +22,95 @@ INITIALIZE_EASYLOGGINGPP
namespace el { namespace el {
// el::base::utils // el::base
namespace base { namespace base {
// el::base::consts
namespace consts {
// Level log values - These are values that are replaced in place of %level format specifier
// Extra spaces after format specifiers are only for readability purposes in log files
static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO");
static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG");
static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING");
static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR");
static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL");
static const base::type::char_t* kVerboseLevelLogValue =
ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level
static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE");
static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I");
static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D");
static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W");
static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E");
static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F");
static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V");
static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T");
// Format specifiers - These are used to define log format
static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app");
static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger");
static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread");
static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level");
static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort");
static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime");
static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file");
static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase");
static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line");
static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc");
static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func");
static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user");
static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host");
static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg");
static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel");
static const char* kDateTimeFormatSpecifierForFilename = "%datetime";
// Date/time
static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August",
"September", "October", "November", "December"
};
static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g";
static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m";
static const int kYearBase = 1900;
static const char* kAm = "AM";
static const char* kPm = "PM";
// Miscellaneous constants
static const char* kNullPointer = "nullptr";
#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
static const base::type::VerboseLevel kMaxVerboseLevel = 9;
static const char* kUnknownUser = "user";
static const char* kUnknownHost = "unknown-host";
//---------------- DEFAULT LOG FILE -----------------------
#if defined(ELPP_NO_DEFAULT_LOG_FILE)
# if ELPP_OS_UNIX
static const char* kDefaultLogFile = "/dev/null";
# elif ELPP_OS_WINDOWS
static const char* kDefaultLogFile = "nul";
# endif // ELPP_OS_UNIX
#elif defined(ELPP_DEFAULT_LOG_FILE)
static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE;
#else
static const char* kDefaultLogFile = "myeasylog.log";
#endif // defined(ELPP_NO_DEFAULT_LOG_FILE)
#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
static const char* kDefaultLogFileParam = "--default-log-file";
#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
static const char* kLoggingFlagsParam = "--logging-flags";
#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
static const char* kValidLoggerIdSymbols =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
static const char* kConfigurationComment = "##";
static const char* kConfigurationLevel = "*";
static const char* kConfigurationLoggerId = "--";
}
// el::base::utils
namespace utils { namespace utils {
/// @brief Aborts application due with user-defined status /// @brief Aborts application due with user-defined status
@ -276,11 +363,7 @@ void Configurations::set(Configuration* conf) {
void Configurations::setToDefault(void) { void Configurations::setToDefault(void) {
setGlobally(ConfigurationType::Enabled, std::string("true"), true); setGlobally(ConfigurationType::Enabled, std::string("true"), true);
#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true);
#else
ELPP_UNUSED(base::consts::kDefaultLogFile);
#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
#if defined(ELPP_NO_LOG_TO_FILE) #if defined(ELPP_NO_LOG_TO_FILE)
setGlobally(ConfigurationType::ToFile, std::string("false"), true); setGlobally(ConfigurationType::ToFile, std::string("false"), true);
#else #else
@ -309,9 +392,7 @@ void Configurations::setRemainingToDefault(void) {
#else #else
unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true"));
#endif // defined(ELPP_NO_LOG_TO_FILE) #endif // defined(ELPP_NO_LOG_TO_FILE)
#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile));
#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE)
unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true"));
unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3")); unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3"));
unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true"));
@ -569,7 +650,6 @@ void Logger::configure(const Configurations& configurations) {
if (m_typedConfigurations != nullptr) { if (m_typedConfigurations != nullptr) {
Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations()); Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations());
if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) {
// This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE
flush(); flush();
} }
} }
@ -613,10 +693,11 @@ void Logger::flush(Level level, base::type::fstream_t* fs) {
} }
if (fs != nullptr) { if (fs != nullptr) {
fs->flush(); fs->flush();
std::map<Level, unsigned int>::iterator iter = m_unflushedCount.find(level); std::unordered_map<Level, unsigned int>::iterator iter = m_unflushedCount.find(level);
if (iter != m_unflushedCount.end()) { if (iter != m_unflushedCount.end()) {
iter->second = 0; iter->second = 0;
} }
Helpers::validateFileRolling(this, level);
} }
} }
@ -675,10 +756,9 @@ std::size_t File::getSizeOfFile(base::type::fstream_t* fs) {
if (fs == nullptr) { if (fs == nullptr) {
return 0; return 0;
} }
std::streampos currPos = fs->tellg(); // Since the file stream is appended to or truncated, the current
fs->seekg(0, fs->end); // offset is the file size.
std::size_t size = static_cast<std::size_t>(fs->tellg()); std::size_t size = static_cast<std::size_t>(fs->tellg());
fs->seekg(currPos);
return size; return size;
} }
@ -850,7 +930,7 @@ void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::st
std::size_t foundAt = base::type::string_t::npos; std::size_t foundAt = base::type::string_t::npos;
while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) {
if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) {
str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); str.erase(foundAt - 1, 1);
++foundAt; ++foundAt;
} else { } else {
str.replace(foundAt, replaceWhat.length(), replaceWith); str.replace(foundAt, replaceWhat.length(), replaceWith);
@ -997,8 +1077,9 @@ const std::string OS::getBashOutput(const char* command) {
char hBuff[4096]; char hBuff[4096];
if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) {
pclose(proc); pclose(proc);
if (hBuff[strlen(hBuff) - 1] == '\n') { const std::size_t buffLen = strlen(hBuff);
hBuff[strlen(hBuff) - 1] = '\0'; if (buffLen > 0 && hBuff[buffLen - 1] == '\n') {
hBuff[buffLen - 1] = '\0';
} }
return std::string(hBuff); return std::string(hBuff);
} else { } else {
@ -1272,7 +1353,7 @@ bool CommandLineArgs::hasParamWithValue(const char* paramKey) const {
} }
const char* CommandLineArgs::getParamValue(const char* paramKey) const { const char* CommandLineArgs::getParamValue(const char* paramKey) const {
std::map<std::string, std::string>::const_iterator iter = m_paramsWithValue.find(std::string(paramKey)); std::unordered_map<std::string, std::string>::const_iterator iter = m_paramsWithValue.find(std::string(paramKey));
return iter != m_paramsWithValue.end() ? iter->second.c_str() : ""; return iter != m_paramsWithValue.end() ? iter->second.c_str() : "";
} }
@ -1418,7 +1499,7 @@ void LogFormat::parseFromFormat(const base::type::string_t& userFormat) {
if (hasFlag(flag)) { if (hasFlag(flag)) {
// If we already have flag we remove the escape chars so that '%%' is turned to '%' // If we already have flag we remove the escape chars so that '%%' is turned to '%'
// even after specifier resolution - this is because we only replaceFirst specifier // even after specifier resolution - this is because we only replaceFirst specifier
formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); formatCopy.erase(foundAt - 1, 1);
++foundAt; ++foundAt;
} }
} else { } else {
@ -1621,10 +1702,11 @@ void TypedConfigurations::build(Configurations* configurations) {
} else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) {
setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap);
} else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) {
setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_maxLogFileSizeMap); auto v = getULong(conf->value());
#if !defined(ELPP_NO_DEFAULT_LOG_FILE) setValue(conf->level(), static_cast<std::size_t>(v), &m_maxLogFileSizeMap);
withFileSizeLimit.push_back(conf); if (v != 0) {
#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) withFileSizeLimit.push_back(conf);
}
} else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) {
setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap); setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap);
} }
@ -1698,12 +1780,6 @@ std::string TypedConfigurations::resolveFilename(const std::string& filename) {
} }
void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) { void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) {
#if defined(ELPP_NO_LOG_TO_FILE)
setValue(level, false, &m_toFileMap);
ELPP_UNUSED(fullFilename);
m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(nullptr)));
return;
#endif
std::string resolvedFilename = resolveFilename(fullFilename); std::string resolvedFilename = resolveFilename(fullFilename);
if (resolvedFilename.empty()) { if (resolvedFilename.empty()) {
std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" std::cerr << "Could not load empty file for logging, please re-check your configurations for level ["
@ -1839,8 +1915,10 @@ bool RegisteredLoggers::remove(const std::string& id) {
if (id == base::consts::kDefaultLoggerId) { if (id == base::consts::kDefaultLoggerId) {
return false; return false;
} }
// get has internal lock
Logger* logger = base::utils::Registry<Logger, std::string>::get(id); Logger* logger = base::utils::Registry<Logger, std::string>::get(id);
if (logger != nullptr) { if (logger != nullptr) {
// unregister has internal lock
unregister(logger); unregister(logger);
} }
return true; return true;
@ -1948,7 +2026,7 @@ bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) {
} else { } else {
char baseFilename[base::consts::kSourceFilenameMaxLength] = ""; char baseFilename[base::consts::kSourceFilenameMaxLength] = "";
base::utils::File::buildBaseFilename(file, baseFilename); base::utils::File::buildBaseFilename(file, baseFilename);
std::map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin(); std::unordered_map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin();
for (; it != m_modules.end(); ++it) { for (; it != m_modules.end(); ++it) {
if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) { if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) {
return vlevel <= it->second; return vlevel <= it->second;
@ -1989,20 +2067,26 @@ Storage::Storage(const LogBuilderPtr& defaultLogBuilder) :
m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)),
m_flags(ELPP_DEFAULT_LOGGING_FLAGS), m_flags(ELPP_DEFAULT_LOGGING_FLAGS),
m_vRegistry(new base::VRegistry(0, &m_flags)), m_vRegistry(new base::VRegistry(0, &m_flags)),
#if ELPP_ASYNC_LOGGING #if ELPP_ASYNC_LOGGING
m_asyncLogQueue(new base::AsyncLogQueue()), m_asyncLogQueue(new base::AsyncLogQueue()),
m_asyncDispatchWorker(asyncDispatchWorker), m_asyncDispatchWorker(asyncDispatchWorker),
#endif // ELPP_ASYNC_LOGGING #endif // ELPP_ASYNC_LOGGING
m_preRollOutCallback(base::defaultPreRollOutCallback) { m_preRollOutCallback(base::defaultPreRollOutCallback) {
// Register default logger // Register default logger
m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId));
// We register default logger anyway (worse case it's not going to register) just in case // We register default logger anyway (worse case it's not going to register) just in case
m_registeredLoggers->get("default"); m_registeredLoggers->get("default");
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
// Register performance logger and reconfigure format // Register performance logger and reconfigure format
Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId));
m_registeredLoggers->get("performance"); m_registeredLoggers->get("performance");
performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg"));
performanceLogger->reconfigure(); performanceLogger->reconfigure();
#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
#if defined(ELPP_SYSLOG) #if defined(ELPP_SYSLOG)
// Register syslog logger and reconfigure format // Register syslog logger and reconfigure format
Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId));
@ -2045,7 +2129,7 @@ Storage::~Storage(void) {
} }
bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) { bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) {
base::threading::ScopedLock scopedLock(lock()); base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(),
formatSpecifier) != m_customFormatSpecifiers.end(); formatSpecifier) != m_customFormatSpecifiers.end();
} }
@ -2054,12 +2138,12 @@ void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFo
if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) {
return; return;
} }
base::threading::ScopedLock scopedLock(lock()); base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
m_customFormatSpecifiers.push_back(customFormatSpecifier); m_customFormatSpecifiers.push_back(customFormatSpecifier);
} }
bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) { bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) {
base::threading::ScopedLock scopedLock(lock()); base::threading::ScopedLock scopedLock(customFormatSpecifiersLock());
std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(), std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(),
m_customFormatSpecifiers.end(), formatSpecifier); m_customFormatSpecifiers.end(), formatSpecifier);
if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) {
@ -2097,9 +2181,33 @@ void Storage::setApplicationArguments(int argc, char** argv) {
#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) #endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
} }
} // namespace base
// LogDispatchCallback
void LogDispatchCallback::handle(const LogDispatchData* data) {
#if defined(ELPP_THREAD_SAFE)
base::threading::ScopedLock scopedLock(m_fileLocksMapLock);
std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level());
auto lock = m_fileLocks.find(filename);
if (lock == m_fileLocks.end()) {
m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr<base::threading::Mutex>(new base::threading::Mutex)));
}
#endif
}
base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* data) {
auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()));
return *(it->second.get());
}
namespace base {
// DefaultLogDispatchCallback // DefaultLogDispatchCallback
void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { void DefaultLogDispatchCallback::handle(const LogDispatchData* data) {
#if defined(ELPP_THREAD_SAFE)
LogDispatchCallback::handle(data);
base::threading::ScopedLock scopedLock(fileHandle(data));
#endif
m_data = data; m_data = data;
dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(),
m_data->dispatchAction() == base::DispatchAction::NormalLog)); m_data->dispatchAction() == base::DispatchAction::NormalLog));
@ -2348,6 +2456,8 @@ base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool
base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message());
} }
#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) #if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock());
ELPP_UNUSED(lock_);
for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin(); for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin();
it != ELPP->customFormatSpecifiers()->end(); ++it) { it != ELPP->customFormatSpecifiers()->end(); ++it) {
std::string fs(it->formatSpecifier()); std::string fs(it->formatSpecifier());
@ -2368,10 +2478,15 @@ void LogDispatcher::dispatch(void) {
if (!m_proceed) { if (!m_proceed) {
return; return;
} }
#ifndef ELPP_NO_GLOBAL_LOCK
// see https://github.com/muflihun/easyloggingpp/issues/580
// global lock is turned off by default unless
// ELPP_NO_GLOBAL_LOCK is defined
base::threading::ScopedLock scopedLock(ELPP->lock()); base::threading::ScopedLock scopedLock(ELPP->lock());
base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; #endif
base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations;
if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) {
tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback());
} }
LogDispatchCallback* callback = nullptr; LogDispatchCallback* callback = nullptr;
LogDispatchData data; LogDispatchData data;
@ -2379,7 +2494,7 @@ void LogDispatcher::dispatch(void) {
: ELPP->m_logDispatchCallbacks) { : ELPP->m_logDispatchCallbacks) {
callback = h.second.get(); callback = h.second.get();
if (callback != nullptr && callback->enabled()) { if (callback != nullptr && callback->enabled()) {
data.setLogMessage(&m_logMessage); data.setLogMessage(m_logMessage);
data.setDispatchAction(m_dispatchAction); data.setDispatchAction(m_dispatchAction);
callback->handle(&data); callback->handle(&data);
} }
@ -2426,6 +2541,7 @@ Writer& Writer::construct(int count, const char* loggerIds, ...) {
va_list loggersList; va_list loggersList;
va_start(loggersList, loggerIds); va_start(loggersList, loggerIds);
const char* id = loggerIds; const char* id = loggerIds;
m_loggerIds.reserve(count);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
m_loggerIds.push_back(std::string(id)); m_loggerIds.push_back(std::string(id));
id = va_arg(loggersList, const char*); id = va_arg(loggersList, const char*);
@ -2444,12 +2560,12 @@ void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool nee
m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically));
} }
if (m_logger == nullptr) { if (m_logger == nullptr) {
ELPP->acquireLock(); {
if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) {
// Somehow default logger has been unregistered. Not good! Register again // Somehow default logger has been unregistered. Not good! Register again
ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
}
} }
ELPP->releaseLock(); // Need to unlock it for next writer
Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
<< "Logger [" << loggerId << "] is not registered yet!"; << "Logger [" << loggerId << "] is not registered yet!";
m_proceed = false; m_proceed = false;
@ -2510,8 +2626,13 @@ void Writer::processDispatch() {
void Writer::triggerDispatch(void) { void Writer::triggerDispatch(void) {
if (m_proceed) { if (m_proceed) {
base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, if (m_msg == nullptr) {
m_logger), m_dispatchAction).dispatch(); LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel,
m_logger);
base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch();
} else {
base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch();
}
} }
if (m_logger != nullptr) { if (m_logger != nullptr) {
m_logger->stream().str(ELPP_LITERAL("")); m_logger->stream().str(ELPP_LITERAL(""));
@ -2524,7 +2645,7 @@ void Writer::triggerDispatch(void) {
std::stringstream reasonStream; std::stringstream reasonStream;
reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]"
<< " If you wish to disable 'abort on fatal log' please use " << " If you wish to disable 'abort on fatal log' please use "
<< "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)";
base::utils::abort(1, reasonStream.str()); base::utils::abort(1, reasonStream.str());
} }
m_proceed = false; m_proceed = false;
@ -2640,18 +2761,19 @@ namespace debug {
// StackTrace // StackTrace
StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang,
const char* addr) { const std::string& hex,
m_index = index; const std::string& addr) :
m_location = std::string(loc); m_index(index),
m_demangled = std::string(demang); m_location(loc),
m_hex = std::string(hex); m_demangled(demang),
m_addr = std::string(addr); m_hex(hex),
m_addr(addr) {
} }
std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) { std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) {
ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr <<
<< (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; (si.m_demangled.empty() ? "" : ":") << si.m_demangled;
return ss; return ss;
} }
@ -2671,44 +2793,40 @@ void StackTrace::generateNew(void) {
char** strings = backtrace_symbols(stack, size); char** strings = backtrace_symbols(stack, size);
if (size > kStackStart) { // Skip StackTrace c'tor and generateNew if (size > kStackStart) { // Skip StackTrace c'tor and generateNew
for (std::size_t i = kStackStart; i < size; ++i) { for (std::size_t i = kStackStart; i < size; ++i) {
char* mangName = nullptr; std::string mangName;
char* hex = nullptr; std::string location;
char* addr = nullptr; std::string hex;
for (char* c = strings[i]; *c; ++c) { std::string addr;
switch (*c) {
case '(': // entry: 2 crash.cpp.bin 0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21
mangName = c; const std::string line(strings[i]);
break; auto p = line.find("_");
case '+': if (p != std::string::npos) {
hex = c; mangName = line.substr(p);
break; mangName = mangName.substr(0, mangName.find(" +"));
case ')': }
addr = c; p = line.find("0x");
break; if (p != std::string::npos) {
default: addr = line.substr(p);
break; addr = addr.substr(0, addr.find("_"));
}
} }
// Perform demangling if parsed properly // Perform demangling if parsed properly
if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { if (!mangName.empty()) {
*mangName++ = '\0';
*hex++ = '\0';
*addr++ = '\0';
int status = 0; int status = 0;
char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status);
// if demangling is successful, output the demangled function name // if demangling is successful, output the demangled function name
if (status == 0) { if (status == 0) {
// Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html)
StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); StackTraceEntry entry(i - 1, location, demangName, hex, addr);
m_stack.push_back(entry); m_stack.push_back(entry);
} else { } else {
// Not successful - we will use mangled name // Not successful - we will use mangled name
StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); StackTraceEntry entry(i - 1, location, mangName, hex, addr);
m_stack.push_back(entry); m_stack.push_back(entry);
} }
free(demangName); free(demangName);
} else { } else {
StackTraceEntry entry(i - 1, strings[i]); StackTraceEntry entry(i - 1, line);
m_stack.push_back(entry); m_stack.push_back(entry);
} }
} }
@ -2742,6 +2860,9 @@ static std::string crashReason(int sig) {
} }
/// @brief Logs reason of crash from sig /// @brief Logs reason of crash from sig
static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) {
if (sig == SIGINT && ELPP->hasFlag(el::LoggingFlag::IgnoreSigInt)) {
return;
}
std::stringstream ss; std::stringstream ss;
ss << "CRASH HANDLED; "; ss << "CRASH HANDLED; ";
ss << crashReason(sig); ss << crashReason(sig);
@ -2820,7 +2941,6 @@ void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, c
// Loggers // Loggers
Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) {
base::threading::ScopedLock scopedLock(ELPP->lock());
return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable);
} }
@ -2829,12 +2949,10 @@ void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) {
} }
bool Loggers::unregisterLogger(const std::string& identity) { bool Loggers::unregisterLogger(const std::string& identity) {
base::threading::ScopedLock scopedLock(ELPP->lock());
return ELPP->registeredLoggers()->remove(identity); return ELPP->registeredLoggers()->remove(identity);
} }
bool Loggers::hasLogger(const std::string& identity) { bool Loggers::hasLogger(const std::string& identity) {
base::threading::ScopedLock scopedLock(ELPP->lock());
return ELPP->registeredLoggers()->has(identity); return ELPP->registeredLoggers()->has(identity);
} }
@ -2984,11 +3102,11 @@ void Loggers::clearVModules(void) {
// VersionInfo // VersionInfo
const std::string VersionInfo::version(void) { const std::string VersionInfo::version(void) {
return std::string("9.95.0"); return std::string("9.96.7");
} }
/// @brief Release date of current version /// @brief Release date of current version
const std::string VersionInfo::releaseDate(void) { const std::string VersionInfo::releaseDate(void) {
return std::string("02-08-2017 2312hrs"); return std::string("24-11-2018 0728hrs");
} }
} // namespace el } // namespace el

View File

@ -1,18 +1,19 @@
// //
// Bismillah ar-Rahmaan ar-Raheem // Bismillah ar-Rahmaan ar-Raheem
// //
// Easylogging++ v9.95.0 // Easylogging++ v9.96.7
// Single-header only, cross-platform logging library for C++ applications // Single-header only, cross-platform logging library for C++ applications
// //
// Copyright (c) 2017 muflihun.com // Copyright (c) 2012-2018 Zuhd Web Services
// Copyright (c) 2012-2018 @abumusamq
// //
// This library is released under the MIT Licence. // This library is released under the MIT Licence.
// http://labs.muflihun.com/easyloggingpp/licence.php // https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE
// //
// https://github.com/muflihun/easyloggingpp // https://zuhd.org
// https://muflihun.github.io/easyloggingpp
// http://muflihun.com // http://muflihun.com
// //
#ifndef EASYLOGGINGPP_H #ifndef EASYLOGGINGPP_H
#define EASYLOGGINGPP_H #define EASYLOGGINGPP_H
// Compilers and C++0x/C++11 Evaluation // Compilers and C++0x/C++11 Evaluation
@ -93,7 +94,7 @@
#else #else
# define ELPP_OS_MAC 0 # define ELPP_OS_MAC 0
#endif #endif
#if (defined(__FreeBSD__)) #if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
# define ELPP_OS_FREEBSD 1 # define ELPP_OS_FREEBSD 1
#else #else
# define ELPP_OS_FREEBSD 0 # define ELPP_OS_FREEBSD 0
@ -103,13 +104,23 @@
#else #else
# define ELPP_OS_SOLARIS 0 # define ELPP_OS_SOLARIS 0
#endif #endif
#if (defined(_AIX))
# define ELPP_OS_AIX 1
#else
# define ELPP_OS_AIX 0
#endif
#if (defined(__NetBSD__)) #if (defined(__NetBSD__))
# define ELPP_OS_NETBSD 1 # define ELPP_OS_NETBSD 1
#else #else
# define ELPP_OS_NETBSD 0 # define ELPP_OS_NETBSD 0
#endif #endif
#if defined(__EMSCRIPTEN__)
# define ELPP_OS_EMSCRIPTEN 1
#else
# define ELPP_OS_EMSCRIPTEN 0
#endif
// Unix // Unix
#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS)) #if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_EMSCRIPTEN) && (!ELPP_OS_WINDOWS))
# define ELPP_OS_UNIX 1 # define ELPP_OS_UNIX 1
#else #else
# define ELPP_OS_UNIX 0 # define ELPP_OS_UNIX 0
@ -194,7 +205,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre
# define ELPP_INTERNAL_INFO(lvl, msg) # define ELPP_INTERNAL_INFO(lvl, msg)
#endif // (defined(ELPP_DEBUG_INFO)) #endif // (defined(ELPP_DEBUG_INFO))
#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) #if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG))
# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_ANDROID) # if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_ANDROID && !ELPP_OS_EMSCRIPTEN)
# define ELPP_STACKTRACE 1 # define ELPP_STACKTRACE 1
# else # else
# if ELPP_COMPILER_MSVC # if ELPP_COMPILER_MSVC
@ -373,6 +384,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
#include <unordered_map>
#include <utility> #include <utility>
#include <functional> #include <functional>
#include <algorithm> #include <algorithm>
@ -410,9 +422,6 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre
# if defined(ELPP_LOG_STD_ARRAY) # if defined(ELPP_LOG_STD_ARRAY)
# include <array> # include <array>
# endif // defined(ELPP_LOG_STD_ARRAY) # endif // defined(ELPP_LOG_STD_ARRAY)
# if defined(ELPP_LOG_UNORDERED_MAP)
# include <unordered_map>
# endif // defined(ELPP_LOG_UNORDERED_MAP)
# if defined(ELPP_LOG_UNORDERED_SET) # if defined(ELPP_LOG_UNORDERED_SET)
# include <unordered_set> # include <unordered_set>
# endif // defined(ELPP_UNORDERED_SET) # endif // defined(ELPP_UNORDERED_SET)
@ -580,6 +589,16 @@ enum class Level : base::type::EnumType {
/// @brief Represents unknown level /// @brief Represents unknown level
Unknown = 1010 Unknown = 1010
}; };
} // namespace el
namespace std {
template<> struct hash<el::Level> {
public:
std::size_t operator()(const el::Level& l) const {
return hash<el::base::type::EnumType> {}(static_cast<el::base::type::EnumType>(l));
}
};
}
namespace el {
/// @brief Static class that contains helper functions for el::Level /// @brief Static class that contains helper functions for el::Level
class LevelHelper : base::StaticClass { class LevelHelper : base::StaticClass {
public: public:
@ -702,115 +721,43 @@ enum class LoggingFlag : base::type::EnumType {
/// @brief Adds spaces b/w logs that separated by left-shift operator /// @brief Adds spaces b/w logs that separated by left-shift operator
AutoSpacing = 8192, AutoSpacing = 8192,
/// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only)
FixedTimeFormat = 16384 FixedTimeFormat = 16384,
// @brief Ignore SIGINT or crash
IgnoreSigInt = 32768,
}; };
namespace base { namespace base {
/// @brief Namespace containing constants used internally. /// @brief Namespace containing constants used internally.
namespace consts { namespace consts {
#if defined(__GNUC__) && !defined(__clang__) static const char kFormatSpecifierCharValue = 'v';
#pragma GCC diagnostic push static const char kFormatSpecifierChar = '%';
#pragma GCC diagnostic ignored "-Wunused-variable" static const unsigned int kMaxLogPerCounter = 100000;
#endif static const unsigned int kMaxLogPerContainer = 100;
// Level log values - These are values that are replaced in place of %level format specifier static const unsigned int kDefaultSubsecondPrecision = 3;
// Extra spaces after format specifiers are only for readability purposes in log files
static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO");
static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG");
static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING");
static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR");
static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL");
static const base::type::char_t* kVerboseLevelLogValue =
ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level
static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE");
static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I");
static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D");
static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W");
static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E");
static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F");
static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V");
static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T");
// Format specifiers - These are used to define log format
static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app");
static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger");
static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread");
static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level");
static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort");
static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime");
static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file");
static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase");
static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line");
static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc");
static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func");
static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user");
static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host");
static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg");
static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel");
static const char* kDateTimeFormatSpecifierForFilename = "%datetime";
// Date/time
static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August",
"September", "October", "November", "December"
};
static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g";
static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m";
static const int kYearBase = 1900;
static const char* kAm = "AM";
static const char* kPm = "PM";
// Miscellaneous constants
#ifdef ELPP_DEFAULT_LOGGER #ifdef ELPP_DEFAULT_LOGGER
static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER;
#else #else
static const char* kDefaultLoggerId = "default"; static const char* kDefaultLoggerId = "default";
#endif #endif
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
#ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER #ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER
static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER;
#else #else
static const char* kPerformanceLoggerId = "performance"; static const char* kPerformanceLoggerId = "performance";
#endif // ELPP_DEFAULT_PERFORMANCE_LOGGER
#endif #endif
#if defined(ELPP_SYSLOG) #if defined(ELPP_SYSLOG)
static const char* kSysLogLoggerId = "syslog"; static const char* kSysLogLoggerId = "syslog";
#endif // defined(ELPP_SYSLOG) #endif // defined(ELPP_SYSLOG)
static const char* kNullPointer = "nullptr";
static const char kFormatSpecifierChar = '%';
#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
static const char kFormatSpecifierCharValue = 'v';
#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
static const unsigned int kMaxLogPerContainer = 100;
static const unsigned int kMaxLogPerCounter = 100000;
static const unsigned int kDefaultSubsecondPrecision = 3;
static const base::type::VerboseLevel kMaxVerboseLevel = 9;
static const char* kUnknownUser = "user";
static const char* kUnknownHost = "unknown-host";
#if defined(ELPP_DEFAULT_LOG_FILE)
static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE;
#else
# if ELPP_OS_UNIX
# if ELPP_OS_ANDROID
static const char* kDefaultLogFile = "logs/myeasylog.log";
# else
static const char* kDefaultLogFile = "logs/myeasylog.log";
# endif // ELPP_OS_ANDROID
# elif ELPP_OS_WINDOWS
static const char* kDefaultLogFile = "logs\\myeasylog.log";
# endif // ELPP_OS_UNIX
#endif // defined(ELPP_DEFAULT_LOG_FILE)
#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
static const char* kDefaultLogFileParam = "--default-log-file";
#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
static const char* kLoggingFlagsParam = "--logging-flags";
#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
#if ELPP_OS_WINDOWS #if ELPP_OS_WINDOWS
static const char* kFilePathSeperator = "\\"; static const char* kFilePathSeperator = "\\";
#else #else
static const char* kFilePathSeperator = "/"; static const char* kFilePathSeperator = "/";
#endif // ELPP_OS_WINDOWS #endif // ELPP_OS_WINDOWS
static const char* kValidLoggerIdSymbols =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
static const char* kConfigurationComment = "##";
static const char* kConfigurationLevel = "*";
static const char* kConfigurationLoggerId = "--";
static const std::size_t kSourceFilenameMaxLength = 100; static const std::size_t kSourceFilenameMaxLength = 100;
static const std::size_t kSourceLineMaxLength = 10; static const std::size_t kSourceLineMaxLength = 10;
static const Level kPerformanceTrackerDefaultLevel = Level::Info; static const Level kPerformanceTrackerDefaultLevel = Level::Info;
@ -855,9 +802,6 @@ const struct {
}, },
}; };
static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]);
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
} // namespace consts } // namespace consts
} // namespace base } // namespace base
typedef std::function<void(const char*, std::size_t)> PreRollOutCallback; typedef std::function<void(const char*, std::size_t)> PreRollOutCallback;
@ -1257,8 +1201,8 @@ class DateTime : base::StaticClass {
base::TimestampUnit timestampUnit); base::TimestampUnit timestampUnit);
private:
static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo);
private:
static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo,
std::size_t msec, const base::SubsecondPrecision* ssPrec); std::size_t msec, const base::SubsecondPrecision* ssPrec);
}; };
@ -1297,7 +1241,7 @@ class CommandLineArgs {
private: private:
int m_argc; int m_argc;
char** m_argv; char** m_argv;
std::map<std::string, std::string> m_paramsWithValue; std::unordered_map<std::string, std::string> m_paramsWithValue;
std::vector<std::string> m_params; std::vector<std::string> m_params;
}; };
/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. /// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type.
@ -1422,7 +1366,7 @@ class AbstractRegistry : public base::threading::ThreadSafe {
/// of AbstractRegistry<T_Ptr, Container>. Any implementation of this class should be /// of AbstractRegistry<T_Ptr, Container>. Any implementation of this class should be
/// explicitly (by using lock functions) /// explicitly (by using lock functions)
template <typename T_Ptr, typename T_Key = const char*> template <typename T_Ptr, typename T_Key = const char*>
class Registry : public AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>> { class Registry : public AbstractRegistry<T_Ptr, std::unordered_map<T_Key, T_Ptr*>> {
public: public:
typedef typename Registry<T_Ptr, T_Key>::iterator iterator; typedef typename Registry<T_Ptr, T_Key>::iterator iterator;
typedef typename Registry<T_Ptr, T_Key>::const_iterator const_iterator; typedef typename Registry<T_Ptr, T_Key>::const_iterator const_iterator;
@ -1486,7 +1430,7 @@ class Registry : public AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>> {
} }
private: private:
virtual void deepCopy(const AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>>& sr) ELPP_FINAL { virtual void deepCopy(const AbstractRegistry<T_Ptr, std::unordered_map<T_Key, T_Ptr*>>& sr) ELPP_FINAL {
for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) {
registerNew(it->first, new T_Ptr(*it->second)); registerNew(it->first, new T_Ptr(*it->second));
} }
@ -1586,7 +1530,7 @@ class RegistryWithPred : public AbstractRegistry<T_Ptr, std::vector<T_Ptr*>> {
class Utils { class Utils {
public: public:
template <typename T, typename TPtr> template <typename T, typename TPtr>
static bool installCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { static bool installCallback(const std::string& id, std::unordered_map<std::string, TPtr>* mapT) {
if (mapT->find(id) == mapT->end()) { if (mapT->find(id) == mapT->end()) {
mapT->insert(std::make_pair(id, TPtr(new T()))); mapT->insert(std::make_pair(id, TPtr(new T())));
return true; return true;
@ -1595,15 +1539,15 @@ class Utils {
} }
template <typename T, typename TPtr> template <typename T, typename TPtr>
static void uninstallCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { static void uninstallCallback(const std::string& id, std::unordered_map<std::string, TPtr>* mapT) {
if (mapT->find(id) != mapT->end()) { if (mapT->find(id) != mapT->end()) {
mapT->erase(id); mapT->erase(id);
} }
} }
template <typename T, typename TPtr> template <typename T, typename TPtr>
static T* callback(const std::string& id, std::map<std::string, TPtr>* mapT) { static T* callback(const std::string& id, std::unordered_map<std::string, TPtr>* mapT) {
typename std::map<std::string, TPtr>::iterator iter = mapT->find(id); typename std::unordered_map<std::string, TPtr>::iterator iter = mapT->find(id);
if (iter != mapT->end()) { if (iter != mapT->end()) {
return static_cast<T*>(iter->second.get()); return static_cast<T*>(iter->second.get());
} }
@ -1948,7 +1892,7 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
namespace base { namespace base {
typedef std::shared_ptr<base::type::fstream_t> FileStreamPtr; typedef std::shared_ptr<base::type::fstream_t> FileStreamPtr;
typedef std::map<std::string, FileStreamPtr> LogStreamsReferenceMap; typedef std::unordered_map<std::string, FileStreamPtr> LogStreamsReferenceMap;
/// @brief Configurations with data types. /// @brief Configurations with data types.
/// ///
/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. /// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations.
@ -1985,16 +1929,16 @@ class TypedConfigurations : public base::threading::ThreadSafe {
private: private:
Configurations* m_configurations; Configurations* m_configurations;
std::map<Level, bool> m_enabledMap; std::unordered_map<Level, bool> m_enabledMap;
std::map<Level, bool> m_toFileMap; std::unordered_map<Level, bool> m_toFileMap;
std::map<Level, std::string> m_filenameMap; std::unordered_map<Level, std::string> m_filenameMap;
std::map<Level, bool> m_toStandardOutputMap; std::unordered_map<Level, bool> m_toStandardOutputMap;
std::map<Level, base::LogFormat> m_logFormatMap; std::unordered_map<Level, base::LogFormat> m_logFormatMap;
std::map<Level, base::SubsecondPrecision> m_subsecondPrecisionMap; std::unordered_map<Level, base::SubsecondPrecision> m_subsecondPrecisionMap;
std::map<Level, bool> m_performanceTrackingMap; std::unordered_map<Level, bool> m_performanceTrackingMap;
std::map<Level, base::FileStreamPtr> m_fileStreamMap; std::unordered_map<Level, base::FileStreamPtr> m_fileStreamMap;
std::map<Level, std::size_t> m_maxLogFileSizeMap; std::unordered_map<Level, std::size_t> m_maxLogFileSizeMap;
std::map<Level, std::size_t> m_logFlushThresholdMap; std::unordered_map<Level, std::size_t> m_logFlushThresholdMap;
base::LogStreamsReferenceMap* m_logStreamsReference; base::LogStreamsReferenceMap* m_logStreamsReference;
friend class el::Helpers; friend class el::Helpers;
@ -2004,21 +1948,21 @@ class TypedConfigurations : public base::threading::ThreadSafe {
friend class el::base::LogDispatcher; friend class el::base::LogDispatcher;
template <typename Conf_T> template <typename Conf_T>
inline Conf_T getConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { inline Conf_T getConfigByVal(Level level, const std::unordered_map<Level, Conf_T>* confMap, const char* confName) {
base::threading::ScopedLock scopedLock(lock()); base::threading::ScopedLock scopedLock(lock());
return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope
} }
template <typename Conf_T> template <typename Conf_T>
inline Conf_T& getConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { inline Conf_T& getConfigByRef(Level level, std::unordered_map<Level, Conf_T>* confMap, const char* confName) {
base::threading::ScopedLock scopedLock(lock()); base::threading::ScopedLock scopedLock(lock());
return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope
} }
template <typename Conf_T> template <typename Conf_T>
Conf_T unsafeGetConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { Conf_T unsafeGetConfigByVal(Level level, const std::unordered_map<Level, Conf_T>* confMap, const char* confName) {
ELPP_UNUSED(confName); ELPP_UNUSED(confName);
typename std::map<Level, Conf_T>::const_iterator it = confMap->find(level); typename std::unordered_map<Level, Conf_T>::const_iterator it = confMap->find(level);
if (it == confMap->end()) { if (it == confMap->end()) {
try { try {
return confMap->at(Level::Global); return confMap->at(Level::Global);
@ -2033,9 +1977,9 @@ class TypedConfigurations : public base::threading::ThreadSafe {
} }
template <typename Conf_T> template <typename Conf_T>
Conf_T& unsafeGetConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { Conf_T& unsafeGetConfigByRef(Level level, std::unordered_map<Level, Conf_T>* confMap, const char* confName) {
ELPP_UNUSED(confName); ELPP_UNUSED(confName);
typename std::map<Level, Conf_T>::iterator it = confMap->find(level); typename std::unordered_map<Level, Conf_T>::iterator it = confMap->find(level);
if (it == confMap->end()) { if (it == confMap->end()) {
try { try {
return confMap->at(Level::Global); return confMap->at(Level::Global);
@ -2049,14 +1993,15 @@ class TypedConfigurations : public base::threading::ThreadSafe {
} }
template <typename Conf_T> template <typename Conf_T>
void setValue(Level level, const Conf_T& value, std::map<Level, Conf_T>* confMap, bool includeGlobalLevel = true) { void setValue(Level level, const Conf_T& value, std::unordered_map<Level, Conf_T>* confMap,
bool includeGlobalLevel = true) {
// If map is empty and we are allowed to add into generic level (Level::Global), do it! // If map is empty and we are allowed to add into generic level (Level::Global), do it!
if (confMap->empty() && includeGlobalLevel) { if (confMap->empty() && includeGlobalLevel) {
confMap->insert(std::make_pair(Level::Global, value)); confMap->insert(std::make_pair(Level::Global, value));
return; return;
} }
// If same value exist in generic level already, dont add it to explicit level // If same value exist in generic level already, dont add it to explicit level
typename std::map<Level, Conf_T>::iterator it = confMap->find(Level::Global); typename std::unordered_map<Level, Conf_T>::iterator it = confMap->find(Level::Global);
if (it != confMap->end() && it->second == value) { if (it != confMap->end() && it->second == value) {
return; return;
} }
@ -2218,21 +2163,26 @@ class LogDispatchData {
inline base::DispatchAction dispatchAction(void) const { inline base::DispatchAction dispatchAction(void) const {
return m_dispatchAction; return m_dispatchAction;
} }
private:
LogMessage* m_logMessage;
base::DispatchAction m_dispatchAction;
friend class base::LogDispatcher;
inline void setLogMessage(LogMessage* logMessage) { inline void setLogMessage(LogMessage* logMessage) {
m_logMessage = logMessage; m_logMessage = logMessage;
} }
inline void setDispatchAction(base::DispatchAction dispatchAction) { inline void setDispatchAction(base::DispatchAction dispatchAction) {
m_dispatchAction = dispatchAction; m_dispatchAction = dispatchAction;
} }
private:
LogMessage* m_logMessage;
base::DispatchAction m_dispatchAction;
friend class base::LogDispatcher;
}; };
class LogDispatchCallback : public Callback<LogDispatchData> { class LogDispatchCallback : public Callback<LogDispatchData> {
protected:
virtual void handle(const LogDispatchData* data);
base::threading::Mutex& fileHandle(const LogDispatchData* data);
private: private:
friend class base::LogDispatcher; friend class base::LogDispatcher;
std::unordered_map<std::string, std::unique_ptr<base::threading::Mutex>> m_fileLocks;
base::threading::Mutex m_fileLocksMapLock;
}; };
class PerformanceTrackingCallback : public Callback<PerformanceTrackingData> { class PerformanceTrackingCallback : public Callback<PerformanceTrackingData> {
private: private:
@ -2350,7 +2300,7 @@ inline void FUNCTION_NAME(const T&);
std::string m_parentApplicationName; std::string m_parentApplicationName;
bool m_isConfigured; bool m_isConfigured;
Configurations m_configurations; Configurations m_configurations;
std::map<Level, unsigned int> m_unflushedCount; std::unordered_map<Level, unsigned int> m_unflushedCount;
base::LogStreamsReferenceMap* m_logStreamsReference; base::LogStreamsReferenceMap* m_logStreamsReference;
LogBuilderPtr m_logBuilder; LogBuilderPtr m_logBuilder;
@ -2456,7 +2406,7 @@ class RegisteredLoggers : public base::utils::Registry<Logger, std::string> {
LogBuilderPtr m_defaultLogBuilder; LogBuilderPtr m_defaultLogBuilder;
Configurations m_defaultConfigurations; Configurations m_defaultConfigurations;
base::LogStreamsReferenceMap m_logStreamsReference; base::LogStreamsReferenceMap m_logStreamsReference;
std::map<std::string, base::type::LoggerRegistrationCallbackPtr> m_loggerRegistrationCallbacks; std::unordered_map<std::string, base::type::LoggerRegistrationCallbackPtr> m_loggerRegistrationCallbacks;
friend class el::base::Storage; friend class el::base::Storage;
void unsafeFlushAll(void); void unsafeFlushAll(void);
@ -2482,7 +2432,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe {
bool allowed(base::type::VerboseLevel vlevel, const char* file); bool allowed(base::type::VerboseLevel vlevel, const char* file);
inline const std::map<std::string, base::type::VerboseLevel>& modules(void) const { inline const std::unordered_map<std::string, base::type::VerboseLevel>& modules(void) const {
return m_modules; return m_modules;
} }
@ -2496,7 +2446,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe {
private: private:
base::type::VerboseLevel m_level; base::type::VerboseLevel m_level;
base::type::EnumType* m_pFlags; base::type::EnumType* m_pFlags;
std::map<std::string, base::type::VerboseLevel> m_modules; std::unordered_map<std::string, base::type::VerboseLevel> m_modules;
}; };
} // namespace base } // namespace base
class LogMessage { class LogMessage {
@ -2680,6 +2630,10 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe {
return &m_customFormatSpecifiers; return &m_customFormatSpecifiers;
} }
base::threading::Mutex& customFormatSpecifiersLock() {
return m_customFormatSpecifiersLock;
}
inline void setLoggingLevel(Level level) { inline void setLoggingLevel(Level level) {
m_loggingLevel = level; m_loggingLevel = level;
} }
@ -2720,12 +2674,13 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe {
/// @brief Sets thread name for current thread. Requires std::thread /// @brief Sets thread name for current thread. Requires std::thread
inline void setThreadName(const std::string& name) { inline void setThreadName(const std::string& name) {
if (name.empty()) return; if (name.empty()) return;
base::threading::ScopedLock scopedLock(lock()); base::threading::ScopedLock scopedLock(m_threadNamesLock);
m_threadNames[base::threading::getCurrentThreadId()] = name; m_threadNames[base::threading::getCurrentThreadId()] = name;
} }
inline std::string getThreadName(const std::string& threadId) { inline std::string getThreadName(const std::string& threadId) {
std::map<std::string, std::string>::const_iterator it = m_threadNames.find(threadId); base::threading::ScopedLock scopedLock(m_threadNamesLock);
std::unordered_map<std::string, std::string>::const_iterator it = m_threadNames.find(threadId);
if (it == m_threadNames.end()) { if (it == m_threadNames.end()) {
return threadId; return threadId;
} }
@ -2742,10 +2697,12 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe {
#endif // ELPP_ASYNC_LOGGING #endif // ELPP_ASYNC_LOGGING
base::utils::CommandLineArgs m_commandLineArgs; base::utils::CommandLineArgs m_commandLineArgs;
PreRollOutCallback m_preRollOutCallback; PreRollOutCallback m_preRollOutCallback;
std::map<std::string, base::type::LogDispatchCallbackPtr> m_logDispatchCallbacks; std::unordered_map<std::string, base::type::LogDispatchCallbackPtr> m_logDispatchCallbacks;
std::map<std::string, base::type::PerformanceTrackingCallbackPtr> m_performanceTrackingCallbacks; std::unordered_map<std::string, base::type::PerformanceTrackingCallbackPtr> m_performanceTrackingCallbacks;
std::map<std::string, std::string> m_threadNames; std::unordered_map<std::string, std::string> m_threadNames;
std::vector<CustomFormatSpecifier> m_customFormatSpecifiers; std::vector<CustomFormatSpecifier> m_customFormatSpecifiers;
base::threading::Mutex m_customFormatSpecifiersLock;
base::threading::Mutex m_threadNamesLock;
Level m_loggingLevel; Level m_loggingLevel;
friend class el::Helpers; friend class el::Helpers;
@ -2788,7 +2745,7 @@ class AsyncDispatchWorker : public base::IWorker, public base::threading::Thread
void run(void); void run(void);
void setContinueRunning(bool value) { void setContinueRunning(bool value) {
base::threading::ScopedLock scopedLock(m_continueRunningMutex); base::threading::ScopedLock scopedLock(m_continueRunningLock);
m_continueRunning = value; m_continueRunning = value;
} }
@ -2798,7 +2755,7 @@ class AsyncDispatchWorker : public base::IWorker, public base::threading::Thread
private: private:
std::condition_variable cv; std::condition_variable cv;
bool m_continueRunning; bool m_continueRunning;
base::threading::Mutex m_continueRunningMutex; base::threading::Mutex m_continueRunningLock;
}; };
#endif // ELPP_ASYNC_LOGGING #endif // ELPP_ASYNC_LOGGING
} // namespace base } // namespace base
@ -2810,9 +2767,9 @@ class DefaultLogBuilder : public LogBuilder {
/// @brief Dispatches log messages /// @brief Dispatches log messages
class LogDispatcher : base::NoCopy { class LogDispatcher : base::NoCopy {
public: public:
LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : LogDispatcher(bool proceed, LogMessage* logMessage, base::DispatchAction dispatchAction) :
m_proceed(proceed), m_proceed(proceed),
m_logMessage(std::move(logMessage)), m_logMessage(logMessage),
m_dispatchAction(std::move(dispatchAction)) { m_dispatchAction(std::move(dispatchAction)) {
} }
@ -2820,7 +2777,7 @@ class LogDispatcher : base::NoCopy {
private: private:
bool m_proceed; bool m_proceed;
LogMessage m_logMessage; LogMessage* m_logMessage;
base::DispatchAction m_dispatchAction; base::DispatchAction m_dispatchAction;
}; };
#if defined(ELPP_STL_LOGGING) #if defined(ELPP_STL_LOGGING)
@ -3233,10 +3190,15 @@ class Writer : base::NoCopy {
Writer(Level level, const char* file, base::type::LineNumber line, Writer(Level level, const char* file, base::type::LineNumber line,
const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
base::type::VerboseLevel verboseLevel = 0) : base::type::VerboseLevel verboseLevel = 0) :
m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel),
m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {
} }
Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) :
m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown),
m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {
}
virtual ~Writer(void) { virtual ~Writer(void) {
processDispatch(); processDispatch();
} }
@ -3267,6 +3229,7 @@ class Writer : base::NoCopy {
Writer& construct(Logger* logger, bool needLock = true); Writer& construct(Logger* logger, bool needLock = true);
Writer& construct(int count, const char* loggerIds, ...); Writer& construct(int count, const char* loggerIds, ...);
protected: protected:
LogMessage* m_msg;
Level m_level; Level m_level;
const char* m_file; const char* m_file;
const base::type::LineNumber m_line; const base::type::LineNumber m_line;
@ -3325,6 +3288,7 @@ void Logger::log_(Level level, int vlevel, const T& log) {
base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; base::DispatchAction::NormalLog, vlevel).construct(this, false) << log;
} else { } else {
stream().str(ELPP_LITERAL("")); stream().str(ELPP_LITERAL(""));
releaseLock();
} }
} else { } else {
base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log;
@ -3332,23 +3296,23 @@ void Logger::log_(Level level, int vlevel, const T& log) {
} }
template <typename T, typename... Args> template <typename T, typename... Args>
inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) { inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) {
base::threading::ScopedLock scopedLock(lock()); acquireLock(); // released in Writer!
log_(level, 0, s, value, args...); log_(level, 0, s, value, args...);
} }
template <typename T> template <typename T>
inline void Logger::log(Level level, const T& log) { inline void Logger::log(Level level, const T& log) {
base::threading::ScopedLock scopedLock(lock()); acquireLock(); // released in Writer!
log_(level, 0, log); log_(level, 0, log);
} }
# if ELPP_VERBOSE_LOG # if ELPP_VERBOSE_LOG
template <typename T, typename... Args> template <typename T, typename... Args>
inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) {
base::threading::ScopedLock scopedLock(lock()); acquireLock(); // released in Writer!
log_(el::Level::Verbose, vlevel, s, value, args...); log_(el::Level::Verbose, vlevel, s, value, args...);
} }
template <typename T> template <typename T>
inline void Logger::verbose(int vlevel, const T& log) { inline void Logger::verbose(int vlevel, const T& log) {
base::threading::ScopedLock scopedLock(lock()); acquireLock(); // released in Writer!
log_(el::Level::Verbose, vlevel, log); log_(el::Level::Verbose, vlevel, log);
} }
# else # else
@ -3603,8 +3567,9 @@ class StackTrace : base::NoCopy {
static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew()
class StackTraceEntry { class StackTraceEntry {
public: public:
StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr); StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, const std::string& hex,
StackTraceEntry(std::size_t index, char* loc) : const std::string& addr);
StackTraceEntry(std::size_t index, const std::string& loc) :
m_index(index), m_index(index),
m_location(loc) { m_location(loc) {
} }
@ -3789,6 +3754,11 @@ class Helpers : base::StaticClass {
static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) {
return ELPP->commandLineArgs(); return ELPP->commandLineArgs();
} }
/// @brief Reserve space for custom format specifiers for performance
/// @see std::vector::reserve
static inline void reserveCustomFormatSpecifiers(std::size_t size) {
ELPP->m_customFormatSpecifiers.reserve(size);
}
/// @brief Installs user defined format specifier and handler /// @brief Installs user defined format specifier and handler
static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) {
ELPP->installCustomFormatSpecifier(customFormatSpecifier); ELPP->installCustomFormatSpecifier(customFormatSpecifier);
@ -3802,7 +3772,7 @@ class Helpers : base::StaticClass {
return ELPP->hasCustomFormatSpecifier(formatSpecifier); return ELPP->hasCustomFormatSpecifier(formatSpecifier);
} }
static inline void validateFileRolling(Logger* logger, Level level) { static inline void validateFileRolling(Logger* logger, Level level) {
if (logger == nullptr) return; if (ELPP == nullptr || logger == nullptr) return;
logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback());
} }
}; };

View File

@ -21,11 +21,14 @@ TEST(FileUtilsTest, GetSizeOfFile) {
EXPECT_EQ(File::getSizeOfFile(fs), strlen(data)); EXPECT_EQ(File::getSizeOfFile(fs), strlen(data));
} }
#if !ELPP_OS_EMSCRIPTEN
// this doesn't work as expected under emscripten's filesystem emulation
TEST(FileUtilsTest, PathExists) { TEST(FileUtilsTest, PathExists) {
EXPECT_TRUE(File::pathExists(filename)); EXPECT_TRUE(File::pathExists(filename));
removeFile(filename); removeFile(filename);
EXPECT_FALSE(File::pathExists(filename)); EXPECT_FALSE(File::pathExists(filename));
} }
#endif
TEST(FileUtilsTest, ExtractPathFromFilename) { TEST(FileUtilsTest, ExtractPathFromFilename) {
EXPECT_EQ("/this/is/path/on/unix/", File::extractPathFromFilename("/this/is/path/on/unix/file.txt")); EXPECT_EQ("/this/is/path/on/unix/", File::extractPathFromFilename("/this/is/path/on/unix/file.txt"));
@ -34,11 +37,17 @@ TEST(FileUtilsTest, ExtractPathFromFilename) {
TEST(FileUtilsTest, CreatePath) { TEST(FileUtilsTest, CreatePath) {
const char* path = "/tmp/my/one/long/path"; const char* path = "/tmp/my/one/long/path";
#if !ELPP_OS_EMSCRIPTEN
// it'll be reported as existing in emscripten
EXPECT_FALSE(File::pathExists(path)); EXPECT_FALSE(File::pathExists(path));
#endif
EXPECT_TRUE(File::createPath(path)); EXPECT_TRUE(File::createPath(path));
EXPECT_TRUE(File::pathExists(path)); EXPECT_TRUE(File::pathExists(path));
removeFile(path); removeFile(path);
#if !ELPP_OS_EMSCRIPTEN
EXPECT_FALSE(File::pathExists(path)); EXPECT_FALSE(File::pathExists(path));
#endif
} }

View File

@ -3,7 +3,8 @@
#include "test.h" #include "test.h"
#if ELPP_OS_UNIX #if ELPP_OS_UNIX && !ELPP_OS_EMSCRIPTEN
// this doesn't make any sense under emscripten; you can't shell out
TEST(OSUtilsTest, GetBashOutput) { TEST(OSUtilsTest, GetBashOutput) {
const char* bashCommand = "echo 'test'"; const char* bashCommand = "echo 'test'";
std::string bashResult = OS::getBashOutput(bashCommand); std::string bashResult = OS::getBashOutput(bashCommand);

View File

@ -101,9 +101,17 @@ TEST(TypedConfigurationsTest, NonExistentFileCreation) {
c.set(Level::Error, ConfigurationType::Filename, "/tmp/logs/el.gtest.log"); c.set(Level::Error, ConfigurationType::Filename, "/tmp/logs/el.gtest.log");
TypedConfigurations tConf(&c, ELPP->registeredLoggers()->logStreamsReference()); TypedConfigurations tConf(&c, ELPP->registeredLoggers()->logStreamsReference());
EXPECT_TRUE(tConf.toFile(Level::Global)); EXPECT_TRUE(tConf.toFile(Level::Global));
EXPECT_FALSE(tConf.toFile(Level::Info));
EXPECT_TRUE(tConf.toFile(Level::Error)); #if ELPP_OS_EMSCRIPTEN == 1
// On Emscripten, all files can be created; we actually expect success here
EXPECT_TRUE(tConf.toFile(Level::Info));
EXPECT_NE(nullptr, tConf.fileStream(Level::Info)); // not nullptr (emulated fs)
#else
EXPECT_EQ(nullptr, tConf.fileStream(Level::Info)); // nullptr EXPECT_EQ(nullptr, tConf.fileStream(Level::Info)); // nullptr
EXPECT_FALSE(tConf.toFile(Level::Info));
#endif
EXPECT_TRUE(tConf.toFile(Level::Error));
EXPECT_NE(nullptr, tConf.fileStream(Level::Error)); // Not null EXPECT_NE(nullptr, tConf.fileStream(Level::Error)); // Not null
} }

View File

@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
# Bash script that helps with releasing new versions of EasyLogging++ # Bash script that helps with releasing new versions of Easylogging++
# Revision: 1.4 # Revision: 1.5
# @author mkhan3189 # author @abumusamq
# #
# Usage: # Usage:
# ./release.sh [repo-root] [homepage-repo-root] [curr-version] [new-version] [do-not-ask] # ./release.sh [repo-root] [homepage-repo-root] [curr-version] [new-version] [do-not-ask]
@ -69,11 +69,12 @@ if [ "$confirm" = "y" ]; then
cp $1/src/easylogging++.h . cp $1/src/easylogging++.h .
cp $1/src/easylogging++.cc . cp $1/src/easylogging++.cc .
cp $1/CHANGELOG.md CHANGELOG.txt cp $1/CHANGELOG.md CHANGELOG.txt
cp $1/LICENCE LICENCE.txt cp $1/README.md README.txt
zip easyloggingpp_v$NEW_VERSION.zip easylogging++.h easylogging++.cc LICENCE.txt CHANGELOG.txt cp $1/LICENSE LICENSE.txt
tar -pczf easyloggingpp_v$NEW_VERSION.tar.gz easylogging++.h easylogging++.cc LICENCE.txt CHANGELOG.txt zip easyloggingpp_v$NEW_VERSION.zip easylogging++.h easylogging++.cc LICENSE.txt CHANGELOG.txt README.txt
tar -pczf easyloggingpp_v$NEW_VERSION.tar.gz easylogging++.h easylogging++.cc LICENSE.txt CHANGELOG.txt README.txt
mv easyloggingpp_v$NEW_VERSION.zip $2/ mv easyloggingpp_v$NEW_VERSION.zip $2/
mv easyloggingpp_v$NEW_VERSION.tar.gz $2/ mv easyloggingpp_v$NEW_VERSION.tar.gz $2/
rm easylogging++.h easylogging++.cc CHANGELOG.txt LICENCE.txt rm easylogging++.h easylogging++.cc CHANGELOG.txt LICENSE.txt README.txt
echo "\n---------- PLEASE CHANGE CMakeLists.txt MANUALLY ----------- \n" echo "\n---------- PLEASE CHANGE CMakeLists.txt MANUALLY ----------- \n"
fi fi