From a7011baa53c374e81e0bc95d923ef8ffc0175a87 Mon Sep 17 00:00:00 2001 From: PedroRod Date: Sun, 20 Mar 2016 11:11:55 -0400 Subject: [PATCH 1/5] Added SQLite3 sink to the solution --- include/spdlog/sinks/database_logger_sink.h | 156 ++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 include/spdlog/sinks/database_logger_sink.h diff --git a/include/spdlog/sinks/database_logger_sink.h b/include/spdlog/sinks/database_logger_sink.h new file mode 100644 index 00000000..9c596bf8 --- /dev/null +++ b/include/spdlog/sinks/database_logger_sink.h @@ -0,0 +1,156 @@ +#pragma once +#include +#include +#include +#include +#include + +struct Column +{ + enum TableColumn + { + TimeStamp, + Level, + Message, + LoggerName, + ThreadId + }; + + Column(std::string columnName, TableColumn columnMap) + { + ColumnName = columnName; + ColumnMap = columnMap; + } + + std::string ColumnName; + TableColumn ColumnMap; + std::string Value; +}; + +struct database_schema +{ + std::string TableName; + + std::vector Columns; + + database_schema(const std::string& tableName, const std::vector& columns) + { + TableName = tableName; + Columns = columns; + } + + database_schema() + { + TableName = "Logs"; + + Columns = + { + Column("TimeStamp", Column::TimeStamp), + Column("Level", Column::Level), + Column("Message", Column::Message), + Column("LoggerName", Column::LoggerName), + Column("ThreadId", Column::ThreadId) + }; + } +}; + +namespace spdlog +{ + namespace sinks + { + class database_logger_sink : + public sink + { + public: + void flush() override + { + sqlite3_close(_database); + } + + void log(const details::log_msg& msg) override + { + + for (auto& column : _schema.Columns) + { + switch (column.ColumnMap) + { + case Column::TimeStamp: + { + auto time = std::chrono::system_clock::to_time_t(msg.time); + char str[26]; + ctime_s(str, sizeof(str), &time); + column.Value = str; + break; + } + + case Column::Level: + { + column.Value = level::to_str(msg.level); + break; + } + case Column::Message: + { + column.Value = msg.raw.str(); + break; + } + case Column::LoggerName: + { + column.Value = msg.logger_name; + break; + } + case Column::ThreadId: + { + column.Value = std::to_string(msg.thread_id); + break; + } + } + } + + auto query = fmt::format("INSERT INTO {0} ({1},{3},{5},{7},{9}) VALUES ('{2}','{4}','{6}','{8}',{10})", + _schema.TableName, + _schema.Columns[0].ColumnName, + _schema.Columns[0].Value, + _schema.Columns[1].ColumnName, + _schema.Columns[1].Value, + _schema.Columns[2].ColumnName, + _schema.Columns[2].Value, + _schema.Columns[3].ColumnName, + _schema.Columns[3].Value, + _schema.Columns[4].ColumnName, + _schema.Columns[4].Value); + + char *errorMessage = nullptr; + + if (sqlite3_exec(_database, query.c_str(), nullptr, nullptr, &errorMessage) != SQLITE_OK) + { + throw spdlog_ex(errorMessage); + } + + + } + + explicit database_logger_sink(const std::string& databaseName) + { + if (sqlite3_open(databaseName.c_str(), &_database)) + throw spdlog_ex("Error opening database"); + } + + explicit database_logger_sink(const std::string& databaseName, const database_schema& databaseSchema) + { + _schema = databaseSchema; + + if (sqlite3_open(databaseName.c_str(), &_database)) + throw spdlog_ex("Error opening database"); + } + + ~database_logger_sink() + { + sqlite3_close(_database); + } + + private: + database_schema _schema; + sqlite3 *_database; + }; + } +} From 571e85d0f412e97c44446c4c361b1c85cdf0cba7 Mon Sep 17 00:00:00 2001 From: PedroRod Date: Sun, 20 Mar 2016 19:12:34 -0400 Subject: [PATCH 2/5] changed ctrs,variables,argsguments to match code style of spdlog, also removed columns mapping since there is no clean way of set column name dynamically on a prepared statement and changed query to use prepared statements --- include/spdlog/sinks/database_logger_sink.h | 171 +++++--------------- 1 file changed, 43 insertions(+), 128 deletions(-) diff --git a/include/spdlog/sinks/database_logger_sink.h b/include/spdlog/sinks/database_logger_sink.h index 9c596bf8..3967adc9 100644 --- a/include/spdlog/sinks/database_logger_sink.h +++ b/include/spdlog/sinks/database_logger_sink.h @@ -1,59 +1,9 @@ #pragma once -#include -#include -#include -#include +#include "spdlog\sinks\sink.h" +#include "spdlog/details/log_msg.h" +#include "spdlog/common.h" #include -struct Column -{ - enum TableColumn - { - TimeStamp, - Level, - Message, - LoggerName, - ThreadId - }; - - Column(std::string columnName, TableColumn columnMap) - { - ColumnName = columnName; - ColumnMap = columnMap; - } - - std::string ColumnName; - TableColumn ColumnMap; - std::string Value; -}; - -struct database_schema -{ - std::string TableName; - - std::vector Columns; - - database_schema(const std::string& tableName, const std::vector& columns) - { - TableName = tableName; - Columns = columns; - } - - database_schema() - { - TableName = "Logs"; - - Columns = - { - Column("TimeStamp", Column::TimeStamp), - Column("Level", Column::Level), - Column("Message", Column::Message), - Column("LoggerName", Column::LoggerName), - Column("ThreadId", Column::ThreadId) - }; - } -}; - namespace spdlog { namespace sinks @@ -62,72 +12,6 @@ namespace spdlog public sink { public: - void flush() override - { - sqlite3_close(_database); - } - - void log(const details::log_msg& msg) override - { - - for (auto& column : _schema.Columns) - { - switch (column.ColumnMap) - { - case Column::TimeStamp: - { - auto time = std::chrono::system_clock::to_time_t(msg.time); - char str[26]; - ctime_s(str, sizeof(str), &time); - column.Value = str; - break; - } - - case Column::Level: - { - column.Value = level::to_str(msg.level); - break; - } - case Column::Message: - { - column.Value = msg.raw.str(); - break; - } - case Column::LoggerName: - { - column.Value = msg.logger_name; - break; - } - case Column::ThreadId: - { - column.Value = std::to_string(msg.thread_id); - break; - } - } - } - - auto query = fmt::format("INSERT INTO {0} ({1},{3},{5},{7},{9}) VALUES ('{2}','{4}','{6}','{8}',{10})", - _schema.TableName, - _schema.Columns[0].ColumnName, - _schema.Columns[0].Value, - _schema.Columns[1].ColumnName, - _schema.Columns[1].Value, - _schema.Columns[2].ColumnName, - _schema.Columns[2].Value, - _schema.Columns[3].ColumnName, - _schema.Columns[3].Value, - _schema.Columns[4].ColumnName, - _schema.Columns[4].Value); - - char *errorMessage = nullptr; - - if (sqlite3_exec(_database, query.c_str(), nullptr, nullptr, &errorMessage) != SQLITE_OK) - { - throw spdlog_ex(errorMessage); - } - - - } explicit database_logger_sink(const std::string& databaseName) { @@ -135,21 +19,52 @@ namespace spdlog throw spdlog_ex("Error opening database"); } - explicit database_logger_sink(const std::string& databaseName, const database_schema& databaseSchema) - { - _schema = databaseSchema; - - if (sqlite3_open(databaseName.c_str(), &_database)) - throw spdlog_ex("Error opening database"); - } - ~database_logger_sink() { sqlite3_close(_database); } + void flush() override + { + sqlite3_close(_database); + } + + sqlite3_stmt * prepare_query(const details::log_msg& msg) const + { + auto time = std::chrono::system_clock::to_time_t(msg.time); + + char time_str[26]; + + ctime_s(time_str, sizeof(time_str), &time); + + sqlite3_stmt * query_stmt; + + if (sqlite3_prepare_v2(_database, "INSERT INTO Logs (TimeStamp,Level,Message,LoggerName,ThreadId) VALUES (?,?,?,?,?)", -1, &query_stmt, nullptr) != SQLITE_OK) + throw spdlog_ex(sqlite3_errmsg(_database)); + + if (sqlite3_bind_text(query_stmt, 1, time_str, -1, SQLITE_STATIC) != SQLITE_OK || + sqlite3_bind_text(query_stmt, 2, to_str(msg.level), -1, SQLITE_STATIC) != SQLITE_OK || + sqlite3_bind_text(query_stmt, 3, msg.raw.c_str(), -1, nullptr) != SQLITE_OK || + sqlite3_bind_text(query_stmt, 4, "'''''''''''", -1, SQLITE_STATIC) != SQLITE_OK || + sqlite3_bind_int(query_stmt, 5, msg.thread_id) != SQLITE_OK) + throw spdlog_ex(sqlite3_errmsg(_database)); + + return query_stmt; + } + + void log(const details::log_msg& msg) override + { + auto query_stmt = prepare_query(msg); + + if (sqlite3_step(query_stmt) != SQLITE_DONE) + { + throw spdlog_ex(sqlite3_errmsg(_database)); + } + + sqlite3_finalize(query_stmt); + } + private: - database_schema _schema; sqlite3 *_database; }; } From d8d8dfd3e22a0aea86ceb5ec84fc859854573401 Mon Sep 17 00:00:00 2001 From: PedroRod Date: Sun, 20 Mar 2016 22:02:23 -0400 Subject: [PATCH 3/5] made the prepared statement re-usable and renamed .h to sqlite_sink --- .../{database_logger_sink.h => sqlite_sink.h} | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) rename include/spdlog/sinks/{database_logger_sink.h => sqlite_sink.h} (51%) diff --git a/include/spdlog/sinks/database_logger_sink.h b/include/spdlog/sinks/sqlite_sink.h similarity index 51% rename from include/spdlog/sinks/database_logger_sink.h rename to include/spdlog/sinks/sqlite_sink.h index 3967adc9..8c4519e0 100644 --- a/include/spdlog/sinks/database_logger_sink.h +++ b/include/spdlog/sinks/sqlite_sink.h @@ -8,28 +8,33 @@ namespace spdlog { namespace sinks { - class database_logger_sink : + class sqlite_sink : public sink { public: - explicit database_logger_sink(const std::string& databaseName) + explicit sqlite_sink(const std::string& databaseName) { if (sqlite3_open(databaseName.c_str(), &_database)) throw spdlog_ex("Error opening database"); + + if (sqlite3_prepare_v2(_database, "INSERT INTO Logs (TimeStamp,Level,Message,LoggerName,ThreadId) VALUES (?,?,?,?,?)", -1, &_query_stmt, nullptr) != SQLITE_OK) + throw spdlog_ex(sqlite3_errmsg(_database)); } - ~database_logger_sink() + ~sqlite_sink() { - sqlite3_close(_database); + sqlite_sink::flush(); } void flush() override { sqlite3_close(_database); + + sqlite3_finalize(_query_stmt); } - sqlite3_stmt * prepare_query(const details::log_msg& msg) const + void bind_to_statement(const details::log_msg& msg) const { auto time = std::chrono::system_clock::to_time_t(msg.time); @@ -37,35 +42,31 @@ namespace spdlog ctime_s(time_str, sizeof(time_str), &time); - sqlite3_stmt * query_stmt; - - if (sqlite3_prepare_v2(_database, "INSERT INTO Logs (TimeStamp,Level,Message,LoggerName,ThreadId) VALUES (?,?,?,?,?)", -1, &query_stmt, nullptr) != SQLITE_OK) + if (sqlite3_bind_text(_query_stmt, 1, time_str, -1, SQLITE_STATIC) != SQLITE_OK || + sqlite3_bind_text(_query_stmt, 2, to_str(msg.level), -1, SQLITE_STATIC) != SQLITE_OK || + sqlite3_bind_text(_query_stmt, 3, msg.raw.c_str(), -1, nullptr) != SQLITE_OK || + sqlite3_bind_text(_query_stmt, 4, msg.logger_name.c_str(), -1, SQLITE_STATIC) != SQLITE_OK || + sqlite3_bind_int(_query_stmt, 5, msg.thread_id) != SQLITE_OK) throw spdlog_ex(sqlite3_errmsg(_database)); - - if (sqlite3_bind_text(query_stmt, 1, time_str, -1, SQLITE_STATIC) != SQLITE_OK || - sqlite3_bind_text(query_stmt, 2, to_str(msg.level), -1, SQLITE_STATIC) != SQLITE_OK || - sqlite3_bind_text(query_stmt, 3, msg.raw.c_str(), -1, nullptr) != SQLITE_OK || - sqlite3_bind_text(query_stmt, 4, "'''''''''''", -1, SQLITE_STATIC) != SQLITE_OK || - sqlite3_bind_int(query_stmt, 5, msg.thread_id) != SQLITE_OK) - throw spdlog_ex(sqlite3_errmsg(_database)); - - return query_stmt; } void log(const details::log_msg& msg) override { - auto query_stmt = prepare_query(msg); + bind_to_statement(msg); - if (sqlite3_step(query_stmt) != SQLITE_DONE) + if (sqlite3_step(_query_stmt) != SQLITE_DONE) { throw spdlog_ex(sqlite3_errmsg(_database)); } - sqlite3_finalize(query_stmt); + sqlite3_reset(_query_stmt); + sqlite3_clear_bindings(_query_stmt); } private: sqlite3 *_database; + + sqlite3_stmt * _query_stmt; }; } } From 26ab30aba57c55c13cfea5a9945b0d6144522aa4 Mon Sep 17 00:00:00 2001 From: PedroRod Date: Sun, 20 Mar 2016 22:07:51 -0400 Subject: [PATCH 4/5] forgot to reset the null pointer of the bind back to SQLITE_STATIC --- include/spdlog/sinks/sqlite_sink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/sqlite_sink.h b/include/spdlog/sinks/sqlite_sink.h index 8c4519e0..81019a71 100644 --- a/include/spdlog/sinks/sqlite_sink.h +++ b/include/spdlog/sinks/sqlite_sink.h @@ -44,7 +44,7 @@ namespace spdlog if (sqlite3_bind_text(_query_stmt, 1, time_str, -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(_query_stmt, 2, to_str(msg.level), -1, SQLITE_STATIC) != SQLITE_OK || - sqlite3_bind_text(_query_stmt, 3, msg.raw.c_str(), -1, nullptr) != SQLITE_OK || + sqlite3_bind_text(_query_stmt, 3, msg.raw.c_str(), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_text(_query_stmt, 4, msg.logger_name.c_str(), -1, SQLITE_STATIC) != SQLITE_OK || sqlite3_bind_int(_query_stmt, 5, msg.thread_id) != SQLITE_OK) throw spdlog_ex(sqlite3_errmsg(_database)); From ea1d0fd37b64d20ea943c1fd61e12370e487e3f4 Mon Sep 17 00:00:00 2001 From: PedroRod Date: Sun, 20 Mar 2016 23:13:28 -0400 Subject: [PATCH 5/5] assigned nullptr to _database and _query_stmt upon flushing so that if flush gets called again, will not throw an exception --- include/spdlog/sinks/sqlite_sink.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/spdlog/sinks/sqlite_sink.h b/include/spdlog/sinks/sqlite_sink.h index 81019a71..e4b1de2f 100644 --- a/include/spdlog/sinks/sqlite_sink.h +++ b/include/spdlog/sinks/sqlite_sink.h @@ -32,6 +32,9 @@ namespace spdlog sqlite3_close(_database); sqlite3_finalize(_query_stmt); + + _database = nullptr; + _query_stmt = nullptr; } void bind_to_statement(const details::log_msg& msg) const