added logging module and a sandbox project
This commit is contained in:
parent
c4a00f19fd
commit
20d6a77275
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
add_executable(demo main.cpp)
|
add_executable(demo main.cpp)
|
||||||
target_link_libraries(demo PRIVATE
|
target_link_libraries(demo PRIVATE
|
||||||
matador-core
|
matador-core
|
||||||
|
|
@ -7,3 +5,11 @@ target_link_libraries(demo PRIVATE
|
||||||
${CMAKE_DL_LIBS}
|
${CMAKE_DL_LIBS}
|
||||||
${SQLite3_LIBRARIES}
|
${SQLite3_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_executable(sandbox sandbox.cpp)
|
||||||
|
target_link_libraries(sandbox PRIVATE
|
||||||
|
matador-core
|
||||||
|
matador-orm
|
||||||
|
${CMAKE_DL_LIBS}
|
||||||
|
${SQLite3_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef AUTHOR_HPP
|
||||||
|
#define AUTHOR_HPP
|
||||||
|
|
||||||
|
#include "matador/object/collection.hpp"
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/access.hpp"
|
||||||
|
|
||||||
|
namespace demo {
|
||||||
|
struct book;
|
||||||
|
|
||||||
|
struct author {
|
||||||
|
unsigned int id{};
|
||||||
|
std::string first_name;
|
||||||
|
std::string last_name;
|
||||||
|
std::string date_of_birth;
|
||||||
|
unsigned short year_of_birth{};
|
||||||
|
bool distinguished{false};
|
||||||
|
matador::object::collection<matador::object::object_ptr<book>> books;
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Operator>
|
||||||
|
void process( Operator& op ) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::primary_key( op, "id", id );
|
||||||
|
field::attribute( op, "first_name", first_name, 63 );
|
||||||
|
field::attribute( op, "last_name", last_name, 63 );
|
||||||
|
field::attribute( op, "date_of_birth", date_of_birth, 31 );
|
||||||
|
field::attribute( op, "year_of_birth", year_of_birth );
|
||||||
|
field::attribute( op, "distinguished", distinguished );
|
||||||
|
field::has_many( op, "books", books, "author_id" );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //AUTHOR_HPP
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef BOOK_HPP
|
||||||
|
#define BOOK_HPP
|
||||||
|
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
#include "matador/utils/access.hpp"
|
||||||
|
|
||||||
|
namespace demo {
|
||||||
|
struct author;
|
||||||
|
|
||||||
|
struct book {
|
||||||
|
unsigned int id{};
|
||||||
|
matador::object::object_ptr<author> book_author;
|
||||||
|
std::string title;
|
||||||
|
unsigned short published_in{};
|
||||||
|
|
||||||
|
template<typename Operator>
|
||||||
|
void process( Operator& op ) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::primary_key( op, "id", id );
|
||||||
|
field::attribute( op, "title", title, 511 );
|
||||||
|
field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes );
|
||||||
|
field::attribute( op, "published_in", published_in );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //BOOK_HPP
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include "matador/object/schema.hpp"
|
||||||
|
|
||||||
|
#include "author.hpp"
|
||||||
|
#include "book.hpp"
|
||||||
|
|
||||||
|
using namespace demo;
|
||||||
|
using namespace matador;
|
||||||
|
int main() {
|
||||||
|
object::schema tree;
|
||||||
|
|
||||||
|
|
||||||
|
tree.attach<author>("authors");
|
||||||
|
tree.attach<book>("books");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -52,15 +52,15 @@ int main() {
|
||||||
object::schema schema("Administration");
|
object::schema schema("Administration");
|
||||||
|
|
||||||
auto result = schema.attach<admin::CollectionCenter>("collection_center")
|
auto result = schema.attach<admin::CollectionCenter>("collection_center")
|
||||||
.and_then([&schema] { return schema.attach<admin::InternalUserDirectory>("internal_user_directories"); })
|
.and_then([&schema] { return schema.attach<admin::UserDirectory>("user_directories"); })
|
||||||
.and_then([&schema] { return schema.attach<admin::LdapGroupSchemaSettings>("ldap_group_schema_settings"); })
|
.and_then([&schema] { return schema.attach<admin::LdapGroupSchemaSettings>("ldap_group_schema_settings"); })
|
||||||
.and_then([&schema] { return schema.attach<admin::LdapImportSettings>("ldap_import_settings"); })
|
.and_then([&schema] { return schema.attach<admin::LdapImportSettings>("ldap_import_settings"); })
|
||||||
.and_then([&schema] { return schema.attach<admin::LdapUserDirectory>("ldap_user_directories"); } )
|
|
||||||
.and_then([&schema] { return schema.attach<admin::LdapUserSchemaSettings>("ldap_user_schema_settings"); })
|
.and_then([&schema] { return schema.attach<admin::LdapUserSchemaSettings>("ldap_user_schema_settings"); })
|
||||||
|
.and_then([&schema] { return schema.attach<admin::UserDirectory, admin::InternalUserDirectory>("internal_user_directories"); })
|
||||||
|
.and_then([&schema] { return schema.attach<admin::UserDirectory, admin::LdapUserDirectory>("ldap_user_directories"); } )
|
||||||
.and_then([&schema] { return schema.attach<admin::LoginHistory>("login_histories"); })
|
.and_then([&schema] { return schema.attach<admin::LoginHistory>("login_histories"); })
|
||||||
.and_then([&schema] { return schema.attach<admin::Scenario>("scenarios"); })
|
.and_then([&schema] { return schema.attach<admin::Scenario>("scenarios"); })
|
||||||
.and_then([&schema] { return schema.attach<admin::User>("users"); })
|
.and_then([&schema] { return schema.attach<admin::User>("users"); })
|
||||||
.and_then([&schema] { return schema.attach<admin::UserDirectory>("user_directories"); })
|
|
||||||
.and_then([&schema] { return schema.attach<admin::UserSession>("user_sessions"); })
|
.and_then([&schema] { return schema.attach<admin::UserSession>("user_sessions"); })
|
||||||
.and_then([&schema] { return schema.attach<jobs::Job>("jobs"); })
|
.and_then([&schema] { return schema.attach<jobs::Job>("jobs"); })
|
||||||
.and_then([&schema] { return schema.attach<jobs::Payload>("payloads"); })
|
.and_then([&schema] { return schema.attach<jobs::Payload>("payloads"); })
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef MATADOR_BASIC_FILE_SINK_HPP
|
||||||
|
#define MATADOR_BASIC_FILE_SINK_HPP
|
||||||
|
|
||||||
|
#include "matador/logger/log_sink.hpp"
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A base class for the file-stream-based sinks
|
||||||
|
*
|
||||||
|
* This class acts like a base class for all
|
||||||
|
* concrete sinks working with a file stream to write
|
||||||
|
* the log message.
|
||||||
|
*/
|
||||||
|
class basic_file_sink : public log_sink
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
basic_file_sink() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a basic_file_sink with a given file stream
|
||||||
|
*
|
||||||
|
* @param f File stream to write on
|
||||||
|
*/
|
||||||
|
explicit basic_file_sink(FILE *f);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Writes the log message to the internal
|
||||||
|
* file stream.
|
||||||
|
*
|
||||||
|
* @param message The message to write
|
||||||
|
* @param size The size of the message
|
||||||
|
*/
|
||||||
|
void write(const char *message, size_t size) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the internal file stream.
|
||||||
|
*/
|
||||||
|
void close() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond MATADOR_DEV
|
||||||
|
FILE *stream = nullptr;
|
||||||
|
/// @endcond
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //MATADOR_BASIC_FILE_SINK_HPP
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
#ifndef MATADOR_FILE_SINK_HPP
|
||||||
|
#define MATADOR_FILE_SINK_HPP
|
||||||
|
|
||||||
|
#include "matador/logger/basic_file_sink.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A file sink writing the log message to one file
|
||||||
|
*
|
||||||
|
* The log sink writes all log messages to one single
|
||||||
|
* file identified by a given path.
|
||||||
|
*
|
||||||
|
* Note because there is no limit, the file grows infinitely.
|
||||||
|
*/
|
||||||
|
class file_sink final : public basic_file_sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a file_sink with the given path.
|
||||||
|
* If the path doesn't exist, it is created.
|
||||||
|
*
|
||||||
|
* @param path The log file to write to
|
||||||
|
*/
|
||||||
|
explicit file_sink(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a file_sink with the given path.
|
||||||
|
* If the path doesn't exist, it is created.
|
||||||
|
*
|
||||||
|
* @param path The log file to write to
|
||||||
|
*/
|
||||||
|
explicit file_sink(const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the file_sink
|
||||||
|
*/
|
||||||
|
~file_sink() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path to the log file.
|
||||||
|
*
|
||||||
|
* @return The path to the log file
|
||||||
|
*/
|
||||||
|
std::string path() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string path_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Log sink writing to stdout
|
||||||
|
*
|
||||||
|
* This log sink writes all messages to stdout.
|
||||||
|
*/
|
||||||
|
class stdout_sink final : public basic_file_sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
stdout_sink();
|
||||||
|
~stdout_sink() override = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do nothing on close
|
||||||
|
*/
|
||||||
|
void close() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Log sink writing to stderr
|
||||||
|
*
|
||||||
|
* This log sink writes all messages to stderr.
|
||||||
|
*/
|
||||||
|
class stderr_sink final : public basic_file_sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
stderr_sink();
|
||||||
|
~stderr_sink() override = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do nothing on close
|
||||||
|
*/
|
||||||
|
void close() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MATADOR_FILE_SINK_HPP
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
#ifndef MATADOR_LOG_DOMAIN_HPP
|
||||||
|
#define MATADOR_LOG_DOMAIN_HPP
|
||||||
|
|
||||||
|
#include "matador/logger/log_level.hpp"
|
||||||
|
#include "matador/logger/log_sink.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connection to a set of log sinks
|
||||||
|
*
|
||||||
|
* A log domain is the connection point between
|
||||||
|
* a set of log sinks and the logger objects
|
||||||
|
* in the user code.
|
||||||
|
*
|
||||||
|
* A domain consists of a unique name and a
|
||||||
|
* list of sinks
|
||||||
|
*/
|
||||||
|
class log_domain final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* The time format for each log line
|
||||||
|
*/
|
||||||
|
static constexpr auto TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%f";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a log_domain with the given name
|
||||||
|
* and the given log range
|
||||||
|
*
|
||||||
|
* @param name The name of the log domain
|
||||||
|
* @param log_range The log range of this domain
|
||||||
|
*/
|
||||||
|
log_domain(std::string name, log_level_range log_range);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the domain
|
||||||
|
*
|
||||||
|
* @return The name of the domain
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::string name() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the max log level. The default
|
||||||
|
* max level is LVL_FATAL
|
||||||
|
*
|
||||||
|
* @param max_level max log level
|
||||||
|
*/
|
||||||
|
void max_log_level(log_level max_level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the max log level
|
||||||
|
*
|
||||||
|
* @return The max log level
|
||||||
|
*/
|
||||||
|
[[nodiscard]] log_level max_log_level() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the min log level. Default
|
||||||
|
* min leven is LVL_INFO
|
||||||
|
*
|
||||||
|
* @param min_level min log level
|
||||||
|
*/
|
||||||
|
void min_log_level(log_level min_level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the min log level
|
||||||
|
*
|
||||||
|
* @return The min log level
|
||||||
|
*/
|
||||||
|
[[nodiscard]] log_level min_log_level() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a sink to the domain.
|
||||||
|
*
|
||||||
|
* The sink must be packed into a std::shared_ptr
|
||||||
|
* because it can be shared among other domains
|
||||||
|
*
|
||||||
|
* @param sink The sink to add
|
||||||
|
*/
|
||||||
|
void add_sink(sink_ptr sink);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs the given message for the given source and log level
|
||||||
|
* to this log domain.
|
||||||
|
*
|
||||||
|
* @param lvl Log level
|
||||||
|
* @param source Source of the log message
|
||||||
|
* @param message Message to log
|
||||||
|
*/
|
||||||
|
void log(log_level lvl, const std::string &source, const char *message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the list of log sinks
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void get_time_stamp(char* timestamp_buffer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::map<log_level, std::string> level_strings;
|
||||||
|
|
||||||
|
std::string name_;
|
||||||
|
std::list<sink_ptr> sinks{};
|
||||||
|
|
||||||
|
log_level_range log_level_range_;
|
||||||
|
|
||||||
|
std::mutex mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //MATADOR_LOG_DOMAIN_HPP
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef MATADOR_LOG_LEVEL_HPP
|
||||||
|
#define MATADOR_LOG_LEVEL_HPP
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents all available log levels
|
||||||
|
*/
|
||||||
|
enum class log_level
|
||||||
|
{
|
||||||
|
LVL_FATAL, /**< If a serious error occurred, use FATAL level */
|
||||||
|
LVL_ERROR, /**< On error use ERROR level */
|
||||||
|
LVL_WARN, /**< Warnings should use WARN level */
|
||||||
|
LVL_INFO, /**< Information should go with INFO level */
|
||||||
|
LVL_DEBUG, /**< Debug output should use DEBUG level */
|
||||||
|
LVL_TRACE, /**< Trace information should use TRACE level */
|
||||||
|
LVL_ALL /**< This level represents all log levels and should be used for logging */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write log level in a human-readable string
|
||||||
|
* to a given std::ostream.
|
||||||
|
*
|
||||||
|
* @param os std::stream to write to
|
||||||
|
* @param lvl Log level to write
|
||||||
|
* @return The std::ostream
|
||||||
|
*/
|
||||||
|
std::ostream& operator<<(std::ostream &os, log_level lvl);
|
||||||
|
|
||||||
|
/// @cond MATADOR_DEV
|
||||||
|
|
||||||
|
struct log_level_range
|
||||||
|
{
|
||||||
|
log_level min_level = log_level::LVL_INFO;
|
||||||
|
log_level max_level = log_level::LVL_FATAL;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //MATADOR_LOG_LEVEL_HPP
|
||||||
|
|
@ -0,0 +1,303 @@
|
||||||
|
#ifndef MATADOR_LOG_MANAGER_HPP
|
||||||
|
#define MATADOR_LOG_MANAGER_HPP
|
||||||
|
|
||||||
|
#include "matador/utils/singleton.hpp"
|
||||||
|
|
||||||
|
#include "matador/logger/logger.hpp"
|
||||||
|
#include "matador/logger/log_sink.hpp"
|
||||||
|
#include "matador/logger/file_sink.hpp"
|
||||||
|
#include "matador/logger/rotating_file_sink.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Manages all log domains
|
||||||
|
*
|
||||||
|
* The log_manager class is a singleton and
|
||||||
|
* manages all available log_domains
|
||||||
|
*
|
||||||
|
* There ist always a default log domain
|
||||||
|
* with the name "default"
|
||||||
|
* available for which sinks can be added
|
||||||
|
* and loggers can be created.
|
||||||
|
*/
|
||||||
|
class log_manager final : public utils::singleton<log_manager>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a logger with the given source name
|
||||||
|
* for the default log domain
|
||||||
|
*
|
||||||
|
* @param source Name of the source
|
||||||
|
* @return The created logger
|
||||||
|
*/
|
||||||
|
[[nodiscard]] logger create_logger(std::string source) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a logger with the given source name
|
||||||
|
* for the log domain identified by the given
|
||||||
|
* log domain name.
|
||||||
|
*
|
||||||
|
* If the log domain with the given name doesn't exist,
|
||||||
|
* the domain is created
|
||||||
|
*
|
||||||
|
* @param source Name of the source
|
||||||
|
* @param domain_name The name of the log domain to execute to
|
||||||
|
* @return The created logger
|
||||||
|
*/
|
||||||
|
logger create_logger(std::string source, const std::string &domain_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a log sink to the default log_domain
|
||||||
|
*
|
||||||
|
* @param sink Sink to add to the default log_domain
|
||||||
|
*/
|
||||||
|
void add_sink(sink_ptr sink) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a log sink to the log_domain with the given name.
|
||||||
|
* If the log domain doesn't exist, it is automatically created.
|
||||||
|
*
|
||||||
|
* @param sink Sink to add
|
||||||
|
* @param domain_name Name of the log domain
|
||||||
|
*/
|
||||||
|
void add_sink(sink_ptr sink, const std::string &domain_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all sinks from the default log domain
|
||||||
|
*/
|
||||||
|
void clear_all_sinks() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all sinks from the log domain
|
||||||
|
* with the given name
|
||||||
|
*
|
||||||
|
* @param domain_name Domain name to clear all sinks from
|
||||||
|
*/
|
||||||
|
void clear_all_sinks(const std::string &domain_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all log domains but the default log domain.
|
||||||
|
* Clears all sinks from the default log domain.
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the max default log level. The default
|
||||||
|
* max leven is LVL_FATAL. All log domains
|
||||||
|
* will start with this default max log range
|
||||||
|
*
|
||||||
|
* @param max_level max log level
|
||||||
|
*/
|
||||||
|
static void max_default_log_level(log_level max_level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default max log level
|
||||||
|
*
|
||||||
|
* @return The max log level
|
||||||
|
*/
|
||||||
|
static log_level max_default_log_level();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default min log level. The default
|
||||||
|
* min leven is LVL_INFO. All log domains
|
||||||
|
* will start with this default max log range
|
||||||
|
*
|
||||||
|
* @param min_level min log level
|
||||||
|
*/
|
||||||
|
static void min_default_log_level(log_level min_level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default min log level
|
||||||
|
*
|
||||||
|
* @return The min log level
|
||||||
|
*/
|
||||||
|
static log_level min_default_log_level();
|
||||||
|
|
||||||
|
/// @cond MATADOR_DEV
|
||||||
|
std::shared_ptr<log_domain> find_domain(const std::string &name);
|
||||||
|
void log_default(log_level lvl, const std::string &source, const char *message) const;
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @cond MATADOR_DEV
|
||||||
|
log_manager()
|
||||||
|
{
|
||||||
|
default_log_domain_ = log_domain_map_.insert(std::make_pair("default", std::make_shared<log_domain>("default", default_log_level_range_))).first->second;
|
||||||
|
}
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<log_domain> acquire_domain(const std::string &name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class utils::singleton<log_manager>;
|
||||||
|
|
||||||
|
std::shared_ptr<log_domain> default_log_domain_;
|
||||||
|
|
||||||
|
std::map<std::string, std::shared_ptr<log_domain>> log_domain_map_;
|
||||||
|
|
||||||
|
static log_level_range default_log_level_range_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to create a file log sink
|
||||||
|
* with the given path. If the path doesn't
|
||||||
|
* exist, it is created.
|
||||||
|
*
|
||||||
|
* @param logfile Path to the logfile
|
||||||
|
* @return A shared_ptr to the file_sink
|
||||||
|
*/
|
||||||
|
std::shared_ptr<file_sink> create_file_sink(const std::string &logfile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to create a stderr log sink.
|
||||||
|
*
|
||||||
|
* @return A shared_ptr to the stderr_sink
|
||||||
|
*/
|
||||||
|
std::shared_ptr<stderr_sink> create_stderr_sink();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to create a stdout log sink.
|
||||||
|
*
|
||||||
|
* @return A shared_ptr to the stdout_sink
|
||||||
|
*/
|
||||||
|
std::shared_ptr<stdout_sink> create_stdout_sink();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut to create a rotating file log sink
|
||||||
|
* with the given path, max log files and max
|
||||||
|
* log file size. If the path doesn't
|
||||||
|
* exist, it is created.
|
||||||
|
*
|
||||||
|
* @param logfile Path to the log file
|
||||||
|
* @param max_size Max log file size
|
||||||
|
* @param file_count Max number of log files
|
||||||
|
* @return A shared_ptr to the rotating_file_sink
|
||||||
|
*/
|
||||||
|
std::shared_ptr<rotating_file_sink> create_rotating_file_sink(const std::string &logfile, size_t max_size, size_t file_count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default min log level.
|
||||||
|
*
|
||||||
|
* @param min_lvl Default min log level
|
||||||
|
*/
|
||||||
|
void default_min_log_level(log_level min_lvl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default max log level.
|
||||||
|
*
|
||||||
|
* @param max_lvl Default max log level
|
||||||
|
*/
|
||||||
|
void default_max_log_level(log_level max_lvl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the domain min log level for the
|
||||||
|
* domain with the given name.
|
||||||
|
*
|
||||||
|
* @param name Log domain name
|
||||||
|
* @param min_lvl Default min log level
|
||||||
|
*/
|
||||||
|
void domain_min_log_level(const std::string &name, log_level min_lvl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default max log level for the
|
||||||
|
* domain with the given name.
|
||||||
|
*
|
||||||
|
* @param name Log domain name
|
||||||
|
* @param max_lvl Default max log level
|
||||||
|
*/
|
||||||
|
void domain_max_log_level(const std::string &name, log_level max_lvl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a log sink to the default log domain
|
||||||
|
*
|
||||||
|
* @param sink The log sink to add
|
||||||
|
*/
|
||||||
|
void add_log_sink(sink_ptr sink);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a log sink to the log domain
|
||||||
|
* with the given name. If the domain
|
||||||
|
* doesn't exist, it is created.
|
||||||
|
*
|
||||||
|
* @param sink The log sink to add
|
||||||
|
* @param domain The log domain name to add
|
||||||
|
*/
|
||||||
|
void add_log_sink(sink_ptr sink, const std::string &domain);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all sinks from the
|
||||||
|
* default domain
|
||||||
|
*/
|
||||||
|
void clear_all_log_sinks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all sinks from the log domain
|
||||||
|
* with the given domain name
|
||||||
|
*
|
||||||
|
* @param domain Domain name to clear all sinks
|
||||||
|
*/
|
||||||
|
void clear_all_log_sinks(const std::string &domain);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a logger with the given source name
|
||||||
|
* connected to the default log domain.
|
||||||
|
*
|
||||||
|
* @param source The name of the source
|
||||||
|
* @return The logger instance
|
||||||
|
*/
|
||||||
|
logger create_logger(std::string source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a logger with the given source name
|
||||||
|
* connected to the log domain with the given
|
||||||
|
* name. If the domain doesn't exist, it is created
|
||||||
|
*
|
||||||
|
* @param source The name of the source
|
||||||
|
* @param domain The name of the log domain
|
||||||
|
* @return The logger instance
|
||||||
|
*/
|
||||||
|
logger create_logger(std::string source, const std::string &domain);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs the given message for the given source and log level
|
||||||
|
* to the default log domain.
|
||||||
|
*
|
||||||
|
* @param lvl Log level
|
||||||
|
* @param source Source of the log message
|
||||||
|
* @param message Message to log
|
||||||
|
*/
|
||||||
|
void log_default(log_level lvl, const std::string &source, const char *message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the given message with source and log level
|
||||||
|
* to the default domain. The message will be created
|
||||||
|
* from the what-argument and the args while the preprocessed
|
||||||
|
* message uses the printf style to add the arguments.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments
|
||||||
|
* @param lvl Log level
|
||||||
|
* @param source Source of the log message
|
||||||
|
* @param what The printf style message
|
||||||
|
* @param args The arguments for the message
|
||||||
|
*/
|
||||||
|
template<typename... ARGS>
|
||||||
|
void log(const log_level lvl, const std::string &source, const char *what, ARGS const &... args)
|
||||||
|
{
|
||||||
|
char message_buffer[16384];
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
sprintf_s(message_buffer, 912, what, args...);
|
||||||
|
#else
|
||||||
|
sprintf(message_buffer, what, args...);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
log_default(lvl, source, message_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MATADOR_LOG_MANAGER_HPP
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef MATADOR_LOG_SINK_HPP
|
||||||
|
#define MATADOR_LOG_SINK_HPP
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base class for all log sinks
|
||||||
|
*
|
||||||
|
* This class must be the base class for all
|
||||||
|
* log sinks and provides their interface
|
||||||
|
*
|
||||||
|
* The main interface is the write() interface
|
||||||
|
* defining how the log message is written.
|
||||||
|
*
|
||||||
|
* The close() interface defines a way to close
|
||||||
|
* the concrete log sink
|
||||||
|
*/
|
||||||
|
class log_sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Destroys the log sink
|
||||||
|
*/
|
||||||
|
virtual ~log_sink() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the given log message with the given size
|
||||||
|
* to the concrete sink
|
||||||
|
*
|
||||||
|
* @param message The message to log
|
||||||
|
* @param size The size of the message
|
||||||
|
*/
|
||||||
|
virtual void write(const char *message, std::size_t size) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the log sink if necessary.
|
||||||
|
*/
|
||||||
|
virtual void close() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using sink_ptr = std::shared_ptr<log_sink>; /**< Shortcut to the log sink shared pointer */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MATADOR_LOG_SINK_HPP
|
||||||
|
|
@ -0,0 +1,267 @@
|
||||||
|
#ifndef MATADOR_LOGGER_HPP
|
||||||
|
#define MATADOR_LOGGER_HPP
|
||||||
|
|
||||||
|
#include "matador/logger/log_level.hpp"
|
||||||
|
#include "matador/logger/log_domain.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief logger to write log messages to log domains
|
||||||
|
*
|
||||||
|
* This class is used to write log messages to a connected
|
||||||
|
* log domain (@sa log_domain).
|
||||||
|
* Everywhere a logger is needed, it can be instantiated with
|
||||||
|
* @code
|
||||||
|
* matador::create_logger(source name)
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* The interface provides methods to log to each relevant
|
||||||
|
* log level (@sa log_level)
|
||||||
|
*
|
||||||
|
* The message format syntax is like the printf syntax.
|
||||||
|
* If the message string contains placeholder (beginning with %)
|
||||||
|
* an argument is expected to be part of the argument list of the
|
||||||
|
* calling method.
|
||||||
|
*
|
||||||
|
* All log messages are written through the internal
|
||||||
|
* log_domain object to the sinks.
|
||||||
|
*/
|
||||||
|
class logger final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a logger with a given source name connected
|
||||||
|
* to the given log_domain
|
||||||
|
*
|
||||||
|
* @param source The name of the source
|
||||||
|
* @param log_domain The log_domain containing the log sinks
|
||||||
|
*/
|
||||||
|
logger(std::string source, std::shared_ptr<log_domain> log_domain);
|
||||||
|
|
||||||
|
logger(const logger& l) = delete;
|
||||||
|
logger(logger&& l) noexcept;
|
||||||
|
logger& operator=(const logger& l) = delete;
|
||||||
|
logger& operator=(logger&& l) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message string with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void fatal(const std::string &what, ARGS const &... args) { fatal(what.c_str(), args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void fatal(const char *what, ARGS const &... args) { log(log_level::LVL_FATAL, what, args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message string with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void error(const std::string &what, ARGS const &... args) { error(what.c_str(), args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void error(const char *what, ARGS const &... args) { log(log_level::LVL_ERROR, what, args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message string with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void warn(const std::string &what, ARGS const &... args) { warn(what.c_str(), args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void warn(const char *what, ARGS const &... args) { log(log_level::LVL_WARN, what, args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message string with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void info(const std::string &what, ARGS const &... args) { info(what.c_str(), args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void info(const char *what, ARGS const &... args) { log(log_level::LVL_INFO, what, args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message string with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void debug(const std::string &what, ARGS const &... args) { debug(what.c_str(), args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void debug(const char *what, ARGS const &... args) { log(log_level::LVL_DEBUG, what, args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message string with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void trace(const std::string &what, ARGS const &... args) { trace(what.c_str(), args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message represented by a char pointer with log level LVL_FATAL
|
||||||
|
* to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void trace(const char *what, ARGS const &... args) { log(log_level::LVL_TRACE, what, args...); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message represented by a char pointer
|
||||||
|
* with the given log level to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @tparam ARGS Type of the arguments to be replaced for the message placeholder
|
||||||
|
* @param lvl The log level
|
||||||
|
* @param what The message to log
|
||||||
|
* @param args The arguments to be replaced in the message
|
||||||
|
*/
|
||||||
|
template<typename ... ARGS>
|
||||||
|
void log(log_level lvl, const char *what, ARGS const &... args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a log message represented by a char pointer
|
||||||
|
* with the given log level to the connected log_domain.
|
||||||
|
*
|
||||||
|
* @param lvl The log level
|
||||||
|
* @param what The message to log
|
||||||
|
*/
|
||||||
|
void log(log_level lvl, const char *what) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the source the logger represents
|
||||||
|
*
|
||||||
|
* @return Represented log source name
|
||||||
|
*/
|
||||||
|
const std::string& source() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the connected log domain
|
||||||
|
*
|
||||||
|
* @return The name of the log domain
|
||||||
|
*/
|
||||||
|
std::string domain() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string source_;
|
||||||
|
std::shared_ptr<log_domain> logger_domain_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... ARGS>
|
||||||
|
void logger::log(log_level lvl, const char *what, ARGS const &... args)
|
||||||
|
{
|
||||||
|
char message_buffer[16384];
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
sprintf_s(message_buffer, 16384, what, args...);
|
||||||
|
#else
|
||||||
|
sprintf(message_buffer, what, args...);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
logger_domain_->log(lvl, source_, message_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
decltype(auto) myForward(T&& t)
|
||||||
|
{
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
decltype(auto) myForward(std::string& t)
|
||||||
|
{
|
||||||
|
return t.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
decltype(auto) myForward(std::string&& t)
|
||||||
|
{
|
||||||
|
return t.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
static void log(const char* pszFmt, Args&&... args)
|
||||||
|
{
|
||||||
|
doSomething(pszFmt, myForward<Args>(std::forward<Args>(args))...);
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MATADOR_LOGGER_HPP
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
#ifndef MATADOR_ROTATING_FILE_SINK_HPP
|
||||||
|
#define MATADOR_ROTATING_FILE_SINK_HPP
|
||||||
|
|
||||||
|
#include "matador/logger/log_sink.hpp"
|
||||||
|
#include "matador/utils/file.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A rotating log file sink
|
||||||
|
*
|
||||||
|
* This log sink provides a possibility to
|
||||||
|
* rotate several log files if the current
|
||||||
|
* log file reaches the maximum size.
|
||||||
|
*
|
||||||
|
* The user can define the maximum number of log files
|
||||||
|
* and the maximum size of the current log file
|
||||||
|
*
|
||||||
|
* The name of the current log file is defined within the
|
||||||
|
* given logfile path. Each rotated (moved) log file gets
|
||||||
|
* an incremented number extension right before the
|
||||||
|
* file extension, e.g.:
|
||||||
|
*
|
||||||
|
* Log file name is 'log.txt' the first rotated log file
|
||||||
|
* is named 'log-1.txt' and so on until the maximum
|
||||||
|
* number of log files is reached. Then it starts from
|
||||||
|
* the beginning.
|
||||||
|
* Keep in mind that the log file to which is currently
|
||||||
|
* written to is always named like the file name
|
||||||
|
* given within the path.
|
||||||
|
*/
|
||||||
|
class rotating_file_sink final : public log_sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a rotating log file sink within the given path
|
||||||
|
* with the given maximum number of rotating log files where
|
||||||
|
* each file size is never greater than the given max file
|
||||||
|
* size.
|
||||||
|
*
|
||||||
|
* @param path Path of the log file
|
||||||
|
* @param max_size Max log file size
|
||||||
|
* @param file_count Max log file count
|
||||||
|
*/
|
||||||
|
rotating_file_sink(const std::string& path, size_t max_size, size_t file_count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the message to the current log file. If the
|
||||||
|
* actual size exceeds the file size limit, the log files
|
||||||
|
* are rotated.
|
||||||
|
*
|
||||||
|
* @param message Message to write
|
||||||
|
* @param size The size of the log message
|
||||||
|
*/
|
||||||
|
void write(const char *message, size_t size) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close all open log files
|
||||||
|
*/
|
||||||
|
void close() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string calculate_filename(size_t fileno);
|
||||||
|
|
||||||
|
void rotate();
|
||||||
|
void prepare(const std::string &path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
file logfile_;
|
||||||
|
std::string path_;
|
||||||
|
std::string base_path_;
|
||||||
|
std::string extension_;
|
||||||
|
size_t max_size_ = 0;
|
||||||
|
size_t current_size_ = 0;
|
||||||
|
size_t current_file_no_ = 0;
|
||||||
|
size_t file_count_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //MATADOR_ROTATING_FILE_SINK_HPP
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef SCHEMA_HPP
|
#ifndef SCHEMA_HPP
|
||||||
#define SCHEMA_HPP
|
#define SCHEMA_HPP
|
||||||
|
|
||||||
|
#include "matador/logger/log_manager.hpp"
|
||||||
#include "matador/object/many_to_many_relation.hpp"
|
#include "matador/object/many_to_many_relation.hpp"
|
||||||
#include "matador/object/primary_key_resolver.hpp"
|
#include "matador/object/primary_key_resolver.hpp"
|
||||||
#include "matador/object/error_code.hpp"
|
#include "matador/object/error_code.hpp"
|
||||||
|
|
@ -10,6 +11,8 @@
|
||||||
#include "matador/utils/result.hpp"
|
#include "matador/utils/result.hpp"
|
||||||
#include "matador/utils/error.hpp"
|
#include "matador/utils/error.hpp"
|
||||||
|
|
||||||
|
#include "matador/logger/logger.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -77,9 +80,7 @@ public:
|
||||||
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||||
|
|
||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
void on_belongs_to(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) {
|
void on_belongs_to(const char *id, ForeignPointerType &obj, const utils::foreign_attributes &attr);
|
||||||
on_foreign_key<ForeignPointerType>();
|
|
||||||
}
|
|
||||||
template<class ForeignPointerType>
|
template<class ForeignPointerType>
|
||||||
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
||||||
|
|
||||||
|
|
@ -93,19 +94,16 @@ public:
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
void on_has_many_to_many(const char *id, ContainerType &collection, const utils::foreign_attributes &attr);
|
void on_has_many_to_many(const char *id, ContainerType &collection, const utils::foreign_attributes &attr);
|
||||||
|
|
||||||
private:
|
|
||||||
template<class ForeignPointerType>
|
|
||||||
void on_foreign_key();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit relation_completer(schema_node& node)
|
explicit relation_completer(schema_node& node)
|
||||||
: node_(node)
|
: node_(node)
|
||||||
, schema_(node.schema_){}
|
, schema_(node.schema_)
|
||||||
|
, log_(logger::create_logger("relation_completer")) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
schema_node &node_;
|
schema_node &node_;
|
||||||
schema& schema_;
|
schema& schema_;
|
||||||
|
logger::logger log_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -239,6 +237,7 @@ private:
|
||||||
t_node_map node_map_;
|
t_node_map node_map_;
|
||||||
t_type_index_node_map type_index_node_map_;
|
t_type_index_node_map type_index_node_map_;
|
||||||
t_type_index_node_map expected_node_map_;
|
t_type_index_node_map expected_node_map_;
|
||||||
|
logger::logger log_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
|
|
@ -250,7 +249,7 @@ void relation_completer<Type>::on_has_many( const char *id, CollectionType&, con
|
||||||
return new many_to_many_relation<value_type, Type>(join_column, "id");
|
return new many_to_many_relation<value_type, Type>(join_column, "id");
|
||||||
});
|
});
|
||||||
|
|
||||||
schema_.attach_node(node, typeid(many_to_many_relation<value_type, Type>));
|
// schema_.attach_node(node, typeid(many_to_many_relation<value_type, Type>));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
|
|
@ -262,7 +261,7 @@ void relation_completer<Type>::on_has_many( const char *id, CollectionType&, con
|
||||||
return new many_to_many_relation<value_type, Type>(join_column, "value");
|
return new many_to_many_relation<value_type, Type>(join_column, "value");
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto result = schema_.attach<many_to_many_relation<value_type, Type>>(id);
|
const auto result = schema_.attach<many_to_relation<Type, value_type>>(id);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// Todo: throw internal exception
|
// Todo: throw internal exception
|
||||||
}
|
}
|
||||||
|
|
@ -280,15 +279,23 @@ void relation_completer<Type>::on_has_many_to_many( const char *id, CollectionTy
|
||||||
return new many_to_many_relation<typename CollectionType::value_type, Type>(join_column, inverse_join_column);
|
return new many_to_many_relation<typename CollectionType::value_type, Type>(join_column, inverse_join_column);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto node = schema_node::make_relation_node<relation_type>(schema_, id);
|
// auto node = schema_node::make_relation_node<relation_type>(schema_, id);
|
||||||
|
|
||||||
schema_.attach_node(node, typeid(relation_type));
|
// schema_.attach_node(node, typeid(relation_type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
void relation_completer<Type>::on_has_many_to_many( const char *id, ContainerType &collection, const utils::foreign_attributes &attr ) {
|
void relation_completer<Type>::on_has_many_to_many( const char *id, ContainerType &collection, const utils::foreign_attributes &attr ) {
|
||||||
|
auto result = schema_.find_node(id);
|
||||||
|
if (!result) {
|
||||||
|
using relation_type = many_to_many_relation<typename ContainerType::value_type, Type>;
|
||||||
|
// auto creator = [attr] {
|
||||||
|
// return new relation_type(attr.join_column, attr.inverse_join_column);
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
|
|
@ -311,6 +318,11 @@ void relation_completer<Type>::on_has_one(const char * id, ForeignPointerType &/
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
template<class ForeignPointerType>
|
||||||
|
void relation_completer<Type>::on_belongs_to( const char* id, ForeignPointerType& obj, const utils::foreign_attributes& attr ) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,11 @@ public:
|
||||||
static std::shared_ptr<schema_node> make_relation_node(object::schema& tree, const std::string& name, CreatorFunc &&creator) {
|
static std::shared_ptr<schema_node> make_relation_node(object::schema& tree, const std::string& name, CreatorFunc &&creator) {
|
||||||
auto node = std::shared_ptr<schema_node>(new schema_node(tree, name));
|
auto node = std::shared_ptr<schema_node>(new schema_node(tree, name));
|
||||||
|
|
||||||
auto info = std::make_unique<object_info<Type>>(
|
// auto info = std::make_unique<object_info<Type>>(
|
||||||
node,
|
// node,
|
||||||
object_definition{attribute_definition_generator::generate<Type>(tree)}
|
// object_definition{attribute_definition_generator::generate<Type>(tree)}
|
||||||
);
|
// );
|
||||||
node->info_ = std::move(info);
|
// node->info_ = std::move(info);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
#ifndef MATADOR_FILE_HPP
|
||||||
|
#define MATADOR_FILE_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace matador {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File class representing file stream
|
||||||
|
*
|
||||||
|
* The open methods uses internally fopen() thus
|
||||||
|
* the open modes are the same:
|
||||||
|
*
|
||||||
|
* "r" read: Open file for input operations. The file must exist.
|
||||||
|
* "w" write: Create an empty file for output operations. If a
|
||||||
|
* file with the same name already exists, its contents are discarded and
|
||||||
|
* the file is treated as a new empty file.
|
||||||
|
* "a" append: Open file for output at the end of a file. Output operations
|
||||||
|
* always write data at the end of the file, expanding it. Repositioning
|
||||||
|
* operations (fseek, fsetpos, rewind) are ignored. The file is created
|
||||||
|
* if it does not exist.
|
||||||
|
* "r+" read/update: Open a file for update (both for input and output).
|
||||||
|
* The file must exist.
|
||||||
|
* "w+" write/update: Create an empty file and open it for update (both for input
|
||||||
|
* and output). If a file with the same name already exists its contents
|
||||||
|
* are discarded and the file is treated as a new empty file.
|
||||||
|
* "a+" append/update: Open a file for update (both for input and output) with
|
||||||
|
* all output operations writing data at the end of the file. Repositioning
|
||||||
|
* operations (fseek, fsetpos, rewind) affects the next input operations, but
|
||||||
|
* output operations move the position back to the end of file. The file is
|
||||||
|
* created if it does not exist.
|
||||||
|
*/
|
||||||
|
class file final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates an uninitialized file.
|
||||||
|
*/
|
||||||
|
file() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and open a file stream.
|
||||||
|
*
|
||||||
|
* @param path Path of the file to create
|
||||||
|
* @param mode The file mode to open
|
||||||
|
*/
|
||||||
|
file(const char *path, const char *mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and open a file stream.
|
||||||
|
*
|
||||||
|
* @param path Path of the file to create
|
||||||
|
* @param mode The file mode to open
|
||||||
|
*/
|
||||||
|
file(const std::string &path, const char *mode);
|
||||||
|
file(const file&) = delete;
|
||||||
|
file operator=(const file&) = delete;
|
||||||
|
~file();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a file stream with the given path.
|
||||||
|
* If the file is already open it is closed
|
||||||
|
*
|
||||||
|
* @param path Path of the file to create
|
||||||
|
* @param mode The file mode to open
|
||||||
|
*/
|
||||||
|
void open(const char *path, const char *mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a file stream with the given path.
|
||||||
|
* If the file is already open it is closed
|
||||||
|
*
|
||||||
|
* @param path Path of the file to create
|
||||||
|
* @param mode The file mode to open
|
||||||
|
*/
|
||||||
|
void open(const std::string &path, const char *mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the file stream if it is open
|
||||||
|
*/
|
||||||
|
void close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the file
|
||||||
|
*
|
||||||
|
* @return The size of the file
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_t size() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path to the file
|
||||||
|
*
|
||||||
|
* @return The path to the file
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::string path() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the internal file stream pointer
|
||||||
|
*
|
||||||
|
* @return The internal file stream pointer
|
||||||
|
*/
|
||||||
|
[[nodiscard]] FILE* stream() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if file is open.
|
||||||
|
*
|
||||||
|
* @return True if file is open
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool is_open() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string path_;
|
||||||
|
FILE *stream_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a given file as text and
|
||||||
|
* returns its content as string
|
||||||
|
*
|
||||||
|
* @param f File to read in
|
||||||
|
* @return The content of the file as string
|
||||||
|
*/
|
||||||
|
std::string read_as_text(const file &f);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //MATADOR_FILE_HPP
|
||||||
|
|
@ -18,7 +18,7 @@ template < typename T >
|
||||||
class singleton
|
class singleton
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef T value_type; /**< Shortcut for the singletons type */
|
typedef T value_type; /**< Shortcut for the singleton type */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access the instance of the class.
|
* @brief Access the instance of the class.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,12 @@
|
||||||
add_library(matador-core STATIC
|
add_library(matador-core STATIC
|
||||||
|
../../include/matador/logger/basic_file_sink.hpp
|
||||||
|
../../include/matador/logger/file_sink.hpp
|
||||||
|
../../include/matador/logger/log_domain.hpp
|
||||||
|
../../include/matador/logger/log_level.hpp
|
||||||
|
../../include/matador/logger/log_manager.hpp
|
||||||
|
../../include/matador/logger/log_sink.hpp
|
||||||
|
../../include/matador/logger/logger.hpp
|
||||||
|
../../include/matador/logger/rotating_file_sink.hpp
|
||||||
../../include/matador/net/event_type.hpp
|
../../include/matador/net/event_type.hpp
|
||||||
../../include/matador/net/handler.hpp
|
../../include/matador/net/handler.hpp
|
||||||
../../include/matador/net/os.hpp
|
../../include/matador/net/os.hpp
|
||||||
|
|
@ -36,6 +44,7 @@ add_library(matador-core STATIC
|
||||||
../../include/matador/utils/errors.hpp
|
../../include/matador/utils/errors.hpp
|
||||||
../../include/matador/utils/export.hpp
|
../../include/matador/utils/export.hpp
|
||||||
../../include/matador/utils/fetch_type.hpp
|
../../include/matador/utils/fetch_type.hpp
|
||||||
|
../../include/matador/utils/file.hpp
|
||||||
../../include/matador/utils/field_attributes.hpp
|
../../include/matador/utils/field_attributes.hpp
|
||||||
../../include/matador/utils/foreign_attributes.hpp
|
../../include/matador/utils/foreign_attributes.hpp
|
||||||
../../include/matador/utils/identifier.hpp
|
../../include/matador/utils/identifier.hpp
|
||||||
|
|
@ -52,6 +61,13 @@ add_library(matador-core STATIC
|
||||||
../../include/matador/utils/uuid.hpp
|
../../include/matador/utils/uuid.hpp
|
||||||
../../include/matador/utils/value.hpp
|
../../include/matador/utils/value.hpp
|
||||||
../../include/matador/utils/version.hpp
|
../../include/matador/utils/version.hpp
|
||||||
|
logger/basic_file_sink.cpp
|
||||||
|
logger/file_sink.cpp
|
||||||
|
logger/log_domain.cpp
|
||||||
|
logger/log_level.cpp
|
||||||
|
logger/log_manager.cpp
|
||||||
|
logger/logger.cpp
|
||||||
|
logger/rotating_file_sink.cpp
|
||||||
object/attribute_definition_generator.cpp
|
object/attribute_definition_generator.cpp
|
||||||
object/attribute_definition.cpp
|
object/attribute_definition.cpp
|
||||||
object/basic_object_info.cpp
|
object/basic_object_info.cpp
|
||||||
|
|
@ -66,6 +82,7 @@ add_library(matador-core STATIC
|
||||||
utils/error.cpp
|
utils/error.cpp
|
||||||
utils/errors.cpp
|
utils/errors.cpp
|
||||||
utils/field_attributes.cpp
|
utils/field_attributes.cpp
|
||||||
|
utils/file.cpp
|
||||||
utils/foreign_attributes.cpp
|
utils/foreign_attributes.cpp
|
||||||
utils/identifier.cpp
|
utils/identifier.cpp
|
||||||
utils/leader_follower_thread_pool.cpp
|
utils/leader_follower_thread_pool.cpp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include "matador/logger/basic_file_sink.hpp"
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
basic_file_sink::basic_file_sink(FILE *f)
|
||||||
|
: stream(f)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void basic_file_sink::write(const char *message, const size_t size)
|
||||||
|
{
|
||||||
|
fwrite(message, sizeof(char), size, stream);
|
||||||
|
fflush(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void basic_file_sink::close()
|
||||||
|
{
|
||||||
|
if (stream) {
|
||||||
|
fclose(stream);
|
||||||
|
stream = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include "matador/logger/file_sink.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/os.hpp"
|
||||||
|
#include "matador/utils/string.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
file_sink::file_sink(const std::string &path)
|
||||||
|
: path_(path)
|
||||||
|
{
|
||||||
|
std::string filename(path);
|
||||||
|
// find last dir delimiter
|
||||||
|
const char *last = strrchr(path.c_str(), os::DIR_SEPARATOR);
|
||||||
|
if (last != nullptr) {
|
||||||
|
path_.assign(path.data(), last-path.data());
|
||||||
|
} else {
|
||||||
|
path_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last != nullptr) {
|
||||||
|
filename = (last + 1);
|
||||||
|
}
|
||||||
|
// extract base path and extension
|
||||||
|
if (std::vector<std::string> result; utils::split(filename, '.', result) != 2) {
|
||||||
|
throw std::logic_error("split path must consists of two elements");
|
||||||
|
}
|
||||||
|
// get the current path
|
||||||
|
const auto pwd = os::get_current_dir();
|
||||||
|
// make the path
|
||||||
|
os::mkpath(path_);
|
||||||
|
// change into the path
|
||||||
|
os::chdir(path_);
|
||||||
|
// create the file
|
||||||
|
stream = os::fopen(filename, "a");
|
||||||
|
if (stream == nullptr) {
|
||||||
|
os::chdir(pwd);
|
||||||
|
throw std::logic_error("error opening file");
|
||||||
|
}
|
||||||
|
os::chdir(pwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_sink::file_sink(const char *path)
|
||||||
|
: file_sink(std::string(path))
|
||||||
|
{}
|
||||||
|
|
||||||
|
file_sink::~file_sink()
|
||||||
|
{
|
||||||
|
if (stream) {
|
||||||
|
fclose(stream);
|
||||||
|
stream = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string file_sink::path() const
|
||||||
|
{
|
||||||
|
return path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout_sink::stdout_sink()
|
||||||
|
: basic_file_sink(stdout)
|
||||||
|
{}
|
||||||
|
|
||||||
|
stderr_sink::stderr_sink()
|
||||||
|
: basic_file_sink(stderr)
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
#include "matador/logger/log_domain.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
std::size_t acquire_thread_index(const std::thread::id id) {
|
||||||
|
static std::size_t next_index = 0;
|
||||||
|
static std::mutex thread_mutex;
|
||||||
|
static std::map<std::thread::id, std::size_t> ids;
|
||||||
|
std::lock_guard lock(thread_mutex);
|
||||||
|
if(ids.find(id) == ids.end()) {
|
||||||
|
ids[id] = next_index++;
|
||||||
|
}
|
||||||
|
return ids[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
char* gettimestamp(char* const buffer, const size_t size) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
const auto now = system_clock::now();
|
||||||
|
const auto now_ms = time_point_cast<milliseconds>(now);
|
||||||
|
|
||||||
|
std::time_t t = system_clock::to_time_t(now);
|
||||||
|
const auto ms = duration_cast<milliseconds>(now_ms.time_since_epoch()) % 1000;
|
||||||
|
|
||||||
|
std::tm tm{};
|
||||||
|
#ifdef _WIN32
|
||||||
|
localtime_s(&tm, &t);
|
||||||
|
#else
|
||||||
|
localtime_r(&t, &tm);
|
||||||
|
#endif
|
||||||
|
char buf[32];
|
||||||
|
std::strftime(buf, 32, "%Y-%m-%d %H:%M:%S", &tm);
|
||||||
|
|
||||||
|
std::snprintf(buffer, size, "%s.%03ld", buf, static_cast<long>(ms.count()));
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<log_level, std::string> log_domain::level_strings = { /* NOLINT */
|
||||||
|
{ log_level::LVL_DEBUG, "DEBUG" },
|
||||||
|
{ log_level::LVL_INFO, "INFO" },
|
||||||
|
{ log_level::LVL_WARN, "WARN" },
|
||||||
|
{ log_level::LVL_ERROR, "ERROR" },
|
||||||
|
{ log_level::LVL_TRACE, "TRACE" }
|
||||||
|
};
|
||||||
|
|
||||||
|
log_domain::log_domain(std::string name, const log_level_range log_range)
|
||||||
|
: name_(std::move(name))
|
||||||
|
, log_level_range_(log_range)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string log_domain::name() const
|
||||||
|
{
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_domain::max_log_level(log_level max_level)
|
||||||
|
{
|
||||||
|
log_level_range_.max_level = max_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_level log_domain::max_log_level() const
|
||||||
|
{
|
||||||
|
return log_level_range_.max_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_domain::min_log_level(log_level min_level)
|
||||||
|
{
|
||||||
|
log_level_range_.min_level = min_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_level log_domain::min_log_level() const
|
||||||
|
{
|
||||||
|
return log_level_range_.min_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_domain::add_sink(sink_ptr sink)
|
||||||
|
{
|
||||||
|
sinks.push_back(std::move(sink));
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_domain::log(log_level lvl, const std::string &source, const char *message)
|
||||||
|
{
|
||||||
|
if (lvl < log_level_range_.max_level || lvl > log_level_range_.min_level) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char timestamp[80];
|
||||||
|
get_time_stamp(timestamp);
|
||||||
|
|
||||||
|
char buffer[1024];
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
int ret = sprintf_s(buffer, 1024, "%s [Thread %zu] [%-7s] [%s]: %s\n", timestamp, details::acquire_thread_index(std::this_thread::get_id()), level_strings[lvl].c_str(), source.c_str(), message);
|
||||||
|
#else
|
||||||
|
int ret = sprintf(buffer, "%s [Thread %lu] [%-7s] [%s]: %s\n", timestamp, acquire_thread_index(std::this_thread::get_id()), level_strings[lvl].c_str(), source.c_str(), message);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> l(mutex_);
|
||||||
|
for (auto &sink : sinks) {
|
||||||
|
sink->write(buffer, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_domain::clear()
|
||||||
|
{
|
||||||
|
sinks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_domain::get_time_stamp(char* const timestamp_buffer)
|
||||||
|
{
|
||||||
|
std::lock_guard l(mutex_);
|
||||||
|
details::gettimestamp(timestamp_buffer, 80);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include "matador/logger/log_level.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream &os, const log_level lvl)
|
||||||
|
{
|
||||||
|
switch (lvl) {
|
||||||
|
case log_level::LVL_ERROR:
|
||||||
|
os << "ERROR";
|
||||||
|
break;
|
||||||
|
case log_level::LVL_FATAL:
|
||||||
|
os << "FATAL";
|
||||||
|
break;
|
||||||
|
case log_level::LVL_DEBUG:
|
||||||
|
os << "DEBUG";
|
||||||
|
break;
|
||||||
|
case log_level::LVL_INFO:
|
||||||
|
os << "INFO";
|
||||||
|
break;
|
||||||
|
case log_level::LVL_TRACE:
|
||||||
|
os << "TRACE";
|
||||||
|
break;
|
||||||
|
case log_level::LVL_WARN:
|
||||||
|
os << "WARN";
|
||||||
|
break;
|
||||||
|
case log_level::LVL_ALL:
|
||||||
|
os << "ALL";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
os << "UNKNOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
#include "matador/logger/log_manager.hpp"
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
log_level_range log_manager::default_log_level_range_ = {};
|
||||||
|
|
||||||
|
logger log_manager::create_logger(std::string source) const {
|
||||||
|
return logger(std::move(source), default_log_domain_);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger log_manager::create_logger(std::string source, const std::string &domain_name) {
|
||||||
|
return logger(std::move(source), acquire_domain(domain_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_manager::add_sink(sink_ptr sink) const {
|
||||||
|
default_log_domain_->add_sink(std::move(sink));
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_manager::add_sink(sink_ptr sink, const std::string &domain_name)
|
||||||
|
{
|
||||||
|
const auto log_domain = acquire_domain(domain_name);
|
||||||
|
|
||||||
|
log_domain->add_sink(std::move(sink));
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_manager::clear_all_sinks() const {
|
||||||
|
default_log_domain_->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_manager::clear_all_sinks(const std::string &domain_name)
|
||||||
|
{
|
||||||
|
if (const auto it = log_domain_map_.find(domain_name); it != log_domain_map_.end()) {
|
||||||
|
it->second->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_manager::clear()
|
||||||
|
{
|
||||||
|
log_domain_map_.clear();
|
||||||
|
default_log_domain_->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_manager::max_default_log_level(log_level max_level)
|
||||||
|
{
|
||||||
|
default_log_level_range_.max_level = max_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_level log_manager::max_default_log_level()
|
||||||
|
{
|
||||||
|
return default_log_level_range_.max_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_manager::min_default_log_level(log_level min_level)
|
||||||
|
{
|
||||||
|
default_log_level_range_.min_level = min_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_level log_manager::min_default_log_level()
|
||||||
|
{
|
||||||
|
return default_log_level_range_.min_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<log_domain> log_manager::acquire_domain(const std::string &name)
|
||||||
|
{
|
||||||
|
if (name == "default") {
|
||||||
|
return default_log_domain_;
|
||||||
|
}
|
||||||
|
auto it = log_domain_map_.find(name);
|
||||||
|
if (it == log_domain_map_.end()) {
|
||||||
|
it = log_domain_map_.insert(std::make_pair(name, std::make_shared<log_domain>(name, default_log_level_range_))).first;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<log_domain> log_manager::find_domain(const std::string &name)
|
||||||
|
{
|
||||||
|
if (name == "default") {
|
||||||
|
return default_log_domain_;
|
||||||
|
}
|
||||||
|
auto it = log_domain_map_.find(name);
|
||||||
|
if (it != log_domain_map_.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
return std::shared_ptr<log_domain>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_manager::log_default(const log_level lvl, const std::string &source, const char *message) const {
|
||||||
|
default_log_domain_->log(lvl, source, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<file_sink> create_file_sink(const std::string &logfile)
|
||||||
|
{
|
||||||
|
return std::make_shared<file_sink>(logfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<stderr_sink> create_stderr_sink()
|
||||||
|
{
|
||||||
|
return std::make_shared<stderr_sink>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<stdout_sink> create_stdout_sink()
|
||||||
|
{
|
||||||
|
return std::make_shared<stdout_sink>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<rotating_file_sink> create_rotating_file_sink(const std::string &logfile, size_t max_size, size_t file_count)
|
||||||
|
{
|
||||||
|
return std::make_shared<rotating_file_sink>(logfile, max_size, file_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void default_min_log_level(const log_level min_lvl)
|
||||||
|
{
|
||||||
|
log_manager::min_default_log_level(min_lvl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void default_max_log_level(const log_level max_lvl)
|
||||||
|
{
|
||||||
|
log_manager::max_default_log_level(max_lvl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void domain_min_log_level(const std::string &name, const log_level min_lvl)
|
||||||
|
{
|
||||||
|
if (const auto domain = log_manager::instance().find_domain(name)) {
|
||||||
|
domain->min_log_level(min_lvl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void domain_max_log_level(const std::string &name, const log_level max_lvl)
|
||||||
|
{
|
||||||
|
if (const auto domain = log_manager::instance().find_domain(name)) {
|
||||||
|
domain->min_log_level(max_lvl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_log_sink(sink_ptr sink)
|
||||||
|
{
|
||||||
|
log_manager::instance().add_sink(std::move(sink));
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_log_sink(sink_ptr sink, const std::string &domain)
|
||||||
|
{
|
||||||
|
log_manager::instance().add_sink(std::move(sink), domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_all_log_sinks()
|
||||||
|
{
|
||||||
|
log_manager::instance().clear_all_sinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_all_log_sinks(const std::string &domain)
|
||||||
|
{
|
||||||
|
log_manager::instance().clear_all_sinks(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger create_logger(std::string source)
|
||||||
|
{
|
||||||
|
return log_manager::instance().create_logger(std::move(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger create_logger(std::string source, const std::string &domain)
|
||||||
|
{
|
||||||
|
return log_manager::instance().create_logger(std::move(source), domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_default(const log_level lvl, const std::string &source, const char *message)
|
||||||
|
{
|
||||||
|
log_manager::instance().log_default(lvl, source, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include "matador/logger/logger.hpp"
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
logger::logger(std::string source, std::shared_ptr<log_domain> log_domain)
|
||||||
|
: source_(std::move(source))
|
||||||
|
, logger_domain_(std::move(log_domain))
|
||||||
|
{}
|
||||||
|
|
||||||
|
logger::logger(logger&& l) noexcept
|
||||||
|
: source_(std::move(l.source_))
|
||||||
|
, logger_domain_(std::move(l.logger_domain_))
|
||||||
|
{}
|
||||||
|
|
||||||
|
logger& logger::operator=(logger&& l) noexcept {
|
||||||
|
source_ = std::move(l.source_);
|
||||||
|
logger_domain_ = std::move(l.logger_domain_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& logger::source() const
|
||||||
|
{
|
||||||
|
return source_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string logger::domain() const
|
||||||
|
{
|
||||||
|
return logger_domain_->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
void logger::log(const log_level lvl, const char *what) const {
|
||||||
|
logger_domain_->log(lvl, source_, what);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
#include "matador/logger/rotating_file_sink.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/string.hpp"
|
||||||
|
#include "matador/utils/os.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace matador::logger {
|
||||||
|
|
||||||
|
rotating_file_sink::rotating_file_sink(const std::string& path, size_t max_size, size_t file_count)
|
||||||
|
: max_size_(max_size)
|
||||||
|
, file_count_(file_count)
|
||||||
|
{
|
||||||
|
// split path and file
|
||||||
|
prepare(path);
|
||||||
|
|
||||||
|
current_size_ = logfile_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rotating_file_sink::write(const char *message, size_t size)
|
||||||
|
{
|
||||||
|
current_size_ += size;
|
||||||
|
if (current_size_ > max_size_) {
|
||||||
|
current_size_ = size;
|
||||||
|
rotate();
|
||||||
|
}
|
||||||
|
fwrite(message, sizeof(char), size, logfile_.stream());
|
||||||
|
fflush(logfile_.stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
void rotating_file_sink::close()
|
||||||
|
{
|
||||||
|
logfile_.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rotating_file_sink::calculate_filename(size_t fileno)
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
#ifdef _WIN32
|
||||||
|
sprintf_s(buffer, 1024, "%s.%zu.%s", base_path_.c_str(), fileno, extension_.c_str());
|
||||||
|
#else
|
||||||
|
sprintf(buffer, "%s.%zu.%s", base_path_.c_str(), fileno, extension_.c_str());
|
||||||
|
#endif
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rotate log files:
|
||||||
|
* log_path.log -> log_path.01.log
|
||||||
|
* log_path.01.log -> log_path.02.log
|
||||||
|
* log_path.02.log -> log_path.03.log
|
||||||
|
* ...
|
||||||
|
* log_path.[n-1].log -> log_path.[n].log
|
||||||
|
* delete log_path.[n].log
|
||||||
|
* create new log_path.log
|
||||||
|
*/
|
||||||
|
void rotating_file_sink::rotate()
|
||||||
|
{
|
||||||
|
std::string filename;
|
||||||
|
for (size_t i = file_count_; i != 0; --i) {
|
||||||
|
filename = calculate_filename(i);
|
||||||
|
if (!matador::os::exists(filename.c_str())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (i == file_count_) {
|
||||||
|
matador::os::remove(filename.c_str());
|
||||||
|
} else {
|
||||||
|
matador::os::rename(filename.c_str(), calculate_filename(i+1).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string path = logfile_.path();
|
||||||
|
logfile_.close();
|
||||||
|
matador::os::rename(path.c_str(), filename.c_str());
|
||||||
|
logfile_.open(path, "a");
|
||||||
|
}
|
||||||
|
|
||||||
|
void rotating_file_sink::prepare(const std::string &path)
|
||||||
|
{
|
||||||
|
std::string filename(path);
|
||||||
|
// find last dir delimiter
|
||||||
|
const char *last = strrchr(path.c_str(), os::DIR_SEPARATOR);
|
||||||
|
if (last != nullptr) {
|
||||||
|
path_.assign(path.data(), last-path.data());
|
||||||
|
} else {
|
||||||
|
path_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last != nullptr) {
|
||||||
|
filename = (last + 1);
|
||||||
|
}
|
||||||
|
// extract base path and extension
|
||||||
|
std::vector<std::string> result;
|
||||||
|
if (utils::split(filename, '.', result) != 2) {
|
||||||
|
throw std::logic_error("split path must consists of two elements");
|
||||||
|
}
|
||||||
|
base_path_.assign(result[0]);
|
||||||
|
extension_.assign(result[1]);
|
||||||
|
// get the current path
|
||||||
|
const auto pwd = os::get_current_dir();
|
||||||
|
// make the path
|
||||||
|
os::mkpath(path_);
|
||||||
|
// change into the path
|
||||||
|
os::chdir(path_);
|
||||||
|
// create the file
|
||||||
|
logfile_.open(filename, "a");
|
||||||
|
// change back to the origin path
|
||||||
|
os::chdir(pwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,8 @@ utils::error make_error(const error_code ec, const std::string &msg) {
|
||||||
|
|
||||||
schema::schema(std::string name)
|
schema::schema(std::string name)
|
||||||
: name_(std::move(name))
|
: name_(std::move(name))
|
||||||
, root_(schema_node::make_null_node(*this)) {
|
, root_(schema_node::make_null_node(*this))
|
||||||
|
, log_(logger::create_logger("schema")) {
|
||||||
root_->first_child_ = std::shared_ptr<schema_node>(new schema_node(*this));
|
root_->first_child_ = std::shared_ptr<schema_node>(new schema_node(*this));
|
||||||
root_->last_child_ = std::shared_ptr<schema_node>(new schema_node(*this));
|
root_->last_child_ = std::shared_ptr<schema_node>(new schema_node(*this));
|
||||||
root_->first_child_->next_sibling_ = root_->last_child_;
|
root_->first_child_->next_sibling_ = root_->last_child_;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
#include "matador/utils/file.hpp"
|
||||||
|
#include "matador/utils/os.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace matador {
|
||||||
|
|
||||||
|
file::file(const char *path, const char *mode)
|
||||||
|
: file(std::string(path), mode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
file::file(const std::string &path, const char *mode)
|
||||||
|
{
|
||||||
|
open(path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
file::~file()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void file::open(const char *path, const char *mode)
|
||||||
|
{
|
||||||
|
open(std::string(path), mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file::open(const std::string &path, const char *mode)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
path_ = path;
|
||||||
|
stream_ = os::fopen(path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file::close()
|
||||||
|
{
|
||||||
|
if (stream_ != nullptr) {
|
||||||
|
os::fclose(stream_);
|
||||||
|
stream_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t file::size() const
|
||||||
|
{
|
||||||
|
return os::file_size(stream_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string file::path() const
|
||||||
|
{
|
||||||
|
return path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *file::stream() const
|
||||||
|
{
|
||||||
|
return stream_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::is_open() const
|
||||||
|
{
|
||||||
|
return stream_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string read_as_text(const file &f)
|
||||||
|
{
|
||||||
|
if (!f.is_open()) {
|
||||||
|
throw std::logic_error("file is not open");
|
||||||
|
}
|
||||||
|
|
||||||
|
// get file size:
|
||||||
|
fseek (f.stream() , 0 , SEEK_END);
|
||||||
|
const size_t size = ftell(f.stream());
|
||||||
|
rewind (f.stream());
|
||||||
|
|
||||||
|
std::string result(size, '\0');
|
||||||
|
|
||||||
|
if (const auto ret = fread((result.data()), 1, size, f.stream()); ret == 0 && ferror(f.stream())) {
|
||||||
|
char error_buffer[1024];
|
||||||
|
os::strerror(errno, error_buffer, 1024);
|
||||||
|
throw std::logic_error(error_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ CPMAddPackage("gh:catchorg/Catch2@3.7.1")
|
||||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
||||||
|
|
||||||
add_executable(CoreTests
|
add_executable(CoreTests
|
||||||
|
logger/LoggerTest.cpp
|
||||||
utils/BasicTypeToVisitorTest.cpp
|
utils/BasicTypeToVisitorTest.cpp
|
||||||
utils/ConvertTest.cpp
|
utils/ConvertTest.cpp
|
||||||
utils/DefaultTypeTraitsTest.cpp
|
utils/DefaultTypeTraitsTest.cpp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,374 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include "matador/logger/log_manager.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/os.hpp"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <io.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <regex>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace matador::logger;
|
||||||
|
|
||||||
|
namespace filehelper {
|
||||||
|
|
||||||
|
class std_stream_switcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit std_stream_switcher(FILE *str, const char* redirect)
|
||||||
|
: stream(str) {
|
||||||
|
fflush(stream);
|
||||||
|
fgetpos(stream, &pos);
|
||||||
|
fd = matador::os::dup(stream);
|
||||||
|
matador::os::freopen(redirect, "w+", stream);
|
||||||
|
}
|
||||||
|
~std_stream_switcher() {
|
||||||
|
fflush(stream);
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
_dup2(fd, _fileno(stream));
|
||||||
|
_close(fd);
|
||||||
|
#else
|
||||||
|
dup2(fd, fileno(stream));
|
||||||
|
close(fd);
|
||||||
|
#endif
|
||||||
|
clearerr(stream);
|
||||||
|
fsetpos(stream, &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE *stream = nullptr;
|
||||||
|
int fd = 0;
|
||||||
|
fpos_t pos = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void validate_log_file_line(const std::string &filename, int line_index, const std::string &level, const std::string &src, const std::string &msg);
|
||||||
|
|
||||||
|
TEST_CASE("Test log file sink", "[logger][log][file_sink]") {
|
||||||
|
file_sink test("test.txt");
|
||||||
|
|
||||||
|
REQUIRE(matador::os::exists("test.txt"));
|
||||||
|
|
||||||
|
// UNIT_ASSERT_EQUAL("test.txt", test.path());
|
||||||
|
|
||||||
|
test.close();
|
||||||
|
|
||||||
|
if (::remove("test.txt") == -1) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
char buf[1024];
|
||||||
|
strerror_s(buf, 1024, errno);
|
||||||
|
FAIL(buf);
|
||||||
|
#else
|
||||||
|
UNIT_FAIL(strerror(errno));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(!matador::os::exists("test.txt"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto path = matador::os::build_path("mylog", "test.txt");
|
||||||
|
file_sink test2(path);
|
||||||
|
|
||||||
|
REQUIRE("mylog" == test2.path());
|
||||||
|
|
||||||
|
matador::os::chdir("mylog");
|
||||||
|
|
||||||
|
REQUIRE(matador::os::exists("test.txt"));
|
||||||
|
|
||||||
|
}
|
||||||
|
if (::remove("test.txt") == -1) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
char buf[1024];
|
||||||
|
strerror_s(buf, 1024, errno);
|
||||||
|
FAIL(buf);
|
||||||
|
#else
|
||||||
|
UNIT_FAIL(strerror(errno));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(!matador::os::exists("test.txt"));
|
||||||
|
|
||||||
|
matador::os::chdir("..");
|
||||||
|
|
||||||
|
matador::os::rmdir("mylog");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test log rotating file sink", "[logger][log][rotate_file_sink]") {
|
||||||
|
const auto dir = matador::os::build_path("my", "log");
|
||||||
|
const auto path = matador::os::build_path(dir, "log.txt");
|
||||||
|
|
||||||
|
const auto logsink = create_rotating_file_sink(path, 30, 3);
|
||||||
|
|
||||||
|
matador::os::chdir(dir);
|
||||||
|
|
||||||
|
REQUIRE(matador::os::is_readable("log.txt"));
|
||||||
|
REQUIRE(matador::os::is_writable("log.txt"));
|
||||||
|
|
||||||
|
std::string line = "hello world and first line\n";
|
||||||
|
|
||||||
|
logsink->write(line.c_str(), line.size());
|
||||||
|
|
||||||
|
matador::os::chdir("my");
|
||||||
|
matador::os::chdir("log");
|
||||||
|
|
||||||
|
REQUIRE(!matador::os::exists("log.1.txt"));
|
||||||
|
|
||||||
|
logsink->write(line.c_str(), line.size());
|
||||||
|
|
||||||
|
REQUIRE(matador::os::exists("log.1.txt"));
|
||||||
|
|
||||||
|
logsink->close();
|
||||||
|
|
||||||
|
matador::os::remove("log.txt");
|
||||||
|
matador::os::remove("log.1.txt");
|
||||||
|
|
||||||
|
matador::os::chdir("..");
|
||||||
|
matador::os::chdir("..");
|
||||||
|
|
||||||
|
matador::os::rmpath(matador::os::build_path("my", "log"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test log level range", "[logger][level][range]") {
|
||||||
|
REQUIRE(log_level::LVL_INFO == log_manager::min_default_log_level());
|
||||||
|
REQUIRE(log_level::LVL_FATAL == log_manager::max_default_log_level());
|
||||||
|
|
||||||
|
default_min_log_level(log_level::LVL_DEBUG);
|
||||||
|
default_max_log_level(log_level::LVL_ERROR);
|
||||||
|
|
||||||
|
REQUIRE(log_level::LVL_DEBUG == log_manager::min_default_log_level());
|
||||||
|
REQUIRE(log_level::LVL_ERROR == log_manager::max_default_log_level());
|
||||||
|
|
||||||
|
log_level_range llr;
|
||||||
|
llr.min_level = log_level::LVL_DEBUG;
|
||||||
|
llr.max_level = log_level::LVL_TRACE;
|
||||||
|
log_domain ld("test", llr);
|
||||||
|
|
||||||
|
REQUIRE(log_level::LVL_DEBUG == ld.min_log_level());
|
||||||
|
REQUIRE(log_level::LVL_TRACE == ld.max_log_level());
|
||||||
|
|
||||||
|
ld.min_log_level(log_level::LVL_INFO);
|
||||||
|
ld.max_log_level(log_level::LVL_ERROR);
|
||||||
|
|
||||||
|
REQUIRE(log_level::LVL_INFO == ld.min_log_level());
|
||||||
|
REQUIRE(log_level::LVL_ERROR == ld.max_log_level());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test basic logger functions", "[logger][basic]") {
|
||||||
|
auto logger = create_logger("test");
|
||||||
|
|
||||||
|
REQUIRE("test" == logger.source());
|
||||||
|
REQUIRE("default" == logger.domain());
|
||||||
|
|
||||||
|
auto logsink = create_file_sink("log.txt");
|
||||||
|
|
||||||
|
REQUIRE(matador::os::is_readable("log.txt"));
|
||||||
|
REQUIRE(matador::os::is_writable("log.txt"));
|
||||||
|
|
||||||
|
logsink->close();
|
||||||
|
|
||||||
|
if (::remove("log.txt") == -1) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
char buf[1024];
|
||||||
|
strerror_s(buf, 1024, errno);
|
||||||
|
FAIL(buf);
|
||||||
|
#else
|
||||||
|
FAIL(strerror(errno));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(!matador::os::exists("log.txt"));
|
||||||
|
|
||||||
|
logger = create_logger("net", "system");
|
||||||
|
|
||||||
|
REQUIRE("net" == logger.source());
|
||||||
|
REQUIRE("system" == logger.domain());
|
||||||
|
|
||||||
|
logsink = create_file_sink("net.txt");
|
||||||
|
|
||||||
|
REQUIRE(matador::os::is_readable("net.txt"));
|
||||||
|
REQUIRE(matador::os::is_writable("net.txt"));
|
||||||
|
|
||||||
|
logsink->close();
|
||||||
|
|
||||||
|
matador::os::remove("net.txt");
|
||||||
|
|
||||||
|
REQUIRE(!matador::os::exists("net.txt"));
|
||||||
|
|
||||||
|
log_manager::instance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test logging", "[logger][logging]") {
|
||||||
|
domain_min_log_level("default", log_level::LVL_FATAL);
|
||||||
|
domain_max_log_level("default", log_level::LVL_TRACE);
|
||||||
|
|
||||||
|
auto logger = create_logger("test");
|
||||||
|
|
||||||
|
REQUIRE("test" == logger.source());
|
||||||
|
REQUIRE("default" == logger.domain());
|
||||||
|
|
||||||
|
auto logsink = create_file_sink("log.txt");
|
||||||
|
|
||||||
|
REQUIRE(matador::os::is_readable("log.txt"));
|
||||||
|
REQUIRE(matador::os::is_writable("log.txt"));
|
||||||
|
|
||||||
|
add_log_sink(logsink);
|
||||||
|
|
||||||
|
logger.info("information");
|
||||||
|
logger.info("information %s", "important");
|
||||||
|
logger.warn("warning");
|
||||||
|
logger.warn("warning %s", "important");
|
||||||
|
logger.debug("debugging");
|
||||||
|
logger.debug("debugging %s", "important");
|
||||||
|
logger.trace("tracing something");
|
||||||
|
logger.trace("tracing something %s", "important");
|
||||||
|
logger.error("big error");
|
||||||
|
logger.error("big error %s", "important");
|
||||||
|
log(log_level::LVL_ERROR, "test", "global log test %d", 4711);
|
||||||
|
|
||||||
|
logsink->close();
|
||||||
|
|
||||||
|
validate_log_file_line("log.txt", 0, "INFO", "test", "information");
|
||||||
|
validate_log_file_line("log.txt", 1, "INFO", "test", "information important");
|
||||||
|
validate_log_file_line("log.txt", 2, "WARN", "test", "warning");
|
||||||
|
validate_log_file_line("log.txt", 3, "WARN", "test", "warning important");
|
||||||
|
validate_log_file_line("log.txt", 4, "DEBUG", "test", "debugging");
|
||||||
|
validate_log_file_line("log.txt", 5, "DEBUG", "test", "debugging important");
|
||||||
|
validate_log_file_line("log.txt", 6, "TRACE", "test", "tracing something");
|
||||||
|
validate_log_file_line("log.txt", 7, "TRACE", "test", "tracing something important");
|
||||||
|
validate_log_file_line("log.txt", 8, "ERROR", "test", "big error");
|
||||||
|
validate_log_file_line("log.txt", 9, "ERROR", "test", "big error important");
|
||||||
|
validate_log_file_line("log.txt", 10, "ERROR", "test", "global log test 4711");
|
||||||
|
|
||||||
|
matador::os::remove("log.txt");
|
||||||
|
|
||||||
|
REQUIRE(!matador::os::exists("log.txt"));
|
||||||
|
|
||||||
|
log_manager::instance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test log stdout", "[logger][logging][stdout]") {
|
||||||
|
auto logger = create_logger("test");
|
||||||
|
|
||||||
|
REQUIRE("test" == logger.source());
|
||||||
|
REQUIRE("default" == logger.domain());
|
||||||
|
|
||||||
|
auto logsink = create_stdout_sink();
|
||||||
|
|
||||||
|
// Redirect stdout
|
||||||
|
{
|
||||||
|
filehelper::std_stream_switcher stdout_switcher(stdout, "stdout.txt");
|
||||||
|
add_log_sink(logsink);
|
||||||
|
logger.info("information");
|
||||||
|
}
|
||||||
|
|
||||||
|
logsink->close();
|
||||||
|
|
||||||
|
validate_log_file_line("stdout.txt", 0, "INFO", "test", "information");
|
||||||
|
|
||||||
|
matador::os::remove("stdout.txt");
|
||||||
|
|
||||||
|
REQUIRE(!matador::os::exists("stdout.txt"));
|
||||||
|
|
||||||
|
log_manager::instance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test log stderr", "[logger][logging][stderr]") {
|
||||||
|
auto logger = create_logger("test");
|
||||||
|
|
||||||
|
REQUIRE("test" == logger.source());
|
||||||
|
REQUIRE("default" == logger.domain());
|
||||||
|
|
||||||
|
auto logsink = create_stderr_sink();
|
||||||
|
|
||||||
|
// Redirect stdout
|
||||||
|
{
|
||||||
|
filehelper::std_stream_switcher stdout_switcher(stderr, "stderr.txt");
|
||||||
|
add_log_sink(logsink);
|
||||||
|
logger.info("information");
|
||||||
|
}
|
||||||
|
|
||||||
|
logsink->close();
|
||||||
|
|
||||||
|
validate_log_file_line("stderr.txt", 0, "INFO", "test", "information");
|
||||||
|
|
||||||
|
matador::os::remove("stderr.txt");
|
||||||
|
|
||||||
|
REQUIRE(!matador::os::exists("stderr.txt"));
|
||||||
|
|
||||||
|
log_manager::instance().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test log levels", "[logger][levels]") {
|
||||||
|
std::stringstream out;
|
||||||
|
|
||||||
|
out << log_level::LVL_ERROR;
|
||||||
|
|
||||||
|
REQUIRE("ERROR" == out.str());
|
||||||
|
|
||||||
|
out.str("");
|
||||||
|
out.clear();
|
||||||
|
out << log_level::LVL_DEBUG;
|
||||||
|
|
||||||
|
REQUIRE("DEBUG" == out.str());
|
||||||
|
|
||||||
|
out.str("");
|
||||||
|
out.clear();
|
||||||
|
out << log_level::LVL_INFO;
|
||||||
|
|
||||||
|
REQUIRE("INFO" == out.str());
|
||||||
|
|
||||||
|
out.str("");
|
||||||
|
out.clear();
|
||||||
|
out << log_level::LVL_FATAL;
|
||||||
|
|
||||||
|
REQUIRE("FATAL" == out.str());
|
||||||
|
|
||||||
|
out.str("");
|
||||||
|
out.clear();
|
||||||
|
out << log_level::LVL_TRACE;
|
||||||
|
|
||||||
|
REQUIRE("TRACE" == out.str());
|
||||||
|
|
||||||
|
out.str("");
|
||||||
|
out.clear();
|
||||||
|
out << log_level::LVL_WARN;
|
||||||
|
|
||||||
|
REQUIRE("WARN" == out.str());
|
||||||
|
|
||||||
|
out.str("");
|
||||||
|
out.clear();
|
||||||
|
out << log_level::LVL_ALL;
|
||||||
|
|
||||||
|
REQUIRE("ALL" == out.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void validate_log_file_line(const std::string &filename, int line_index, const std::string &level, const std::string &src, const std::string &msg) {
|
||||||
|
std::ifstream logfile(filename);
|
||||||
|
|
||||||
|
REQUIRE(logfile.good());
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
int line_count = 0;
|
||||||
|
do {
|
||||||
|
if (!std::getline(logfile, line).good()) {
|
||||||
|
FAIL("couldn't find line");
|
||||||
|
}
|
||||||
|
} while (line_count++ < line_index);
|
||||||
|
|
||||||
|
const std::regex logline_regex(R"(^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d{3}.*\[([A-Z]+)\s*\] \[(.*)\]: (.*)$)");
|
||||||
|
std::smatch match;
|
||||||
|
|
||||||
|
REQUIRE(std::regex_match(line, match, logline_regex));
|
||||||
|
REQUIRE(4 == match.size());
|
||||||
|
|
||||||
|
REQUIRE(level == match[1].str());
|
||||||
|
REQUIRE(src ==match[2].str());
|
||||||
|
REQUIRE(msg == match[3].str());
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue