Initial commit

This commit is contained in:
Brendan LE GLAUNEC
2016-05-20 13:43:28 +02:00
committed by Brendan Le Glaunec
parent 2af5f4475e
commit 95276760be
88 changed files with 17719 additions and 0 deletions
+175
View File
@@ -0,0 +1,175 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <vector>
#include <memory>
#include <stream_model.h>
#include <configuration.h>
namespace etix {
namespace cameradar {
//! The interface a cache_manager should implement to be valid
class cache_manager_iface {
public:
virtual ~cache_manager_iface() {}
//! Launches the manager configuration
//! \return false if failed
virtual bool configure(std::shared_ptr<etix::cameradar::configuration> configuration) = 0;
//! get the name of the cache manager
virtual const std::string& get_name() const = 0;
//! Replaces all cached streams by the content of the vector given as
//! parameter
virtual void set_streams(std::vector<etix::cameradar::stream_model> model) = 0;
//! Inserts a single stream to the cache
virtual void update_stream(const etix::cameradar::stream_model& newmodel) = 0;
//! Gets all cached streams
virtual std::vector<etix::cameradar::stream_model> get_streams() const = 0;
//! Gets all valid streams which have been accessed
virtual std::vector<etix::cameradar::stream_model> get_valid_streams() const = 0;
};
class cache_manager_base : public cache_manager_iface {
public:
cache_manager_base() = default;
virtual ~cache_manager_base() = default;
//! Launches the cache manager configuration
//! \return false if failed
virtual bool configure(std::shared_ptr<etix::cameradar::configuration> configuration) = 0;
//! get the name of the cache manager
virtual const std::string& get_name() const = 0;
//! Replaces all cached streams by the content of the vector given as
//! parameter
virtual void set_streams(std::vector<etix::cameradar::stream_model> model) = 0;
//! Updates a single stream to the cache
virtual void update_stream(const etix::cameradar::stream_model& newmodel) = 0;
//! Gets all cached streams
virtual std::vector<etix::cameradar::stream_model> get_streams() const = 0;
//! Gets all valid streams which have been accessed
virtual std::vector<etix::cameradar::stream_model> get_valid_streams() const = 0;
//! Get the manager's instance
cache_manager_base& get_instance();
template <typename I, typename T>
std::shared_ptr<T>
get() {
static_assert(std::is_base_of<cache_manager_base, I>::value,
"I must implement cache_manager_base");
std::shared_ptr<I> cache_manager(dynamic_cast<I*>(this));
if (not cache_manager) return nullptr;
return cache_manager->template get<T>();
}
};
//! The representation of a cache manager
//!
//! This class loads a shared library, and tries to call an extern "C"
//! function which should instanciate a new instance of the plugin.
class cache_manager {
private:
static const std::string PLUGIN_EXT;
static const std::string default_symbol;
//! the name of the cache manager
std::string name;
//! The path where the manager is located
//! should be specified in the configuration file
std::string path;
//! The symbol entry point of the manager to
//! call to create an instance from the shared library
std::string symbol;
//! The handle to the shared library where is stored the manager
void* handle = nullptr;
//! The cache manager instance if it is successfully loaded
cache_manager_iface* ptr = nullptr;
//! Internal function that creates the full path of the cache manager
//!
//! full path is composed of: the path, the name, the string "_cache-manager"
//! and the extension PLUGIN_EXT depending of the platform
std::string make_full_path();
public:
//! Delete constructor
cache_manager() = delete;
//! The manager needs a path and a symbol to be instantiated.
//! The symbol can be changed if the plugin entry point
//! is different than the standard one.
cache_manager(const std::string& path,
const std::string& name,
const std::string& symbol = default_symbol);
// //! Copy constructor
// cache_manager(cache_manager &other);
//! Move constructor
cache_manager(cache_manager&& old);
~cache_manager();
//! Creates the instance of the cache_manager
//!
// \return false if the cache_manager failed to be instantiated or if
// the cache_manager is not a valid cache manager, true otherwise
bool make_instance();
template <typename I, typename T>
std::shared_ptr<T>
get() {
static_assert(std::is_base_of<cache_manager_base, I>::value,
"I must implement plugin_base");
return this->get<I, T>();
}
//! Helper to access internal loaded cache_manager
//!
//! Gives access to the methods of the cache_manager using the operator
//! -> (e.g.: cache_manager->get_name());
cache_manager_iface* operator->();
const cache_manager_iface* operator->() const;
//! helper function to check if a cache_manager is instantiated or not
friend bool operator==(std::nullptr_t nullp, const cache_manager& p);
//! helper function to check if a cache_manager is instantiated or not
friend bool operator==(const cache_manager& p, std::nullptr_t nullp);
//! helper function to check if a cache_manager is instantiated or not
friend bool operator!=(std::nullptr_t nullp, const cache_manager& p);
//! helper function to check if a cache_manager is instantiated or not
friend bool operator!=(const cache_manager& p, std::nullptr_t nullp);
};
}
}
@@ -0,0 +1,30 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <string> // std::string
#include <memory> // std::shared_ptr
#include <configuration.h> // conf
namespace etix {
namespace cameradar {
class cameradar_task {
public:
virtual bool run() const = 0;
};
}
}
@@ -0,0 +1,72 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <string> // std::string
#include <utility> // std::pair
#include <logger.h> // _LOG_
#include <json/value.h> // Json::Value
#include <json/reader.h> // Json::Value
namespace etix {
namespace cameradar {
static const std::string default_configuration_path = "conf/cameradar.conf.json";
static const std::string default_ids_file_path_ = "conf/ids.json";
static const std::string default_urls_file_path_ = "conf/url.json";
struct configuration {
std::string thumbnail_storage_path;
std::string subnets;
std::string rtsp_url_file;
std::string rtsp_ids_file;
std::string ports;
std::string cache_manager_path;
std::string cache_manager_name;
std::vector<std::string> paths;
std::vector<std::string> usernames;
std::vector<std::string> passwords;
Json::Value raw_conf;
configuration() = default;
configuration(const std::string& thumbnail_storage_path,
const std::string& subnets,
const std::string& rtsp_url_file,
const std::string& rtsp_ids_file,
const std::string& cache_manager_path,
const std::string& cache_manager_name,
const std::string& ports = "1-65535")
: thumbnail_storage_path(thumbnail_storage_path)
, subnets(subnets)
, rtsp_url_file(rtsp_url_file)
, rtsp_ids_file(rtsp_ids_file)
, ports(ports)
, cache_manager_path(cache_manager_path)
, cache_manager_name(cache_manager_name) {}
static const std::string name_;
bool load_ids();
bool load_url();
Json::Value get_raw() const;
};
std::pair<bool, std::string> read_file(const std::string& path);
std::pair<bool, configuration> load(const std::string& path);
}
}
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <memory> // std::shared_ptr
#include <logger.h> // LOG
#include <curl/curl.h> // cURL client for discovery
#include <encode.h> // b64
namespace etix {
namespace cameradar {
bool curl_describe(const std::string& path, bool logs);
}
}
+84
View File
@@ -0,0 +1,84 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <list> // sig
#include <memory> // std::shared_ptr
#include <opt_parse.h> // parsing opt
#include <logger.h> // LOG
#include <configuration.h> // conf
#include <thread> // std::thread
#include <chrono> // operator""ms
#include <signal_handler.h> // sig
// All the tasks managed by the dispatcher
#include <tasks/mapping.h>
#include <tasks/parsing.h>
#include <tasks/brutelogs.h>
#include <tasks/brutepath.h>
#include <tasks/thumbnail.h>
#include <tasks/stream_check.h>
#include <tasks/print.h>
namespace etix {
namespace cameradar {
enum class task {
init,
preparation,
mapping,
parsing,
brutepath,
bruteforce,
thumb_generation,
print,
finished
};
class dispatcher {
private:
bool busy;
task current;
std::string nmap_output;
const configuration& conf;
std::shared_ptr<etix::cameradar::cache_manager> cache;
const std::pair<bool, etix::tool::opt_parse>& opts;
std::list<cameradar_task*> queue;
public:
dispatcher() = delete;
dispatcher(const configuration& conf,
std::shared_ptr<etix::cameradar::cache_manager> cache,
const std::pair<bool, etix::tool::opt_parse>& opts)
: busy(false)
, current(task::init)
, nmap_output("scans/scan" + std::to_string(std::chrono::system_clock::to_time_t(
std::chrono::system_clock::now())) +
".xml")
, conf(conf)
, cache(cache)
, opts(opts){};
~dispatcher() = default;
bool
doing_stuff() const {
return this->busy;
}
void do_stuff();
void run();
};
}
}
+39
View File
@@ -0,0 +1,39 @@
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#pragma once
#include <string>
namespace etix {
namespace tool {
namespace encode {
std::string encode64(const std::string& str_to_encode);
std::string decode64(const std::string& str_to_decode);
std::string base64_encode(unsigned char const*, unsigned int len);
std::string base64_decode(std::string const& s);
} //! encode
} //! tool
} //! etix
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <string>
#include <iostream>
#include <mutex>
namespace etix {
namespace tool {
static std::mutex mutex;
//! Format a string with the given arguments
//! same behavior as sprintf.
template <class... Args>
std::string
fmt(const std::string& base, Args... args) {
std::lock_guard<std::mutex> guard(mutex);
static char buf[512];
std::sprintf(buf, base.c_str(), args...);
return std::string(buf);
}
} // tool
} // etix
+47
View File
@@ -0,0 +1,47 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
#include <string>
namespace etix {
namespace tool {
namespace fs {
enum class fs_error { is_dir, is_not_dir, dont_exist };
fs_error is_folder(const std::string& folder);
bool get_or_create_folder(const std::string& folder);
bool create_folder(const std::string& folder);
bool create_recursive_folder(const std::string& folder);
std::string home();
//! this functions take a copy because we need to make some operations on the string
//! for example, we need to apply std::string::pop_back
std::string get_file_folder(std::string full_file_path);
bool copy(const std::string& src, const std::string& dst);
} // fs
} // tool
} // etix
@@ -0,0 +1,25 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <string> // std::string
#include <logger.h> // LOG
#include <stdlib.h> // system
namespace etix {
namespace cameradar {
bool launch_command(const std::string& cmd);
}
}
+116
View File
@@ -0,0 +1,116 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <sstream>
#include <string>
#include "spdlog/spdlog.h"
namespace etix {
namespace tool {
inline std::string
format_output(const std::string& from, const std::string& message) {
auto ss = std::stringstream{};
ss << "(" << from << "): ";
ss << message;
return ss.str();
}
enum class loglevel { DEBUG = 1, INFO = 2, WARN = 4, ERR = 5, CRITICAL = 6 };
class logger {
std::string name;
std::shared_ptr<spdlog::logger> console;
logger(const std::string& plugin)
: name(plugin), console(spdlog::stdout_logger_mt("cameradar")) {}
public:
static logger&
get_instance(const std::string& name = "") {
static logger self(name);
return self;
}
void
set_level(loglevel level) {
switch (level) {
case loglevel::DEBUG: this->console->set_level(spdlog::level::level_enum::debug); break;
case loglevel::INFO: this->console->set_level(spdlog::level::level_enum::info); break;
case loglevel::WARN: this->console->set_level(spdlog::level::level_enum::warn); break;
case loglevel::ERR: this->console->set_level(spdlog::level::level_enum::err); break;
case loglevel::CRITICAL:
this->console->set_level(spdlog::level::level_enum::critical);
break;
}
}
std::string
get_name() const {
return this->name;
}
static void
info(const std::string& message) {
etix::tool::logger::get_instance().console->info(message);
}
static void
warn(const std::string& message) {
etix::tool::logger::get_instance().console->warn(message);
}
static void
err(const std::string& message) {
etix::tool::logger::get_instance().console->error(message);
}
static void
crit(const std::string& message) {
etix::tool::logger::get_instance().console->critical(message);
}
static void
debug(const std::string& message) {
etix::tool::logger::get_instance().console->debug(message);
}
};
}
}
// Should be replaced to calls to spdlog::logger::getlogger(const std::string&
// name)
#define LOG_WARN_(message, from) \
etix::tool::logger::get_instance().warn(etix::tool::format_output( \
std::string(from) + "::" + __FUNCTION__ + ":" + std::to_string(__LINE__), message))
#define LOG_ERR_(message, from) \
etix::tool::logger::get_instance().err(etix::tool::format_output( \
std::string(from) + "::" + __FUNCTION__ + ":" + std::to_string(__LINE__), message))
#define LOG_DEBUG_(message, from) \
etix::tool::logger::get_instance().debug(etix::tool::format_output( \
std::string(from) + "::" + __FUNCTION__ + ":" + std::to_string(__LINE__), message))
#define LOG_INFO_(message, from) \
etix::tool::logger::get_instance().info(etix::tool::format_output( \
std::string(from) + "::" + __FUNCTION__ + ":" + std::to_string(__LINE__), message))
#define LOG_CRIT_(message, from) \
etix::tool::logger::get_instance().crit(etix::tool::format_output( \
std::string(from) + "::" + __FUNCTION__ + ":" + std::to_string(__LINE__), message))
+138
View File
@@ -0,0 +1,138 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <string> // for string
#include <unordered_map> // for unordered_map
#include <utility> // for pair
#include <vector> // for vector
namespace etix {
namespace tool {
//! Parse command line arguments
class opt_parse {
private:
//! An argumetn representation to be passed to the program
struct opt_param {
//! is it required
bool required;
//! Does he needs arguments
bool need_arg;
//! What is its name
std::string name;
//! Description
std::string desc;
//! the argument of the arguments !
std::string argument;
bool is_passed = false;
opt_param(bool required, bool need_arg, std::string name, std::string desc)
: required(required), need_arg(need_arg), name(name), desc(desc) {}
};
//! Map of the different possibles argument as string and their
//! rertpresntation
std::unordered_map<std::string, opt_param> params;
//! The total count of arguments for this program
int argc;
//! The list of arguments as a String
char** argv;
//! The total count of params
int params_cnt = 0;
public:
//! An iterator to iterate over all the arguments of
//! the program
class iterator {
private:
//! The arguments vector and their argumetns
std::vector<std::pair<std::string, std::string>> args;
unsigned int opt_pos = 0;
public:
iterator(std::vector<std::pair<std::string, std::string>> args, unsigned int opt_pos)
: args(args), opt_pos(opt_pos) {}
iterator operator++() {
this->opt_pos += 1;
return *this;
}
std::pair<std::string, std::string>& operator*() { return this->args.at(this->opt_pos); }
bool operator==(const iterator& rhs) const { return this->opt_pos == rhs.opt_pos; }
bool operator!=(const iterator& rhs) const { return this->opt_pos != rhs.opt_pos; }
};
opt_parse() = delete;
//! \param argc Total count of arguements
//! \param argv Cmdline arguments from program startup
opt_parse(int argc, char* argv[]);
~opt_parse();
//! Add a argument required for your program
//!
//! If the specified argument is not given in cmdline, a error will be
//! generated
//! \param name The name of the parameter as a string (e.g "-l")
//! \param desc A description that will be used by the function `print_help`
//! \param need_arg Does the argument require a parameter
void required(const std::string& name, const std::string& desc = "", bool need_arg = true);
//! Add an optional argument for your program
//!
//! If the specified argument is not given in cmdline, a error will be
//! generated
//! \param name The name of the parameter as a string (e.g "-l")
//! \param desc A description that will be used by the function `print_help`
//! \param need_arg Does the argument require a parameter
void optional(const std::string& name, const std::string& desc = "", bool need_arg = true);
//! Process the parsing of the arguments
bool execute();
//! \return an iterator on the begin of the arguments
iterator begin() const;
//! \return the iterator on the end of the arguments
iterator end() const;
//! Print the usage using the parameter setted when referencing the arguments
//! for the program
void print_usage() const;
//! Print an help message generated using all the specified arguments
void print_help() const;
//! Is there on the parameters (missing parameter ? unknows ? missing
//! arguments ?)
//! \return true if there is error, false otherwise
bool has_error() const;
//! Does the option exist or not ?
//! \param opt The name of the option to check
//! \return true if the param exist, false otherwise
bool exist(const std::string& opt) const;
//! Acces to an argument from its name
//! \param opt The name of the option to check
//! \return the the argument of the param as a string
std::string operator[](const std::string& opt) const;
};
} // tool
} // etix
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <string> // for string
#include <stream_model.h> // for stream_model
namespace etix {
namespace cameradar {
const std::string make_path(const stream_model& model);
}
}
@@ -0,0 +1,65 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <csignal> // sigint
#include <iostream> // stc::cout
#include <assert.h> // assert
namespace etix {
namespace cameradar {
enum class stop_priority { running, stop, force_stop };
class event_handler {
public:
event_handler(void) : ss(stop_priority::running) {}
virtual int
handle_signal(int signum) {
assert(signum == SIGINT);
std::cout << "\b\b\b\033[K";
if (this->ss == stop_priority::running)
this->ss = stop_priority::stop;
else
this->ss = stop_priority::force_stop;
return 0;
}
etix::cameradar::stop_priority
should_stop(void) const {
return this->ss;
}
private:
stop_priority ss;
};
class signal_handler {
private:
signal_handler(void);
signal_handler(const signal_handler&);
signal_handler& operator=(const signal_handler&);
static void call_handler(int signum);
static event_handler handler;
public:
static signal_handler& instance(void);
etix::cameradar::stop_priority should_stop(void) const;
};
}
}
@@ -0,0 +1,71 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// Very fast asynchronous logger (millions of logs per second on an average desktop)
// Uses pre allocated lockfree queue for maximum throughput even under large number of threads.
// Creates a single back thread to pop messages from the queue and log them.
//
// Upon each log write the logger:
// 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)
// 3. will throw spdlog_ex upon log exceptions
// Upong destruction, logs all remaining messages in the queue before destructing..
#include <chrono>
#include <functional>
#include "common.h"
#include "logger.h"
#include "spdlog.h"
namespace spdlog {
namespace details {
class async_log_helper;
}
class async_logger : public logger {
public:
template <class It>
async_logger(
const std::string& name,
const It& begin,
const It& end,
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());
async_logger(
const std::string& logger_name,
sinks_init_list sinks,
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());
async_logger(
const std::string& logger_name,
sink_ptr single_sink,
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());
void flush() override;
protected:
void _log_msg(details::log_msg& msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
void _set_pattern(const std::string& pattern) override;
private:
std::unique_ptr<details::async_log_helper> _async_log_helper;
};
}
#include "./details/async_logger_impl.h"
@@ -0,0 +1,88 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <string>
#include <initializer_list>
#include <chrono>
#include <memory>
// visual studio does not support noexcept yet
#ifndef _MSC_VER
#define SPDLOG_NOEXCEPT noexcept
#else
#define SPDLOG_NOEXCEPT throw()
#endif
namespace spdlog {
class formatter;
namespace sinks {
class sink;
}
// Common types across the lib
using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list<sink_ptr>;
using formatter_ptr = std::shared_ptr<spdlog::formatter>;
// Log level enum
namespace level {
typedef enum {
trace = 0,
debug = 1,
info = 2,
notice = 3,
warn = 4,
err = 5,
critical = 6,
alert = 7,
emerg = 8,
off = 9
} level_enum;
static const char* level_names[]{ "trace", "debug", "info", "notice", "warning",
"error", "critical", "alert", "emerg", "off" };
static const char* short_level_names[]{ "T", "D", "I", "N", "W", "E", "C", "A", "M", "O" };
inline const char*
to_str(spdlog::level::level_enum l) {
return level_names[l];
}
inline const char*
to_short_str(spdlog::level::level_enum l) {
return short_level_names[l];
}
} // level
//
// Async overflow policy - block by default.
//
enum class async_overflow_policy {
block_retry, // Block / yield / sleep until message can be enqueued
discard_log_msg // Discard the message it enqueue fails
};
//
// Log exception
//
class spdlog_ex : public std::exception {
public:
explicit spdlog_ex(const std::string& msg) : _msg(msg) {}
const char*
what() const SPDLOG_NOEXCEPT override {
return _msg.c_str();
}
private:
std::string _msg;
};
} // spdlog
@@ -0,0 +1,313 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// async log helper :
// Process logs asynchronously using a back thread.
//
// If the internal queue of log messages reaches its max size,
// then the client call will block until there is more room.
//
// If the back thread throws during logging, a spdlog::spdlog_ex exception
// will be thrown in client's thread when tries to log the next message
#pragma once
#include <chrono>
#include <thread>
#include <functional>
#include "../common.h"
#include "../sinks/sink.h"
#include "./mpmc_bounded_q.h"
#include "./log_msg.h"
#include "./format.h"
#include "./os.h"
namespace spdlog {
namespace details {
class async_log_helper {
// Async msg to move to/from the queue
// Movable only. should never be copied
enum class async_msg_type { log, flush, terminate };
struct async_msg {
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
std::string txt;
async_msg_type msg_type;
async_msg() = default;
~async_msg() = default;
async_msg(async_msg&& other) SPDLOG_NOEXCEPT : logger_name(std::move(other.logger_name)),
level(std::move(other.level)),
time(std::move(other.time)),
txt(std::move(other.txt)),
msg_type(std::move(other.msg_type)) {}
async_msg(async_msg_type m_type) : msg_type(m_type){};
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT {
logger_name = std::move(other.logger_name);
level = other.level;
time = std::move(other.time);
thread_id = other.thread_id;
txt = std::move(other.txt);
msg_type = other.msg_type;
return *this;
}
// never copy or assign. should only be moved..
async_msg(const async_msg&) = delete;
async_msg& operator=(async_msg& other) = delete;
// construct from log_msg
async_msg(const details::log_msg& m)
: logger_name(m.logger_name)
, level(m.level)
, time(m.time)
, thread_id(m.thread_id)
, txt(m.raw.data(), m.raw.size())
, msg_type(async_msg_type::log) {}
// copy into log_msg
void
fill_log_msg(log_msg& msg) {
msg.clear();
msg.logger_name = logger_name;
msg.level = level;
msg.time = time;
msg.thread_id = thread_id;
msg.raw << txt;
}
};
public:
using item_type = async_msg;
using q_type = details::mpmc_bounded_queue<item_type>;
using clock = std::chrono::steady_clock;
async_log_helper(
formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
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());
void log(const details::log_msg& msg);
// stop logging and join the back thread
~async_log_helper();
void set_formatter(formatter_ptr);
void flush();
private:
formatter_ptr _formatter;
std::vector<std::shared_ptr<sinks::sink>> _sinks;
// queue of messages to log
q_type _q;
bool _flush_requested;
bool _terminate_requested;
// last exception thrown from the worker thread
std::shared_ptr<spdlog_ex> _last_workerthread_ex;
// overflow policy
const async_overflow_policy _overflow_policy;
// worker thread warmup callback - one can set thread priority, affinity, etc
const std::function<void()> _worker_warmup_cb;
// auto periodic sink flush parameter
const std::chrono::milliseconds _flush_interval_ms;
// worker thread
std::thread _worker_thread;
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
void worker_loop();
// pop next message from the queue and process it. will set the last_pop to the pop time
// return false if termination of the queue is required
bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush);
void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
// 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);
};
}
}
///////////////////////////////////////////////////////////////////////////////
// async_sink class implementation
///////////////////////////////////////////////////////////////////////////////
inline spdlog::details::async_log_helper::async_log_helper(
formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms)
: _formatter(formatter)
, _sinks(sinks)
, _q(queue_size)
, _flush_requested(false)
, _terminate_requested(false)
, _overflow_policy(overflow_policy)
, _worker_warmup_cb(worker_warmup_cb)
, _flush_interval_ms(flush_interval_ms)
, _worker_thread(&async_log_helper::worker_loop, this) {}
// Send to the worker thread termination message(level=off)
// and wait for it to finish gracefully
inline spdlog::details::async_log_helper::~async_log_helper() {
try {
push_msg(async_msg(async_msg_type::terminate));
_worker_thread.join();
} catch (...) // don't crash in destructor
{}
}
// Try to push and block until succeeded
inline void
spdlog::details::async_log_helper::log(const details::log_msg& msg) {
push_msg(async_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) {
throw_if_bad_worker();
if (!_q.enqueue(std::move(new_msg)) &&
_overflow_policy != async_overflow_policy::discard_log_msg) {
auto last_op_time = details::os::now();
auto now = last_op_time;
do {
now = details::os::now();
sleep_or_yield(now, last_op_time);
} while (!_q.enqueue(std::move(new_msg)));
}
}
inline void
spdlog::details::async_log_helper::flush() {
push_msg(async_msg(async_msg_type::flush));
}
inline void
spdlog::details::async_log_helper::worker_loop() {
try {
if (_worker_warmup_cb) _worker_warmup_cb();
auto last_pop = details::os::now();
auto last_flush = last_pop;
while (process_next_msg(last_pop, last_flush))
;
} catch (const std::exception& ex) {
_last_workerthread_ex = std::make_shared<spdlog_ex>(
std::string("async_logger worker thread exception: ") + ex.what());
} catch (...) {
_last_workerthread_ex = std::make_shared<spdlog_ex>("async_logger worker thread exception");
}
}
// process next message in the queue
// return true if this thread should still be active (no msg with level::off was received)
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;
log_msg incoming_log_msg;
if (_q.dequeue(incoming_async_msg)) {
last_pop = details::os::now();
switch (incoming_async_msg.msg_type) {
case async_msg_type::flush: _flush_requested = true; break;
case async_msg_type::terminate:
_flush_requested = true;
_terminate_requested = true;
break;
default:
incoming_async_msg.fill_log_msg(incoming_log_msg);
_formatter->format(incoming_log_msg);
for (auto& s : _sinks) s->log(incoming_log_msg);
}
return true;
}
// Handle empty queue..
// This is the only place where the queue can terminate or flush to avoid losing messages
// already in the queue
else {
auto now = details::os::now();
handle_flush_interval(now, last_flush);
sleep_or_yield(now, last_pop);
return !_terminate_requested;
}
}
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);
if (should_flush) {
for (auto& s : _sinks) s->flush();
now = last_flush = details::os::now();
_flush_requested = false;
}
}
inline void
spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) {
_formatter = msg_formatter;
}
// sleep,yield or return immediatly using 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) {
using std::chrono::milliseconds;
using namespace std::this_thread;
auto time_since_op = now - last_op_time;
// spin upto 1 ms
if (time_since_op <= milliseconds(1)) return;
// yield upto 10ms
if (time_since_op <= milliseconds(10)) return yield();
// sleep for half of duration since last op
if (time_since_op <= milliseconds(100)) return sleep_for(time_since_op / 2);
return sleep_for(milliseconds(100));
}
// throw if the worker thread threw an exception or not active
inline void
spdlog::details::async_log_helper::throw_if_bad_worker() {
if (_last_workerthread_ex) {
auto ex = std::move(_last_workerthread_ex);
throw * ex;
}
}
@@ -0,0 +1,72 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread
#include "./async_log_helper.h"
template <class It>
inline spdlog::async_logger::async_logger(const std::string& logger_name,
const It& begin,
const It& end,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms)
: 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)) {}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sinks_init_list sinks,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
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) {}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms)
: async_logger(logger_name,
{ single_sink },
queue_size,
overflow_policy,
worker_warmup_cb,
flush_interval_ms) {}
inline void
spdlog::async_logger::flush() {
_async_log_helper->flush();
}
inline void
spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) {
_formatter = msg_formatter;
_async_log_helper->set_formatter(_formatter);
}
inline void
spdlog::async_logger::_set_pattern(const std::string& pattern) {
_formatter = std::make_shared<pattern_formatter>(pattern);
_async_log_helper->set_formatter(_formatter);
}
inline void
spdlog::async_logger::_log_msg(details::log_msg& msg) {
_async_log_helper->log(msg);
}
@@ -0,0 +1,117 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// Helper class for file sink
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
// Can be set to auto flush on every line
// Throw spdlog_ex exception on errors
#include <string>
#include <thread>
#include <chrono>
#include "os.h"
#include "log_msg.h"
namespace spdlog {
namespace details {
class file_helper {
public:
const int open_tries = 5;
const int open_interval = 10;
explicit file_helper(bool force_flush) : _fd(nullptr), _force_flush(force_flush) {}
file_helper(const file_helper&) = delete;
file_helper& operator=(const file_helper&) = delete;
~file_helper() { close(); }
void
open(const std::string& fname, bool truncate = false) {
close();
const char* mode = truncate ? "wb" : "ab";
_filename = fname;
for (int tries = 0; tries < open_tries; ++tries) {
if (!os::fopen_s(&_fd, fname, mode)) return;
std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
}
throw spdlog_ex("Failed opening file " + fname + " for writing");
}
void
reopen(bool truncate) {
if (_filename.empty()) throw spdlog_ex("Failed re opening file - was not opened before");
open(_filename, truncate);
}
void
flush() {
std::fflush(_fd);
}
void
close() {
if (_fd) {
std::fclose(_fd);
_fd = nullptr;
}
}
void
write_string(const std::string& msg) {
if (std::fwrite(msg.c_str(), 1, msg.size(), _fd) != msg.size())
throw spdlog_ex("Failed writing to file " + _filename);
if (_force_flush) std::fflush(_fd);
}
void
write(const log_msg& msg) {
size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
throw spdlog_ex("Failed writing to file " + _filename);
if (_force_flush) std::fflush(_fd);
}
long
size() {
if (!_fd) throw spdlog_ex("Cannot use size() on closed file " + _filename);
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 {
return _filename;
}
static bool
file_exists(const std::string& name) {
return os::file_exists(name);
}
private:
FILE* _fd;
std::string _filename;
bool _force_flush;
};
}
}
@@ -0,0 +1,934 @@
/*
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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,156 @@
//
// 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
@@ -0,0 +1,79 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <thread>
#include "../common.h"
#include "./format.h"
namespace spdlog
{
namespace details
{
struct log_msg
{
log_msg() = default;
log_msg(level::level_enum l):
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())
raw << fmt::BasicStringRef<char>(other.raw.data(), other.raw.size());
if (other.formatted.size())
formatted << fmt::BasicStringRef<char>(other.formatted.data(), other.formatted.size());
}
log_msg(log_msg&& other) :
logger_name(std::move(other.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();
}
log_msg& operator=(log_msg&& other)
{
if (this == &other)
return *this;
logger_name = std::move(other.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;
log_clock::time_point time;
size_t thread_id;
fmt::MemoryWriter raw;
fmt::MemoryWriter formatted;
};
}
}
@@ -0,0 +1,299 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "./line_logger.h"
// create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one
template<class It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end) :
_name(logger_name),
_sinks(begin, end),
_formatter(std::make_shared<pattern_formatter>("%+"))
{
// no support under vs2013 for member initialization for std::atomic
_level = level::info;
}
// ctor with sinks as init list
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list) :
logger(logger_name, sinks_list.begin(), sinks_list.end()) {}
// ctor with single sink
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink) :
logger(logger_name,
{
single_sink
}) {}
inline spdlog::logger::~logger() = default;
inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
{
_set_formatter(msg_formatter);
}
inline void spdlog::logger::set_pattern(const std::string& pattern)
{
_set_pattern(pattern);
}
//
// log only if given level>=logger's log level
//
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl, const char* fmt, const Args&... args)
{
bool msg_enabled = should_log(lvl);
details::line_logger l(this, lvl, msg_enabled);
l.write(fmt, args...);
return l;
}
inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl)
{
return details::line_logger(this, lvl, should_log(lvl));
}
template<typename T>
inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl, const T& msg)
{
bool msg_enabled = should_log(lvl);
details::line_logger l(this, lvl, msg_enabled);
l << msg;
return l;
}
//
// logger.info(cppformat_string, arg1, arg2, arg3, ...) call style
//
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::trace(const char* fmt, const Args&... args)
{
return _log_if_enabled(level::trace, fmt, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::debug(const char* fmt, const Args&... args)
{
return _log_if_enabled(level::debug, fmt, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::info(const char* fmt, const Args&... args)
{
return _log_if_enabled(level::info, fmt, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::notice(const char* fmt, const Args&... args)
{
return _log_if_enabled(level::notice, fmt, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::warn(const char* fmt, const Args&... args)
{
return _log_if_enabled(level::warn, 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>
inline spdlog::details::line_logger spdlog::logger::info(const T& msg)
{
return _log_if_enabled(level::info, msg);
}
template<typename T>
inline spdlog::details::line_logger spdlog::logger::notice(const T& msg)
{
return _log_if_enabled(level::notice, msg);
}
template<typename T>
inline spdlog::details::line_logger spdlog::logger::warn(const T& msg)
{
return _log_if_enabled(level::warn, msg);
}
template<typename T>
inline spdlog::details::line_logger spdlog::logger::error(const T& msg)
{
return _log_if_enabled(level::err, 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
//
inline const std::string& spdlog::logger::name() const
{
return _name;
}
inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
{
_level.store(log_level);
}
inline spdlog::level::level_enum spdlog::logger::level() const
{
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
}
inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
{
return msg_level >= _level.load(std::memory_order_relaxed);
}
//
// 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)
{
_formatter->format(msg);
for (auto &sink : _sinks)
sink->log(msg);
}
inline void spdlog::logger::_set_pattern(const std::string& pattern)
{
_formatter = std::make_shared<pattern_formatter>(pattern);
}
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
}
inline void spdlog::logger::flush()
{
for (auto& sink : _sinks)
sink->flush();
}
@@ -0,0 +1,157 @@
/*
A modified version of Bounded MPMC queue by Dmitry Vyukov.
Original code from:
http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
licensed by Dmitry Vyukov under the terms below:
Simplified BSD license
Copyright (c) 2010-2011 Dmitry Vyukov. 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 DMITRY VYUKOV "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 DMITRY VYUKOV 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.
The views and conclusions contained in the software and documentation are those of the authors and
should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov.
*/
/*
The code in its current form adds the license below:
Copyright(c) 2015 Gabi Melman.
Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
#pragma once
#include <atomic>
#include "../common.h"
namespace spdlog
{
namespace details
{
template<typename T>
class mpmc_bounded_queue
{
public:
using item_type = T;
mpmc_bounded_queue(size_t buffer_size)
: buffer_(new cell_t [buffer_size]),
buffer_mask_(buffer_size - 1)
{
//queue size must be power of two
if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
throw spdlog_ex("async logger queue size must be power of two");
for (size_t i = 0; i != buffer_size; i += 1)
buffer_[i].sequence_.store(i, std::memory_order_relaxed);
enqueue_pos_.store(0, std::memory_order_relaxed);
dequeue_pos_.store(0, std::memory_order_relaxed);
}
~mpmc_bounded_queue()
{
delete [] buffer_;
}
bool enqueue(T&& data)
{
cell_t* cell;
size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
for (;;)
{
cell = &buffer_[pos & buffer_mask_];
size_t seq = cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
if (dif == 0)
{
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
break;
}
else if (dif < 0)
{
return false;
}
else
{
pos = enqueue_pos_.load(std::memory_order_relaxed);
}
}
cell->data_ = std::move(data);
cell->sequence_.store(pos + 1, std::memory_order_release);
return true;
}
bool dequeue(T& data)
{
cell_t* cell;
size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
for (;;)
{
cell = &buffer_[pos & buffer_mask_];
size_t seq =
cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
if (dif == 0)
{
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
break;
}
else if (dif < 0)
return false;
else
pos = dequeue_pos_.load(std::memory_order_relaxed);
}
data = std::move(cell->data_);
cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release);
return true;
}
private:
struct cell_t
{
std::atomic<size_t> sequence_;
T data_;
};
static size_t const cacheline_size = 64;
typedef char cacheline_pad_t [cacheline_size];
cacheline_pad_t pad0_;
cell_t* const buffer_;
size_t const buffer_mask_;
cacheline_pad_t pad1_;
std::atomic<size_t> enqueue_pos_;
cacheline_pad_t pad2_;
std::atomic<size_t> dequeue_pos_;
cacheline_pad_t pad3_;
mpmc_bounded_queue(mpmc_bounded_queue const&);
void operator = (mpmc_bounded_queue const&);
};
} // ns details
} // ns spdlog
@@ -0,0 +1,24 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// null, no cost mutex
namespace spdlog
{
namespace details
{
struct null_mutex
{
void lock() {}
void unlock() {}
bool try_lock()
{
return true;
}
};
}
}
@@ -0,0 +1,215 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include<string>
#include<cstdio>
#include<ctime>
#ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
#ifdef __MINGW32__
#include <share.h>
#endif
#elif __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#include <sys/stat.h>
#include <unistd.h>
#else
#include <thread>
#endif
#include "../common.h"
namespace spdlog
{
namespace details
{
namespace os
{
inline spdlog::log_clock::time_point now()
{
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
timespec ts;
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
return std::chrono::time_point<log_clock, typename log_clock::duration>(
std::chrono::duration_cast<typename log_clock::duration>(
std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
#else
return log_clock::now();
#endif
}
inline std::tm localtime(const std::time_t &time_tt)
{
#ifdef _WIN32
std::tm tm;
localtime_s(&tm, &time_tt);
#else
std::tm tm;
localtime_r(&time_tt, &tm);
#endif
return tm;
}
inline std::tm localtime()
{
std::time_t now_t = time(nullptr);
return localtime(now_t);
}
inline std::tm gmtime(const std::time_t &time_tt)
{
#ifdef _WIN32
std::tm tm;
gmtime_s(&tm, &time_tt);
#else
std::tm tm;
gmtime_r(&time_tt, &tm);
#endif
return tm;
}
inline std::tm gmtime()
{
std::time_t now_t = time(nullptr);
return gmtime(now_t);
}
inline bool operator==(const std::tm& tm1, const std::tm& tm2)
{
return (tm1.tm_sec == tm2.tm_sec &&
tm1.tm_min == tm2.tm_min &&
tm1.tm_hour == tm2.tm_hour &&
tm1.tm_mday == tm2.tm_mday &&
tm1.tm_mon == tm2.tm_mon &&
tm1.tm_year == tm2.tm_year &&
tm1.tm_isdst == tm2.tm_isdst);
}
inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
{
return !(tm1 == tm2);
}
#ifdef _WIN32
inline const char* eol()
{
return "\r\n";
}
#else
constexpr inline const char* eol()
{
return "\n";
}
#endif
#ifdef _WIN32
inline unsigned short eol_size()
{
return 2;
}
#else
constexpr inline unsigned short eol_size()
{
return 1;
}
#endif
//fopen_s on non windows for writing
inline int fopen_s(FILE** fp, const std::string& filename, const char* mode)
{
#ifdef _WIN32
*fp = _fsopen((filename.c_str()), mode, _SH_DENYWR);
return *fp == nullptr;
#else
*fp = fopen((filename.c_str()), mode);
return *fp == nullptr;
#endif
}
//Return if file exists
inline bool file_exists(const std::string& filename)
{
#ifdef _WIN32
auto attribs = GetFileAttributesA(filename.c_str());
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#elif __linux__
struct stat buffer;
return (stat (filename.c_str(), &buffer) == 0);
#else
auto *file = fopen(filename.c_str(), "r");
if (file != nullptr)
{
fclose(file);
return true;
}
return false;
#endif
}
//Return utc offset in minutes or throw spdlog_ex on failure
inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
{
#ifdef _WIN32
#if _WIN32_WINNT < _WIN32_WINNT_WS08
TIME_ZONE_INFORMATION tzinfo;
auto rv = GetTimeZoneInformation(&tzinfo);
#else
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
auto rv = GetDynamicTimeZoneInformation(&tzinfo);
#endif
if (rv == TIME_ZONE_ID_INVALID)
throw spdlog::spdlog_ex("Failed getting timezone info. Last error: " + GetLastError());
int offset = -tzinfo.Bias;
if (tm.tm_isdst)
offset -= tzinfo.DaylightBias;
else
offset -= tzinfo.StandardBias;
return offset;
#else
return static_cast<int>(tm.tm_gmtoff / 60);
#endif
}
//Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
inline size_t thread_id()
{
#ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId());
#elif __linux__
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
# define SYS_gettid __NR_gettid
# endif
return static_cast<size_t>(syscall(SYS_gettid));
#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()));
#endif
}
} //os
} //details
} //spdlog
@@ -0,0 +1,625 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <string>
#include <chrono>
#include <memory>
#include <vector>
#include <thread>
#include "../formatter.h"
#include "./log_msg.h"
#include "./os.h"
namespace spdlog
{
namespace details
{
class flag_formatter
{
public:
virtual ~flag_formatter() {}
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
};
///////////////////////////////////////////////////////////////////////
// name & level pattern appenders
///////////////////////////////////////////////////////////////////////
namespace
{
class name_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << msg.logger_name;
}
};
}
// log level appender
class level_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << level::to_str(msg.level);
}
};
// short log level appender
class short_level_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << level::to_short_str(msg.level);
}
};
///////////////////////////////////////////////////////////////////////
// Date time pattern appenders
///////////////////////////////////////////////////////////////////////
static const char* ampm(const tm& t)
{
return t.tm_hour >= 12 ? "PM" : "AM";
}
static int to12h(const tm& t)
{
return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
}
//Abbreviated weekday name
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
class a_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << days[tm_time.tm_wday];
}
};
//Full weekday name
static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
class A_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << full_days[tm_time.tm_wday];
}
};
//Abbreviated month
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
class b_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted<< months[tm_time.tm_mon];
}
};
//Full month name
static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
class B_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << full_months[tm_time.tm_mon];
}
};
//write 2 ints seperated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
return w;
}
//write 3 ints seperated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
return w;
}
//Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
}
};
// year - 2 digit
class C_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
}
};
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
class D_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
}
};
// year - 4 digit
class Y_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << tm_time.tm_year + 1900;
}
};
// month 1-12
class m_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
}
};
// day of month 1-31
class d_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
}
};
// hours in 24 format 0-23
class H_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
}
};
// hours in 12 format 1-12
class I_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
}
};
// minutes 0-59
class M_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
}
};
// seconds 0-59
class S_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
}
};
// milliseconds
class e_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0');
}
};
// microseconds
class f_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0');
}
};
// nanoseconds
class F_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0');
}
};
// AM/PM
class p_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << ampm(tm_time);
}
};
// 12 hour clock 02:55:02 pm
class r_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
}
};
// 24-hour HH:MM time, equivalent to %H:%M
class R_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
}
};
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':');
}
};
// ISO 8601 offset from UTC in timezone (+-HH:MM)
class z_formatter :public flag_formatter
{
public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
z_formatter() :_last_update(std::chrono::seconds(0)) {}
z_formatter(const z_formatter&) = delete;
z_formatter& operator=(const z_formatter&) = delete;
void format(details::log_msg& msg, const std::tm& tm_time) override
{
#ifdef _WIN32
int total_minutes = get_cached_offset(msg, tm_time);
#else
// No need to chache under gcc,
// it is very fast (already stored in tm.tm_gmtoff)
int total_minutes = os::utc_minutes_offset(tm_time);
#endif
int h = total_minutes / 60;
int m = total_minutes % 60;
if (h >= 0) //minus sign will be printed anyway if negative
{
msg.formatted << '+';
}
pad_n_join(msg.formatted, h, m, ':');
}
private:
log_clock::time_point _last_update;
int _offset_minutes;
std::mutex _mutex;
int get_cached_offset(const log_msg& msg, const std::tm& tm_time)
{
using namespace std::chrono;
std::lock_guard<std::mutex> l(_mutex);
if (msg.time - _last_update >= cache_refresh)
{
_offset_minutes = os::utc_minutes_offset(tm_time);
_last_update = msg.time;
}
return _offset_minutes;
}
};
//Thread id
class t_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << msg.thread_id;
}
};
class v_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
}
};
class ch_formatter :public flag_formatter
{
public:
explicit ch_formatter(char ch) : _ch(ch)
{}
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << _ch;
}
private:
char _ch;
};
//aggregate user chars to display as is
class aggregate_formatter :public flag_formatter
{
public:
aggregate_formatter()
{}
void add_ch(char ch)
{
_str += ch;
}
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << _str;
}
private:
std::string _str;
};
// Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
class full_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
#ifndef SPDLOG_NO_DATETIME
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
/* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ",
tm_time.tm_year + 1900,
tm_time.tm_mon + 1,
tm_time.tm_mday,
tm_time.tm_hour,
tm_time.tm_min,
tm_time.tm_sec,
static_cast<int>(millis),
msg.logger_name,
level::to_str(msg.level),
msg.raw.str());*/
// Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' '
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
//no datetime needed
#else
(void)tm_time;
#endif
#ifndef SPDLOG_NO_NAME
msg.formatted << '[' << msg.logger_name << "] ";
#endif
msg.formatted << '[' << level::to_str(msg.level) << "] ";
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
}
};
}
}
///////////////////////////////////////////////////////////////////////////////
// pattern_formatter inline impl
///////////////////////////////////////////////////////////////////////////////
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern)
{
compile_pattern(pattern);
}
inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
{
auto end = pattern.end();
std::unique_ptr<details::aggregate_formatter> user_chars;
for (auto it = pattern.begin(); it != end; ++it)
{
if (*it == '%')
{
if (user_chars) //append user chars found so far
_formatters.push_back(std::move(user_chars));
if (++it != end)
handle_flag(*it);
else
break;
}
else // chars not following the % sign should be displayed as is
{
if (!user_chars)
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
user_chars->add_ch(*it);
}
}
if (user_chars) //append raw chars found so far
{
_formatters.push_back(std::move(user_chars));
}
}
inline void spdlog::pattern_formatter::handle_flag(char flag)
{
switch (flag)
{
// logger name
case 'n':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
break;
case 'l':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
break;
case 'L':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
break;
case('t') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
break;
case('v') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
break;
case('a') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
break;
case('A') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
break;
case('b') :
case('h') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
break;
case('B') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
break;
case('c') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
break;
case('C') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
break;
case('Y') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
break;
case('D') :
case('x') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
break;
case('m') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
break;
case('d') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
break;
case('H') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
break;
case('I') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
break;
case('M') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
break;
case('S') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
break;
case('e') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
break;
case('f') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
break;
case('F') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
break;
case('p') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
break;
case('r') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
break;
case('R') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
break;
case('T') :
case('X') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
break;
case('z') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
break;
case ('+'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
break;
default: //Unkown flag appears as is
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
break;
}
}
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{
try
{
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
for (auto &f : _formatters)
{
f->format(msg, tm_time);
}
//write eol
msg.formatted << details::os::eol();
}
catch(const fmt::FormatError& e)
{
throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what()));
}
}
@@ -0,0 +1,162 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// Loggers registy of unique name->logger pointer
// An attempt to create a logger with an already existing name will be ignored
// If user requests a non existing logger, nullptr will be returned
// This class is thread safe
#include <string>
#include <mutex>
#include <unordered_map>
#include <functional>
#include "./null_mutex.h"
#include "../logger.h"
#include "../async_logger.h"
#include "../common.h"
namespace spdlog
{
namespace details
{
template <class Mutex> class registry_t
{
public:
void register_logger(std::shared_ptr<logger> logger)
{
std::lock_guard<Mutex> lock(_mutex);
register_logger_impl(logger);
}
std::shared_ptr<logger> get(const std::string& logger_name)
{
std::lock_guard<Mutex> lock(_mutex);
auto found = _loggers.find(logger_name);
return found == _loggers.end() ? nullptr : found->second;
}
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> new_logger;
std::lock_guard<Mutex> lock(_mutex);
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);
else
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
if (_formatter)
new_logger->set_formatter(_formatter);
new_logger->set_level(_level);
register_logger_impl(new_logger);
return new_logger;
}
void drop(const std::string& logger_name)
{
std::lock_guard<Mutex> lock(_mutex);
_loggers.erase(logger_name);
}
void drop_all()
{
std::lock_guard<Mutex> lock(_mutex);
_loggers.clear();
}
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
{
return create(logger_name, sinks.begin(), sinks.end());
}
std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
{
return create(logger_name, { sink });
}
void formatter(formatter_ptr f)
{
std::lock_guard<Mutex> lock(_mutex);
_formatter = f;
for (auto& l : _loggers)
l.second->set_formatter(_formatter);
}
void set_pattern(const std::string& pattern)
{
std::lock_guard<Mutex> lock(_mutex);
_formatter = std::make_shared<pattern_formatter>(pattern);
for (auto& l : _loggers)
l.second->set_formatter(_formatter);
}
void set_level(level::level_enum log_level)
{
std::lock_guard<Mutex> lock(_mutex);
for (auto& l : _loggers)
l.second->set_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)
{
std::lock_guard<Mutex> lock(_mutex);
_async_mode = true;
_async_q_size = q_size;
_overflow_policy = overflow_policy;
_worker_warmup_cb = worker_warmup_cb;
_flush_interval_ms = flush_interval_ms;
}
void set_sync_mode()
{
std::lock_guard<Mutex> lock(_mutex);
_async_mode = false;
}
static registry_t<Mutex>& instance()
{
static registry_t<Mutex> s_instance;
return s_instance;
}
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>(const registry_t<Mutex>&) = delete;
registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete;
Mutex _mutex;
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
formatter_ptr _formatter;
level::level_enum _level = level::info;
bool _async_mode = false;
size_t _async_q_size = 0;
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
std::function<void()> _worker_warmup_cb = nullptr;
std::chrono::milliseconds _flush_interval_ms;
};
#ifdef SPDLOG_NO_REGISTRY_MUTEX
typedef registry_t<spdlog::details::null_mutex> registry;
#else
typedef registry_t<std::mutex> registry;
#endif
}
}
@@ -0,0 +1,135 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Global registry functions
//
#include "registry.h"
#include "../sinks/file_sinks.h"
#include "../sinks/stdout_sinks.h"
#include "../sinks/syslog_sink.h"
inline void spdlog::register_logger(std::shared_ptr<logger> logger)
{
return details::registry::instance().register_logger(logger);
}
inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
{
return details::registry::instance().get(name);
}
inline void spdlog::drop(const std::string &name)
{
details::registry::instance().drop(name);
}
// Create multi/single threaded rotating 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)
{
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, "txt", max_file_size, max_files, force_flush);
}
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)
{
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, "txt", max_file_size, max_files, force_flush);
}
// 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)
{
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, "txt", hour, minute, force_flush);
}
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);
}
// 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());
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
{
return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
{
return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
{
return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
}
#ifdef __linux__
// 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)
{
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option);
}
#endif
//Create logger with multiple sinks
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks)
{
return details::registry::instance().create(logger_name, sinks);
}
template <typename Sink, typename... Args>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args)
{
sink_ptr sink = std::make_shared<Sink>(args...);
return details::registry::instance().create(logger_name, { sink });
}
template<class It>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
}
inline void spdlog::set_formatter(spdlog::formatter_ptr f)
{
details::registry::instance().formatter(f);
}
inline void spdlog::set_pattern(const std::string& format_string)
{
return details::registry::instance().set_pattern(format_string);
}
inline void spdlog::set_level(level::level_enum log_level)
{
return details::registry::instance().set_level(log_level);
}
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);
}
inline void spdlog::set_sync_mode()
{
details::registry::instance().set_sync_mode();
}
inline void spdlog::drop_all()
{
details::registry::instance().drop_all();
}
@@ -0,0 +1,35 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "details/log_msg.h"
namespace spdlog {
namespace details {
class flag_formatter;
}
class formatter {
public:
virtual ~formatter() {}
virtual void format(details::log_msg& msg) = 0;
};
class pattern_formatter : public formatter {
public:
explicit pattern_formatter(const std::string& pattern);
pattern_formatter(const pattern_formatter&) = delete;
pattern_formatter& operator=(const pattern_formatter&) = delete;
void format(details::log_msg& msg) override;
private:
const std::string _pattern;
std::vector<std::unique_ptr<details::flag_formatter>> _formatters;
void handle_flag(char flag);
void compile_pattern(const std::string& pattern);
};
}
#include "details/pattern_formatter_impl.h"
@@ -0,0 +1,123 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// Thread safe logger
// Has name, log level, vector of std::shared sink pointers and formatter
// Upon each log write the logger:
// 1. Checks if its log level is enough to log the message
// 2. Format the message using the formatter function
// 3. Pass the formatted message to its sinks to performa the actual logging
#include <vector>
#include <memory>
#include "sinks/base_sink.h"
#include "common.h"
namespace spdlog {
namespace details {
class line_logger;
}
class logger {
public:
logger(const std::string& logger_name, sink_ptr single_sink);
logger(const std::string& name, sinks_init_list);
template <class It>
logger(const std::string& name, const It& begin, const It& end);
virtual ~logger();
logger(const logger&) = delete;
logger& operator=(const logger&) = delete;
void set_level(level::level_enum);
level::level_enum level() 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_formatter(formatter_ptr);
virtual void flush();
protected:
virtual void _log_msg(details::log_msg&);
virtual void _set_pattern(const std::string&);
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);
friend details::line_logger;
std::string _name;
std::vector<sink_ptr> _sinks;
formatter_ptr _formatter;
std::atomic_int _level;
};
}
#include "./details/logger_impl.h"
@@ -0,0 +1,67 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#if defined(__ANDROID__)
#include <mutex>
#include "base_sink.h"
#include "../details/null_mutex.h"
#include <android/log.h>
namespace spdlog {
namespace sinks {
/*
* Android sink (logging using __android_log_write)
*/
template <class Mutex>
class base_android_sink : public base_sink<Mutex> {
public:
explicit base_android_sink(std::string tag = "spdlog") : _tag(tag) {}
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:
static android_LogPriority
convert_to_android(spdlog::level::level_enum level) {
switch (level) {
case spdlog::level::trace: return ANDROID_LOG_VERBOSE;
case spdlog::level::debug: return ANDROID_LOG_DEBUG;
case spdlog::level::info: return ANDROID_LOG_INFO;
case spdlog::level::notice: return ANDROID_LOG_INFO;
case spdlog::level::warn: return ANDROID_LOG_WARN;
case spdlog::level::err: return ANDROID_LOG_ERROR;
case spdlog::level::critical: return ANDROID_LOG_FATAL;
case spdlog::level::alert: return ANDROID_LOG_FATAL;
case spdlog::level::emerg: return ANDROID_LOG_FATAL;
default: throw spdlog_ex("Incorrect level value");
}
}
std::string _tag;
};
typedef base_android_sink<std::mutex> android_sink_mt;
typedef base_android_sink<details::null_mutex> android_sink_st;
}
}
#endif
@@ -0,0 +1,43 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// base sink templated over a mutex (either dummy or realy)
// concrete implementation should only overrid the _sink_it method.
// all locking is taken care of here so no locking needed by the implementors..
//
#include <string>
#include <mutex>
#include <atomic>
#include "./sink.h"
#include "../formatter.h"
#include "../common.h"
#include "../details/log_msg.h"
namespace spdlog {
namespace sinks {
template <class Mutex>
class base_sink : public sink {
public:
base_sink() : _mutex() {}
virtual ~base_sink() = default;
base_sink(const base_sink&) = delete;
base_sink& operator=(const base_sink&) = delete;
void
log(const details::log_msg& msg) override {
std::lock_guard<Mutex> lock(_mutex);
_sink_it(msg);
}
protected:
virtual void _sink_it(const details::log_msg& msg) = 0;
Mutex _mutex;
};
}
}
@@ -0,0 +1,62 @@
//
// Copyright (c) 2015 David Schury, Gabi Melman
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <algorithm>
#include <memory>
#include <mutex>
#include <list>
#include "../details/log_msg.h"
#include "../details/null_mutex.h"
#include "./base_sink.h"
#include "./sink.h"
namespace spdlog {
namespace sinks {
template <class Mutex>
class dist_sink : public base_sink<Mutex> {
public:
explicit dist_sink() : _sinks() {}
dist_sink(const dist_sink&) = delete;
dist_sink& operator=(const dist_sink&) = delete;
virtual ~dist_sink() = default;
protected:
void
_sink_it(const details::log_msg& msg) override {
for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++) (*iter)->log(msg);
}
std::vector<std::shared_ptr<sink>> _sinks;
public:
void
flush() override {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++) (*iter)->flush();
}
void
add_sink(std::shared_ptr<sink> sink) {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
if (sink && _sinks.end() == std::find(_sinks.begin(), _sinks.end(), sink)) {
_sinks.push_back(sink);
}
}
void
remove_sink(std::shared_ptr<sink> sink) {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
auto pos = std::find(_sinks.begin(), _sinks.end(), sink);
if (pos != _sinks.end()) { _sinks.erase(pos); }
}
};
typedef dist_sink<std::mutex> dist_sink_mt;
typedef dist_sink<details::null_mutex> dist_sink_st;
}
}
@@ -0,0 +1,210 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <mutex>
#include "base_sink.h"
#include "../details/null_mutex.h"
#include "../details/file_helper.h"
#include "../details/format.h"
namespace spdlog {
namespace sinks {
/*
* Trivial file sink with single file as target
*/
template <class Mutex>
class simple_file_sink : public base_sink<Mutex> {
public:
explicit simple_file_sink(const std::string& filename, bool force_flush = false)
: _file_helper(force_flush) {
_file_helper.open(filename);
}
void
flush() override {
_file_helper.flush();
}
protected:
void
_sink_it(const details::log_msg& msg) override {
_file_helper.write(msg);
}
private:
details::file_helper _file_helper;
};
typedef simple_file_sink<std::mutex> simple_file_sink_mt;
typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
/*
* Rotating file sink based on size
*/
template <class Mutex>
class rotating_file_sink : public base_sink<Mutex> {
public:
rotating_file_sink(const std::string& base_filename,
const std::string& extension,
std::size_t max_size,
std::size_t max_files,
bool force_flush = false)
: _base_filename(base_filename)
, _extension(extension)
, _max_size(max_size)
, _max_files(max_files)
, _current_size(0)
, _file_helper(force_flush) {
_file_helper.open(calc_filename(_base_filename, 0, _extension));
_current_size = _file_helper.size(); // expensive. called only once
}
void
flush() override {
_file_helper.flush();
}
protected:
void
_sink_it(const details::log_msg& msg) override {
_current_size += msg.formatted.size();
if (_current_size > _max_size) {
_rotate();
_current_size = msg.formatted.size();
}
_file_helper.write(msg);
}
private:
static std::string
calc_filename(const std::string& filename, std::size_t index, const std::string& extension) {
fmt::MemoryWriter w;
if (index)
w.write("{}.{}.{}", filename, index, extension);
else
w.write("{}.{}", filename, extension);
return w.str();
}
// Rotate files:
// log.txt -> log.1.txt
// log.1.txt -> log2.txt
// log.2.txt -> log3.txt
// log.3.txt -> delete
void
_rotate() {
_file_helper.close();
for (auto i = _max_files; i > 0; --i) {
std::string src = calc_filename(_base_filename, i - 1, _extension);
std::string target = calc_filename(_base_filename, i, _extension);
if (details::file_helper::file_exists(target)) {
if (std::remove(target.c_str()) != 0) {
throw spdlog_ex("rotating_file_sink: failed removing " + target);
}
}
if (details::file_helper::file_exists(src) &&
std::rename(src.c_str(), target.c_str())) {
throw spdlog_ex("rotating_file_sink: failed renaming " + src + " to " + target);
}
}
_file_helper.reopen(true);
}
std::string _base_filename;
std::string _extension;
std::size_t _max_size;
std::size_t _max_files;
std::size_t _current_size;
details::file_helper _file_helper;
};
typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex> rotating_file_sink_st;
/*
* Rotating file sink based on date. rotates at midnight
*/
template <class Mutex>
class daily_file_sink : public base_sink<Mutex> {
public:
// create daily file sink which rotates on given time
daily_file_sink(const std::string& base_filename,
const std::string& extension,
int rotation_hour,
int rotation_minute,
bool force_flush = false)
: _base_filename(base_filename)
, _extension(extension)
, _rotation_h(rotation_hour)
, _rotation_m(rotation_minute)
, _file_helper(force_flush) {
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
_rotation_tp = _next_rotation_tp();
_file_helper.open(calc_filename(_base_filename, _extension));
}
void
flush() override {
_file_helper.flush();
}
protected:
void
_sink_it(const details::log_msg& msg) override {
if (std::chrono::system_clock::now() >= _rotation_tp) {
_file_helper.open(calc_filename(_base_filename, _extension));
_rotation_tp = _next_rotation_tp();
}
_file_helper.write(msg);
}
private:
std::chrono::system_clock::time_point
_next_rotation_tp() {
using namespace std::chrono;
auto now = system_clock::now();
time_t tnow = std::chrono::system_clock::to_time_t(now);
tm date = spdlog::details::os::localtime(tnow);
date.tm_hour = _rotation_h;
date.tm_min = _rotation_m;
date.tm_sec = 0;
auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
if (rotation_time > now)
return rotation_time;
else
return system_clock::time_point(rotation_time + hours(24));
}
// Create filename for the form basename.YYYY-MM-DD.extension
static std::string
calc_filename(const std::string& basename, const std::string& 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_m;
std::chrono::system_clock::time_point _rotation_tp;
details::file_helper _file_helper;
};
typedef daily_file_sink<std::mutex> daily_file_sink_mt;
typedef daily_file_sink<details::null_mutex> daily_file_sink_st;
}
}
@@ -0,0 +1,26 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <mutex>
#include "./base_sink.h"
#include "../details/null_mutex.h"
namespace spdlog {
namespace sinks {
template <class Mutex>
class null_sink : public base_sink<Mutex> {
protected:
void
_sink_it(const details::log_msg&) override {}
void
flush() override {}
};
typedef null_sink<details::null_mutex> null_sink_st;
typedef null_sink<std::mutex> null_sink_mt;
}
}
@@ -0,0 +1,45 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <ostream>
#include <mutex>
#include <memory>
#include "../details/null_mutex.h"
#include "./base_sink.h"
namespace spdlog {
namespace sinks {
template <class Mutex>
class ostream_sink : public base_sink<Mutex> {
public:
explicit ostream_sink(std::ostream& os, bool force_flush = false)
: _ostream(os), _force_flush(force_flush) {}
ostream_sink(const ostream_sink&) = delete;
ostream_sink& operator=(const ostream_sink&) = delete;
virtual ~ostream_sink() = default;
protected:
void
_sink_it(const details::log_msg& msg) override {
_ostream.write(msg.formatted.data(), msg.formatted.size());
if (_force_flush) _ostream.flush();
}
void
flush() override {
_ostream.flush();
}
std::ostream& _ostream;
bool _force_flush;
};
typedef ostream_sink<std::mutex> ostream_sink_mt;
typedef ostream_sink<details::null_mutex> ostream_sink_st;
}
}
@@ -0,0 +1,19 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "../details/log_msg.h"
namespace spdlog {
namespace sinks {
class sink {
public:
virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0;
virtual void flush() = 0;
};
}
}
@@ -0,0 +1,48 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <iostream>
#include <mutex>
#include "./ostream_sink.h"
#include "../details/null_mutex.h"
namespace spdlog {
namespace sinks {
template <class Mutex>
class stdout_sink : public ostream_sink<Mutex> {
using MyType = stdout_sink<Mutex>;
public:
stdout_sink() : ostream_sink<Mutex>(std::cout, true) {}
static std::shared_ptr<MyType>
instance() {
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
};
typedef stdout_sink<details::null_mutex> stdout_sink_st;
typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex>
class stderr_sink : public ostream_sink<Mutex> {
using MyType = stderr_sink<Mutex>;
public:
stderr_sink() : ostream_sink<Mutex>(std::cerr, true) {}
static std::shared_ptr<MyType>
instance() {
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
};
typedef stderr_sink<std::mutex> stderr_sink_mt;
typedef stderr_sink<details::null_mutex> stderr_sink_st;
}
}
@@ -0,0 +1,76 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#ifdef __linux__
#include <array>
#include <string>
#include <syslog.h>
#include "./sink.h"
#include "../common.h"
#include "../details/log_msg.h"
namespace spdlog {
namespace sinks {
/**
* Sink that write to syslog using the `syscall()` library call.
*
* Locking is not needed, as `syslog()` itself is thread-safe.
*/
class syslog_sink : public sink {
public:
//
syslog_sink(const std::string& ident = "",
int syslog_option = 0,
int syslog_facility = LOG_USER)
: _ident(ident) {
_priorities[static_cast<int>(level::trace)] = LOG_DEBUG;
_priorities[static_cast<int>(level::debug)] = LOG_DEBUG;
_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::err)] = LOG_ERR;
_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;
// set ident to be program name if empty
::openlog(_ident.empty() ? nullptr : _ident.c_str(), syslog_option, syslog_facility);
}
~syslog_sink() { ::closelog(); }
syslog_sink(const syslog_sink&) = delete;
syslog_sink& operator=(const syslog_sink&) = delete;
void
log(const details::log_msg& msg) override {
::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str());
}
void
flush() override {}
private:
std::array<int, 10> _priorities;
// must store the ident because the man says openlog might use the pointer as is and not a
// string copy
const std::string _ident;
//
// Simply maps spdlog's log level to syslog priority level.
//
int
syslog_prio_from_level(const details::log_msg& msg) const {
return _priorities[static_cast<int>(msg.level)];
}
};
}
}
#endif
@@ -0,0 +1,151 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// spdlog main header file.
// see example.cpp for usage example
#pragma once
#include "common.h"
#include "logger.h"
namespace spdlog {
// Return an existing logger or nullptr if a logger with such name doesn't exist.
// Examples:
//
// 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);
//
// Set global formatting
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
//
void set_pattern(const std::string& format_string);
void set_formatter(formatter_ptr f);
//
// Set global logging level for
//
void set_level(level::level_enum log_level);
//
// Turn on async mode (off by default) and set the queue size for each async_logger.
// effective only for loggers created after this call.
// queue_size: size of queue (must be power of 2):
// Each logger will pre-allocate a dedicated queue with queue_size entries upon construction.
//
// async_overflow_policy (optional, block_retry by default):
// async_overflow_policy::block_retry - if queue is full, block until queue has room for the new
// log entry.
// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue
// overflows.
//
// worker_warmup_cb (optional):
// 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());
// Turn off async mode
void set_sync_mode();
//
// 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_st(const std::string& logger_name,
const std::string& filename,
size_t max_file_size,
size_t max_files,
bool force_flush = false);
//
// 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_st(const std::string& logger_name,
const std::string& filename,
int hour = 0,
int minute = 0,
bool force_flush = false);
//
// Create and register stdout/stderr loggers
//
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
//
// Create and register a syslog logger
//
#ifdef __linux__
std::shared_ptr<logger>
syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
#endif
// Create and register a logger with multiple sinks
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
template <class It>
std::shared_ptr<logger>
create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
// Create and register a logger with templated sink type
// Example: spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
// Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger);
// Drop the reference to the given logger
void drop(const std::string& name);
// Drop all references
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.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
//
// Example:
// spdlog::set_level(spdlog::level::debug);
// SPDLOG_DEBUG(my_logger, "Some debug message {} {}", 1, 3.2);
///////////////////////////////////////////////////////////////////////////////
#ifdef SPDLOG_TRACE_ON
#define SPDLOG_TRACE(logger, ...) \
logger->trace(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ << ")";
#else
#define SPDLOG_TRACE(logger, ...)
#endif
#ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) \
logger->debug(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ << ")";
#else
#define SPDLOG_DEBUG(logger, ...)
#endif
}
#include "details/spdlog_impl.h"
@@ -0,0 +1,54 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <string>
#include <json/value.h>
namespace etix {
namespace cameradar {
struct stream_model {
// Ex : "172.16.100.113"
std::string address;
// Ex : 8554
unsigned short port;
// Ex : "admin"
std::string username = "";
// Ex : "123456"
std::string password = "";
// Ex : "live.sdp"
std::string route = "";
// Ex : "rtsp"
std::string service_name;
// Ex : "Vivotek HDCam"
std::string product;
// Ex : "RTSP"
std::string protocol;
// Ex : "Open"
std::string state;
// Ex : "true"
bool path_found = false;
// Ex : "true"
bool ids_found = false;
// Ex : "/thumbnails/cameradar"
std::string thumbnail_path = "";
};
Json::Value deserialize(const stream_model& model);
}
}
@@ -0,0 +1,46 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cameradar_task.h> // task interface
#include <cachemanager.h> // cacheManager
#include <describe.h> // send DESCRIBE through cURL
#include <signal_handler.h> // signals
#include <stream_model.h> // data model
namespace etix {
namespace cameradar {
class brutelogs : public etix::cameradar::cameradar_task {
const configuration& conf;
std::shared_ptr<cache_manager> cache;
std::string nmap_output;
public:
brutelogs() = delete;
brutelogs(std::shared_ptr<cache_manager> cache,
const configuration& conf,
std::string nmap_output)
: conf(conf), cache(cache), nmap_output(nmap_output) {}
brutelogs(const brutelogs& ref) = delete;
virtual bool run() const;
bool test_ids(const etix::cameradar::stream_model& cit,
const std::string& pit,
const std::string& uit) const;
};
}
}
@@ -0,0 +1,47 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cameradar_task.h> // task interface
#include <memory> // std::shared_ptr
#include <logger.h> // LOG
#include <curl/curl.h> // cURL client for discovery
#include <describe.h> // send DESCRIBE through cURL
#include <signal_handler.h> // signals
#include <stream_model.h> // data model
#include <cachemanager.h> // cacheManager
namespace etix {
namespace cameradar {
class brutepath : public etix::cameradar::cameradar_task {
const configuration& conf;
std::shared_ptr<cache_manager> cache;
std::string nmap_output;
public:
brutepath() = delete;
brutepath(std::shared_ptr<cache_manager> cache,
const configuration& conf,
std::string nmap_output)
: conf(conf), cache(cache), nmap_output(nmap_output) {}
brutepath(const brutepath& ref) = delete;
virtual bool run() const;
bool test_path(const etix::cameradar::stream_model& cit, const std::string& it) const;
};
}
}
@@ -0,0 +1,43 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cameradar_task.h> // task interface
#include <launch_command.h> // launch_command
#include <algorithm> // launch_command
#include <fmt.h> // fmt
#include <stream_model.h> // data model
#include <cachemanager.h> // cacheManager
namespace etix {
namespace cameradar {
class mapping : public etix::cameradar::cameradar_task {
const configuration& conf;
std::shared_ptr<cache_manager> cache;
std::string nmap_output;
public:
mapping() = delete;
mapping(std::shared_ptr<cache_manager> cache,
const configuration& conf,
std::string nmap_output)
: conf(conf), cache(cache), nmap_output(nmap_output) {}
mapping(const mapping& ref) = delete;
virtual bool run() const;
};
}
}
@@ -0,0 +1,45 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cameradar_task.h> // task interface
#include <tinyxml.h> // parsing
#include <stream_model.h> // data model
#include <cachemanager.h> // cacheManager
namespace etix {
namespace cameradar {
class parsing : public etix::cameradar::cameradar_task {
const configuration& conf;
std::shared_ptr<cache_manager> cache;
std::string nmap_output;
public:
parsing() = delete;
parsing(std::shared_ptr<cache_manager> cache,
const configuration& conf,
std::string nmap_output)
: conf(conf), cache(cache), nmap_output(nmap_output) {}
parsing(const parsing& ref) = delete;
virtual bool run() const;
void parse_camera(TiXmlElement*, std::vector<etix::cameradar::stream_model>& data) const;
bool print_detected_cameras(const std::vector<etix::cameradar::stream_model>& data) const;
};
}
}
@@ -0,0 +1,41 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cameradar_task.h> // task interface
#include <boost/algorithm/string/find.hpp> // boost::find
#include <iostream> // std::ofstream
#include <fstream> // std::ofstream
#include <stream_model.h> // data model
#include <cachemanager.h> // cacheManager
namespace etix {
namespace cameradar {
class print : public etix::cameradar::cameradar_task {
const configuration& conf;
std::shared_ptr<cache_manager> cache;
std::string nmap_output;
public:
print() = delete;
print(std::shared_ptr<cache_manager> cache, const configuration& conf, std::string nmap_output)
: conf(conf), cache(cache), nmap_output(nmap_output) {}
print(const print& ref) = delete;
virtual bool run() const;
};
}
}
@@ -0,0 +1,45 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cameradar_task.h> // task interface
#include <signal_handler.h> // signals
#include <stream_model.h> // data model
#include <cachemanager.h> // cacheManager
#include <glib.h>
#include <gst/gst.h>
#include <gst/gstparse.h>
namespace etix {
namespace cameradar {
class stream_check : public etix::cameradar::cameradar_task {
const configuration& conf;
std::shared_ptr<cache_manager> cache;
std::string nmap_output;
public:
stream_check() = delete;
stream_check(std::shared_ptr<cache_manager> cache,
const configuration& conf,
std::string nmap_output)
: conf(conf), cache(cache), nmap_output(nmap_output) {}
stream_check(const stream_check& ref) = delete;
virtual bool run() const;
};
}
}
@@ -0,0 +1,46 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cameradar_task.h> // task interface
#include <launch_command.h> // launch_command
#include <signal_handler.h> // signals
#include <stream_model.h> // data model
#include <cachemanager.h> // cacheManager
#include <fmt.h> // fmt
#include <rtsp_path.h> // make_path
namespace etix {
namespace cameradar {
class thumbnail : public etix::cameradar::cameradar_task {
const configuration& conf;
std::shared_ptr<cache_manager> cache;
std::string nmap_output;
public:
thumbnail() = delete;
thumbnail(std::shared_ptr<cache_manager> cache,
const configuration& conf,
std::string nmap_output)
: conf(conf), cache(cache), nmap_output(nmap_output) {}
thumbnail(const thumbnail& ref) = delete;
virtual bool run() const;
std::string build_output_file_path(const std::string& path) const;
};
}
}
+303
View File
@@ -0,0 +1,303 @@
/*
www.sourceforge.net/projects/tinyxml
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef TIXML_USE_STL
#ifndef TIXML_STRING_INCLUDED
#define TIXML_STRING_INCLUDED
#include <assert.h>
#include <string.h>
/* The support for explicit isn't that universal, and it isn't really
required - it is used to check that the TiXmlString class isn't
incorrectly
used. Be nice to old compilers and macro it here:
*/
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
// Microsoft visual studio, version 6 and higher.
#define TIXML_EXPLICIT explicit
#elif defined(__GNUC__) && (__GNUC__ >= 3)
// GCC version 3 and higher.s
#define TIXML_EXPLICIT explicit
#else
#define TIXML_EXPLICIT
#endif
/*
TiXmlString is an emulation of a subset of the std::string template.
Its purpose is to allow compiling TinyXML on compilers with no or poor STL
support.
Only the member functions relevant to the TinyXML project have been
implemented.
The buffer allocation is made by a simplistic power of 2 like mechanism : if
we increase
a string and there's no more room, we allocate a buffer twice as big as we
need.
*/
class TiXmlString {
public:
// The size type used
typedef size_t size_type;
// Error value for find primitive
static const size_type npos; // = -1;
// TiXmlString empty constructor
TiXmlString() : rep_(&nullrep_) {}
// TiXmlString copy constructor
TiXmlString(const TiXmlString& copy) : rep_(0) {
init(copy.length());
memcpy(start(), copy.data(), length());
}
// TiXmlString constructor, based on a string
TIXML_EXPLICIT
TiXmlString(const char* copy)
: rep_(0) {
init(static_cast<size_type>(strlen(copy)));
memcpy(start(), copy, length());
}
// TiXmlString constructor, based on a string
TIXML_EXPLICIT
TiXmlString(const char* str, size_type len)
: rep_(0) {
init(len);
memcpy(start(), str, len);
}
// TiXmlString destructor
~TiXmlString() { quit(); }
TiXmlString& operator=(const char* copy) { return assign(copy, (size_type)strlen(copy)); }
TiXmlString& operator=(const TiXmlString& copy) { return assign(copy.start(), copy.length()); }
// += operator. Maps to append
TiXmlString& operator+=(const char* suffix) {
return append(suffix, static_cast<size_type>(strlen(suffix)));
}
// += operator. Maps to append
TiXmlString& operator+=(char single) { return append(&single, 1); }
// += operator. Maps to append
TiXmlString& operator+=(const TiXmlString& suffix) {
return append(suffix.data(), suffix.length());
}
// Convert a TiXmlString into a null-terminated char *
const char*
c_str() const {
return rep_->str;
}
// Convert a TiXmlString into a char * (need not be null terminated).
const char*
data() const {
return rep_->str;
}
// Return the length of a TiXmlString
size_type
length() const {
return rep_->size;
}
// Alias for length()
size_type
size() const {
return rep_->size;
}
// Checks if a TiXmlString is empty
bool
empty() const {
return rep_->size == 0;
}
// Return capacity of string
size_type
capacity() const {
return rep_->capacity;
}
// single char extraction
const char&
at(size_type index) const {
assert(index < length());
return rep_->str[index];
}
// [] operator
char& operator[](size_type index) const {
assert(index < length());
return rep_->str[index];
}
// find a char in a string. Return TiXmlString::npos if not found
size_type
find(char lookup) const {
return find(lookup, 0);
}
// find a char in a string from an offset. Return TiXmlString::npos if not
// found
size_type
find(char tofind, size_type offset) const {
if (offset >= length()) return npos;
for (const char* p = c_str() + offset; *p != '\0'; ++p) {
if (*p == tofind) return static_cast<size_type>(p - c_str());
}
return npos;
}
void
clear() {
// Lee:
// The original was just too strange, though correct:
// TiXmlString().swap(*this);
// Instead use the quit & re-init:
quit();
init(0, 0);
}
/* Function to reserve a big amount of data when we know we'll need it. Be
aware that this
function DOES NOT clear the content of the TiXmlString if any exists.
*/
void reserve(size_type cap);
TiXmlString& assign(const char* str, size_type len);
TiXmlString& append(const char* str, size_type len);
void
swap(TiXmlString& other) {
Rep* r = rep_;
rep_ = other.rep_;
other.rep_ = r;
}
private:
void
init(size_type sz) {
init(sz, sz);
}
void
set_size(size_type sz) {
rep_->str[rep_->size = sz] = '\0';
}
char*
start() const {
return rep_->str;
}
char*
finish() const {
return rep_->str + rep_->size;
}
struct Rep {
size_type size, capacity;
char str[1];
};
void
init(size_type sz, size_type cap) {
if (cap) {
// Lee: the original form:
// rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
// doesn't work in some cases of new being overloaded. Switching
// to the normal allocation, although use an 'int' for systems
// that are overly picky about structure alignment.
const size_type bytesNeeded = sizeof(Rep) + cap;
const size_type intsNeeded = (bytesNeeded + sizeof(int) - 1) / sizeof(int);
rep_ = reinterpret_cast<Rep*>(new int[intsNeeded]);
rep_->str[rep_->size = sz] = '\0';
rep_->capacity = cap;
} else {
rep_ = &nullrep_;
}
}
void
quit() {
if (rep_ != &nullrep_) {
// The rep_ is really an array of ints. (see the allocator, above).
// Cast it back before delete, so the compiler won't incorrectly call
// destructors.
delete[](reinterpret_cast<int*>(rep_));
}
}
Rep* rep_;
static Rep nullrep_;
};
inline bool operator==(const TiXmlString& a, const TiXmlString& b) {
return (a.length() == b.length()) // optimization on some platforms
&& (strcmp(a.c_str(), b.c_str()) == 0); // actual compare
}
inline bool operator<(const TiXmlString& a, const TiXmlString& b) {
return strcmp(a.c_str(), b.c_str()) < 0;
}
inline bool operator!=(const TiXmlString& a, const TiXmlString& b) { return !(a == b); }
inline bool operator>(const TiXmlString& a, const TiXmlString& b) { return b < a; }
inline bool operator<=(const TiXmlString& a, const TiXmlString& b) { return !(b < a); }
inline bool operator>=(const TiXmlString& a, const TiXmlString& b) { return !(a < b); }
inline bool operator==(const TiXmlString& a, const char* b) { return strcmp(a.c_str(), b) == 0; }
inline bool operator==(const char* a, const TiXmlString& b) { return b == a; }
inline bool operator!=(const TiXmlString& a, const char* b) { return !(a == b); }
inline bool operator!=(const char* a, const TiXmlString& b) { return !(b == a); }
TiXmlString operator+(const TiXmlString& a, const TiXmlString& b);
TiXmlString operator+(const TiXmlString& a, const char* b);
TiXmlString operator+(const char* a, const TiXmlString& b);
/*
TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
Only the operators that we need for TinyXML have been developped.
*/
class TiXmlOutStream : public TiXmlString {
public:
// TiXmlOutStream << operator.
TiXmlOutStream& operator<<(const TiXmlString& in) {
*this += in;
return *this;
}
// TiXmlOutStream << operator.
TiXmlOutStream& operator<<(const char* in) {
*this += in;
return *this;
}
};
#endif // TIXML_STRING_INCLUDED
#endif // TIXML_USE_STL
File diff suppressed because it is too large Load Diff