Merge from develop

This commit is contained in:
Sebastian Messmer 2017-02-04 19:07:48 +00:00
commit 9292aab373
65 changed files with 4065 additions and 3077 deletions

View File

@ -117,12 +117,12 @@ boost::optional<cpputils::unique_ref<EncryptedBlock<Cipher>>> EncryptedBlock<Cip
boost::optional<cpputils::Data> plaintextWithHeader = Cipher::decrypt((byte*)baseBlock->data() + sizeof(FORMAT_VERSION_HEADER), baseBlock->size() - sizeof(FORMAT_VERSION_HEADER), encKey); boost::optional<cpputils::Data> plaintextWithHeader = Cipher::decrypt((byte*)baseBlock->data() + sizeof(FORMAT_VERSION_HEADER), baseBlock->size() - sizeof(FORMAT_VERSION_HEADER), encKey);
if(plaintextWithHeader == boost::none) { if(plaintextWithHeader == boost::none) {
//Decryption failed (e.g. an authenticated cipher detected modifications to the ciphertext) //Decryption failed (e.g. an authenticated cipher detected modifications to the ciphertext)
cpputils::logging::LOG(cpputils::logging::WARN) << "Decrypting block " << baseBlock->key().ToString() << " failed. Was the block modified by an attacker?"; cpputils::logging::LOG(cpputils::logging::WARN, "Decrypting block {} failed. Was the block modified by an attacker?", baseBlock->key().ToString());
return boost::none; return boost::none;
} }
if(!_keyHeaderIsCorrect(baseBlock->key(), *plaintextWithHeader)) { if(!_keyHeaderIsCorrect(baseBlock->key(), *plaintextWithHeader)) {
//The stored key in the block data is incorrect - an attacker might have exchanged the contents with the encrypted data from a different block //The stored key in the block data is incorrect - an attacker might have exchanged the contents with the encrypted data from a different block
cpputils::logging::LOG(cpputils::logging::WARN) << "Decrypting block " << baseBlock->key().ToString() << " failed due to invalid block key. Was the block modified by an attacker?"; cpputils::logging::LOG(cpputils::logging::WARN, "Decrypting block {} failed due to invalid block key. Was the block modified by an attacker?", baseBlock->key().ToString());
return boost::none; return boost::none;
} }
return cpputils::make_unique_ref<EncryptedBlock<Cipher>>(std::move(baseBlock), encKey, std::move(*plaintextWithHeader)); return cpputils::make_unique_ref<EncryptedBlock<Cipher>>(std::move(baseBlock), encKey, std::move(*plaintextWithHeader));

View File

@ -99,7 +99,7 @@ void OnDiskBlock::RemoveFromDisk(const bf::path &rootdir, const Key &key) {
ASSERT(bf::is_regular_file(filepath), "Block not found on disk"); ASSERT(bf::is_regular_file(filepath), "Block not found on disk");
bool retval = bf::remove(filepath); bool retval = bf::remove(filepath);
if (!retval) { if (!retval) {
LOG(ERROR) << "Couldn't find block " << key.ToString() << " to remove"; LOG(ERROR, "Couldn't find block {} to remove", key.ToString());
} }
if (bf::is_empty(filepath.parent_path())) { if (bf::is_empty(filepath.parent_path())) {
bf::remove(filepath.parent_path()); bf::remove(filepath.parent_path());

View File

@ -23,13 +23,13 @@ namespace cpputils {
inline void assert_fail_release [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) { inline void assert_fail_release [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) {
auto msg = format(expr, message, file, line); auto msg = format(expr, message, file, line);
using namespace logging; using namespace logging;
LOG(ERROR) << msg; LOG(ERROR, msg);
throw AssertFailed(msg); throw AssertFailed(msg);
} }
inline void assert_fail_debug [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) { inline void assert_fail_debug [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) {
using namespace logging; using namespace logging;
LOG(ERROR) << format(expr, message, file, line); LOG(ERROR, format(expr, message, file, line));
abort(); abort();
} }
} }

View File

@ -58,7 +58,7 @@ namespace cpputils {
} }
void sigsegv_handler(int) { void sigsegv_handler(int) {
LOG(ERROR) << "SIGSEGV\n" << backtrace(); LOG(ERROR, "SIGSEGV\n{}", backtrace());
exit(1); exit(1);
} }

View File

@ -24,7 +24,7 @@ namespace cpputils {
uint32_t size; uint32_t size;
std::memcpy(&size, reinterpret_cast<const char*>(data.data()), sizeof(size)); std::memcpy(&size, reinterpret_cast<const char*>(data.data()), sizeof(size));
if(sizeof(size) + size >= data.size()) { if(sizeof(size) + size >= data.size()) {
LOG(ERROR) << "Config file is invalid: Invalid padding."; LOG(ERROR, "Config file is invalid: Invalid padding.");
return boost::none; return boost::none;
}; };
Data result(size); Data result(size);

View File

@ -2,6 +2,7 @@
#ifndef MESSMER_CPPUTILS_LOGGING_LOGGER_H #ifndef MESSMER_CPPUTILS_LOGGING_LOGGER_H
#define MESSMER_CPPUTILS_LOGGING_LOGGER_H #define MESSMER_CPPUTILS_LOGGING_LOGGER_H
#define SPDLOG_ENABLE_SYSLOG
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include "../macros.h" #include "../macros.h"

View File

@ -4,14 +4,15 @@
#include "Logger.h" #include "Logger.h"
#include <stdexcept> #include <stdexcept>
#include <spdlog/fmt/ostr.h>
namespace cpputils { namespace cpputils {
namespace logging { namespace logging {
extern struct ERROR_TYPE {} ERROR; constexpr struct ERROR_TYPE {} ERROR;
extern struct WARN_TYPE {} WARN; constexpr struct WARN_TYPE {} WARN;
extern struct INFO_TYPE {} INFO; constexpr struct INFO_TYPE {} INFO;
extern struct DEBUG_TYPE {} DEBUG; constexpr struct DEBUG_TYPE {} DEBUG;
inline void setLogger(std::shared_ptr<spdlog::logger> newLogger) { inline void setLogger(std::shared_ptr<spdlog::logger> newLogger) {
logger().setLogger(newLogger); logger().setLogger(newLogger);
@ -37,20 +38,28 @@ namespace cpputils {
logger().setLevel(spdlog::level::debug); logger().setLevel(spdlog::level::debug);
} }
inline spdlog::details::line_logger LOG(ERROR_TYPE) { template<class LogType> inline void LOG(LogType logType, const std::string &msg) {
return logger()->error(); LOG(logType, msg.c_str());
} }
inline spdlog::details::line_logger LOG(WARN_TYPE) { template <typename... Args>
return logger()->warn(); inline void LOG(ERROR_TYPE, const char* fmt, const Args&... args) {
logger()->error(fmt, args...);
} }
inline spdlog::details::line_logger LOG(INFO_TYPE) { template <typename... Args>
return logger()->info(); inline void LOG(WARN_TYPE, const char* fmt, const Args&... args) {
logger()->warn(fmt, args...);
} }
inline spdlog::details::line_logger LOG(DEBUG_TYPE) { template <typename... Args>
return logger()->debug(); inline void LOG(INFO_TYPE, const char* fmt, const Args&... args) {
logger()->info(fmt, args...);
}
template <typename... Args>
inline void LOG(DEBUG_TYPE, const char* fmt, const Args&... args) {
logger()->debug(fmt, args...);
} }
} }
} }

View File

@ -34,13 +34,13 @@ namespace cpputils {
// Create a new SID for the child process // Create a new SID for the child process
pid_t sid = setsid(); pid_t sid = setsid();
if (sid < 0) { if (sid < 0) {
LOG(ERROR) << "Failed to get SID for daemon process"; LOG(ERROR, "Failed to get SID for daemon process");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// Change the current working directory to a directory that's always existin // Change the current working directory to a directory that's always existin
if ((chdir("/")) < 0) { if ((chdir("/")) < 0) {
LOG(ERROR) << "Failed to change working directory for daemon process"; LOG(ERROR, "Failed to change working directory for daemon process");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }

View File

@ -2,11 +2,11 @@
#ifndef MESSMER_CPPUTILS_SYSTEM_CLOCKGETTIME_H #ifndef MESSMER_CPPUTILS_SYSTEM_CLOCKGETTIME_H
#define MESSMER_CPPUTILS_SYSTEM_CLOCKGETTIME_H #define MESSMER_CPPUTILS_SYSTEM_CLOCKGETTIME_H
// Implements clock_gettime for Mac OS X (where it is not implemented by in the standard library) // Implements clock_gettime for Mac OS X before 10.12 (where it is not implemented by in the standard library)
// Source: http://stackoverflow.com/a/9781275/829568 // Source: http://stackoverflow.com/a/9781275/829568
// Caution: The returned value is less precise than the returned value from a linux clock_gettime would be. // Caution: The returned value is less precise than the returned value from a linux clock_gettime would be.
#ifdef __MACH__ #if defined(__MACH__) && !defined(CLOCK_REALTIME)
#include <sys/time.h> #include <sys/time.h>
#define CLOCK_REALTIME 0 #define CLOCK_REALTIME 0
inline int clock_gettime(int /*clk_id*/, struct timespec *result) { inline int clock_gettime(int /*clk_id*/, struct timespec *result) {

View File

@ -21,7 +21,7 @@ void TempDir::remove() {
bf::remove_all(_path); bf::remove_all(_path);
} }
} catch (const boost::filesystem::filesystem_error &e) { } catch (const boost::filesystem::filesystem_error &e) {
LOG(ERROR) << "Could not delete tempfile."; LOG(ERROR, "Could not delete tempfile.");
} }
} }

View File

@ -28,7 +28,7 @@ TempFile::~TempFile() {
bf::remove(_path); bf::remove(_path);
} }
} catch (const boost::filesystem::filesystem_error &e) { } catch (const boost::filesystem::filesystem_error &e) {
LOG(ERROR) << "Could not delete tempfile."; LOG(ERROR, "Could not delete tempfile.");
} }
} }

View File

@ -75,9 +75,9 @@ namespace cpputils {
} catch (const boost::thread_interrupted &e) { } catch (const boost::thread_interrupted &e) {
//Do nothing, exit thread. //Do nothing, exit thread.
} catch (const std::exception &e) { } catch (const std::exception &e) {
LOG(ERROR) << "LoopThread crashed: " << e.what(); LOG(ERROR, "LoopThread crashed: {}", e.what());
} catch (...) { } catch (...) {
LOG(ERROR) << "LoopThread crashed"; LOG(ERROR, "LoopThread crashed");
} }
//TODO We should remove the thread from _runningThreads here, not in stop(). //TODO We should remove the thread from _runningThreads here, not in stop().
} }

View File

@ -252,9 +252,9 @@ namespace cryfs {
#endif #endif
fuse.run(options.mountDir(), options.fuseOptions()); fuse.run(options.mountDir(), options.fuseOptions());
} catch (const std::exception &e) { } catch (const std::exception &e) {
LOG(ERROR) << "Crashed: " << e.what(); LOG(ERROR, "Crashed: {}", e.what());
} catch (...) { } catch (...) {
LOG(ERROR) << "Crashed"; LOG(ERROR, "Crashed");
} }
} }

View File

@ -65,7 +65,7 @@ namespace cryfs {
read_json(input, pt); read_json(input, pt);
return pt; return pt;
} catch (const json_parser_error &e) { } catch (const json_parser_error &e) {
LOG(WARN) << "Error parsing version information json object"; LOG(WARN, "Error parsing version information json object");
return none; return none;
} }
} }

View File

@ -2,11 +2,13 @@
#include "utils.h" #include "utils.h"
#include <iostream> #include <iostream>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <cryfs/config/CryConfigConsole.h>
#include <cryfs-cli/Environment.h> #include <cryfs-cli/Environment.h>
namespace po = boost::program_options; namespace po = boost::program_options;
namespace bf = boost::filesystem; namespace bf = boost::filesystem;
using namespace cryfs::program_options; using namespace cryfs::program_options;
using cryfs::CryConfigConsole;
using std::pair; using std::pair;
using std::vector; using std::vector;
using std::cerr; using std::cerr;
@ -122,12 +124,16 @@ vector<const char*> Parser::_to_const_char_vector(const vector<string> &options)
void Parser::_addAllowedOptions(po::options_description *desc) { void Parser::_addAllowedOptions(po::options_description *desc) {
po::options_description options("Allowed options"); po::options_description options("Allowed options");
string cipher_description = "Cipher to use for encryption. See possible values by calling cryfs with --show-ciphers. Default: ";
cipher_description += CryConfigConsole::DEFAULT_CIPHER;
string blocksize_description = "The block size used when storing ciphertext blocks (in bytes). Default: ";
blocksize_description += std::to_string(CryConfigConsole::DEFAULT_BLOCKSIZE_BYTES);
options.add_options() options.add_options()
("help,h", "show help message") ("help,h", "show help message")
("config,c", po::value<string>(), "Configuration file") ("config,c", po::value<string>(), "Configuration file")
("foreground,f", "Run CryFS in foreground.") ("foreground,f", "Run CryFS in foreground.")
("cipher", po::value<string>(), "Cipher to use for encryption. See possible values by calling cryfs with --show-ciphers.") ("cipher", po::value<string>(), cipher_description.c_str())
("blocksize", po::value<uint32_t>(), "The block size used when storing ciphertext blocks (in bytes).") ("blocksize", po::value<uint32_t>(), blocksize_description.c_str())
("missing-block-is-integrity-violation", po::value<bool>(), "Whether to treat a missing block as an integrity violation. This makes sure you notice if an attacker deleted some of your files, but only works in single-client mode. You will not be able to use the file system on other devices.") ("missing-block-is-integrity-violation", po::value<bool>(), "Whether to treat a missing block as an integrity violation. This makes sure you notice if an attacker deleted some of your files, but only works in single-client mode. You will not be able to use the file system on other devices.")
("show-ciphers", "Show list of supported ciphers.") ("show-ciphers", "Show list of supported ciphers.")
("unmount-idle", po::value<double>(), "Automatically unmount after specified number of idle minutes.") ("unmount-idle", po::value<double>(), "Automatically unmount after specified number of idle minutes.")

View File

@ -28,7 +28,7 @@ CryConfigFile::~CryConfigFile() {
optional<CryConfigFile> CryConfigFile::load(const bf::path &path, const string &password) { optional<CryConfigFile> CryConfigFile::load(const bf::path &path, const string &password) {
auto encryptedConfigData = Data::LoadFromFile(path); auto encryptedConfigData = Data::LoadFromFile(path);
if (encryptedConfigData == none) { if (encryptedConfigData == none) {
LOG(ERROR) << "Config file not found"; LOG(ERROR, "Config file not found");
return none; return none;
} }
auto encryptor = CryConfigEncryptorFactory::loadKey(*encryptedConfigData, password); auto encryptor = CryConfigEncryptorFactory::loadKey(*encryptedConfigData, password);
@ -41,7 +41,7 @@ optional<CryConfigFile> CryConfigFile::load(const bf::path &path, const string &
} }
CryConfig config = CryConfig::load(decrypted->data); CryConfig config = CryConfig::load(decrypted->data);
if (config.Cipher() != decrypted->cipherName) { if (config.Cipher() != decrypted->cipherName) {
LOG(ERROR) << "Inner cipher algorithm used to encrypt config file doesn't match config value"; LOG(ERROR, "Inner cipher algorithm used to encrypt config file doesn't match config value");
return none; return none;
} }
auto configFile = CryConfigFile(path, std::move(config), std::move(*encryptor)); auto configFile = CryConfigFile(path, std::move(config), std::move(*encryptor));

View File

@ -33,12 +33,12 @@ namespace cryfs {
template<class Cipher> template<class Cipher>
boost::optional<cpputils::Data> ConcreteInnerEncryptor<Cipher>::decrypt(const InnerConfig &innerConfig) const { boost::optional<cpputils::Data> ConcreteInnerEncryptor<Cipher>::decrypt(const InnerConfig &innerConfig) const {
if (innerConfig.cipherName != Cipher::NAME) { if (innerConfig.cipherName != Cipher::NAME) {
cpputils::logging::LOG(cpputils::logging::ERROR) << "Initialized ConcreteInnerEncryptor with wrong cipher"; cpputils::logging::LOG(cpputils::logging::ERROR, "Initialized ConcreteInnerEncryptor with wrong cipher");
return boost::none; return boost::none;
} }
auto decrypted = Cipher::decrypt(static_cast<const uint8_t*>(innerConfig.encryptedConfig.data()), innerConfig.encryptedConfig.size(), _key); auto decrypted = Cipher::decrypt(static_cast<const uint8_t*>(innerConfig.encryptedConfig.data()), innerConfig.encryptedConfig.size(), _key);
if (decrypted == boost::none) { if (decrypted == boost::none) {
cpputils::logging::LOG(cpputils::logging::ERROR) << "Failed decrypting configuration file"; cpputils::logging::LOG(cpputils::logging::ERROR, "Failed decrypting configuration file");
return boost::none; return boost::none;
} }
auto configData = cpputils::RandomPadding::remove(*decrypted); auto configData = cpputils::RandomPadding::remove(*decrypted);

View File

@ -23,7 +23,7 @@ namespace cryfs {
serializer.writeTailData(encryptedConfig); serializer.writeTailData(encryptedConfig);
return serializer.finished(); return serializer.finished();
} catch (const exception &e) { } catch (const exception &e) {
LOG(ERROR) << "Error serializing inner configuration: " << e.what(); LOG(ERROR, "Error serializing inner configuration: {}", e.what());
throw; // This is a programming logic error, pass through exception. throw; // This is a programming logic error, pass through exception.
} }
} }
@ -37,7 +37,7 @@ namespace cryfs {
deserializer.finished(); deserializer.finished();
return InnerConfig {cipherName, std::move(result)}; return InnerConfig {cipherName, std::move(result)};
} catch (const exception &e) { } catch (const exception &e) {
LOG(ERROR) << "Error deserializing inner configuration: " << e.what(); LOG(ERROR, "Error deserializing inner configuration: {}", e.what());
return none; // This can be caused by invalid input data and does not have to be a programming error. Don't throw exception. return none; // This can be caused by invalid input data and does not have to be a programming error. Don't throw exception.
} }
} }

View File

@ -38,7 +38,7 @@ namespace cryfs {
serializer.writeTailData(encryptedInnerConfig); serializer.writeTailData(encryptedInnerConfig);
return serializer.finished(); return serializer.finished();
} catch (const exception &e) { } catch (const exception &e) {
LOG(ERROR) << "Error serializing CryConfigEncryptor: " << e.what(); LOG(ERROR, "Error serializing CryConfigEncryptor: {}", e.what());
throw; // This is a programming logic error. Pass through exception. throw; // This is a programming logic error. Pass through exception.
} }
} }
@ -60,7 +60,7 @@ namespace cryfs {
_deserializeNewFormat(&deserializer); _deserializeNewFormat(&deserializer);
#endif #endif
} catch (const exception &e) { } catch (const exception &e) {
LOG(ERROR) << "Error deserializing outer configuration: " << e.what(); LOG(ERROR, "Error deserializing outer configuration: {}", e.what());
return none; // This can be caused by invalid input data and does not have to be a programming error. Don't throw exception. return none; // This can be caused by invalid input data and does not have to be a programming error. Don't throw exception.
} }
} }

