Initial commit
This commit is contained in:
committed by
Brendan Le Glaunec
parent
2af5f4475e
commit
95276760be
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user