View File

@ -202,7 +202,7 @@ CryDevice::BlobWithParent CryDevice::LoadBlobWithParent(const bf::path &path) {
optional<unique_ref<DirBlobRef>> parentBlob = none; optional<unique_ref<DirBlobRef>> parentBlob = none;
optional<unique_ref<FsBlobRef>> currentBlobOpt = _fsBlobStore->load(_rootKey); optional<unique_ref<FsBlobRef>> currentBlobOpt = _fsBlobStore->load(_rootKey);
if (currentBlobOpt == none) { if (currentBlobOpt == none) {
LOG(ERROR) << "Could not load root blob. Is the base directory accessible?"; LOG(ERROR, "Could not load root blob. Is the base directory accessible?");
throw FuseErrnoException(EIO); throw FuseErrnoException(EIO);
} }
unique_ref<FsBlobRef> currentBlob = std::move(*currentBlobOpt); unique_ref<FsBlobRef> currentBlob = std::move(*currentBlobOpt);
@ -267,7 +267,7 @@ unique_ref<SymlinkBlobRef> CryDevice::CreateSymlinkBlob(const bf::path &target,
unique_ref<FsBlobRef> CryDevice::LoadBlob(const blockstore::Key &key) { unique_ref<FsBlobRef> CryDevice::LoadBlob(const blockstore::Key &key) {
auto blob = _fsBlobStore->load(key); auto blob = _fsBlobStore->load(key);
if (blob == none) { if (blob == none) {
LOG(ERROR) << "Could not load blob " << key.ToString() << ". Is the base directory accessible?"; LOG(ERROR, "Could not load blob {}. Is the base directory accessible?", key.ToString());
throw FuseErrnoException(EIO); throw FuseErrnoException(EIO);
} }
return std::move(*blob); return std::move(*blob);
@ -276,7 +276,7 @@ unique_ref<FsBlobRef> CryDevice::LoadBlob(const blockstore::Key &key) {
void CryDevice::RemoveBlob(const blockstore::Key &key) { void CryDevice::RemoveBlob(const blockstore::Key &key) {
auto blob = _fsBlobStore->load(key); auto blob = _fsBlobStore->load(key);
if (blob == none) { if (blob == none) {
LOG(ERROR) << "Could not load blob " << key.ToString() << ". Is the base directory accessible?"; LOG(ERROR, "Could not load blob. Is the base directory accessible?", key.ToString());
throw FuseErrnoException(EIO); throw FuseErrnoException(EIO);
} }
_fsBlobStore->remove(std::move(*blob)); _fsBlobStore->remove(std::move(*blob));

View File

@ -5,6 +5,7 @@
#include <blockstore/utils/Key.h> #include <blockstore/utils/Key.h>
#include <fspp/fs_interface/Dir.h> #include <fspp/fs_interface/Dir.h>
#include <cpp-utils/system/time.h> #include <cpp-utils/system/time.h>
#include <sys/stat.h>
// TODO Implement (and test) atime, noatime, strictatime, relatime mount options // TODO Implement (and test) atime, noatime, strictatime, relatime mount options

View File

@ -222,11 +222,11 @@ Fuse::Fuse(Filesystem *fs, const std::string &fstype, const boost::optional<std:
} }
void Fuse::_logException(const std::exception &e) { void Fuse::_logException(const std::exception &e) {
LOG(ERROR) << "Exception thrown: " << e.what(); LOG(ERROR, "Exception thrown: ", e.what());
} }
void Fuse::_logUnknownException() { void Fuse::_logUnknownException() {
LOG(ERROR) << "Unknown exception thrown"; LOG(ERROR, "Unknown exception thrown");
} }
void Fuse::run(const bf::path &mountdir, const vector<string> &fuseOptions) { void Fuse::run(const bf::path &mountdir, const vector<string> &fuseOptions) {
@ -296,19 +296,19 @@ void Fuse::stop() {
int ret = system(("fusermount -z -u " + _mountdir.native()).c_str()); // "-z" takes care that if the filesystem can't be unmounted right now because something is opened, it will be unmounted as soon as it can be. int ret = system(("fusermount -z -u " + _mountdir.native()).c_str()); // "-z" takes care that if the filesystem can't be unmounted right now because something is opened, it will be unmounted as soon as it can be.
#endif #endif
if (ret != 0) { if (ret != 0) {
LOG(ERROR) << "Could not unmount filesystem"; LOG(ERROR, "Could not unmount filesystem");
} }
} }
int Fuse::getattr(const bf::path &path, struct stat *stbuf) { int Fuse::getattr(const bf::path &path, struct stat *stbuf) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "getattr(" << path << ", _, _)"; LOG(DEBUG, "getattr({}, _, _)", path);
#endif #endif
try { try {
_fs->lstat(path, stbuf); _fs->lstat(path, stbuf);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::getattr: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::getattr: {}", e.what());
return -EIO; return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) { } catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -323,7 +323,7 @@ int Fuse::getattr(const bf::path &path, struct stat *stbuf) {
int Fuse::fgetattr(const bf::path &path, struct stat *stbuf, fuse_file_info *fileinfo) { int Fuse::fgetattr(const bf::path &path, struct stat *stbuf, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "fgetattr(" << path << ", _, _)\n"; LOG(DEBUG, "fgetattr({}, _, _)\n", path);
#endif #endif
// On FreeBSD, trying to do anything with the mountpoint ends up // On FreeBSD, trying to do anything with the mountpoint ends up
@ -339,7 +339,7 @@ int Fuse::fgetattr(const bf::path &path, struct stat *stbuf, fuse_file_info *fil
_fs->fstat(fileinfo->fh, stbuf); _fs->fstat(fileinfo->fh, stbuf);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::fgetattr: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::fgetattr: {}", e.what());
return -EIO; return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) { } catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -354,13 +354,13 @@ int Fuse::fgetattr(const bf::path &path, struct stat *stbuf, fuse_file_info *fil
int Fuse::readlink(const bf::path &path, char *buf, size_t size) { int Fuse::readlink(const bf::path &path, char *buf, size_t size) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "readlink(" << path << ", _, " << size << ")"; LOG(DEBUG, "readlink({}, _, {})", path, size);
#endif #endif
try { try {
_fs->readSymlink(path, buf, size); _fs->readSymlink(path, buf, size);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::readlink: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::readlink: {}", e.what());
return -EIO; return -EIO;
} catch (fspp::fuse::FuseErrnoException &e) { } catch (fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -377,20 +377,20 @@ int Fuse::mknod(const bf::path &path, mode_t mode, dev_t rdev) {
UNUSED(rdev); UNUSED(rdev);
UNUSED(mode); UNUSED(mode);
UNUSED(path); UNUSED(path);
LOG(WARN) << "Called non-implemented mknod(" << path << ", " << mode << ", _)"; LOG(WARN, "Called non-implemented mknod({}, {}, _)", path, mode);
return ENOSYS; return ENOSYS;
} }
int Fuse::mkdir(const bf::path &path, mode_t mode) { int Fuse::mkdir(const bf::path &path, mode_t mode) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "mkdir(" << path << ", " << mode << ")"; LOG(DEBUG, "mkdir({}, {})", path, mode);
#endif #endif
try { try {
auto context = fuse_get_context(); auto context = fuse_get_context();
_fs->mkdir(path, mode, context->uid, context->gid); _fs->mkdir(path, mode, context->uid, context->gid);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::mkdir: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::mkdir: {}", e.what());
return -EIO; return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) { } catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -405,13 +405,13 @@ int Fuse::mkdir(const bf::path &path, mode_t mode) {
int Fuse::unlink(const bf::path &path) { int Fuse::unlink(const bf::path &path) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "unlink(" << path << ")"; LOG(DEBUG, "unlink({})", path);
#endif #endif
try { try {
_fs->unlink(path); _fs->unlink(path);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::unlink: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::unlink: {}", e.what());
return -EIO; return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) { } catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -426,13 +426,13 @@ int Fuse::unlink(const bf::path &path) {
int Fuse::rmdir(const bf::path &path) { int Fuse::rmdir(const bf::path &path) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "rmdir(" << path << ")"; LOG(DEBUG, "rmdir({})", path);
#endif #endif
try { try {
_fs->rmdir(path); _fs->rmdir(path);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::rmdir: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::rmdir: {}", e.what());
return -EIO; return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) { } catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -447,14 +447,14 @@ int Fuse::rmdir(const bf::path &path) {
int Fuse::symlink(const bf::path &from, const bf::path &to) { int Fuse::symlink(const bf::path &from, const bf::path &to) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "symlink(" << from << ", " << to << ")"; LOG(DEBUG, "symlink({}, {})", from, to);
#endif #endif
try { try {
auto context = fuse_get_context(); auto context = fuse_get_context();
_fs->createSymlink(from, to, context->uid, context->gid); _fs->createSymlink(from, to, context->uid, context->gid);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::symlink: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::symlink: {}", e.what());
return -EIO; return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) { } catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -469,7 +469,7 @@ int Fuse::symlink(const bf::path &from, const bf::path &to) {
int Fuse::rename(const bf::path &from, const bf::path &to) { int Fuse::rename(const bf::path &from, const bf::path &to) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "rename(" << from << ", " << to << ")"; LOG(DEBUG, "rename({}, {})", from, to);
#endif #endif
try { try {
ASSERT(from.is_absolute(), "from has to be an absolute path"); ASSERT(from.is_absolute(), "from has to be an absolute path");
@ -477,7 +477,7 @@ int Fuse::rename(const bf::path &from, const bf::path &to) {
_fs->rename(from, to); _fs->rename(from, to);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::rename: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::rename: {}", e.what());
return -EIO; return -EIO;
} catch(fspp::fuse::FuseErrnoException &e) { } catch(fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -492,7 +492,7 @@ int Fuse::rename(const bf::path &from, const bf::path &to) {
//TODO //TODO
int Fuse::link(const bf::path &from, const bf::path &to) { int Fuse::link(const bf::path &from, const bf::path &to) {
LOG(WARN) << "NOT IMPLEMENTED: link(" << from << ", " << to << ")"; LOG(WARN, "NOT IMPLEMENTED: link({}, {})", from, to);
//auto real_from = _impl->RootDir() / from; //auto real_from = _impl->RootDir() / from;
//auto real_to = _impl->RootDir() / to; //auto real_to = _impl->RootDir() / to;
//int retstat = ::link(real_from.c_str(), real_to.c_str()); //int retstat = ::link(real_from.c_str(), real_to.c_str());
@ -502,13 +502,13 @@ int Fuse::link(const bf::path &from, const bf::path &to) {
int Fuse::chmod(const bf::path &path, mode_t mode) { int Fuse::chmod(const bf::path &path, mode_t mode) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "chmod(" << path << ", " << mode << ")"; LOG(DEBUG, "chmod({}, {})", path, mode);
#endif #endif
try { try {
_fs->chmod(path, mode); _fs->chmod(path, mode);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::chmod: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::chmod: {}", e.what());
return -EIO; return -EIO;
} catch (fspp::fuse::FuseErrnoException &e) { } catch (fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -523,13 +523,13 @@ int Fuse::chmod(const bf::path &path, mode_t mode) {
int Fuse::chown(const bf::path &path, uid_t uid, gid_t gid) { int Fuse::chown(const bf::path &path, uid_t uid, gid_t gid) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "chown(" << path << ", " << uid << ", " << gid << ")"; LOG(DEBUG, "chown({}, {}, {})", path, uid, gid);
#endif #endif
try { try {
_fs->chown(path, uid, gid); _fs->chown(path, uid, gid);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::chown: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::chown: {}", e.what());
return -EIO; return -EIO;
} catch (fspp::fuse::FuseErrnoException &e) { } catch (fspp::fuse::FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -544,13 +544,13 @@ int Fuse::chown(const bf::path &path, uid_t uid, gid_t gid) {
int Fuse::truncate(const bf::path &path, off_t size) { int Fuse::truncate(const bf::path &path, off_t size) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "truncate(" << path << ", " << size << ")"; LOG(DEBUG, "truncate({}, {})", path, size);
#endif #endif
try { try {
_fs->truncate(path, size); _fs->truncate(path, size);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::truncate: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::truncate: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -565,14 +565,14 @@ int Fuse::truncate(const bf::path &path, off_t size) {
int Fuse::ftruncate(const bf::path &path, off_t size, fuse_file_info *fileinfo) { int Fuse::ftruncate(const bf::path &path, off_t size, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "ftruncate(" << path << ", " << size << ")"; LOG(DEBUG, "ftruncate({}, {})", path, size);
#endif #endif
UNUSED(path); UNUSED(path);
try { try {
_fs->ftruncate(fileinfo->fh, size); _fs->ftruncate(fileinfo->fh, size);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::ftruncate: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::ftruncate: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -587,13 +587,13 @@ int Fuse::ftruncate(const bf::path &path, off_t size, fuse_file_info *fileinfo)
int Fuse::utimens(const bf::path &path, const timespec times[2]) { int Fuse::utimens(const bf::path &path, const timespec times[2]) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "utimens(" << path << ", _)"; LOG(DEBUG, "utimens({}, _)", path);
#endif #endif
try { try {
_fs->utimens(path, times[0], times[1]); _fs->utimens(path, times[0], times[1]);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::utimens: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::utimens: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -608,13 +608,13 @@ int Fuse::utimens(const bf::path &path, const timespec times[2]) {
int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) { int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "open(" << path << ", _)"; LOG(DEBUG, "open({}, _)", path);
#endif #endif
try { try {
fileinfo->fh = _fs->openFile(path, fileinfo->flags); fileinfo->fh = _fs->openFile(path, fileinfo->flags);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::open: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::open: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -629,14 +629,14 @@ int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) {
int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) { int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "release(" << path << ", _)"; LOG(DEBUG, "release({}, _)", path);
#endif #endif
UNUSED(path); UNUSED(path);
try { try {
_fs->closeFile(fileinfo->fh); _fs->closeFile(fileinfo->fh);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::release: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::release: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -651,13 +651,13 @@ int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) {
int Fuse::read(const bf::path &path, char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) { int Fuse::read(const bf::path &path, char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "read(" << path << ", _, " << size << ", " << offset << ", _ )"; LOG(DEBUG, "read({}, _, {}, {}, _)", path, size, offset);
#endif #endif
UNUSED(path); UNUSED(path);
try { try {
return _fs->read(fileinfo->fh, buf, size, offset); return _fs->read(fileinfo->fh, buf, size, offset);
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::read: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::read: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -672,14 +672,14 @@ int Fuse::read(const bf::path &path, char *buf, size_t size, off_t offset, fuse_
int Fuse::write(const bf::path &path, const char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) { int Fuse::write(const bf::path &path, const char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "write(" << path << ", _, " << size << ", " << offset << ", _)"; LOG(DEBUG, "write({}, _, {}, {}, _)", path, size, offsset);
#endif #endif
UNUSED(path); UNUSED(path);
try { try {
_fs->write(fileinfo->fh, buf, size, offset); _fs->write(fileinfo->fh, buf, size, offset);
return size; return size;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::write: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::write: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -695,13 +695,13 @@ int Fuse::write(const bf::path &path, const char *buf, size_t size, off_t offset
//TODO //TODO
int Fuse::statfs(const bf::path &path, struct statvfs *fsstat) { int Fuse::statfs(const bf::path &path, struct statvfs *fsstat) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "statfs(" << path << ", _)"; LOG(DEBUG, "statfs({}, _)", path);
#endif #endif
try { try {
_fs->statfs(path, fsstat); _fs->statfs(path, fsstat);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::statfs: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::statfs: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -716,14 +716,14 @@ int Fuse::statfs(const bf::path &path, struct statvfs *fsstat) {
int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) { int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(WARN) << "flush(" << path << ", _)"; LOG(WARN, "flush({}, _)", path);
#endif #endif
UNUSED(path); UNUSED(path);
try { try {
_fs->flush(fileinfo->fh); _fs->flush(fileinfo->fh);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::flush: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::flush: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -738,7 +738,7 @@ int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) {
int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) { int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "fsync(" << path << ", " << datasync << ", _)"; LOG(DEBUG, "fsync({}, {}, _)", path, datasync);
#endif #endif
UNUSED(path); UNUSED(path);
try { try {
@ -749,7 +749,7 @@ int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) {
} }
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::fsync: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::fsync: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -765,14 +765,14 @@ int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) {
int Fuse::opendir(const bf::path &path, fuse_file_info *fileinfo) { int Fuse::opendir(const bf::path &path, fuse_file_info *fileinfo) {
UNUSED(path); UNUSED(path);
UNUSED(fileinfo); UNUSED(fileinfo);
//LOG(DEBUG) << "opendir(" << path << ", _)"; //LOG(DEBUG, "opendir({}, _)", path);
//We don't need opendir, because readdir works directly on the path //We don't need opendir, because readdir works directly on the path
return 0; return 0;
} }
int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, off_t offset, fuse_file_info *fileinfo) { int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, off_t offset, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "readdir(" << path << ", _, _, " << offset << ", _)"; LOG(DEBUG, "readdir({}, _, _, {}, _)", path, offest);
#endif #endif
UNUSED(fileinfo); UNUSED(fileinfo);
UNUSED(offset); UNUSED(offset);
@ -799,7 +799,7 @@ int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, off_t
} }
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::readdir: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::readdir: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -815,7 +815,7 @@ int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, off_t
int Fuse::releasedir(const bf::path &path, fuse_file_info *fileinfo) { int Fuse::releasedir(const bf::path &path, fuse_file_info *fileinfo) {
UNUSED(path); UNUSED(path);
UNUSED(fileinfo); UNUSED(fileinfo);
//LOG(DEBUG) << "releasedir(" << path << ", _)"; //LOG(DEBUG, "releasedir({}, _)", path);
//We don't need releasedir, because readdir works directly on the path //We don't need releasedir, because readdir works directly on the path
return 0; return 0;
} }
@ -825,13 +825,13 @@ int Fuse::fsyncdir(const bf::path &path, int datasync, fuse_file_info *fileinfo)
UNUSED(fileinfo); UNUSED(fileinfo);
UNUSED(datasync); UNUSED(datasync);
UNUSED(path); UNUSED(path);
//LOG(WARN) << "Called non-implemented fsyncdir(" << path << ", " << datasync << ", _)"; //LOG(WARN, "Called non-implemented fsyncdir({}, {}, _)", path, datasync);
return 0; return 0;
} }
void Fuse::init(fuse_conn_info *conn) { void Fuse::init(fuse_conn_info *conn) {
UNUSED(conn); UNUSED(conn);
LOG(INFO) << "Filesystem started."; LOG(INFO, "Filesystem started.");
_running = true; _running = true;
@ -841,19 +841,19 @@ void Fuse::init(fuse_conn_info *conn) {
} }
void Fuse::destroy() { void Fuse::destroy() {
LOG(INFO) << "Filesystem stopped."; LOG(INFO, "Filesystem stopped.");
_running = false; _running = false;
} }
int Fuse::access(const bf::path &path, int mask) { int Fuse::access(const bf::path &path, int mask) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "access(" << path << ", " << mask << ")"; LOG(DEBUG, "access({}, {})", path, mask);
#endif #endif
try { try {
_fs->access(path, mask); _fs->access(path, mask);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::access: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::access: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();
@ -868,14 +868,14 @@ int Fuse::access(const bf::path &path, int mask) {
int Fuse::create(const bf::path &path, mode_t mode, fuse_file_info *fileinfo) { int Fuse::create(const bf::path &path, mode_t mode, fuse_file_info *fileinfo) {
#ifdef FSPP_LOG #ifdef FSPP_LOG
LOG(DEBUG) << "create(" << path << ", " << mode << ", _)"; LOG(DEBUG, "create({}, {}, _)", path, mode);
#endif #endif
try { try {
auto context = fuse_get_context(); auto context = fuse_get_context();
fileinfo->fh = _fs->createAndOpenFile(path, mode, context->uid, context->gid); fileinfo->fh = _fs->createAndOpenFile(path, mode, context->uid, context->gid);
return 0; return 0;
} catch(const cpputils::AssertFailed &e) { } catch(const cpputils::AssertFailed &e) {
LOG(ERROR) << "AssertFailed in Fuse::create: " << e.what(); LOG(ERROR, "AssertFailed in Fuse::create: {}", e.what());
return -EIO; return -EIO;
} catch (FuseErrnoException &e) { } catch (FuseErrnoException &e) {
return -e.getErrno(); return -e.getErrno();

View File

@ -88,7 +88,7 @@ FilesystemImpl::~FilesystemImpl() {
<< std::setw(40) << "CreateSymlink (without loading): " << static_cast<double>(_createSymlinkNanosec_withoutLoading)/1000000000 << "\n" << std::setw(40) << "CreateSymlink (without loading): " << static_cast<double>(_createSymlinkNanosec_withoutLoading)/1000000000 << "\n"
<< std::setw(40) << "ReadSymlink: " << static_cast<double>(_readSymlinkNanosec)/1000000000 << "\n" << std::setw(40) << "ReadSymlink: " << static_cast<double>(_readSymlinkNanosec)/1000000000 << "\n"
<< std::setw(40) << "ReadSymlink (without loading): " << static_cast<double>(_readSymlinkNanosec_withoutLoading)/1000000000 << "\n"; << std::setw(40) << "ReadSymlink (without loading): " << static_cast<double>(_readSymlinkNanosec_withoutLoading)/1000000000 << "\n";
LOG(INFO) << profilerInformation.str(); LOG(INFO, profilerInformation.str());
#endif #endif
} }

View File

@ -7,42 +7,42 @@ using testing::MatchesRegex;
class LoggingLevelTest: public LoggingTest { class LoggingLevelTest: public LoggingTest {
public: public:
void EXPECT_DEBUG_LOG_ENABLED() { void EXPECT_DEBUG_LOG_ENABLED() {
LOG(DEBUG) << "My log message"; LOG(DEBUG, "My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*"));
} }
void EXPECT_DEBUG_LOG_DISABLED() { void EXPECT_DEBUG_LOG_DISABLED() {
LOG(DEBUG) << "My log message"; LOG(DEBUG, "My log message");
EXPECT_EQ("", mockLogger.capturedLog()); EXPECT_EQ("", mockLogger.capturedLog());
} }
void EXPECT_INFO_LOG_ENABLED() { void EXPECT_INFO_LOG_ENABLED() {
LOG(INFO) << "My log message"; LOG(INFO, "My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"));
} }
void EXPECT_INFO_LOG_DISABLED() { void EXPECT_INFO_LOG_DISABLED() {
LOG(INFO) << "My log message"; LOG(INFO, "My log message");
EXPECT_EQ("", mockLogger.capturedLog()); EXPECT_EQ("", mockLogger.capturedLog());
} }
void EXPECT_WARNING_LOG_ENABLED() { void EXPECT_WARNING_LOG_ENABLED() {
LOG(WARN) << "My log message"; LOG(WARN, "My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*"));
} }
void EXPECT_WARNING_LOG_DISABLED() { void EXPECT_WARNING_LOG_DISABLED() {
LOG(WARN) << "My log message"; LOG(WARN, "My log message");
EXPECT_EQ("", mockLogger.capturedLog()); EXPECT_EQ("", mockLogger.capturedLog());
} }
void EXPECT_ERROR_LOG_ENABLED() { void EXPECT_ERROR_LOG_ENABLED() {
LOG(ERROR) << "My log message"; LOG(ERROR, "My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*"));
} }
void EXPECT_ERROR_LOG_DISABLED() { void EXPECT_ERROR_LOG_DISABLED() {
LOG(ERROR) << "My log message"; LOG(ERROR, "My log message");
EXPECT_EQ("", mockLogger.capturedLog()); EXPECT_EQ("", mockLogger.capturedLog());
} }
}; };

View File

@ -2,7 +2,7 @@
/* /*
* Contains test cases for the following logging interface: * Contains test cases for the following logging interface:
* LOG(INFO) << "My log message" * LOG(INFO, "My log message)"
*/ */
using namespace cpputils::logging; using namespace cpputils::logging;
@ -11,7 +11,7 @@ using testing::MatchesRegex;
TEST_F(LoggingTest, DefaultLoggerIsStderr) { TEST_F(LoggingTest, DefaultLoggerIsStderr) {
string output = captureStderr([]{ string output = captureStderr([]{
LOG(INFO) << "My log message"; LOG(INFO, "My log message");
}); });
EXPECT_THAT(output, MatchesRegex(".*\\[Log\\].*\\[info\\].*My log message.*")); EXPECT_THAT(output, MatchesRegex(".*\\[Log\\].*\\[info\\].*My log message.*"));
} }
@ -19,52 +19,52 @@ TEST_F(LoggingTest, DefaultLoggerIsStderr) {
TEST_F(LoggingTest, SetLogger_NewLoggerIsUsed) { TEST_F(LoggingTest, SetLogger_NewLoggerIsUsed) {
setLogger(spdlog::stderr_logger_mt("MyTestLog2")); setLogger(spdlog::stderr_logger_mt("MyTestLog2"));
string output = captureStderr([]{ string output = captureStderr([]{
LOG(INFO) << "My log message"; LOG(INFO, "My log message");
}); });
EXPECT_THAT(output, MatchesRegex(".*\\[MyTestLog2\\].*\\[info\\].*My log message.*")); EXPECT_THAT(output, MatchesRegex(".*\\[MyTestLog2\\].*\\[info\\].*My log message.*"));
} }
TEST_F(LoggingTest, SetNonStderrLogger_LogsToNewLogger) { TEST_F(LoggingTest, SetNonStderrLogger_LogsToNewLogger) {
setLogger(mockLogger.get()); setLogger(mockLogger.get());
logger()->info() << "My log message"; logger()->info("My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"));
} }
TEST_F(LoggingTest, SetNonStderrLogger_DoesNotLogToStderr) { TEST_F(LoggingTest, SetNonStderrLogger_DoesNotLogToStderr) {
setLogger(mockLogger.get()); setLogger(mockLogger.get());
string output = captureStderr([] { string output = captureStderr([] {
logger()->info() << "My log message"; logger()->info("My log message");
}); });
EXPECT_EQ("", output); EXPECT_EQ("", output);
} }
TEST_F(LoggingTest, InfoLog) { TEST_F(LoggingTest, InfoLog) {
setLogger(mockLogger.get()); setLogger(mockLogger.get());
LOG(INFO) << "My log message"; LOG(INFO, "My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"));
} }
TEST_F(LoggingTest, WarningLog) { TEST_F(LoggingTest, WarningLog) {
setLogger(mockLogger.get()); setLogger(mockLogger.get());
LOG(WARN) << "My log message"; LOG(WARN, "My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*"));
} }
TEST_F(LoggingTest, DebugLog) { TEST_F(LoggingTest, DebugLog) {
setLevel(DEBUG); setLevel(DEBUG);
setLogger(mockLogger.get()); setLogger(mockLogger.get());
LOG(DEBUG) << "My log message"; LOG(DEBUG, "My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*"));
} }
TEST_F(LoggingTest, ErrorLog) { TEST_F(LoggingTest, ErrorLog) {
setLogger(mockLogger.get()); setLogger(mockLogger.get());
LOG(ERROR) << "My log message"; LOG(ERROR, "My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*"));
} }
void logAndExit(const string &message) { void logAndExit(const string &message) {
LOG(INFO) << message; LOG(INFO, message);
exit(1); exit(1);
} }
@ -78,3 +78,41 @@ TEST_F(LoggingTest, LoggingAlsoWorksAfterFork) {
"My log message" "My log message"
); );
} }
TEST_F(LoggingTest, MessageIsConstChar) {
setLogger(mockLogger.get());
LOG(INFO, "My log message");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"));
}
TEST_F(LoggingTest, MessageIsString) {
setLogger(mockLogger.get());
string msg = "My log message";
LOG(INFO, msg);
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*"));
}
TEST_F(LoggingTest, FormatWithStringPlaceholder) {
setLogger(mockLogger.get());
string str = "placeholder";
LOG(INFO, "My log message: {}", str);
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*"));
}
TEST_F(LoggingTest, FormatWithConstCharPlaceholder) {
setLogger(mockLogger.get());
LOG(INFO, "My log message: {}", "placeholder");
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*"));
}
TEST_F(LoggingTest, FormatWithIntPlaceholder) {
setLogger(mockLogger.get());
LOG(INFO, "My log message: {}", 4);
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4.*"));
}
TEST_F(LoggingTest, FormatWithMultiplePlaceholders) {
setLogger(mockLogger.get());
LOG(INFO, "My log message: {}, {}, {}", 4, "then", true);
EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4, then, true.*"));
}

View File

@ -5,6 +5,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "cpp-utils/logging/logging.h" #include "cpp-utils/logging/logging.h"
#include <spdlog/sinks/ostream_sink.h>
class MockLogger final { class MockLogger final {
public: public:

3
vendor/README vendored
View File

@ -1,5 +1,4 @@
This directory contains external projects, taken from the following locations: This directory contains external projects, taken from the following locations:
scrypt: http://www.tarsnap.com/scrypt.html scrypt: http://www.tarsnap.com/scrypt.html
googletest: https://github.com/google/googletest/tree/release-1.8.0 googletest: https://github.com/google/googletest/tree/release-1.8.0
spdlog: https://github.com/gabime/spdlog/commit/0c7beb2e36008598cf80d0e8eb8635ac403febb9 spdlog: https://github.com/gabime/spdlog/tree/v0.11.0/include/spdlog
- with own fix: https://github.com/cryfs/spdlog/commit/7b8d507615b8075fc6c8793a0965a32a708288c4

View File

@ -13,14 +13,15 @@
// 1. Checks if its log level is enough to log the message // 1. Checks if its log level is enough to log the message
// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue) // 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue)
// 3. will throw spdlog_ex upon log exceptions // 3. will throw spdlog_ex upon log exceptions
// Upong destruction, logs all remaining messages in the queue before destructing.. // Upon destruction, logs all remaining messages in the queue before destructing..
#include <spdlog/common.h>
#include <spdlog/logger.h>
#include <chrono> #include <chrono>
#include <functional> #include <functional>
#include "common.h" #include <string>
#include "logger.h" #include <memory>
#include "spdlog.h"
namespace spdlog namespace spdlog
{ {
@ -40,26 +41,30 @@ public:
size_t queue_size, size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr, const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
async_logger(const std::string& logger_name, async_logger(const std::string& logger_name,
sinks_init_list sinks, sinks_init_list sinks,
size_t queue_size, size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr, const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
async_logger(const std::string& logger_name, async_logger(const std::string& logger_name,
sink_ptr single_sink, sink_ptr single_sink,
size_t queue_size, size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr, const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
//Wait for the queue to be empty, and flush synchronously
//Warning: this can potentialy last forever as we wait it to complete
void flush() override; void flush() override;
protected: protected:
void _log_msg(details::log_msg& msg) override; void _sink_it(details::log_msg& msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override; void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
void _set_pattern(const std::string& pattern) override; void _set_pattern(const std::string& pattern) override;
@ -69,4 +74,4 @@ private:
} }
#include "./details/async_logger_impl.h" #include <spdlog/details/async_logger_impl.h>

View File

@ -9,14 +9,36 @@
#include <initializer_list> #include <initializer_list>
#include <chrono> #include <chrono>
#include <memory> #include <memory>
#include <atomic>
#include <exception>
#include<functional>
//visual studio does not support noexcept yet #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#ifndef _MSC_VER #include <codecvt>
#define SPDLOG_NOEXCEPT noexcept #include <locale>
#else
#define SPDLOG_NOEXCEPT throw()
#endif #endif
#include <spdlog/details/null_mutex.h>
//visual studio upto 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900)
#define SPDLOG_NOEXCEPT throw()
#define SPDLOG_CONSTEXPR
#else
#define SPDLOG_NOEXCEPT noexcept
#define SPDLOG_CONSTEXPR constexpr
#endif
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED __declspec(deprecated)
#else
#define DEPRECATED
#endif
#include <spdlog/fmt/fmt.h>
namespace spdlog namespace spdlog
{ {
@ -28,12 +50,17 @@ namespace sinks
class sink; class sink;
} }
// Common types across the lib
using log_clock = std::chrono::system_clock; using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr < sinks::sink >; using sink_ptr = std::shared_ptr < sinks::sink >;
using sinks_init_list = std::initializer_list < sink_ptr >; using sinks_init_list = std::initializer_list < sink_ptr >;
using formatter_ptr = std::shared_ptr<spdlog::formatter>; using formatter_ptr = std::shared_ptr<spdlog::formatter>;
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int;
#else
using level_t = std::atomic_int;
#endif
using log_err_handler = std::function<void(const std::string &err_msg)>;
//Log level enum //Log level enum
namespace level namespace level
@ -43,18 +70,15 @@ typedef enum
trace = 0, trace = 0,
debug = 1, debug = 1,
info = 2, info = 2,
notice = 3, warn = 3,
warn = 4, err = 4,
err = 5, critical = 5,
critical = 6, off = 6
alert = 7,
emerg = 8,
off = 9
} level_enum; } level_enum;
static const char* level_names[] { "trace", "debug", "info", "notice", "warning", "error", "critical", "alert", "emerg", "off"}; static const char* level_names[] { "trace", "debug", "info", "warning", "error", "critical", "off" };
static const char* short_level_names[] { "T", "D", "I", "N", "W", "E", "C", "A", "M", "O"}; static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" };
inline const char* to_str(spdlog::level::level_enum l) inline const char* to_str(spdlog::level::level_enum l)
{ {
@ -81,10 +105,22 @@ enum class async_overflow_policy
// //
// Log exception // Log exception
// //
namespace details
{
namespace os
{
std::string errno_str(int err_num);
}
}
class spdlog_ex: public std::exception class spdlog_ex: public std::exception
{ {
public: public:
spdlog_ex(const std::string& msg) :_msg(msg) {} spdlog_ex(const std::string& msg):_msg(msg)
{}
spdlog_ex(const std::string& msg, int last_errno)
{
_msg = msg + ": " + details::os::errno_str(last_errno);
}
const char* what() const SPDLOG_NOEXCEPT override const char* what() const SPDLOG_NOEXCEPT override
{ {
return _msg.c_str(); return _msg.c_str();
@ -94,4 +130,14 @@ private:
}; };
//
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
//
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
using filename_t = std::wstring;
#else
using filename_t = std::string;
#endif
} //spdlog } //spdlog

View File

@ -14,17 +14,21 @@
#pragma once #pragma once
#include <spdlog/common.h>
#include <spdlog/sinks/sink.h>
#include <spdlog/details/mpmc_bounded_q.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/details/os.h>
#include <spdlog/formatter.h>
#include <chrono> #include <chrono>
#include <thread> #include <exception>
#include <functional> #include <functional>
#include <memory>
#include "../common.h" #include <string>
#include "../sinks/sink.h" #include <thread>
#include "./mpmc_bounded_q.h" #include <utility>
#include "./log_msg.h" #include <vector>
#include "./format.h"
#include "./os.h"
namespace spdlog namespace spdlog
{ {
@ -82,21 +86,22 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
// construct from log_msg // construct from log_msg
async_msg(const details::log_msg& m) : async_msg(const details::log_msg& m) :
logger_name(m.logger_name),
level(m.level), level(m.level),
time(m.time), time(m.time),
thread_id(m.thread_id), thread_id(m.thread_id),
txt(m.raw.data(), m.raw.size()), txt(m.raw.data(), m.raw.size()),
msg_type(async_msg_type::log) msg_type(async_msg_type::log)
{} {
#ifndef SPDLOG_NO_NAME
logger_name = *m.logger_name;
#endif
}
// copy into log_msg // copy into log_msg
void fill_log_msg(log_msg &msg) void fill_log_msg(log_msg &msg)
{ {
msg.clear(); msg.logger_name = &logger_name;
msg.logger_name = logger_name;
msg.level = level; msg.level = level;
msg.time = time; msg.time = time;
msg.thread_id = thread_id; msg.thread_id = thread_id;
@ -115,9 +120,11 @@ public:
async_log_helper(formatter_ptr formatter, async_log_helper(formatter_ptr formatter,
const std::vector<sink_ptr>& sinks, const std::vector<sink_ptr>& sinks,
size_t queue_size, size_t queue_size,
const log_err_handler err_handler,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr, const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
void log(const details::log_msg& msg); void log(const details::log_msg& msg);
@ -126,7 +133,7 @@ public:
void set_formatter(formatter_ptr); void set_formatter(formatter_ptr);
void flush(); void flush(bool wait_for_q);
private: private:
@ -136,14 +143,13 @@ private:
// queue of messages to log // queue of messages to log
q_type _q; q_type _q;
log_err_handler _err_handler;
bool _flush_requested; bool _flush_requested;
bool _terminate_requested; bool _terminate_requested;
// last exception thrown from the worker thread
std::shared_ptr<spdlog_ex> _last_workerthread_ex;
// overflow policy // overflow policy
const async_overflow_policy _overflow_policy; const async_overflow_policy _overflow_policy;
@ -153,13 +159,13 @@ private:
// auto periodic sink flush parameter // auto periodic sink flush parameter
const std::chrono::milliseconds _flush_interval_ms; const std::chrono::milliseconds _flush_interval_ms;
// worker thread teardown callback
const std::function<void()> _worker_teardown_cb;
// worker thread // worker thread
std::thread _worker_thread; std::thread _worker_thread;
void push_msg(async_msg&& new_msg); void push_msg(async_msg&& new_msg);
// throw last worker thread exception or if worker thread is not active
void throw_if_bad_worker();
// worker thread main loop // worker thread main loop
void worker_loop(); void worker_loop();
@ -173,6 +179,9 @@ private:
// sleep,yield or return immediatly using the time passed since last message as a hint // sleep,yield or return immediatly using the time passed since last message as a hint
static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time); static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
// wait until the queue is empty
void wait_empty_q();
}; };
} }
} }
@ -184,17 +193,21 @@ inline spdlog::details::async_log_helper::async_log_helper(
formatter_ptr formatter, formatter_ptr formatter,
const std::vector<sink_ptr>& sinks, const std::vector<sink_ptr>& sinks,
size_t queue_size, size_t queue_size,
log_err_handler err_handler,
const async_overflow_policy overflow_policy, const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb, const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms): const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb):
_formatter(formatter), _formatter(formatter),
_sinks(sinks), _sinks(sinks),
_q(queue_size), _q(queue_size),
_err_handler(err_handler),
_flush_requested(false), _flush_requested(false),
_terminate_requested(false), _terminate_requested(false),
_overflow_policy(overflow_policy), _overflow_policy(overflow_policy),
_worker_warmup_cb(worker_warmup_cb), _worker_warmup_cb(worker_warmup_cb),
_flush_interval_ms(flush_interval_ms), _flush_interval_ms(flush_interval_ms),
_worker_teardown_cb(worker_teardown_cb),
_worker_thread(&async_log_helper::worker_loop, this) _worker_thread(&async_log_helper::worker_loop, this)
{} {}
@ -212,7 +225,7 @@ inline spdlog::details::async_log_helper::~async_log_helper()
} }
//Try to push and block until succeeded //Try to push and block until succeeded (if the policy is not to discard when the queue is full)
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
{ {
push_msg(async_msg(msg)); push_msg(async_msg(msg));
@ -220,10 +233,8 @@ inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
} }
//Try to push and block until succeeded
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg) inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
{ {
throw_if_bad_worker();
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg) if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
{ {
auto last_op_time = details::os::now(); auto last_op_time = details::os::now();
@ -238,9 +249,12 @@ inline void spdlog::details::async_log_helper::push_msg(details::async_log_helpe
} }
inline void spdlog::details::async_log_helper::flush() // optionally wait for the queue be empty and request flush from the sinks
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
{ {
push_msg(async_msg(async_msg_type::flush)); push_msg(async_msg(async_msg_type::flush));
if(wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed
} }
inline void spdlog::details::async_log_helper::worker_loop() inline void spdlog::details::async_log_helper::worker_loop()
@ -251,24 +265,25 @@ inline void spdlog::details::async_log_helper::worker_loop()
auto last_pop = details::os::now(); auto last_pop = details::os::now();
auto last_flush = last_pop; auto last_flush = last_pop;
while(process_next_msg(last_pop, last_flush)); while(process_next_msg(last_pop, last_flush));
if (_worker_teardown_cb) _worker_teardown_cb();
} }
catch (const std::exception &ex) catch (const std::exception &ex)
{ {
_last_workerthread_ex = std::make_shared<spdlog_ex>(std::string("async_logger worker thread exception: ") + ex.what()); _err_handler(ex.what());
} }
catch (...) catch (...)
{ {
_last_workerthread_ex = std::make_shared<spdlog_ex>("async_logger worker thread exception"); _err_handler("Unknown exception");
} }
} }
// process next message in the queue // process next message in the queue
// return true if this thread should still be active (no msg with level::off was received) // return true if this thread should still be active (while no terminate msg was received)
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush) inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
{ {
async_msg incoming_async_msg; async_msg incoming_async_msg;
log_msg incoming_log_msg;
if (_q.dequeue(incoming_async_msg)) if (_q.dequeue(incoming_async_msg))
{ {
@ -285,11 +300,17 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
break; break;
default: default:
log_msg incoming_log_msg;
incoming_async_msg.fill_log_msg(incoming_log_msg); incoming_async_msg.fill_log_msg(incoming_log_msg);
_formatter->format(incoming_log_msg); _formatter->format(incoming_log_msg);
for (auto &s : _sinks) for (auto &s : _sinks)
{
if(s->should_log( incoming_log_msg.level))
{
s->log(incoming_log_msg); s->log(incoming_log_msg);
} }
}
}
return true; return true;
} }
@ -301,10 +322,10 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
handle_flush_interval(now, last_flush); handle_flush_interval(now, last_flush);
sleep_or_yield(now, last_pop); sleep_or_yield(now, last_pop);
return !_terminate_requested; return !_terminate_requested;
} }
} }
// flush all sinks if _flush_interval_ms has expired
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush) inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
{ {
auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms); auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
@ -316,44 +337,48 @@ inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::
_flush_requested = false; _flush_requested = false;
} }
} }
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
{ {
_formatter = msg_formatter; _formatter = msg_formatter;
} }
// sleep,yield or return immediatly using the time passed since last message as a hint // spin, yield or sleep. use the time passed since last message as a hint
inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time) inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time)
{ {
using std::chrono::milliseconds;
using namespace std::this_thread; using namespace std::this_thread;
using std::chrono::milliseconds;
using std::chrono::microseconds;
auto time_since_op = now - last_op_time; auto time_since_op = now - last_op_time;
// spin upto 1 ms // spin upto 50 micros
if (time_since_op <= milliseconds(1)) if (time_since_op <= microseconds(50))
return; return;
// yield upto 10ms // yield upto 150 micros
if (time_since_op <= milliseconds(10)) if (time_since_op <= microseconds(100))
return yield(); return yield();
// sleep for half of duration since last op // sleep for 20 ms upto 200 ms
if (time_since_op <= milliseconds(100)) if (time_since_op <= milliseconds(200))
return sleep_for(time_since_op / 2); return sleep_for(milliseconds(20));
return sleep_for(milliseconds(100)); // sleep for 200 ms
return sleep_for(milliseconds(200));
} }
// throw if the worker thread threw an exception or not active // wait for the queue to be empty
inline void spdlog::details::async_log_helper::throw_if_bad_worker() inline void spdlog::details::async_log_helper::wait_empty_q()
{ {
if (_last_workerthread_ex) auto last_op = details::os::now();
while (_q.approx_size() > 0)
{ {
auto ex = std::move(_last_workerthread_ex); sleep_or_yield(details::os::now(), last_op);
throw *ex;
} }
} }
@ -362,3 +387,4 @@ inline void spdlog::details::async_log_helper::throw_if_bad_worker()

View File

@ -8,8 +8,13 @@
// Async Logger implementation // Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread // Use an async_sink (queue per logger) to perform the logging in a worker thread
#include "./async_log_helper.h" #include <spdlog/details/async_log_helper.h>
#include <spdlog/async_logger.h>
#include <string>
#include <functional>
#include <chrono>
#include <memory>
template<class It> template<class It>
inline spdlog::async_logger::async_logger(const std::string& logger_name, inline spdlog::async_logger::async_logger(const std::string& logger_name,
@ -18,9 +23,10 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
size_t queue_size, size_t queue_size,
const async_overflow_policy overflow_policy, const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb, const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms) : const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
logger(logger_name, begin, end), logger(logger_name, begin, end),
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms)) _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
{ {
} }
@ -29,25 +35,26 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
size_t queue_size, size_t queue_size,
const async_overflow_policy overflow_policy, const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb, const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms) : const std::chrono::milliseconds& flush_interval_ms,
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {} const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline spdlog::async_logger::async_logger(const std::string& logger_name, inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink, sink_ptr single_sink,
size_t queue_size, size_t queue_size,
const async_overflow_policy overflow_policy, const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb, const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms) : const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name, async_logger(logger_name,
{ {
single_sink single_sink
}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {} }, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline void spdlog::async_logger::flush() inline void spdlog::async_logger::flush()
{ {
_async_log_helper->flush(true);
_async_log_helper->flush();
} }
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
@ -63,7 +70,20 @@ inline void spdlog::async_logger::_set_pattern(const std::string& pattern)
} }
inline void spdlog::async_logger::_log_msg(details::log_msg& msg) inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
{
try
{ {
_async_log_helper->log(msg); _async_log_helper->log(msg);
if (_should_flush_on(msg))
_async_log_helper->flush(false); // do async flush
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
} }

View File

@ -10,13 +10,14 @@
// Can be set to auto flush on every line // Can be set to auto flush on every line
// Throw spdlog_ex exception on errors // Throw spdlog_ex exception on errors
#include <spdlog/details/os.h>
#include <spdlog/details/log_msg.h>
#include <chrono>
#include <cstdio>
#include <string> #include <string>
#include <thread> #include <thread>
#include <chrono> #include <cerrno>
#include "os.h"
#include "log_msg.h"
namespace spdlog namespace spdlog
{ {
@ -25,13 +26,13 @@ namespace details
class file_helper class file_helper
{ {
public: public:
const int open_tries = 5; const int open_tries = 5;
const int open_interval = 10; const int open_interval = 10;
explicit file_helper(bool force_flush) : explicit file_helper() :
_fd(nullptr), _fd(nullptr)
_force_flush(force_flush)
{} {}
file_helper(const file_helper&) = delete; file_helper(const file_helper&) = delete;
@ -43,11 +44,11 @@ public:
} }
void open(const std::string& fname, bool truncate = false) void open(const filename_t& fname, bool truncate = false)
{ {
close(); close();
const char* mode = truncate ? "wb" : "ab"; auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
_filename = fname; _filename = fname;
for (int tries = 0; tries < open_tries; ++tries) for (int tries = 0; tries < open_tries; ++tries)
{ {
@ -57,7 +58,7 @@ public:
std::this_thread::sleep_for(std::chrono::milliseconds(open_interval)); std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
} }
throw spdlog_ex("Failed opening file " + fname + " for writing"); throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
} }
void reopen(bool truncate) void reopen(bool truncate)
@ -88,55 +89,30 @@ public:
size_t msg_size = msg.formatted.size(); size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data(); auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size) if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
throw spdlog_ex("Failed writing to file " + _filename); throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
if (_force_flush)
std::fflush(_fd);
} }
long size() size_t size()
{ {
if (!_fd) if (!_fd)
throw spdlog_ex("Cannot use size() on closed file " + _filename); throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
return os::filesize(_fd);
auto pos = ftell(_fd);
if (fseek(_fd, 0, SEEK_END) != 0)
throw spdlog_ex("fseek failed on file " + _filename);
auto file_size = ftell(_fd);
if(fseek(_fd, pos, SEEK_SET) !=0)
throw spdlog_ex("fseek failed on file " + _filename);
if (file_size == -1)
throw spdlog_ex("ftell failed on file " + _filename);
return file_size;
} }
const std::string& filename() const const filename_t& filename() const
{ {
return _filename; return _filename;
} }
static bool file_exists(const std::string& name) static bool file_exists(const filename_t& name)
{ {
return os::file_exists(name); return os::file_exists(name);
} }
private: private:
FILE* _fd; FILE* _fd;
std::string _filename; filename_t _filename;
bool _force_flush;
}; };
} }
} }

View File

@ -1,949 +0,0 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2015, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "format.h"
#include <string.h>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
#endif
#if FMT_USE_WINDOWS_H
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
using fmt::internal::Arg;
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#endif
#ifdef FMT_HEADER_ONLY
# define FMT_FUNC inline
#else
# define FMT_FUNC
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>();
}
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>();
}
namespace fmt {
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf
#else
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker {
template <typename T>
static bool fits_in_int(T value) {
unsigned max = INT_MAX;
return value <= max;
}
static bool fits_in_int(bool) {
return true;
}
};
template <>
struct IntChecker<true> {
template <typename T>
static bool fits_in_int(T value) {
return value >= INT_MIN && value <= INT_MAX;
}
static bool fits_in_int(int) {
return true;
}
};
const char RESET_COLOR[] = "\x1b[0m";
typedef void(*FormatFunc)(fmt::Writer &, int, fmt::StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT{
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(fmt::internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(fmt::internal::Null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r.
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT{
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
fmt::internal::IntTraits<int>::MainType ec_value = error_code;
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
error_code_size += fmt::internal::count_digits(ec_value);
if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func,
int error_code, fmt::StringRef message) FMT_NOEXCEPT{
fmt::MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public fmt::internal::ArgVisitor<IsZeroInt, bool> {
public:
template <typename T>
bool visit_any_int(T value) {
return value == 0;
}
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
private:
fmt::FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() {
FMT_THROW(fmt::FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value) {
typedef typename fmt::internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = value;
if (fmt::internal::is_negative(value)) {
spec_.align_ = fmt::ALIGN_LEFT;
width = 0 - width;
}
if (width > INT_MAX)
FMT_THROW(fmt::FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
class PrecisionHandler :
public fmt::internal::ArgVisitor<PrecisionHandler, int> {
public:
void report_unhandled_arg() {
FMT_THROW(fmt::FormatError("precision is not integer"));
}
template <typename T>
int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(fmt::FormatError("number is too big"));
return static_cast<int>(value);
}
};
// Converts an integer argument to an integral type T for printf.
template <typename T>
class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {
private:
fmt::internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(fmt::internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
void visit_bool(bool value) {
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using fmt::internal::Arg;
if (sizeof(T) <= sizeof(int)) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<T>(value));
}
else {
arg_.type = Arg::UINT;
arg_.uint_value = static_cast<unsigned>(
static_cast<typename fmt::internal::MakeUnsigned<T>::Type>(value));
}
}
else {
if (is_signed) {
arg_.type = Arg::LONG_LONG;
arg_.long_long_value =
static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
}
else {
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter : public fmt::internal::ArgVisitor<CharConverter, void> {
private:
fmt::internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {}
template <typename T>
void visit_any_int(T value) {
arg_.type = Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
} // namespace
namespace internal {
template <typename Char>
class PrintfArgFormatter :
public ArgFormatterBase<PrintfArgFormatter<Char>, Char> {
void write_null_pointer() {
this->spec().type_ = 0;
this->write("(nil)");
}
typedef ArgFormatterBase<PrintfArgFormatter<Char>, Char> Base;
public:
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: ArgFormatterBase<PrintfArgFormatter<Char>, Char>(w, s) {}
void visit_bool(bool value) {
FormatSpec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
this->write(value);
}
void visit_char(int value) {
const FormatSpec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (fmt_spec.width_ > 1) {
Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT) {
std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1;
}
else {
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
}
}
else {
out = w.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
void visit_cstring(const char *value) {
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
void visit_pointer(const void *value) {
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
void visit_custom(Arg::CustomValue c) {
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = { '}', 0 };
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
};
} // namespace internal
} // namespace fmt
FMT_FUNC void fmt::SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
int fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
const char fmt::internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \
factor * 10, \
factor * 100, \
factor * 1000, \
factor * 10000, \
factor * 100000, \
factor * 1000000, \
factor * 10000000, \
factor * 100000000, \
factor * 1000000000
template <typename T>
const uint32_t fmt::internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t fmt::internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(fmt::ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10
};
FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) {
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) {
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
return 0;
}
FMT_FUNC void fmt::WindowsError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
FMT_FUNC void fmt::internal::format_windows_error(
fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT{
class String {
private:
LPWSTR str_;
public:
String() : str_() {}
~String() {
LocalFree(str_);
}
LPWSTR *ptr() {
return &str_;
}
LPCWSTR c_str() const { return str_; }
};
FMT_TRY{
String system_message;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void fmt::internal::format_system_error(
fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT{
FMT_TRY{
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
template <typename Char>
void fmt::internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = 0;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
for (unsigned i = 0;/*nothing*/; ++i) {
internal::Arg::Type arg_type = args.type(i);
switch (arg_type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.insert(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/
;
}
}
return;
}
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
internal::Arg::Type arg_type = args.type(i);
if (arg_type == internal::Arg::NAMED_ARG) {
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.insert(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
switch (args.args_[i].type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.insert(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/
;
}
}
}
template <typename Char>
void fmt::internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index];
switch (arg.type) {
case Arg::NONE:
error = "argument index out of range";
break;
case Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
default:
/*nothing*/
;
}
return arg;
}
template <typename Char>
void fmt::internal::PrintfFormatter<Char>::parse_flags(
FormatSpec &spec, const Char *&s) {
for (;;) {
switch (*s++) {
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template <typename Char>
Arg fmt::internal::PrintfFormatter<Char>::get_arg(
const Char *s, unsigned arg_index) {
(void)s;
const char *error = 0;
Arg arg = arg_index == UINT_MAX ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char>
unsigned fmt::internal::PrintfFormatter<Char>::parse_header(
const Char *&s, FormatSpec &spec) {
unsigned arg_index = UINT_MAX;
Char c = *s;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = parse_nonnegative_int(s);
if (*s == '$') { // value is an argument index
++s;
arg_index = value;
}
else {
if (c == '0')
spec.fill_ = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9') {
spec.width_ = parse_nonnegative_int(s);
}
else if (*s == '*') {
++s;
spec.width_ = WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char>
void fmt::internal::PrintfFormatter<Char>::format(
BasicWriter<Char> &writer, BasicCStringRef<Char> format_str) {
const Char *start = format_str.c_str();
const Char *s = start;
while (*s) {
Char c = *s++;
if (c != '%') continue;
if (*s == c) {
write(writer, start, s);
start = ++s;
continue;
}
write(writer, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.') {
++s;
if ('0' <= *s && *s <= '9') {
spec.precision_ = parse_nonnegative_int(s);
}
else if (*s == '*') {
++s;
spec.precision_ = PrecisionHandler().visit(get_arg(s));
}
}
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
spec.flags_ &= ~HASH_FLAG;
if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
switch (*s++) {
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<int>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (arg.type <= Arg::LAST_INTEGER_TYPE) {
// Normalize type.
switch (spec.type_) {
case 'i':
case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
internal::PrintfArgFormatter<Char>(writer, spec).visit(arg);
}
write(writer, start, s);
}
FMT_FUNC void fmt::report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT{
// 'fmt::' is for bcc32.
fmt::report_error(internal::format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void fmt::report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT{
// 'fmt::' is for bcc32.
fmt::report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void fmt::print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void fmt::print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
os.write(w.data(), w.size());
}
FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
}
FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
#ifndef FMT_HEADER_ONLY
template struct fmt::internal::BasicData<void>;
// Explicit instantiations for char.
template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
template void fmt::internal::ArgMap<char>::init(const fmt::ArgList &args);
template void fmt::internal::PrintfFormatter<char>::format(
BasicWriter<char> &writer, CStringRef format);
template int fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template int fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void fmt::internal::ArgMap<wchar_t>::init(const fmt::ArgList &args);
template void fmt::internal::PrintfFormatter<wchar_t>::format(
BasicWriter<wchar_t> &writer, WCStringRef format);
template int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
#ifdef _MSC_VER
# pragma warning(pop)
#endif

View File

@ -1,201 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <type_traits>
#include "../common.h"
#include "../logger.h"
// Line logger class - aggregates operator<< calls to fast ostream
// and logs upon destruction
namespace spdlog
{
namespace details
{
class line_logger
{
public:
line_logger(logger* callback_logger, level::level_enum msg_level, bool enabled):
_callback_logger(callback_logger),
_log_msg(msg_level),
_enabled(enabled)
{}
// No copy intended. Only move
line_logger(const line_logger& other) = delete;
line_logger& operator=(const line_logger&) = delete;
line_logger& operator=(line_logger&&) = delete;
line_logger(line_logger&& other) :
_callback_logger(other._callback_logger),
_log_msg(std::move(other._log_msg)),
_enabled(other._enabled)
{
other.disable();
}
//Log the log message using the callback logger
~line_logger()
{
if (_enabled)
{
#ifndef SPDLOG_NO_NAME
_log_msg.logger_name = _callback_logger->name();
#endif
#ifndef SPDLOG_NO_DATETIME
_log_msg.time = os::now();
#endif
#ifndef SPDLOG_NO_THREAD_ID
_log_msg.thread_id = os::thread_id();
#endif
_callback_logger->_log_msg(_log_msg);
}
}
//
// Support for format string with variadic args
//
void write(const char* what)
{
if (_enabled)
_log_msg.raw << what;
}
template <typename... Args>
void write(const char* fmt, const Args&... args)
{
if (!_enabled)
return;
try
{
_log_msg.raw.write(fmt, args...);
}
catch (const fmt::FormatError& e)
{
throw spdlog_ex(fmt::format("formatting error while processing format string '{}': {}", fmt, e.what()));
}
}
//
// Support for operator<<
//
line_logger& operator<<(const char* what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(const std::string& what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(int what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(unsigned int what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(long what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(unsigned long what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(long long what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(unsigned long long what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(double what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(long double what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(float what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
line_logger& operator<<(char what)
{
if (_enabled)
_log_msg.raw << what;
return *this;
}
//Support user types which implements operator<<
template<typename T>
line_logger& operator<<(const T& what)
{
if (_enabled)
_log_msg.raw.write("{}", what);
return *this;
}
void disable()
{
_enabled = false;
}
bool is_enabled() const
{
return _enabled;
}
private:
logger* _callback_logger;
log_msg _log_msg;
bool _enabled;
};
} //Namespace details
} // Namespace spdlog

View File

@ -5,9 +5,12 @@
#pragma once #pragma once
#include <thread> #include <spdlog/common.h>
#include "../common.h" #include <spdlog/details/os.h>
#include "./format.h"
#include <string>
#include <utility>
namespace spdlog namespace spdlog
{ {
@ -16,59 +19,23 @@ namespace details
struct log_msg struct log_msg
{ {
log_msg() = default; log_msg() = default;
log_msg(level::level_enum l): log_msg(const std::string *loggers_name, level::level_enum lvl) : logger_name(loggers_name), level(lvl)
logger_name(),
level(l),
raw(),
formatted() {}
log_msg(const log_msg& other) :
logger_name(other.logger_name),
level(other.level),
time(other.time),
thread_id(other.thread_id)
{ {
if (other.raw.size()) #ifndef SPDLOG_NO_DATETIME
raw << fmt::BasicStringRef<char>(other.raw.data(), other.raw.size()); time = os::now();
if (other.formatted.size()) #endif
formatted << fmt::BasicStringRef<char>(other.formatted.data(), other.formatted.size());
#ifndef SPDLOG_NO_THREAD_ID
thread_id = os::thread_id();
#endif
} }
log_msg(log_msg&& other) : log_msg(const log_msg& other) = delete;
logger_name(std::move(other.logger_name)), log_msg& operator=(log_msg&& other) = delete;
level(other.level), log_msg(log_msg&& other) = delete;
time(std::move(other.time)),
thread_id(other.thread_id),
raw(std::move(other.raw)),
formatted(std::move(other.formatted))
{
other.clear();
}
log_msg& operator=(log_msg&& other)
{
if (this == &other)
return *this;
logger_name = std::move(other.logger_name); const std::string *logger_name;
level = other.level;
time = std::move(other.time);
thread_id = other.thread_id;
raw = std::move(other.raw);
formatted = std::move(other.formatted);
other.clear();
return *this;
}
void clear()
{
level = level::off;
raw.clear();
formatted.clear();
}
std::string logger_name;
level::level_enum level; level::level_enum level;
log_clock::time_point time; log_clock::time_point time;
size_t thread_id; size_t thread_id;

View File

@ -5,7 +5,12 @@
#pragma once #pragma once
#include "./line_logger.h" #include <spdlog/logger.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <memory>
#include <string>
// create logger with given name, sinks and the default pattern formatter // create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one // all other ctors will call this one
@ -15,14 +20,19 @@ inline spdlog::logger::logger(const std::string& logger_name, const It& begin, c
_sinks(begin, end), _sinks(begin, end),
_formatter(std::make_shared<pattern_formatter>("%+")) _formatter(std::make_shared<pattern_formatter>("%+"))
{ {
// no support under vs2013 for member initialization for std::atomic
_level = level::info; _level = level::info;
_flush_level = level::off;
_last_err_time = 0;
_err_handler = [this](const std::string &msg)
{
this->_default_err_handler(msg);
};
} }
// ctor with sinks as init list // ctor with sinks as init list
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list): inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list):
logger(logger_name, sinks_list.begin(), sinks_list.end()) {} logger(logger_name, sinks_list.begin(), sinks_list.end())
{}
// ctor with single sink // ctor with single sink
@ -30,7 +40,8 @@ inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr s
logger(logger_name, logger(logger_name,
{ {
single_sink single_sink
}) {} })
{}
inline spdlog::logger::~logger() = default; inline spdlog::logger::~logger() = default;
@ -46,210 +57,149 @@ inline void spdlog::logger::set_pattern(const std::string& pattern)
_set_pattern(pattern); _set_pattern(pattern);
} }
//
// log only if given level>=logger's log level
//
template <typename... Args> template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl, const char* fmt, const Args&... args) inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
{ {
bool msg_enabled = should_log(lvl); if (!should_log(lvl)) return;
details::line_logger l(this, lvl, msg_enabled);
l.write(fmt, args...); try
return l; {
details::log_msg log_msg(&_name, lvl);
log_msg.raw.write(fmt, args...);
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
} }
inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl) template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
{ {
return details::line_logger(this, lvl, should_log(lvl)); if (!should_log(lvl)) return;
try
{
details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg;
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
} }
template<typename T> template<typename T>
inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl, const T& msg) inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
{ {
bool msg_enabled = should_log(lvl); if (!should_log(lvl)) return;
details::line_logger l(this, lvl, msg_enabled); try
l << msg; {
return l; details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg;
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
} }
//
// logger.info(cppformat_string, arg1, arg2, arg3, ...) call style
//
template <typename... Args> template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::trace(const char* fmt, const Args&... args) inline void spdlog::logger::trace(const char* fmt, const Args&... args)
{ {
return _log_if_enabled(level::trace, fmt, args...); log(level::trace, fmt, args...);
} }
template <typename... Args> template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::debug(const char* fmt, const Args&... args) inline void spdlog::logger::debug(const char* fmt, const Args&... args)
{ {
return _log_if_enabled(level::debug, fmt, args...); log(level::debug, fmt, args...);
} }
template <typename... Args> template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::info(const char* fmt, const Args&... args) inline void spdlog::logger::info(const char* fmt, const Args&... args)
{ {
return _log_if_enabled(level::info, fmt, args...); log(level::info, fmt, args...);
}
template <typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Args&... args)
{
log(level::warn, fmt, args...);
} }
template <typename... Args> template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::notice(const char* fmt, const Args&... args) inline void spdlog::logger::error(const char* fmt, const Args&... args)
{ {
return _log_if_enabled(level::notice, fmt, args...); log(level::err, fmt, args...);
} }
template <typename... Args> template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::warn(const char* fmt, const Args&... args) inline void spdlog::logger::critical(const char* fmt, const Args&... args)
{ {
return _log_if_enabled(level::warn, fmt, args...); log(level::critical, fmt, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::error(const char* fmt, const Args&... args)
{
return _log_if_enabled(level::err, fmt, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::critical(const char* fmt, const Args&... args)
{
return _log_if_enabled(level::critical, fmt, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::alert(const char* fmt, const Args&... args)
{
return _log_if_enabled(level::alert, fmt, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::emerg(const char* fmt, const Args&... args)
{
return _log_if_enabled(level::emerg, fmt, args...);
}
//
// logger.info(msg) << ".." call style
//
template<typename T>
inline spdlog::details::line_logger spdlog::logger::trace(const T& msg)
{
return _log_if_enabled(level::trace, msg);
}
template<typename T>
inline spdlog::details::line_logger spdlog::logger::debug(const T& msg)
{
return _log_if_enabled(level::debug, msg);
} }
template<typename T> template<typename T>
inline spdlog::details::line_logger spdlog::logger::info(const T& msg) inline void spdlog::logger::trace(const T& msg)
{ {
return _log_if_enabled(level::info, msg); log(level::trace, msg);
} }
template<typename T> template<typename T>
inline spdlog::details::line_logger spdlog::logger::notice(const T& msg) inline void spdlog::logger::debug(const T& msg)
{ {
return _log_if_enabled(level::notice, msg); log(level::debug, msg);
}
template<typename T>
inline void spdlog::logger::info(const T& msg)
{
log(level::info, msg);
}
template<typename T>
inline void spdlog::logger::warn(const T& msg)
{
log(level::warn, msg);
} }
template<typename T> template<typename T>
inline spdlog::details::line_logger spdlog::logger::warn(const T& msg) inline void spdlog::logger::error(const T& msg)
{ {
return _log_if_enabled(level::warn, msg); log(level::err, msg);
} }
template<typename T> template<typename T>
inline spdlog::details::line_logger spdlog::logger::error(const T& msg) inline void spdlog::logger::critical(const T& msg)
{ {
return _log_if_enabled(level::err, msg); log(level::critical, msg);
}
template<typename T>
inline spdlog::details::line_logger spdlog::logger::critical(const T& msg)
{
return _log_if_enabled(level::critical, msg);
}
template<typename T>
inline spdlog::details::line_logger spdlog::logger::alert(const T& msg)
{
return _log_if_enabled(level::alert, msg);
}
template<typename T>
inline spdlog::details::line_logger spdlog::logger::emerg(const T& msg)
{
return _log_if_enabled(level::emerg, msg);
} }
//
// logger.info() << ".." call style
//
inline spdlog::details::line_logger spdlog::logger::trace()
{
return _log_if_enabled(level::trace);
}
inline spdlog::details::line_logger spdlog::logger::debug()
{
return _log_if_enabled(level::debug);
}
inline spdlog::details::line_logger spdlog::logger::info()
{
return _log_if_enabled(level::info);
}
inline spdlog::details::line_logger spdlog::logger::notice()
{
return _log_if_enabled(level::notice);
}
inline spdlog::details::line_logger spdlog::logger::warn()
{
return _log_if_enabled(level::warn);
}
inline spdlog::details::line_logger spdlog::logger::error()
{
return _log_if_enabled(level::err);
}
inline spdlog::details::line_logger spdlog::logger::critical()
{
return _log_if_enabled(level::critical);
}
inline spdlog::details::line_logger spdlog::logger::alert()
{
return _log_if_enabled(level::alert);
}
inline spdlog::details::line_logger spdlog::logger::emerg()
{
return _log_if_enabled(level::emerg);
}
// always log, no matter what is the actual logger's log level
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::force_log(level::level_enum lvl, const char* fmt, const Args&... args)
{
details::line_logger l(this, lvl, true);
l.write(fmt, args...);
return l;
}
// //
// name and level // name and level
// //
@ -263,6 +213,22 @@ inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
_level.store(log_level); _level.store(log_level);
} }
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
}
inline spdlog::log_err_handler spdlog::logger::error_handler()
{
return _err_handler;
}
inline void spdlog::logger::flush_on(level::level_enum log_level)
{
_flush_level.store(log_level);
}
inline spdlog::level::level_enum spdlog::logger::level() const inline spdlog::level::level_enum spdlog::logger::level() const
{ {
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed)); return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
@ -276,12 +242,20 @@ inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) cons
// //
// protected virtual called at end of each user log call (if enabled) by the line_logger // protected virtual called at end of each user log call (if enabled) by the line_logger
// //
inline void spdlog::logger::_log_msg(details::log_msg& msg) inline void spdlog::logger::_sink_it(details::log_msg& msg)
{ {
_formatter->format(msg); _formatter->format(msg);
for (auto &sink : _sinks) for (auto &sink : _sinks)
{
if( sink->should_log( msg.level))
{
sink->log(msg); sink->log(msg);
} }
}
if(_should_flush_on(msg))
flush();
}
inline void spdlog::logger::_set_pattern(const std::string& pattern) inline void spdlog::logger::_set_pattern(const std::string& pattern)
{ {
@ -297,3 +271,23 @@ inline void spdlog::logger::flush()
for (auto& sink : _sinks) for (auto& sink : _sinks)
sink->flush(); sink->flush();
} }
inline void spdlog::logger::_default_err_handler(const std::string &msg)
{
auto now = time(nullptr);
if (now - _last_err_time < 60)
return;
auto tm_time = details::os::localtime(now);
char date_buf[100];
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
details::log_msg err_msg;
err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
sinks::stderr_sink_mt::instance()->log(err_msg);
_last_err_time = now;
}
inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
{
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off);
}

View File

@ -43,8 +43,10 @@ Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once #pragma once
#include <spdlog/common.h>
#include <atomic> #include <atomic>
#include "../common.h" #include <utility>
namespace spdlog namespace spdlog
{ {
@ -58,7 +60,8 @@ public:
using item_type = T; using item_type = T;
mpmc_bounded_queue(size_t buffer_size) mpmc_bounded_queue(size_t buffer_size)
: buffer_(new cell_t [buffer_size]), :max_size_(buffer_size),
buffer_(new cell_t [buffer_size]),
buffer_mask_(buffer_size - 1) buffer_mask_(buffer_size - 1)
{ {
//queue size must be power of two //queue size must be power of two
@ -130,6 +133,16 @@ public:
return true; return true;
} }
size_t approx_size()
{
size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed);
size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed);
if (last_pos <= first_pos)
return 0;
auto size = last_pos - first_pos;
return size < max_size_ ? size : max_size_;
}
private: private:
struct cell_t struct cell_t
{ {
@ -137,6 +150,8 @@ private:
T data_; T data_;
}; };
size_t const max_size_;
static size_t const cacheline_size = 64; static size_t const cacheline_size = 64;
typedef char cacheline_pad_t [cacheline_size]; typedef char cacheline_pad_t [cacheline_size];
@ -149,8 +164,8 @@ private:
std::atomic<size_t> dequeue_pos_; std::atomic<size_t> dequeue_pos_;
cacheline_pad_t pad3_; cacheline_pad_t pad3_;
mpmc_bounded_queue(mpmc_bounded_queue const&); mpmc_bounded_queue(mpmc_bounded_queue const&) = delete;
void operator = (mpmc_bounded_queue const&); void operator= (mpmc_bounded_queue const&) = delete;
}; };
} // ns details } // ns details

View File

@ -5,7 +5,8 @@
#pragma once #pragma once
// null, no cost mutex #include <atomic>
// null, no cost dummy "mutex" and dummy "atomic" int
namespace spdlog namespace spdlog
{ {
@ -20,5 +21,25 @@ struct null_mutex
return true; return true;
} }
}; };
struct null_atomic_int
{
int value;
null_atomic_int() = default;
null_atomic_int(int val):value(val)
{}
int load(std::memory_order) const
{
return value;
}
void store(int val)
{
value = val;
}
};
} }
} }

View File

@ -4,11 +4,23 @@
// //
#pragma once #pragma once
#include<string> #include <spdlog/common.h>
#include <cstdio> #include <cstdio>
#include <ctime> #include <ctime>
#include <functional>
#include <string>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#ifdef _WIN32 #ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX //prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
@ -18,15 +30,21 @@
#include <share.h> #include <share.h>
#endif #endif
#include <sys/types.h>
#elif __linux__ #elif __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id #include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <chrono>
#elif __FreeBSD__
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#else #else
#include <thread> #include <thread>
#endif
#include "../common.h" #endif
namespace spdlog namespace spdlog
{ {
@ -105,66 +123,111 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
return !(tm1 == tm2); return !(tm1 == tm2);
} }
// eol definition
#if !defined (SPDLOG_EOL)
#ifdef _WIN32 #ifdef _WIN32
inline const char* eol() #define SPDLOG_EOL "\r\n"
{
return "\r\n";
}
#else #else
constexpr inline const char* eol() #define SPDLOG_EOL "\n"
{ #endif
return "\n";
}
#endif #endif
#ifdef _WIN32 SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
inline unsigned short eol_size() SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
{
return 2;
}
#else
constexpr inline unsigned short eol_size()
{
return 1;
}
#endif
//fopen_s on non windows for writing //fopen_s on non windows for writing
inline int fopen_s(FILE** fp, const std::string& filename, const char* mode) inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
{ {
#ifdef _WIN32 #ifdef _WIN32
*fp = _fsopen((filename.c_str()), mode, _SH_DENYWR); #ifdef SPDLOG_WCHAR_FILENAMES
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif
return *fp == nullptr; return *fp == nullptr;
#else #else
*fp = fopen((filename.c_str()), mode); *fp = fopen((filename.c_str()), mode.c_str());
return *fp == nullptr; return *fp == nullptr;
#endif #endif
}
inline int remove(const filename_t &filename)
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wremove(filename.c_str());
#else
return std::remove(filename.c_str());
#endif
}
inline int rename(const filename_t& filename1, const filename_t& filename2)
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wrename(filename1.c_str(), filename2.c_str());
#else
return std::rename(filename1.c_str(), filename2.c_str());
#endif
} }
//Return if file exists //Return if file exists
inline bool file_exists(const std::string& filename) inline bool file_exists(const filename_t& filename)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES
auto attribs = GetFileAttributesW(filename.c_str());
#else
auto attribs = GetFileAttributesA(filename.c_str()); auto attribs = GetFileAttributesA(filename.c_str());
#endif
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#elif __linux__ #else //common linux/unix all have the stat system call
struct stat buffer; struct stat buffer;
return (stat (filename.c_str(), &buffer) == 0); return (stat (filename.c_str(), &buffer) == 0);
#else #endif
auto *file = fopen(filename.c_str(), "r");
if (file != nullptr)
{
fclose(file);
return true;
} }
return false;
//Return file size according to open FILE* object
inline size_t filesize(FILE *f)
{
if (f == nullptr)
throw spdlog_ex("Failed getting file size. fd is null");
#ifdef _WIN32
int fd = _fileno(f);
#if _WIN64 //64 bits
struct _stat64 st;
if (_fstat64(fd, &st) == 0)
return st.st_size;
#else //windows 32 bits
struct _stat st;
if (_fstat(fd, &st) == 0)
return st.st_size;
#endif #endif
#else // unix
int fd = fileno(f);
//64 bits(but not in osx, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
struct stat64 st;
if (fstat64(fd, &st) == 0)
return st.st_size;
#else // unix 32 bits or osx
struct stat st;
if (fstat(fd, &st) == 0)
return st.st_size;
#endif
#endif
throw spdlog_ex("Failed getting file size from fd", errno);
} }
//Return utc offset in minutes or throw spdlog_ex on failure //Return utc offset in minutes or throw spdlog_ex on failure
inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
{ {
@ -178,7 +241,7 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
auto rv = GetDynamicTimeZoneInformation(&tzinfo); auto rv = GetDynamicTimeZoneInformation(&tzinfo);
#endif #endif
if (rv == TIME_ZONE_ID_INVALID) if (rv == TIME_ZONE_ID_INVALID)
throw spdlog::spdlog_ex("Failed getting timezone info. Last error: " + GetLastError()); throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
int offset = -tzinfo.Bias; int offset = -tzinfo.Bias;
if (tm.tm_isdst) if (tm.tm_isdst)
@ -187,7 +250,43 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
offset -= tzinfo.StandardBias; offset -= tzinfo.StandardBias;
return offset; return offset;
#else #else
return static_cast<int>(tm.tm_gmtoff / 60);
#if defined(sun) || defined(__sun)
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper
{
static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime())
{
int local_year = localtm.tm_year + (1900 - 1);
int gmt_year = gmtm.tm_year + (1900 - 1);
long int days = (
// difference in day of year
localtm.tm_yday - gmtm.tm_yday
// + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2))
- (local_year / 100 - gmt_year / 100)
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
// + difference in years * 365 */
+ (long int)(local_year - gmt_year) * 365
);
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
return secs;
}
};
long int offset_seconds = helper::calculate_gmt_offset(tm);
#else
long int offset_seconds = tm.tm_gmtoff;
#endif
return static_cast<int>(offset_seconds / 60);
#endif #endif
} }
@ -202,14 +301,60 @@ inline size_t thread_id()
# define SYS_gettid __NR_gettid # define SYS_gettid __NR_gettid
# endif # endif
return static_cast<size_t>(syscall(SYS_gettid)); return static_cast<size_t>(syscall(SYS_gettid));
#elif __FreeBSD__
long tid;
thr_self(&tid);
return static_cast<size_t>(tid);
#else //Default to standard C++11 (OSX and other Unix) #else //Default to standard C++11 (OSX and other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id())); return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif #endif
}
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#define SPDLOG_FILENAME_T(s) L ## s
inline std::string filename_to_str(const filename_t& filename)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
return c.to_bytes(filename);
}
#else
#define SPDLOG_FILENAME_T(s) s
inline std::string filename_to_str(const filename_t& filename)
{
return filename;
}
#endif
// Return errno string (thread safe)
inline std::string errno_str(int err_num)
{
char buf[256];
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
#ifdef _WIN32
if(strerror_s(buf, buf_size, err_num) == 0)
return std::string(buf);
else
return "Unkown error";
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || \
((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version
if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf);
else
return "Unkown error";
#else // gnu version (might not use the given buf, so its retval pointer must be used)
return std::string(strerror_r(err_num, buf, buf_size));
#endif
} }
} //os } //os
} //details } //details
} //spdlog } //spdlog

View File

@ -5,16 +5,19 @@
#pragma once #pragma once
#include <string> #include <spdlog/formatter.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/details/os.h>
#include <spdlog/fmt/fmt.h>
#include <chrono> #include <chrono>
#include <ctime>
#include <memory> #include <memory>
#include <vector> #include <mutex>
#include <string>
#include <thread> #include <thread>
#include <utility>
#include <vector>
#include "../formatter.h"
#include "./log_msg.h"
#include "./os.h"
namespace spdlog namespace spdlog
{ {
@ -23,7 +26,8 @@ namespace details
class flag_formatter class flag_formatter
{ {
public: public:
virtual ~flag_formatter() {} virtual ~flag_formatter()
{}
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0; virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
}; };
@ -36,7 +40,7 @@ class name_formatter :public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm&) override void format(details::log_msg& msg, const std::tm&) override
{ {
msg.formatted << msg.logger_name; msg.formatted << *msg.logger_name;
} }
}; };
} }
@ -301,7 +305,8 @@ class z_formatter :public flag_formatter
public: public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5); const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
z_formatter() :_last_update(std::chrono::seconds(0)) {} z_formatter():_last_update(std::chrono::seconds(0))
{}
z_formatter(const z_formatter&) = delete; z_formatter(const z_formatter&) = delete;
z_formatter& operator=(const z_formatter&) = delete; z_formatter& operator=(const z_formatter&) = delete;
@ -314,13 +319,21 @@ public:
// it is very fast (already stored in tm.tm_gmtoff) // it is very fast (already stored in tm.tm_gmtoff)
int total_minutes = os::utc_minutes_offset(tm_time); int total_minutes = os::utc_minutes_offset(tm_time);
#endif #endif
bool is_negative = total_minutes < 0;
char sign;
if (is_negative)
{
total_minutes = -total_minutes;
sign = '-';
}
else
{
sign = '+';
}
int h = total_minutes / 60; int h = total_minutes / 60;
int m = total_minutes % 60; int m = total_minutes % 60;
if (h >= 0) //minus sign will be printed anyway if negative msg.formatted << sign;
{
msg.formatted << '+';
}
pad_n_join(msg.formatted, h, m, ':'); pad_n_join(msg.formatted, h, m, ':');
} }
private: private:
@ -432,7 +445,7 @@ class full_formatter :public flag_formatter
#endif #endif
#ifndef SPDLOG_NO_NAME #ifndef SPDLOG_NO_NAME
msg.formatted << '[' << msg.logger_name << "] "; msg.formatted << '[' << *msg.logger_name << "] ";
#endif #endif
msg.formatted << '[' << level::to_str(msg.level) << "] "; msg.formatted << '[' << level::to_str(msg.level) << "] ";
@ -608,18 +621,16 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
inline void spdlog::pattern_formatter::format(details::log_msg& msg) inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{ {
try
{ #ifndef SPDLOG_NO_DATETIME
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time)); auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
#else
std::tm tm_time;
#endif
for (auto &f : _formatters) for (auto &f : _formatters)
{ {
f->format(msg, tm_time); f->format(msg, tm_time);
} }
//write eol //write eol
msg.formatted << details::os::eol(); msg.formatted.write(details::os::eol, details::os::eol_size);
}
catch(const fmt::FormatError& e)
{
throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what()));
}
} }

View File

@ -10,15 +10,17 @@
// If user requests a non existing logger, nullptr will be returned // If user requests a non existing logger, nullptr will be returned
// This class is thread safe // This class is thread safe
#include <string> #include <spdlog/details/null_mutex.h>
#include <mutex> #include <spdlog/logger.h>
#include <unordered_map> #include <spdlog/async_logger.h>
#include <functional> #include <spdlog/common.h>
#include "./null_mutex.h" #include <chrono>
#include "../logger.h" #include <functional>
#include "../async_logger.h" #include <memory>
#include "../common.h" #include <mutex>
#include <string>
#include <unordered_map>
namespace spdlog namespace spdlog
{ {
@ -31,7 +33,9 @@ public:
void register_logger(std::shared_ptr<logger> logger) void register_logger(std::shared_ptr<logger> logger)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<Mutex> lock(_mutex);
register_logger_impl(logger); auto logger_name = logger->name();
throw_if_exists(logger_name);
_loggers[logger_name] = logger;
} }
@ -45,25 +49,35 @@ public:
template<class It> template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end) std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{ {
std::shared_ptr<logger> new_logger;
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<Mutex> lock(_mutex);
throw_if_exists(logger_name);
std::shared_ptr<logger> new_logger;
if (_async_mode) if (_async_mode)
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms); new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
else else
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end); new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
if (_formatter) if (_formatter)
new_logger->set_formatter(_formatter); new_logger->set_formatter(_formatter);
if (_err_handler)
new_logger->set_error_handler(_err_handler);
new_logger->set_level(_level); new_logger->set_level(_level);
register_logger_impl(new_logger);
//Add to registry
_loggers[logger_name] = new_logger;
return new_logger; return new_logger;
} }
void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
std::lock_guard<Mutex> lock(_mutex);
for (auto &l : _loggers)
fun(l.second);
}
void drop(const std::string& logger_name) void drop(const std::string& logger_name)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<Mutex> lock(_mutex);
@ -110,7 +124,14 @@ public:
_level = log_level; _level = log_level;
} }
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) void set_error_handler(log_err_handler handler)
{
for (auto& l : _loggers)
l.second->set_error_handler(handler);
_err_handler = handler;
}
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<Mutex> lock(_mutex);
_async_mode = true; _async_mode = true;
@ -118,6 +139,7 @@ public:
_overflow_policy = overflow_policy; _overflow_policy = overflow_policy;
_worker_warmup_cb = worker_warmup_cb; _worker_warmup_cb = worker_warmup_cb;
_flush_interval_ms = flush_interval_ms; _flush_interval_ms = flush_interval_ms;
_worker_teardown_cb = worker_teardown_cb;
} }
void set_sync_mode() void set_sync_mode()
@ -133,25 +155,26 @@ public:
} }
private: private:
void register_logger_impl(std::shared_ptr<logger> logger)
{
auto logger_name = logger->name();
if (_loggers.find(logger_name) != std::end(_loggers))
throw spdlog_ex("logger with name " + logger_name + " already exists");
_loggers[logger->name()] = logger;
}
registry_t<Mutex>() {} registry_t<Mutex>() {}
registry_t<Mutex>(const registry_t<Mutex>&) = delete; registry_t<Mutex>(const registry_t<Mutex>&) = delete;
registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete; registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete;
void throw_if_exists(const std::string &logger_name)
{
if (_loggers.find(logger_name) != _loggers.end())
throw spdlog_ex("logger with name '" + logger_name + "' already exists");
}
Mutex _mutex; Mutex _mutex;
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers; std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
formatter_ptr _formatter; formatter_ptr _formatter;
level::level_enum _level = level::info; level::level_enum _level = level::info;
log_err_handler _err_handler;
bool _async_mode = false; bool _async_mode = false;
size_t _async_q_size = 0; size_t _async_q_size = 0;
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry; async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
std::function<void()> _worker_warmup_cb = nullptr; std::function<void()> _worker_warmup_cb = nullptr;
std::chrono::milliseconds _flush_interval_ms; std::chrono::milliseconds _flush_interval_ms;
std::function<void()> _worker_teardown_cb = nullptr;
}; };
#ifdef SPDLOG_NO_REGISTRY_MUTEX #ifdef SPDLOG_NO_REGISTRY_MUTEX
typedef registry_t<spdlog::details::null_mutex> registry; typedef registry_t<spdlog::details::null_mutex> registry;

View File

@ -8,10 +8,18 @@
// //
// Global registry functions // Global registry functions
// //
#include "registry.h" #include <spdlog/spdlog.h>
#include "../sinks/file_sinks.h" #include <spdlog/details/registry.h>
#include "../sinks/stdout_sinks.h" #include <spdlog/sinks/file_sinks.h>
#include "../sinks/syslog_sink.h" #include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/sinks/syslog_sink.h>
#include <spdlog/sinks/ansicolor_sink.h>
#include <spdlog/sinks/android_sink.h>
#include <chrono>
#include <functional>
#include <memory>
#include <string>
inline void spdlog::register_logger(std::shared_ptr<logger> logger) inline void spdlog::register_logger(std::shared_ptr<logger> logger)
{ {
@ -28,50 +36,68 @@ inline void spdlog::drop(const std::string &name)
details::registry::instance().drop(name); details::registry::instance().drop(name);
} }
// Create multi/single threaded rotating file logger // Create multi/single threaded simple file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush) inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate)
{ {
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, "txt", max_file_size, max_files, force_flush); return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
} }
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush) inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate)
{ {
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, "txt", max_file_size, max_files, force_flush); return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
}
// Create multi/single threaded rotating file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
}
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
} }
// Create file logger which creates new file at midnight): // Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush) inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{ {
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, "txt", hour, minute, force_flush); return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
}
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush)
{
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, "txt", hour, minute, force_flush);
} }
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
// Create stdout/stderr loggers
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
{ {
return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance()); return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name) // Create stdout/stderr loggers (with optinal color support)
inline std::shared_ptr<spdlog::logger> create_console_logger(const std::string& logger_name, spdlog::sink_ptr sink, bool color)
{ {
return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance()); if (color) //use color wrapper sink
sink = std::make_shared<spdlog::sinks::ansicolor_sink>(sink);
return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name, bool color)
{ {
return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance()); return create_console_logger(logger_name, sinks::stdout_sink_mt::instance(), color);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name, bool color)
{ {
return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance()); return create_console_logger(logger_name, sinks::stdout_sink_st::instance(), color);
} }
#if defined(__linux__) || defined(__APPLE__) inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name, bool color)
{
return create_console_logger(logger_name, sinks::stderr_sink_mt::instance(), color);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name, bool color)
{
return create_console_logger(logger_name, sinks::stderr_sink_st::instance(), color);
}
#ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger // Create syslog logger
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option) inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
{ {
@ -79,6 +105,18 @@ inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string&
} }
#endif #endif
#if defined(__ANDROID__)
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
{
return create<spdlog::sinks::android_sink>(logger_name, tag);
}
#endif
// Create and register a logger a single sink
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink)
{
return details::registry::instance().create(logger_name, sink);
}
//Create logger with multiple sinks //Create logger with multiple sinks
@ -117,10 +155,15 @@ inline void spdlog::set_level(level::level_enum log_level)
return details::registry::instance().set_level(log_level); return details::registry::instance().set_level(log_level);
} }
inline void spdlog::set_error_handler(log_err_handler handler)
inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms)
{ {
details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms); return details::registry::instance().set_error_handler(handler);
}
inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{
details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
} }
inline void spdlog::set_sync_mode() inline void spdlog::set_sync_mode()
@ -128,8 +171,12 @@ inline void spdlog::set_sync_mode()
details::registry::instance().set_sync_mode(); details::registry::instance().set_sync_mode();
} }
inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
details::registry::instance().apply_all(fun);
}
inline void spdlog::drop_all() inline void spdlog::drop_all()
{ {
details::registry::instance().drop_all(); details::registry::instance().drop_all();
} }

View File

@ -0,0 +1,557 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Commented out by spdlog to use header only
// #include "fmt/format.h"
// #include "fmt/printf.h"
#include <string.h>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
#endif
#if FMT_USE_WINDOWS_H
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
using fmt::internal::Arg;
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>();
}
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>();
}
namespace fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() throw() {}
FMT_FUNC FormatError::~FormatError() throw() {}
FMT_FUNC SystemError::~SystemError() throw() {}
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf
#else
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r.
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(Writer &out, int error_code,
StringRef message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
typedef internal::IntTraits<int>::MainType MainType;
MainType abs_value = static_cast<MainType>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::count_digits(abs_value);
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func, int error_code,
StringRef message) FMT_NOEXCEPT {
MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
} // namespace
namespace internal {
// This method is used to preserve binary compatibility with fmt 3.0.
// It can be removed in 4.0.
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
fmt::format_system_error(out, error_code, message);
}
} // namespace internal
FMT_FUNC void SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
const char internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \
factor * 10, \
factor * 100, \
factor * 1000, \
factor * 10000, \
factor * 100000, \
factor * 1000000, \
factor * 10000000, \
factor * 100000000, \
factor * 1000000000
template <typename T>
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10
};
FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(FormatError(
format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(FormatError(
format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
return 0;
}
FMT_FUNC void WindowsError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
FMT_FUNC void internal::format_windows_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
wchar_t *system_message = &buffer[0];
int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), 0);
if (result != 0) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
buffer.resize(internal::INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
template <typename Char>
void internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = 0;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
for (unsigned i = 0;/*nothing*/; ++i) {
internal::Arg::Type arg_type = args.type(i);
switch (arg_type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
return;
}
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
internal::Arg::Type arg_type = args.type(i);
if (arg_type == internal::Arg::NAMED_ARG) {
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
switch (args.args_[i].type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
}
template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC Arg internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index];
switch (arg.type) {
case Arg::NONE:
error = "argument index out of range";
break;
case Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
break;
default:
/*nothing*/;
}
return arg;
}
FMT_FUNC void report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
}
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
#ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>;
// Explicit instantiations for char.
template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template void PrintfFormatter<char>::format(CStringRef format);
template int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
template int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
} // namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif

View File

@ -0,0 +1,36 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
// Commented out by spdlog to use header only
// #include "fmt/ostream.h"
namespace fmt {
namespace internal {
FMT_FUNC void write(std::ostream &os, Writer &w) {
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
size -= n;
} while (size != 0);
}
}
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
internal::write(os, w);
}
} // namespace fmt

View File

@ -0,0 +1,118 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
// Commented out by spdlog to use header only
// #include "fmt/format.h"
#include <ostream>
namespace fmt
{
namespace internal
{
template <class Char>
class FormatBuf : public std::basic_streambuf<Char>
{
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
Char *start_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0])
{
this->setp(start_, start_ + buffer_.capacity());
}
int_type overflow(int_type ch = traits_type::eof())
{
if (!traits_type::eq_int_type(ch, traits_type::eof()))
{
size_t buf_size = size();
buffer_.resize(buf_size);
buffer_.reserve(buf_size * 2);
start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
}
return ch;
}
size_t size() const
{
return to_unsigned(this->pptr() - start_);
}
};
Yes &convert(std::ostream &);
struct DummyStream : std::ostream
{
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
};
No &operator<<(std::ostream &, int);
template<typename T>
struct ConvertToIntImpl<T, true>
{
// Convert to int only if T doesn't have an overloaded operator<<.
enum
{
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
void write(std::ostream &os, Writer &w);
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value)
{
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output << value;
BasicStringRef<Char> str(&buffer[0], format_buf.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#endif
#endif // FMT_OSTREAM_H_

View File

@ -0,0 +1,642 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "fmt/ostream.h"
namespace fmt
{
namespace internal
{
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker
{
template <typename T>
static bool fits_in_int(T value)
{
unsigned max = std::numeric_limits<int>::max();
return value <= max;
}
static bool fits_in_int(bool)
{
return true;
}
};
template <>
struct IntChecker<true>
{
template <typename T>
static bool fits_in_int(T value)
{
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int)
{
return true;
}
};
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int>
{
public:
void report_unhandled_arg()
{
FMT_THROW(FormatError("precision is not integer"));
}
template <typename T>
int visit_any_int(T value)
{
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
};
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool>
{
public:
template <typename T>
bool visit_any_int(T value)
{
return value == 0;
}
};
template <typename T, typename U>
struct is_same
{
enum { value = 0 };
};
template <typename T>
struct is_same<T, T>
{
enum { value = 1 };
};
// An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is void, the argument is converted to
// corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template <typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void>
{
private:
internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
void visit_bool(bool value)
{
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i';
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType;
if (sizeof(TargetType) <= sizeof(int))
{
// Extra casts are used to silence warnings.
if (is_signed)
{
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
}
else
{
arg_.type = Arg::UINT;
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
}
}
else
{
if (is_signed)
{
arg_.type = Arg::LONG_LONG;
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_.long_long_value = static_cast<LongLong>(value);
}
else
{
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void>
{
private:
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
template <typename T>
void visit_any_int(T value)
{
arg_.type = internal::Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned>
{
private:
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg()
{
FMT_THROW(FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value)
{
typedef typename internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value))
{
spec_.align_ = ALIGN_LEFT;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
} // namespace internal
/**
\rst
A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
will be called. If the subclass doesn't contain a method with this signature,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called.
\endrst
*/
template <typename Impl, typename Char>
class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char>
{
private:
void write_null_pointer()
{
this->spec().type_ = 0;
this->write("(nil)");
}
typedef internal::ArgFormatterBase<Impl, Char> Base;
public:
/**
\rst
Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &writer, FormatSpec &spec)
: internal::ArgFormatterBase<Impl, Char>(writer, spec) {}
/** Formats an argument of type ``bool``. */
void visit_bool(bool value)
{
FormatSpec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
this->write(value);
}
/** Formats a character. */
void visit_char(int value)
{
const FormatSpec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (fmt_spec.width_ > 1)
{
Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT)
{
std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1;
}
else
{
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
}
}
else
{
out = w.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
/** Formats a null-terminated C string. */
void visit_cstring(const char *value)
{
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
/** Formats a pointer. */
void visit_pointer(const void *value)
{
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c)
{
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
};
/** The default printf argument formatter. */
template <typename Char>
class PrintfArgFormatter
: public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>
{
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s) {}
};
/** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase
{
private:
BasicWriter<Char> &writer_;
void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
internal::Arg get_arg(
const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
/**
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &args, BasicWriter<Char> &w)
: FormatterBase(args), writer_(w) {}
/** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicCStringRef<Char> format_str);
};
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s)
{
for (;;)
{
switch (*s++)
{
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index)
{
(void)s;
const char *error = 0;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header(
const Char *&s, FormatSpec &spec)
{
unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s;
if (c >= '0' && c <= '9')
{
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = internal::parse_nonnegative_int(s);
if (*s == '$') // value is an argument index
{
++s;
arg_index = value;
}
else
{
if (c == '0')
spec.fill_ = '0';
if (value != 0)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9')
{
spec.width_ = internal::parse_nonnegative_int(s);
}
else if (*s == '*')
{
++s;
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
{
const Char *start = format_str.c_str();
const Char *s = start;
while (*s)
{
Char c = *s++;
if (c != '%') continue;
if (*s == c)
{
write(writer_, start, s);
start = ++s;
continue;
}
write(writer_, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.')
{
++s;
if ('0' <= *s && *s <= '9')
{
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
}
else if (*s == '*')
{
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
}
}
using internal::Arg;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0')
{
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::ArgConverter;
switch (*s++)
{
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (arg.type <= Arg::LAST_INTEGER_TYPE)
{
// Normalize type.
switch (spec.type_)
{
case 'i':
case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
internal::CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
AF(writer_, spec).visit(arg);
}
write(writer_, start, s);
}
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
{
PrintfFormatter<Char>(args, w).format(format);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(CStringRef format, ArgList args)
{
MemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args)
{
WMemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(CStringRef format, ArgList args)
{
return fprintf(stdout, format, args);
}
FMT_VARIADIC(int, printf, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
{
MemoryWriter w;
printf(w, format_str, args);
internal::write(os, w);
return static_cast<int>(w.size());
}
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#endif // FMT_PRINTF_H_

28
vendor/spdlog/spdlog/fmt/fmt.h vendored Normal file
View File

@ -0,0 +1,28 @@
//
// Copyright(c) 2016 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Include a bundled header-only copy of fmtlib or an external one.
// By default spdlog include its own copy.
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY
#endif
#ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0
#endif
#include <spdlog/fmt/bundled/format.h>
#else //external fmtlib
#include <fmt/format.h>
#endif

17
vendor/spdlog/spdlog/fmt/ostr.h vendored Normal file
View File

@ -0,0 +1,17 @@
//
// Copyright(c) 2016 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// include external or bundled copy of fmtlib's ostream support
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#include <spdlog/fmt/fmt.h>
#include <spdlog/fmt/bundled/ostream.h>
#else
#include <fmt/ostream.h>
#endif

View File

@ -5,7 +5,12 @@
#pragma once #pragma once
#include "details/log_msg.h" #include <spdlog/details/log_msg.h>
#include <vector>
#include <string>
#include <memory>
namespace spdlog namespace spdlog
{ {
namespace details namespace details
@ -36,5 +41,5 @@ private:
}; };
} }
#include "details/pattern_formatter_impl.h" #include <spdlog/details/pattern_formatter_impl.h>

View File

@ -12,19 +12,16 @@
// 2. Format the message using the formatter function // 2. Format the message using the formatter function
// 3. Pass the formatted message to its sinks to performa the actual logging // 3. Pass the formatted message to its sinks to performa the actual logging
#include <spdlog/sinks/base_sink.h>
#include <spdlog/common.h>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include "sinks/base_sink.h" #include <string>
#include "common.h"
namespace spdlog namespace spdlog
{ {
namespace details
{
class line_logger;
}
class logger class logger
{ {
public: public:
@ -37,77 +34,61 @@ public:
logger(const logger&) = delete; logger(const logger&) = delete;
logger& operator=(const logger&) = delete; logger& operator=(const logger&) = delete;
template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
template <typename... Args> void log(level::level_enum lvl, const char* msg);
template <typename... Args> void trace(const char* fmt, const Args&... args);
template <typename... Args> void debug(const char* fmt, const Args&... args);
template <typename... Args> void info(const char* fmt, const Args&... args);
template <typename... Args> void warn(const char* fmt, const Args&... args);
template <typename... Args> void error(const char* fmt, const Args&... args);
template <typename... Args> void critical(const char* fmt, const Args&... args);
template <typename T> void log(level::level_enum lvl, const T&);
template <typename T> void trace(const T&);
template <typename T> void debug(const T&);
template <typename T> void info(const T&);
template <typename T> void warn(const T&);
template <typename T> void error(const T&);
template <typename T> void critical(const T&);
bool should_log(level::level_enum) const;
void set_level(level::level_enum); void set_level(level::level_enum);
level::level_enum level() const; level::level_enum level() const;
const std::string& name() const; const std::string& name() const;
bool should_log(level::level_enum) const;
// logger.info(cppformat_string, arg1, arg2, arg3, ...) call style
template <typename... Args> details::line_logger trace(const char* fmt, const Args&... args);
template <typename... Args> details::line_logger debug(const char* fmt, const Args&... args);
template <typename... Args> details::line_logger info(const char* fmt, const Args&... args);
template <typename... Args> details::line_logger notice(const char* fmt, const Args&... args);
template <typename... Args> details::line_logger warn(const char* fmt, const Args&... args);
template <typename... Args> details::line_logger error(const char* fmt, const Args&... args);
template <typename... Args> details::line_logger critical(const char* fmt, const Args&... args);
template <typename... Args> details::line_logger alert(const char* fmt, const Args&... args);
template <typename... Args> details::line_logger emerg(const char* fmt, const Args&... args);
// logger.info(msg) << ".." call style
template <typename T> details::line_logger trace(const T&);
template <typename T> details::line_logger debug(const T&);
template <typename T> details::line_logger info(const T&);
template <typename T> details::line_logger notice(const T&);
template <typename T> details::line_logger warn(const T&);
template <typename T> details::line_logger error(const T&);
template <typename T> details::line_logger critical(const T&);
template <typename T> details::line_logger alert(const T&);
template <typename T> details::line_logger emerg(const T&);
// logger.info() << ".." call style
details::line_logger trace();
details::line_logger debug();
details::line_logger info();
details::line_logger notice();
details::line_logger warn();
details::line_logger error();
details::line_logger critical();
details::line_logger alert();
details::line_logger emerg();
// Create log message with the given level, no matter what is the actual logger's level
template <typename... Args>
details::line_logger force_log(level::level_enum lvl, const char* fmt, const Args&... args);
// Set the format of the log messages from this logger
void set_pattern(const std::string&); void set_pattern(const std::string&);
void set_formatter(formatter_ptr); void set_formatter(formatter_ptr);
// error handler
void set_error_handler(log_err_handler);
log_err_handler error_handler();
// automatically call flush() if message level >= log_level
void flush_on(level::level_enum log_level);
virtual void flush(); virtual void flush();
protected: protected:
virtual void _log_msg(details::log_msg&); virtual void _sink_it(details::log_msg&);
virtual void _set_pattern(const std::string&); virtual void _set_pattern(const std::string&);
virtual void _set_formatter(formatter_ptr); virtual void _set_formatter(formatter_ptr);
details::line_logger _log_if_enabled(level::level_enum lvl);
template <typename... Args>
details::line_logger _log_if_enabled(level::level_enum lvl, const char* fmt, const Args&... args);
template<typename T>
inline details::line_logger _log_if_enabled(level::level_enum lvl, const T& msg);
// default error handler: print the error to stderr with the max rate of 1 message/minute
virtual void _default_err_handler(const std::string &msg);
friend details::line_logger; // return true if the given message level should trigger a flush
std::string _name; bool _should_flush_on(const details::log_msg&);
const std::string _name;
std::vector<sink_ptr> _sinks; std::vector<sink_ptr> _sinks;
formatter_ptr _formatter; formatter_ptr _formatter;
std::atomic_int _level; spdlog::level_t _level;
spdlog::level_t _flush_level;
log_err_handler _err_handler;
std::atomic<time_t> _last_err_time;
}; };
} }
#include "./details/logger_impl.h" #include <spdlog/details/logger_impl.h>

View File

@ -7,49 +7,43 @@
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include <mutex> #include <spdlog/sinks/sink.h>
#include "base_sink.h"
#include "../details/null_mutex.h"
#include <mutex>
#include <string>
#include <android/log.h> #include <android/log.h>
namespace spdlog namespace spdlog
{ {
namespace sinks namespace sinks
{ {
/* /*
* Android sink (logging using __android_log_write) * Android sink (logging using __android_log_write)
* __android_log_write is thread-safe. No lock is needed.
*/ */
template<class Mutex> class android_sink : public sink
class base_android_sink : public base_sink < Mutex >
{ {
public: public:
explicit base_android_sink(std::string tag="spdlog"): _tag(tag) explicit android_sink(const std::string& tag = "spdlog"): _tag(tag) {}
void log(const details::log_msg& msg) override
{ {
const android_LogPriority priority = convert_to_android(msg.level);
// See system/core/liblog/logger_write.c for explanation of return value
const int ret = __android_log_write(
priority, _tag.c_str(), msg.formatted.c_str()
);
if (ret < 0)
{
throw spdlog_ex("__android_log_write() failed", ret);
}
} }
void flush() override void flush() override
{ {
} }
protected:
void _sink_it(const details::log_msg& msg) override
{
const android_LogPriority priority = convert_to_android(msg.level);
const int expected_size = msg.formatted.size();
const int size = __android_log_write(
priority, _tag.c_str(), msg.formatted.c_str()
);
if (size > expected_size)
{
// Will write a little bit more than original message
}
else
{
throw spdlog_ex("Send to Android logcat failed");
}
}
private: private:
static android_LogPriority convert_to_android(spdlog::level::level_enum level) static android_LogPriority convert_to_android(spdlog::level::level_enum level)
{ {
@ -61,29 +55,20 @@ private:
return ANDROID_LOG_DEBUG; return ANDROID_LOG_DEBUG;
case spdlog::level::info: case spdlog::level::info:
return ANDROID_LOG_INFO; return ANDROID_LOG_INFO;
case spdlog::level::notice:
return ANDROID_LOG_INFO;
case spdlog::level::warn: case spdlog::level::warn:
return ANDROID_LOG_WARN; return ANDROID_LOG_WARN;
case spdlog::level::err: case spdlog::level::err:
return ANDROID_LOG_ERROR; return ANDROID_LOG_ERROR;
case spdlog::level::critical: case spdlog::level::critical:
return ANDROID_LOG_FATAL; return ANDROID_LOG_FATAL;
case spdlog::level::alert:
return ANDROID_LOG_FATAL;
case spdlog::level::emerg:
return ANDROID_LOG_FATAL;
default: default:
throw spdlog_ex("Incorrect level value"); return ANDROID_LOG_DEFAULT;
} }
} }
std::string _tag; std::string _tag;
}; };
typedef base_android_sink<std::mutex> android_sink_mt;
typedef base_android_sink<details::null_mutex> android_sink_st;
} }
} }

View File

@ -0,0 +1,116 @@
//
// Copyright(c) 2016 Kevin M. Godby (a modified version by spdlog).
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/common.h>
#include <string>
#include <map>
namespace spdlog
{
namespace sinks
{
/**
* @brief The ansi_color_sink is a decorator around another sink and prefixes
* the output with an ANSI escape sequence color code depending on the severity
* of the message.
*/
class ansicolor_sink : public sink
{
public:
ansicolor_sink(sink_ptr wrapped_sink);
virtual ~ansicolor_sink();
ansicolor_sink(const ansicolor_sink& other) = delete;
ansicolor_sink& operator=(const ansicolor_sink& other) = delete;
virtual void log(const details::log_msg& msg) override;
virtual void flush() override;
void set_color(level::level_enum level, const std::string& color);
/// Formatting codes
const std::string reset = "\033[00m";
const std::string bold = "\033[1m";
const std::string dark = "\033[2m";
const std::string underline = "\033[4m";
const std::string blink = "\033[5m";
const std::string reverse = "\033[7m";
const std::string concealed = "\033[8m";
// Foreground colors
const std::string grey = "\033[30m";
const std::string red = "\033[31m";
const std::string green = "\033[32m";
const std::string yellow = "\033[33m";
const std::string blue = "\033[34m";
const std::string magenta = "\033[35m";
const std::string cyan = "\033[36m";
const std::string white = "\033[37m";
/// Background colors
const std::string on_grey = "\033[40m";
const std::string on_red = "\033[41m";
const std::string on_green = "\033[42m";
const std::string on_yellow = "\033[43m";
const std::string on_blue = "\033[44m";
const std::string on_magenta = "\033[45m";
const std::string on_cyan = "\033[46m";
const std::string on_white = "\033[47m";
protected:
sink_ptr sink_;
std::map<level::level_enum, std::string> colors_;
};
inline ansicolor_sink::ansicolor_sink(sink_ptr wrapped_sink) : sink_(wrapped_sink)
{
colors_[level::trace] = cyan;
colors_[level::debug] = cyan;
colors_[level::info] = bold;
colors_[level::warn] = yellow + bold;
colors_[level::err] = red + bold;
colors_[level::critical] = bold + on_red;
colors_[level::off] = reset;
}
inline void ansicolor_sink::log(const details::log_msg& msg)
{
// Wrap the originally formatted message in color codes
const std::string& prefix = colors_[msg.level];
const std::string& s = msg.formatted.str();
const std::string& suffix = reset;
details::log_msg m;
m.level = msg.level;
m.logger_name = msg.logger_name;
m.time = msg.time;
m.thread_id = msg.thread_id;
m.formatted << prefix << s << suffix;
sink_->log(m);
}
inline void ansicolor_sink::flush()
{
sink_->flush();
}
inline void ansicolor_sink::set_color(level::level_enum level, const std::string& color)
{
colors_[level] = color;
}
inline ansicolor_sink::~ansicolor_sink()
{
flush();
}
} // namespace sinks
} // namespace spdlog

View File

@ -7,17 +7,15 @@
// //
// base sink templated over a mutex (either dummy or realy) // base sink templated over a mutex (either dummy or realy)
// concrete implementation should only overrid the _sink_it method. // concrete implementation should only overrid the _sink_it method.
// all locking is taken care of here so no locking needed by the implementors.. // all locking is taken care of here so no locking needed by the implementers..
// //
#include<string> #include <spdlog/sinks/sink.h>
#include<mutex> #include <spdlog/formatter.h>
#include<atomic> #include <spdlog/common.h>
#include "./sink.h" #include <spdlog/details/log_msg.h>
#include "../formatter.h"
#include "../common.h"
#include "../details/log_msg.h"
#include <mutex>
namespace spdlog namespace spdlog
{ {

View File

@ -5,15 +5,17 @@
#pragma once #pragma once
#include <algorithm> #include <spdlog/details/log_msg.h>
#include <memory> #include <spdlog/details/null_mutex.h>
#include <mutex> #include <spdlog/sinks/base_sink.h>
#include <list> #include <spdlog/sinks/sink.h>
#include "../details/log_msg.h" #include <algorithm>
#include "../details/null_mutex.h" #include <mutex>
#include "./base_sink.h" #include <memory>
#include "./sink.h" #include <vector>
// Distribution sink (mux). Stores a vector of sinks which get called when log is called
namespace spdlog namespace spdlog
{ {
@ -29,40 +31,37 @@ public:
virtual ~dist_sink() = default; virtual ~dist_sink() = default;
protected: protected:
std::vector<std::shared_ptr<sink>> _sinks;
void _sink_it(const details::log_msg& msg) override void _sink_it(const details::log_msg& msg) override
{ {
for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++) for (auto &sink : _sinks)
(*iter)->log(msg); {
if( sink->should_log( msg.level))
{
sink->log(msg);
}
}
} }
std::vector<std::shared_ptr<sink>> _sinks;
public: public:
void flush() override void flush() override
{ {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++) for (auto &sink : _sinks)
(*iter)->flush(); sink->flush();
} }
void add_sink(std::shared_ptr<sink> sink) void add_sink(std::shared_ptr<sink> sink)
{ {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
if (sink &&
_sinks.end() == std::find(_sinks.begin(), _sinks.end(), sink))
{
_sinks.push_back(sink); _sinks.push_back(sink);
} }
}
void remove_sink(std::shared_ptr<sink> sink) void remove_sink(std::shared_ptr<sink> sink)
{ {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
auto pos = std::find(_sinks.begin(), _sinks.end(), sink); _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end());
if (pos != _sinks.end())
{
_sinks.erase(pos);
}
} }
}; };

View File

@ -5,11 +5,18 @@
#pragma once #pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/details/file_helper.h>
#include <spdlog/fmt/fmt.h>
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <ctime>
#include <mutex> #include <mutex>
#include "base_sink.h" #include <string>
#include "../details/null_mutex.h" #include <cerrno>
#include "../details/file_helper.h"
#include "../details/format.h"
namespace spdlog namespace spdlog
{ {
@ -22,11 +29,9 @@ template<class Mutex>
class simple_file_sink : public base_sink < Mutex > class simple_file_sink : public base_sink < Mutex >
{ {
public: public:
explicit simple_file_sink(const std::string &filename, explicit simple_file_sink(const filename_t &filename, bool truncate = false)
bool force_flush = false) :
_file_helper(force_flush)
{ {
_file_helper.open(filename); _file_helper.open(filename, truncate);
} }
void flush() override void flush() override
{ {
@ -52,15 +57,14 @@ template<class Mutex>
class rotating_file_sink : public base_sink < Mutex > class rotating_file_sink : public base_sink < Mutex >
{ {
public: public:
rotating_file_sink(const std::string &base_filename, const std::string &extension, rotating_file_sink(const filename_t &base_filename, const filename_t &extension,
std::size_t max_size, std::size_t max_files, std::size_t max_size, std::size_t max_files ) :
bool force_flush = false) :
_base_filename(base_filename), _base_filename(base_filename),
_extension(extension), _extension(extension),
_max_size(max_size), _max_size(max_size),
_max_files(max_files), _max_files(max_files),
_current_size(0), _current_size(0),
_file_helper(force_flush) _file_helper()
{ {
_file_helper.open(calc_filename(_base_filename, 0, _extension)); _file_helper.open(calc_filename(_base_filename, 0, _extension));
_current_size = _file_helper.size(); //expensive. called only once _current_size = _file_helper.size(); //expensive. called only once
@ -84,13 +88,13 @@ protected:
} }
private: private:
static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension) static filename_t calc_filename(const filename_t& filename, std::size_t index, const filename_t& extension)
{ {
fmt::MemoryWriter w; std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index) if (index)
w.write("{}.{}.{}", filename, index, extension); w.write(SPDLOG_FILENAME_T("{}.{}.{}"), filename, index, extension);
else else
w.write("{}.{}", filename, extension); w.write(SPDLOG_FILENAME_T("{}.{}"), filename, extension);
return w.str(); return w.str();
} }
@ -102,28 +106,29 @@ private:
void _rotate() void _rotate()
{ {
using details::os::filename_to_str;
_file_helper.close(); _file_helper.close();
for (auto i = _max_files; i > 0; --i) for (auto i = _max_files; i > 0; --i)
{ {
std::string src = calc_filename(_base_filename, i - 1, _extension); filename_t src = calc_filename(_base_filename, i - 1, _extension);
std::string target = calc_filename(_base_filename, i, _extension); filename_t target = calc_filename(_base_filename, i, _extension);
if (details::file_helper::file_exists(target)) if (details::file_helper::file_exists(target))
{ {
if (std::remove(target.c_str()) != 0) if (details::os::remove(target) != 0)
{ {
throw spdlog_ex("rotating_file_sink: failed removing " + target); throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
} }
} }
if (details::file_helper::file_exists(src) && std::rename(src.c_str(), target.c_str())) if (details::file_helper::file_exists(src) && details::os::rename(src, target))
{ {
throw spdlog_ex("rotating_file_sink: failed renaming " + src + " to " + target); throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
} }
} }
_file_helper.reopen(true); _file_helper.reopen(true);
} }
std::string _base_filename; filename_t _base_filename;
std::string _extension; filename_t _extension;
std::size_t _max_size; std::size_t _max_size;
std::size_t _max_files; std::size_t _max_files;
std::size_t _current_size; std::size_t _current_size;
@ -133,29 +138,57 @@ private:
typedef rotating_file_sink<std::mutex> rotating_file_sink_mt; typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st; typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
/*
* Default generator of daily log file names.
*/
struct default_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD_hh-mm.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
return w.str();
}
};
/*
* Generator of daily log file names in format basename.YYYY-MM-DD.extension
*/
struct dateonly_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
return w.str();
}
};
/* /*
* Rotating file sink based on date. rotates at midnight * Rotating file sink based on date. rotates at midnight
*/ */
template<class Mutex> template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
class daily_file_sink :public base_sink < Mutex > class daily_file_sink :public base_sink < Mutex >
{ {
public: public:
//create daily file sink which rotates on given time //create daily file sink which rotates on given time
daily_file_sink( daily_file_sink(
const std::string& base_filename, const filename_t& base_filename,
const std::string& extension, const filename_t& extension,
int rotation_hour, int rotation_hour,
int rotation_minute, int rotation_minute) : _base_filename(base_filename),
bool force_flush = false) : _base_filename(base_filename),
_extension(extension), _extension(extension),
_rotation_h(rotation_hour), _rotation_h(rotation_hour),
_rotation_m(rotation_minute), _rotation_m(rotation_minute)
_file_helper(force_flush)
{ {
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
_rotation_tp = _next_rotation_tp(); _rotation_tp = _next_rotation_tp();
_file_helper.open(calc_filename(_base_filename, _extension)); _file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
} }
void flush() override void flush() override
@ -168,7 +201,7 @@ protected:
{ {
if (std::chrono::system_clock::now() >= _rotation_tp) if (std::chrono::system_clock::now() >= _rotation_tp)
{ {
_file_helper.open(calc_filename(_base_filename, _extension)); _file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
_rotation_tp = _next_rotation_tp(); _rotation_tp = _next_rotation_tp();
} }
_file_helper.write(msg); _file_helper.write(msg);
@ -177,8 +210,7 @@ protected:
private: private:
std::chrono::system_clock::time_point _next_rotation_tp() std::chrono::system_clock::time_point _next_rotation_tp()
{ {
using namespace std::chrono; auto now = std::chrono::system_clock::now();
auto now = system_clock::now();
time_t tnow = std::chrono::system_clock::to_time_t(now); time_t tnow = std::chrono::system_clock::to_time_t(now);
tm date = spdlog::details::os::localtime(tnow); tm date = spdlog::details::os::localtime(tnow);
date.tm_hour = _rotation_h; date.tm_hour = _rotation_h;
@ -188,20 +220,11 @@ private:
if (rotation_time > now) if (rotation_time > now)
return rotation_time; return rotation_time;
else else
return system_clock::time_point(rotation_time + hours(24)); return std::chrono::system_clock::time_point(rotation_time + std::chrono::hours(24));
} }
//Create filename for the form basename.YYYY-MM-DD.extension filename_t _base_filename;
static std::string calc_filename(const std::string& basename, const std::string& extension) filename_t _extension;
{
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
return w.str();
}
std::string _base_filename;
std::string _extension;
int _rotation_h; int _rotation_h;
int _rotation_m; int _rotation_m;
std::chrono::system_clock::time_point _rotation_tp; std::chrono::system_clock::time_point _rotation_tp;

50
vendor/spdlog/spdlog/sinks/msvc_sink.h vendored Normal file
View File

@ -0,0 +1,50 @@
//
// Copyright(c) 2016 Alexander Dalshov.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#if defined(_MSC_VER)
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <WinBase.h>
#include <mutex>
#include <string>
namespace spdlog
{
namespace sinks
{
/*
* MSVC sink (logging using OutputDebugStringA)
*/
template<class Mutex>
class msvc_sink : public base_sink < Mutex >
{
public:
explicit msvc_sink()
{
}
void flush() override
{
}
protected:
void _sink_it(const details::log_msg& msg) override
{
OutputDebugStringA(msg.formatted.c_str());
}
};
typedef msvc_sink<std::mutex> msvc_sink_mt;
typedef msvc_sink<details::null_mutex> msvc_sink_st;
}
}
#endif

View File

@ -4,10 +4,11 @@
// //
#pragma once #pragma once
#include <mutex>
#include "./base_sink.h"
#include "../details/null_mutex.h"
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <mutex>
namespace spdlog namespace spdlog
{ {

View File

@ -5,12 +5,11 @@
#pragma once #pragma once
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <ostream> #include <ostream>
#include <mutex> #include <mutex>
#include <memory>
#include "../details/null_mutex.h"
#include "./base_sink.h"
namespace spdlog namespace spdlog
{ {

View File

@ -6,7 +6,7 @@
#pragma once #pragma once
#include "../details/log_msg.h" #include <spdlog/details/log_msg.h>
namespace spdlog namespace spdlog
{ {
@ -15,10 +15,36 @@ namespace sinks
class sink class sink
{ {
public: public:
sink(): _level( level::trace ) {}
virtual ~sink() {} virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0; virtual void log(const details::log_msg& msg) = 0;
virtual void flush() = 0; virtual void flush() = 0;
bool should_log(level::level_enum msg_level) const;
void set_level(level::level_enum log_level);
level::level_enum level() const;
private:
level_t _level;
}; };
inline bool sink::should_log(level::level_enum msg_level) const
{
return msg_level >= _level.load(std::memory_order_relaxed);
}
inline void sink::set_level(level::level_enum log_level)
{
_level.store(log_level);
}
inline level::level_enum sink::level() const
{
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
}
} }
} }

View File

@ -5,10 +5,12 @@
#pragma once #pragma once
#include <iostream> #include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <cstdio>
#include <memory>
#include <mutex> #include <mutex>
#include "./ostream_sink.h"
#include "../details/null_mutex.h"
namespace spdlog namespace spdlog
{ {
@ -16,16 +18,27 @@ namespace sinks
{ {
template <class Mutex> template <class Mutex>
class stdout_sink : public ostream_sink<Mutex> class stdout_sink : public base_sink<Mutex>
{ {
using MyType = stdout_sink<Mutex>; using MyType = stdout_sink<Mutex>;
public: public:
stdout_sink() : ostream_sink<Mutex>(std::cout, true) {} stdout_sink() {}
static std::shared_ptr<MyType> instance() static std::shared_ptr<MyType> instance()
{ {
static std::shared_ptr<MyType> instance = std::make_shared<MyType>(); static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance; return instance;
} }
void _sink_it(const details::log_msg& msg) override
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout);
flush();
}
void flush() override
{
fflush(stdout);
}
}; };
typedef stdout_sink<details::null_mutex> stdout_sink_st; typedef stdout_sink<details::null_mutex> stdout_sink_st;
@ -33,17 +46,27 @@ typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex> template <class Mutex>
class stderr_sink : public ostream_sink<Mutex> class stderr_sink : public base_sink<Mutex>
{ {
using MyType = stderr_sink<Mutex>; using MyType = stderr_sink<Mutex>;
public: public:
stderr_sink() : ostream_sink<Mutex>(std::cerr, true) {} stderr_sink() {}
static std::shared_ptr<MyType> instance() static std::shared_ptr<MyType> instance()
{ {
static std::shared_ptr<MyType> instance = std::make_shared<MyType>(); static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance; return instance;
} }
void _sink_it(const details::log_msg& msg) override
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr);
flush();
}
void flush() override
{
fflush(stderr);
}
}; };
typedef stderr_sink<std::mutex> stderr_sink_mt; typedef stderr_sink<std::mutex> stderr_sink_mt;

View File

@ -5,16 +5,17 @@
#pragma once #pragma once
#if defined(__linux__) || defined(__APPLE__) #include <spdlog/common.h>
#ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/sink.h>
#include <spdlog/details/log_msg.h>
#include <array> #include <array>
#include <string> #include <string>
#include <syslog.h> #include <syslog.h>
#include "./sink.h"
#include "../common.h"
#include "../details/log_msg.h"
namespace spdlog namespace spdlog
{ {
@ -35,12 +36,9 @@ public:
_priorities[static_cast<int>(level::trace)] = LOG_DEBUG; _priorities[static_cast<int>(level::trace)] = LOG_DEBUG;
_priorities[static_cast<int>(level::debug)] = LOG_DEBUG; _priorities[static_cast<int>(level::debug)] = LOG_DEBUG;
_priorities[static_cast<int>(level::info)] = LOG_INFO; _priorities[static_cast<int>(level::info)] = LOG_INFO;
_priorities[static_cast<int>(level::notice)] = LOG_NOTICE;
_priorities[static_cast<int>(level::warn)] = LOG_WARNING; _priorities[static_cast<int>(level::warn)] = LOG_WARNING;
_priorities[static_cast<int>(level::err)] = LOG_ERR; _priorities[static_cast<int>(level::err)] = LOG_ERR;
_priorities[static_cast<int>(level::critical)] = LOG_CRIT; _priorities[static_cast<int>(level::critical)] = LOG_CRIT;
_priorities[static_cast<int>(level::alert)] = LOG_ALERT;
_priorities[static_cast<int>(level::emerg)] = LOG_EMERG;
_priorities[static_cast<int>(level::off)] = LOG_INFO; _priorities[static_cast<int>(level::off)] = LOG_INFO;
//set ident to be program name if empty //set ident to be program name if empty
@ -65,7 +63,7 @@ public:
private: private:
std::array<int, 10> _priorities; std::array<int, 7> _priorities;
//must store the ident because the man says openlog might use the pointer as is and not a string copy //must store the ident because the man says openlog might use the pointer as is and not a string copy
const std::string _ident; const std::string _ident;

View File

@ -8,21 +8,25 @@
#pragma once #pragma once
#include "tweakme.h" #include <spdlog/tweakme.h>
#include "common.h" #include <spdlog/common.h>
#include "logger.h" #include <spdlog/logger.h>
#include <memory>
#include <functional>
#include <chrono>
#include <string>
namespace spdlog namespace spdlog
{ {
// Return an existing logger or nullptr if a logger with such name doesn't exist.
// Examples: //
// Return an existing logger or nullptr if a logger with such name doesn't exist.
// example: spdlog::get("my_logger")->info("hello {}", "world");
// //
// spdlog::get("mylog")->info("Hello");
// auto logger = spdlog::get("mylog");
// logger.info("This is another message" , x, y, z);
// logger.info() << "This is another message" << x << y << z;
std::shared_ptr<logger> get(const std::string& name); std::shared_ptr<logger> get(const std::string& name);
// //
// Set global formatting // Set global formatting
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); // example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
@ -35,6 +39,11 @@ void set_formatter(formatter_ptr f);
// //
void set_level(level::level_enum log_level); void set_level(level::level_enum log_level);
//
// Set global error handler
//
void set_error_handler(log_err_handler);
// //
// Turn on async mode (off by default) and set the queue size for each async_logger. // Turn on async mode (off by default) and set the queue size for each async_logger.
// effective only for loggers created after this call. // effective only for loggers created after this call.
@ -48,40 +57,56 @@ void set_level(level::level_enum log_level);
// worker_warmup_cb (optional): // worker_warmup_cb (optional):
// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity) // callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity)
// //
void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); // worker_teardown_cb (optional):
// callback function that will be called in worker thread upon exit
//
void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
// Turn off async mode // Turn off async mode
void set_sync_mode(); void set_sync_mode();
//
// Create and register multi/single threaded basic file logger.
// Basic logger simply writes to given file without any limitatons or rotations.
//
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false);
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
// //
// Create and register multi/single threaded rotating file logger // Create and register multi/single threaded rotating file logger
// //
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const std::string& filenameB, size_t max_file_size, size_t max_files, bool force_flush = false); std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush = false); std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
// //
// Create file logger which creates new file on the given time (default in midnight): // Create file logger which creates new file on the given time (default in midnight):
// //
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false); std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false); std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
// //
// Create and register stdout/stderr loggers // Create and register stdout/stderr loggers
// //
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name); std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name); std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name); std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name); std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name, bool color = false);
// //
// Create and register a syslog logger // Create and register a syslog logger
// //
#if defined(__linux__) || defined(__APPLE__) #ifdef SPDLOG_ENABLE_SYSLOG
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0); std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
#endif #endif
#if defined(__ANDROID__)
std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
#endif
// Create and register a logger a single sink
std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
// Create and register a logger with multiple sinks // Create and register a logger with multiple sinks
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks); std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
@ -90,7 +115,8 @@ std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_b
// Create and register a logger with templated sink type // Create and register a logger with templated sink type
// Example: spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt"); // Example:
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
template <typename Sink, typename... Args> template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...); std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
@ -98,32 +124,41 @@ std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
// Register the given logger with the given name // Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger); void register_logger(std::shared_ptr<logger> logger);
// Apply a user defined function on all registered loggers
// Example:
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
void apply_all(std::function<void(std::shared_ptr<logger>)> fun);
// Drop the reference to the given logger // Drop the reference to the given logger
void drop(const std::string &name); void drop(const std::string &name);
// Drop all references // Drop all references from the registry
void drop_all(); void drop_all();
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// Macros to be display source file & line
// Trace & Debug can be switched on/off at compile time for zero cost debug statements. // Trace & Debug can be switched on/off at compile time for zero cost debug statements.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable. // Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
// SPDLOG_TRACE(..) will also print current file and line.
// //
// Example: // Example:
// spdlog::set_level(spdlog::level::debug); // spdlog::set_level(spdlog::level::trace);
// SPDLOG_DEBUG(my_logger, "Some debug message {} {}", 1, 3.2); // SPDLOG_TRACE(my_logger, "some trace message");
// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4);
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#ifdef SPDLOG_TRACE_ON #ifdef SPDLOG_TRACE_ON
#define SPDLOG_TRACE(logger, ...) logger->trace(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")"; #define SPDLOG_STR_H(x) #x
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
#else #else
#define SPDLOG_TRACE(logger, ...) #define SPDLOG_TRACE(logger, ...)
#endif #endif
#ifdef SPDLOG_DEBUG_ON #ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")"; #define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
#else #else
#define SPDLOG_DEBUG(logger, ...) #define SPDLOG_DEBUG(logger, ...)
#endif #endif
@ -132,4 +167,4 @@ void drop_all();
} }
#include "details/spdlog_impl.h" #include <spdlog/details/spdlog_impl.h>

View File

@ -5,21 +5,29 @@
#pragma once #pragma once
///////////////////////////////////////////////////////////////////////////////
// //
// Edit this file to squeeze every last drop of performance out of spdlog. // Edit this file to squeeze more performance, and to customize supported features
// //
///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used. // Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
// This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ. // This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ.
// Uncomment to use it instead of the regular (but slower) clock. // Uncomment to use it instead of the regular clock.
//
// #define SPDLOG_CLOCK_COARSE // #define SPDLOG_CLOCK_COARSE
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment if date/time logging is not needed. // Uncomment if date/time logging is not needed and never appear in the log pattern.
// This will prevent spdlog from quering the clock on each log call. // This will prevent spdlog from quering the clock on each log call.
//
// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined.
// You must set new pattern(spdlog::set_pattern(..") without any date/time in it
//
// #define SPDLOG_NO_DATETIME // #define SPDLOG_NO_DATETIME
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -27,6 +35,9 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern). // Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
// This will prevent spdlog from quering the thread id on each log call. // This will prevent spdlog from quering the thread id on each log call.
//
// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined.
//
// #define SPDLOG_NO_THREAD_ID // #define SPDLOG_NO_THREAD_ID
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -34,12 +45,13 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment if logger name logging is not needed. // Uncomment if logger name logging is not needed.
// This will prevent spdlog from copying the logger name on each log call. // This will prevent spdlog from copying the logger name on each log call.
//
// #define SPDLOG_NO_NAME // #define SPDLOG_NO_NAME
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros. // Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros.
//
// #define SPDLOG_DEBUG_ON // #define SPDLOG_DEBUG_ON
// #define SPDLOG_TRACE_ON // #define SPDLOG_TRACE_ON
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -49,5 +61,43 @@
// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()). // Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()).
// Use only if your code never modifes concurrently the registry. // Use only if your code never modifes concurrently the registry.
// Note that upon creating a logger the registry is modified by spdlog.. // Note that upon creating a logger the registry is modified by spdlog..
//
// #define SPDLOG_NO_REGISTRY_MUTEX // #define SPDLOG_NO_REGISTRY_MUTEX
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid spdlog's usage of atomic log levels
// Use only if your code never modifies a logger's log levels concurrently by different threads.
//
// #define SPDLOG_NO_ATOMIC_LEVELS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable usage of wchar_t for file names on Windows.
//
// #define SPDLOG_WCHAR_FILENAMES
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
//
// #define SPDLOG_EOL ";-)\n"
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
// In this case spdlog will try to include <fmt/format.h> so set your -I flag accordingly.
//
// #define SPDLOG_FMT_EXTERNAL
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable syslog (disabled by default)
//
// #define SPDLOG_ENABLE_SYSLOG
///////////////////////////////////////////////////////////////////////////////