diff --git a/CHANGELOG.md b/CHANGELOG.md index 61d48aaebd50731104612da10f4c3d35864c9d5c..86853ffac98fbfbc5fa4c85696ccba2b8a8efdd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +- Added support for syslog logging. + ### Fixed - Close logging down in destructor so linked device server can be restarted. @@ -17,6 +21,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Removed Clang path from build (CMake checks PATH) - Corrected static library build - Install now places header in include/hdb++/ +- Entire library now uses the global default logger from spdlog. + +#### Submodules + +- Updated spdlog submodule to release v1.4.3 ## [0.9.1] - 2019-07-18 diff --git a/CMakeLists.txt b/CMakeLists.txt index 0602e9b62a08f2c4cc10f82dbc4d76520437c5b9..e36b9adaa3f498ae6ec824fa2a319469242218ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,9 +38,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # Build options -option(BUILD_UNIT_TESTS "Build unit tests" OFF) -option(BUILD_BENCHMARK_TESTS "Build benchmarking tests (Forces RELEASE build)" OFF) -option(ENABLE_CLANG "Enable clang code and layout analysis" OFF) +option(BUILD_UNIT_TESTS "Build unit tests" ON) +option(BUILD_BENCHMARK_TESTS "Build benchmarking tests (Forces RELEASE build)" ON) +option(ENABLE_CLANG "Enable clang code and layout analysis" ON) if(BUILD_UNIT_TESTS) message(STATUS "Unit tests will be built") @@ -137,7 +137,7 @@ add_subdirectory(src) add_library(libhdbpp_timescale_shared_library SHARED ${SRC_FILES}) target_link_libraries(libhdbpp_timescale_shared_library - PUBLIC ${TDB_FOUND_LIBRARIES} pqxx_static libhdbpp_headers spdlog Threads::Threads + PUBLIC ${TDB_FOUND_LIBRARIES} pqxx_static libhdbpp_headers spdlog::spdlog_header_only Threads::Threads PRIVATE TangoInterfaceLibrary) target_include_directories(libhdbpp_timescale_shared_library diff --git a/benchmark/QueryBuilderTests.cpp b/benchmark/QueryBuilderTests.cpp index 1a02b5a7702d4d0206aec979ea60e77b9db98d2e..5da87889875fa1c9db4de13e2f0d41b2fbbb88be 100644 --- a/benchmark/QueryBuilderTests.cpp +++ b/benchmark/QueryBuilderTests.cpp @@ -26,8 +26,7 @@ void bmAllocateQueryBuilder(benchmark::State& state) { // Test - Testing the time it takes to allocate a QueryBuilder, mainly for future test // reference - hdbpp_internal::LogConfigurator::initLoggingMetrics(false, false); - hdbpp_internal::LogConfigurator::setLoggingLevel(spdlog::level::err); + hdbpp_internal::LogConfigurator::initLogging(); for (auto _ : state) hdbpp_internal::pqxx_conn::QueryBuilder query_builder; diff --git a/src/ColumnCache.hpp b/src/ColumnCache.hpp index c451eb76bd0fc32c464af39c8ebe05fff0b90a0f..44674be924839302a1a1923a56367f4554a30480 100644 --- a/src/ColumnCache.hpp +++ b/src/ColumnCache.hpp @@ -80,9 +80,6 @@ namespace pqxx_conn // cache of values to a reference, the unordered map is not sorted // so we do not loose time on each insert having it resorted std::unordered_map<TRef, TValue> _values; - - // logging subsystem - std::shared_ptr<spdlog::logger> _logger; }; //============================================================================= @@ -106,9 +103,7 @@ namespace pqxx_conn _fetch_all_query_name = _column_name + _table_name + _reference + "_all"; _fetch_id_query_name = _column_name + _table_name + _reference + "_id"; - _logger = spdlog::get(LibLoggerName); - - _logger->trace("Cache created for table: {} using columns {}/{}", _table_name, _column_name, _reference); + spdlog::trace("Cache created for table: {} using columns {}/{}", _table_name, _column_name, _reference); } //============================================================================= @@ -133,7 +128,7 @@ namespace pqxx_conn tx.conn().prepare(_fetch_all_query_name, QueryBuilder::fetchAllValuesQuery(_column_name, _table_name, _reference)); - _logger->trace("Created prepared statement for: {}", _fetch_all_query_name); + spdlog::trace("Created prepared statement for: {}", _fetch_all_query_name); } auto result = tx.exec_prepared(_fetch_all_query_name); @@ -143,7 +138,7 @@ namespace pqxx_conn for (const auto &row : result) _values.insert({row[1].template as<TRef>(), row[0].template as<TValue>()}); - _logger->debug("Loaded: {} values into cache", _values.size()); + spdlog::debug("Loaded: {} values into cache", _values.size()); }); } catch (const pqxx::pqxx_exception &ex) @@ -151,9 +146,9 @@ namespace pqxx_conn string msg {"The database transaction failed. Unable to fetchAll for column: " + _column_name + " in table: " + _table_name + ". Error: " + ex.base().what()}; - _logger->error("Error: An unexpected error occurred when trying to run the database query"); - _logger->error("Caught error: \"{}\"", ex.base().what()); - _logger->error("Throwing storage error with message: \"{}\"", msg); + spdlog::error("Error: An unexpected error occurred when trying to run the database query"); + spdlog::error("Caught error: \"{}\"", ex.base().what()); + spdlog::error("Throwing storage error with message: \"{}\"", msg); Tango::Except::throw_exception("Storage Error", msg, LOCATION_INFO); } @@ -189,7 +184,7 @@ namespace pqxx_conn tx.conn().prepare( _fetch_id_query_name, QueryBuilder::fetchValueQuery(_column_name, _table_name, _reference)); - _logger->trace("Created prepared statement for: {}", _fetch_id_query_name); + spdlog::trace("Created prepared statement for: {}", _fetch_id_query_name); } auto result = tx.exec_prepared(_fetch_id_query_name, reference); @@ -206,7 +201,7 @@ namespace pqxx_conn auto value = result.at(0).at(0).template as<TValue>(); _values.insert({reference, value}); - _logger->debug("Cached value: \'{} \' with reference: \'{}\'", value, reference); + spdlog::debug("Cached value: \'{} \' with reference: \'{}\'", value, reference); return true; } else @@ -224,9 +219,9 @@ namespace pqxx_conn string msg {"The database transaction failed. Unable to query column: " + _column_name + " in table: " + _table_name + ". Error: " + ex.base().what()}; - _logger->error("Error: An unexpected error occurred when trying to run the database query"); - _logger->error("Caught error: \"{}\"", ex.base().what()); - _logger->error("Throwing storage error with message: \"{}\"", msg); + spdlog::error("Error: An unexpected error occurred when trying to run the database query"); + spdlog::error("Caught error: \"{}\"", ex.base().what()); + spdlog::error("Throwing storage error with message: \"{}\"", msg); Tango::Except::throw_exception("Storage Error", msg, LOCATION_INFO); } @@ -248,7 +243,7 @@ namespace pqxx_conn { // this is pretty fatal, we can not store information if it does not exist string msg {"Unable to find a value in either the cache or database for reference: " + reference}; - _logger->error("Error: {}", msg); + spdlog::error("Error: {}", msg); Tango::Except::throw_exception("Storage Error", msg, LOCATION_INFO); } @@ -267,12 +262,12 @@ namespace pqxx_conn // the caller to deal with if (_values.find(reference) != _values.end()) { - _logger->warn("Value already exists in cache, not caching. Value: {} with reference: {}", value, reference); + spdlog::warn("Value already exists in cache, not caching. Value: {} with reference: {}", value, reference); return; } _values.insert({reference, value}); - _logger->debug("Cached new value: {} with reference: {} by request", value, reference); + spdlog::debug("Cached new value: {} with reference: {} by request", value, reference); } //============================================================================= diff --git a/src/DbConnection.cpp b/src/DbConnection.cpp index 00af4f9876e7270ee9cc3af79f46936678c9fc93..85e16048df8c92e446e2a8e762c6b888a072d2f6 100644 --- a/src/DbConnection.cpp +++ b/src/DbConnection.cpp @@ -33,13 +33,13 @@ namespace pqxx_conn { //============================================================================= //============================================================================= - DbConnection::DbConnection() { _logger = spdlog::get(LibLoggerName); } + DbConnection::DbConnection() {} //============================================================================= //============================================================================= void DbConnection::connect(const string &connect_string) { - _logger->info("Connecting to postgres database with string: \"{}\"", connect_string); + spdlog::info("Connecting to postgres database with string: \"{}\"", connect_string); // construct the database connection try @@ -54,16 +54,16 @@ namespace pqxx_conn // mark the connected flag as true to cache this state _connected = true; - _logger->info("Connected to postgres successfully"); + spdlog::info("Connected to postgres successfully"); } catch (const pqxx::broken_connection &ex) { string msg {"Failed to connect to database. Exception: "}; msg += ex.what(); - _logger->error("Error: Connecting to postgres database with connect string: \"{}\"", connect_string); - _logger->error("Caught error: \"{}\"", ex.what()); - _logger->error("Throwing connection error with message: \"{}\"", msg); + spdlog::error("Error: Connecting to postgres database with connect string: \"{}\"", connect_string); + spdlog::error("Caught error: \"{}\"", ex.what()); + spdlog::error("Throwing connection error with message: \"{}\"", msg); Tango::Except::throw_exception("Connection Error", msg, LOCATION_INFO); } @@ -96,7 +96,7 @@ namespace pqxx_conn // stop attempts to use the connection _connected = false; - _logger->debug("Disconnected from the postgres database"); + spdlog::debug("Disconnected from the postgres database"); } //============================================================================= @@ -121,7 +121,7 @@ namespace pqxx_conn assert(_error_desc_id_cache != nullptr); assert(_event_id_cache != nullptr); - _logger->trace("Storing new attribute {} of type {}", full_attr_name, traits); + spdlog::trace("Storing new attribute {} of type {}", full_attr_name, traits); checkConnection(LOCATION_INFO); @@ -132,9 +132,9 @@ namespace pqxx_conn string msg { "This attribute [" + full_attr_name + "] already exists in the database. Unable to add it again."}; - _logger->error("Error: The attribute already exists in the database and can not be added again"); - _logger->error("Attribute details. Name: {} traits: {}", full_attr_name, traits); - _logger->error("Throwing consistency error with message: \"{}\"", msg); + spdlog::error("Error: The attribute already exists in the database and can not be added again"); + spdlog::error("Attribute details. Name: {} traits: {}", full_attr_name, traits); + spdlog::error("Throwing consistency error with message: \"{}\"", msg); Tango::Except::throw_exception("Consistency Error", msg, LOCATION_INFO); } @@ -147,7 +147,7 @@ namespace pqxx_conn if (!tx.prepared(StoreAttribute).exists()) { tx.conn().prepare(StoreAttribute, QueryBuilder::storeAttributeQuery()); - _logger->trace("Created prepared statement for: {}", StoreAttribute); + spdlog::trace("Created prepared statement for: {}", StoreAttribute); } // execute the statement with the expectation that we get a row back @@ -171,7 +171,7 @@ namespace pqxx_conn return row.at(0).as<int>(); }); - _logger->debug("Stored new attribute {} of type {} with db id: {}", full_attr_name, traits, conf_id); + spdlog::debug("Stored new attribute {} of type {} with db id: {}", full_attr_name, traits, conf_id); // cache the new conf id for future use _conf_id_cache->cacheValue(conf_id, full_attr_name); @@ -196,7 +196,7 @@ namespace pqxx_conn assert(_error_desc_id_cache != nullptr); assert(_event_id_cache != nullptr); - _logger->trace("Storing history event {} for attribute {}", event, full_attr_name); + spdlog::trace("Storing history event {} for attribute {}", event, full_attr_name); checkConnection(LOCATION_INFO); checkAttributeExists(full_attr_name, LOCATION_INFO); @@ -210,10 +210,10 @@ namespace pqxx_conn string msg { "The event [" + event + "] is missing in both the cache and database, this is an unrecoverable error."}; - _logger->error( + spdlog::error( "Event found missing, this occurred when storing event: {} for attribute: {}", event, full_attr_name); - _logger->error("Throwing consistency error with message: \"{}\"", msg); + spdlog::error("Throwing consistency error with message: \"{}\"", msg); Tango::Except::throw_exception("Consistency Error", msg, LOCATION_INFO); } @@ -226,7 +226,7 @@ namespace pqxx_conn if (!tx.prepared(StoreHistoryEvent).exists()) { tx.conn().prepare(StoreHistoryEvent, QueryBuilder::storeHistoryEventQuery()); - _logger->trace("Created prepared statement for: {}", StoreHistoryEvent); + spdlog::trace("Created prepared statement for: {}", StoreHistoryEvent); } // expect no result, this is an insert only query @@ -234,7 +234,7 @@ namespace pqxx_conn tx.commit(); }); - _logger->debug("Stored event {} and for attribute {}", event, full_attr_name); + spdlog::debug("Stored event {} and for attribute {}", event, full_attr_name); } catch (const pqxx::pqxx_exception &ex) { @@ -274,9 +274,9 @@ namespace pqxx_conn assert(_error_desc_id_cache != nullptr); assert(_event_id_cache != nullptr); - _logger->trace("Storing parameter event for attribute {}", full_attr_name); + spdlog::trace("Storing parameter event for attribute {}", full_attr_name); - _logger->trace("Parmater event data: event_time {}, label {}, unit {}, standard_unit {}, display_unit {}, " + spdlog::trace("Parmater event data: event_time {}, label {}, unit {}, standard_unit {}, display_unit {}, " "format {}, archive_rel_change {}, archive_abs_change {}, archive_period {}, description {}", event_time, label, @@ -301,7 +301,7 @@ namespace pqxx_conn if (!tx.prepared(StoreParameterEvent).exists()) { tx.conn().prepare(StoreParameterEvent, QueryBuilder::storeParameterEventQuery()); - _logger->trace("Created prepared statement for: {}", StoreParameterEvent); + spdlog::trace("Created prepared statement for: {}", StoreParameterEvent); } // no result expected @@ -321,7 +321,7 @@ namespace pqxx_conn tx.commit(); }); - _logger->debug("Stored parameter event and for attribute {}", full_attr_name); + spdlog::debug("Stored parameter event and for attribute {}", full_attr_name); } catch (const pqxx::pqxx_exception &ex) { @@ -348,7 +348,7 @@ namespace pqxx_conn assert(_error_desc_id_cache != nullptr); assert(_event_id_cache != nullptr); - _logger->trace("Storing error message event for attribute {}. Quality: {}. Error message: \"{}\"", + spdlog::trace("Storing error message event for attribute {}. Quality: {}. Error message: \"{}\"", full_attr_name, quality, error_msg); @@ -367,11 +367,11 @@ namespace pqxx_conn string msg {"The error message [" + error_msg + "] is missing in both the cache and database, this is an unrecoverable error."}; - _logger->error("Error message found missing, this occurred when storing msg: \"{}\" for attribute: {}", + spdlog::error("Error message found missing, this occurred when storing msg: \"{}\" for attribute: {}", error_msg, full_attr_name); - _logger->error("Throwing consistency error with message: \"{}\"", msg); + spdlog::error("Throwing consistency error with message: \"{}\"", msg); Tango::Except::throw_exception("Consistency Error", msg, LOCATION_INFO); } @@ -385,7 +385,7 @@ namespace pqxx_conn { tx.conn().prepare(_query_builder.storeDataEventErrorName(traits), _query_builder.storeDataEventErrorQuery(traits)); - _logger->trace( + spdlog::trace( "Created prepared statement for: {}", _query_builder.storeDataEventErrorName(traits)); } @@ -421,7 +421,7 @@ namespace pqxx_conn checkConnection(LOCATION_INFO); checkAttributeExists(full_attr_name, LOCATION_INFO); - _logger->trace("Fetching last history event for attribute: {}", full_attr_name); + spdlog::trace("Fetching last history event for attribute: {}", full_attr_name); // the result string last_event; @@ -471,11 +471,11 @@ namespace pqxx_conn if (_conf_id_cache->valueExists(full_attr_name)) { - _logger->trace("Query attribute archived returns true for: {}", full_attr_name); + spdlog::trace("Query attribute archived returns true for: {}", full_attr_name); return true; } - _logger->trace("Query attribute archived returns false for: {}", full_attr_name); + spdlog::trace("Query attribute archived returns false for: {}", full_attr_name); return false; } @@ -492,7 +492,7 @@ namespace pqxx_conn checkConnection(LOCATION_INFO); checkAttributeExists(full_attr_name, LOCATION_INFO); - _logger->trace("Fetching attribute traits for attribute: {}", full_attr_name); + spdlog::trace("Fetching attribute traits for attribute: {}", full_attr_name); AttributeTraits traits; @@ -530,7 +530,7 @@ namespace pqxx_conn //============================================================================= void DbConnection::storeEvent(const std::string &full_attr_name, const std::string &event) { - _logger->debug("Event {} needs adding to the database, by request of attribute {}", event, full_attr_name); + spdlog::debug("Event {} needs adding to the database, by request of attribute {}", event, full_attr_name); try { @@ -542,7 +542,7 @@ namespace pqxx_conn if (!tx.prepared(StoreHistoryString).exists()) { tx.conn().prepare(StoreHistoryString, QueryBuilder::storeHistoryStringQuery()); - _logger->trace("Created prepared statement for: {}", StoreHistoryString); + spdlog::trace("Created prepared statement for: {}", StoreHistoryString); } auto row = tx.exec_prepared1(StoreHistoryString, event); @@ -552,7 +552,7 @@ namespace pqxx_conn return row.at(0).as<int>(); }); - _logger->debug( + spdlog::debug( "Stored event {} for attribute {} and got database id for it: {}", event, full_attr_name, event_id); // cache the new event id for future use @@ -571,7 +571,7 @@ namespace pqxx_conn //============================================================================= void DbConnection::storeErrorMsg(const std::string &full_attr_name, const std::string &error_msg) { - _logger->debug( + spdlog::debug( "Error message \"{}\" needs adding to the database, by request of attribute {}", error_msg, full_attr_name); try @@ -583,7 +583,7 @@ namespace pqxx_conn if (!tx.prepared(StoreErrorString).exists()) { tx.conn().prepare(StoreErrorString, QueryBuilder::storeErrorQuery()); - _logger->trace("Created prepared statement for: {}", StoreErrorString); + spdlog::trace("Created prepared statement for: {}", StoreErrorString); } // expect a single row returned @@ -594,7 +594,7 @@ namespace pqxx_conn return row.at(0).as<int>(); }); - _logger->debug("Stored error message \"{}\" for attribute {} and got database id for it: {}", + spdlog::debug("Stored error message \"{}\" for attribute {} and got database id for it: {}", error_msg, full_attr_name, error_id); @@ -622,9 +622,9 @@ namespace pqxx_conn string msg {"This attribute [" + full_attr_name + "] does not exist in the database. Unable to work with this attribute until it is added."}; - _logger->error("Error: The attribute does not exist in the database, add it first."); - _logger->error("Attribute details. Name: {}", full_attr_name); - _logger->error("Throwing consistency error with message: \"{}\"", msg); + spdlog::error("Error: The attribute does not exist in the database, add it first."); + spdlog::error("Attribute details. Name: {}", full_attr_name); + spdlog::error("Throwing consistency error with message: \"{}\"", msg); Tango::Except::throw_exception("Consistency Error", msg, location); } } @@ -638,10 +638,10 @@ namespace pqxx_conn string msg { "Connection to database is closed. Ensure it has been opened before trying to use the connection."}; - _logger->error( + spdlog::error( "Error: The DbConnection is showing a closed connection status, open it before using store functions"); - _logger->error("Throwing connection error with message: \"{}\"", msg); + spdlog::error("Throwing connection error with message: \"{}\"", msg); Tango::Except::throw_exception("Connection Error", msg, location); } } @@ -652,12 +652,11 @@ namespace pqxx_conn const string &msg, const string &what, const string &query, const std::string &location) { string full_msg {"The database transaction failed. " + msg}; - _logger->error("Error: An unexpected error occurred when trying to run the database query"); - _logger->error("Caught error at: {} Error: \"{}\"", location, what); - _logger->error("Error: Failed query: {}", query); - _logger->error("Throwing storage error with message: \"{}\"", full_msg); + spdlog::error("Error: An unexpected error occurred when trying to run the database query"); + spdlog::error("Caught error at: {} Error: \"{}\"", location, what); + spdlog::error("Error: Failed query: {}", query); + spdlog::error("Throwing storage error with message: \"{}\"", full_msg); Tango::Except::throw_exception("Storage Error", full_msg, location); } - } // namespace pqxx_conn } // namespace hdbpp_internal diff --git a/src/DbConnection.hpp b/src/DbConnection.hpp index c1bfc0d88707b94c42b9da875478c606246ece7f..8082c821df46d7d79173470e17a8012ed8fca48f 100644 --- a/src/DbConnection.hpp +++ b/src/DbConnection.hpp @@ -144,9 +144,6 @@ namespace pqxx_conn std::unique_ptr<ColumnCache<int, std::string>> _error_desc_id_cache; std::unique_ptr<ColumnCache<int, std::string>> _event_id_cache; std::unique_ptr<ColumnCache<int, int>> _type_id_cache; - - // logging subsystem - std::shared_ptr<spdlog::logger> _logger; }; } // namespace pqxx_conn } // namespace hdbpp_internal diff --git a/src/DbConnection.tpp b/src/DbConnection.tpp index 6c4e14b618621bcdda25e1bf8e15d83cb814168b..8c71da326167988670f43790152e7ab01f59b7bc 100644 --- a/src/DbConnection.tpp +++ b/src/DbConnection.tpp @@ -30,26 +30,6 @@ namespace pqxx_conn // specialisations can not be handled by the main function, so we break them out here. namespace store_data_utils { - //============================================================================= - //============================================================================= - template<typename T> - struct Preprocess - { - static void run(std::unique_ptr<std::vector<T>> &, pqxx::work &) {} - }; - - //============================================================================= - //============================================================================= - template<> - struct Preprocess<std::string> - { - static void run(std::unique_ptr<std::vector<std::string>> &value, pqxx::work &tx) - { - for (auto &str : *value) - str = tx.quote(str); - } - }; - //============================================================================= //============================================================================= template<typename T> @@ -105,7 +85,7 @@ namespace pqxx_conn assert(!full_attr_name.empty()); assert(traits.isValid()); - _logger->trace("Storing data event for attribute {} with traits {}, value_r valid: {}, value_w valid: {}", + spdlog::trace("Storing data event for attribute {} with traits {}, value_r valid: {}, value_w valid: {}", full_attr_name, traits, value_r->size() > 0, @@ -119,53 +99,98 @@ namespace pqxx_conn return pqxx::perform([&, this]() { pqxx::work tx {(*_conn), StoreDataEvent}; - // prepare as a prepared statement, we are going to use these - // queries often - if (!tx.prepared(_query_builder.storeDataEventName(traits)).exists()) + // there is a single special case here, arrays of strings need a different syntax to store, + // to avoid the quoting. Its likely we will need more for DevEncoded and DevEnum + if (traits.isArray() && traits.type() == Tango::DEV_STRING) { - tx.conn().prepare( - _query_builder.storeDataEventName(traits), _query_builder.storeDataEventQuery<T>(traits)); - } + auto prepare_array = [](auto &value) { + auto iter = value->begin(); + std::string result = "ARRAY["; - // get the pqxx prepared statement invocation object to allow us to - // bind each parameter in turn, this gives us the flexibility to bind - // conditional parameters (as long as the query string matches) - auto inv = tx.prepared(_query_builder.storeDataEventName(traits)); - - // this lambda stores the data value correctly into the invocation, - // we must treat scalar/spectrum in different ways, one is a single - // element and the other an array. Further, the unique_ptr may be - // empty and signify a null should be stored in the column instead - auto store_value = [&tx, &inv, &traits](auto &value) { - if (value && value->size() > 0) - { - // this ensures strings are quoted and escaped, other types are ignored - store_data_utils::Preprocess<T>::run(value, tx); - store_data_utils::Store<T>::run(value, inv, traits); - } - else - { - // no value was given for this field, simply add a null - // instead, this allows invalid quality attributes to be saved - // with no data - inv(); - } - }; + result = "$$" + pqxx::to_string((*iter)) + "$$"; + + for (++iter; iter != value->end(); ++iter) + { + result += ","; + result += "$$" + pqxx::to_string((*iter)) + "$$"; + } + + result += "]"; + return result; + }; + + auto query = "INSERT INTO " + QueryBuilder::tableName(traits) + " (" + DAT_COL_ID + "," + DAT_COL_DATA_TIME; + + if (traits.hasReadData()) + query = query + "," + DAT_COL_VALUE_R; + + if (traits.hasWriteData()) + query = query + "," + DAT_COL_VALUE_W; - // bind all the parameters - inv(_conf_id_cache->value(full_attr_name)); - inv(event_time); + // split to ensure increments are in the correct order + query = query + "," + DAT_COL_QUALITY + ") VALUES (" + pqxx::to_string(full_attr_name); + query = query + ",TO_TIMESTAMP(" + pqxx::to_string(event_time) + ")"; - if (traits.hasReadData()) - store_value(value_r); + // add the read parameter with cast + if (traits.hasReadData()) + query = query + "," + prepare_array(value_r); - if (traits.hasWriteData()) - store_value(value_w); + // add the write parameter with cast + if (traits.hasWriteData()) + query = query + "," + prepare_array(value_w); - inv(quality); + query = query + "," + pqxx::to_string(quality) + ")"; - // execute - inv.exec(); + tx.exec0(query); + } + else + { + // prepare as a prepared statement, we are going to use these + // queries often + if (!tx.prepared(_query_builder.storeDataEventName(traits)).exists()) + { + tx.conn().prepare( + _query_builder.storeDataEventName(traits), _query_builder.storeDataEventQuery<T>(traits)); + } + + // get the pqxx prepared statement invocation object to allow us to + // bind each parameter in turn, this gives us the flexibility to bind + // conditional parameters (as long as the query string matches) + auto inv = tx.prepared(_query_builder.storeDataEventName(traits)); + + // this lambda stores the data value correctly into the invocation, + // we must treat scalar/spectrum in different ways, one is a single + // element and the other an array. Further, the unique_ptr may be + // empty and signify a null should be stored in the column instead + auto store_value = [&tx, &inv, &traits](auto &value) { + if (value && value->size() > 0) + { + store_data_utils::Store<T>::run(value, inv, traits); + } + else + { + // no value was given for this field, simply add a null + // instead, this allows invalid quality attributes to be saved + // with no data + inv(); + } + }; + + // bind all the parameters + inv(_conf_id_cache->value(full_attr_name)); + inv(event_time); + + if (traits.hasReadData()) + store_value(value_r); + + if (traits.hasWriteData()) + store_value(value_w); + + inv(quality); + + // execute + inv.exec(); + } // commit the result tx.commit(); @@ -179,7 +204,6 @@ namespace pqxx_conn LOCATION_INFO); } } - } // namespace pqxx_conn } // namespace hdbpp_internal #endif // _PSQL_CONNECTION_TPP diff --git a/src/HdbppTimescaleDb.cpp b/src/HdbppTimescaleDb.cpp index 164a99b041eecc698fd7b77204ff04dded38e277..07e1e419aa5274430798f3d43813a36c7dbf6ee0 100644 --- a/src/HdbppTimescaleDb.cpp +++ b/src/HdbppTimescaleDb.cpp @@ -104,10 +104,20 @@ HdbppTimescaleDb::HdbppTimescaleDb(const vector<string> &configuration) auto level = param_to_lower(HdbppTimescaleDbUtils::getConfigParam(libhdb_conf, "logging_level", false)); auto log_file = HdbppTimescaleDbUtils::getConfigParam(libhdb_conf, "log_file", false); auto log_console = HdbppTimescaleDbUtils::getConfigParam(libhdb_conf, "log_console", false); + auto log_syslog = HdbppTimescaleDbUtils::getConfigParam(libhdb_conf, "log_syslog", false); auto log_file_name = HdbppTimescaleDbUtils::getConfigParam(libhdb_conf, "log_file_name", false); - LogConfigurator::initLogging( - param_to_lower(log_file) == "true", param_to_lower(log_console) == "true", log_file_name); + // init the base logging system + LogConfigurator::initLogging(); + + if (param_to_lower(log_file) == "true") + LogConfigurator::initFileLogging(log_file_name); + + if (param_to_lower(log_console) == "true") + LogConfigurator::initConsoleLogging(); + + if (param_to_lower(log_syslog) == "true") + LogConfigurator::initSyslogLogging(); if (level == "error" || level.empty()) LogConfigurator::setLoggingLevel(spdlog::level::level_enum::err); @@ -125,7 +135,9 @@ HdbppTimescaleDb::HdbppTimescaleDb(const vector<string> &configuration) LogConfigurator::setLoggingLevel(spdlog::level::level_enum::err); spdlog::info("Logging level: {}", level); - spdlog::info("Logging to file: {}, logging to console: {}", log_file, log_console); + spdlog::info("Logging to console: {}", log_console); + spdlog::info("Logging to syslog: {}", log_syslog); + spdlog::info("Logging to file: {}", log_file); spdlog::info("Logfile (if any): {}", log_file_name); spdlog::info("Starting libhdbpp-timescale shared library..."); diff --git a/src/HdbppTxNewAttribute.hpp b/src/HdbppTxNewAttribute.hpp index 063bf643ea99fe11d27d7cf494ecc19063d70790..060446b47e64702a9c8d6f44df9f20fc3c0781f9 100644 --- a/src/HdbppTxNewAttribute.hpp +++ b/src/HdbppTxNewAttribute.hpp @@ -75,7 +75,7 @@ HdbppTxNewAttribute<Conn> &HdbppTxNewAttribute<Conn>::store() } else if (_traits.isInvalid()) { - std::string msg {"AttributeTraits are invalid. Unable to complete the transaction. For attribute" + + std::string msg {"AttributeTraits are invalid. Unable to complete the transaction. For attribute: " + _attr_name.fqdnAttributeName()}; spdlog::error("Error: {}", msg); @@ -83,7 +83,7 @@ HdbppTxNewAttribute<Conn> &HdbppTxNewAttribute<Conn>::store() } else if (HdbppTxBase<Conn>::connection().isClosed()) { - std::string msg {"The connection is reporting it is closed. Unable to store new attribute. For attribute" + + std::string msg {"The connection is reporting it is closed. Unable to store new attribute. For attribute: " + _attr_name.fqdnAttributeName()}; spdlog::error("Error: {}", msg); @@ -94,7 +94,7 @@ HdbppTxNewAttribute<Conn> &HdbppTxNewAttribute<Conn>::store() if (_traits.isImage()) { std::string msg { - "Image type attributes are currently not supported. For attribute" + _attr_name.fqdnAttributeName()}; + "Image type attributes are currently not supported. For attribute: " + _attr_name.fqdnAttributeName()}; spdlog::error("Error: {}", msg); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); @@ -103,7 +103,7 @@ HdbppTxNewAttribute<Conn> &HdbppTxNewAttribute<Conn>::store() // unsupported types if (_traits.type() == Tango::DEV_ENUM || _traits.type() == Tango::DEV_ENCODED) { - std::string msg {"Unsupported attribute type: " + tangoEnumToString(_traits.type()) + ". For attribute" + + std::string msg {"Unsupported attribute type: " + tangoEnumToString(_traits.type()) + ". For attribute: " + _attr_name.fqdnAttributeName()}; spdlog::error("Error: {}", msg); @@ -123,7 +123,7 @@ HdbppTxNewAttribute<Conn> &HdbppTxNewAttribute<Conn>::store() { // oops, someone is trying to change types, this is not supported yet, throw an exception std::string msg { - "Attempt to add an attribute which is already stored with different type information. For attribute" + + "Attempt to add an attribute which is already stored with different type information. For attribute: " + _attr_name.fqdnAttributeName()}; spdlog::error("Error: {}", msg); @@ -151,7 +151,7 @@ HdbppTxNewAttribute<Conn> &HdbppTxNewAttribute<Conn>::store() { // someone is trying to add the same attribute over and over? std::string msg {"The attribute already exists in the database. Can not add again. "}; - spdlog::warn("Warning: {} For attribute {}", msg, _attr_name.fqdnAttributeName()); + spdlog::warn("Warning: {} For attribute: {}", msg, _attr_name.fqdnAttributeName()); // bad black box behaviour, this is not an error, in fact, the system // built top assume this undocumented behaviour!! @@ -159,7 +159,7 @@ HdbppTxNewAttribute<Conn> &HdbppTxNewAttribute<Conn>::store() } else { - spdlog::info("Adding a new attribute to the system: {}", _attr_name.fqdnAttributeName()); + spdlog::debug("Adding a new attribute to the system: {}", _attr_name.fqdnAttributeName()); // attempt to store the new attribute into the database for the first time HdbppTxBase<Conn>::connection().storeAttribute(prepared_attr_name, diff --git a/src/LibUtils.cpp b/src/LibUtils.cpp index 9be85ba0a6d3dcbb4acaf45d74f45c1357844226..5f14b3ba3ff3f759148a2c08abd54f5017fe7df8 100644 --- a/src/LibUtils.cpp +++ b/src/LibUtils.cpp @@ -23,6 +23,7 @@ #include "spdlog/sinks/null_sink.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/sinks/dist_sink.h" #include "spdlog/sinks/syslog_sink.h" namespace hdbpp_internal @@ -134,47 +135,69 @@ ostream &operator<<(ostream &os, Tango::AttrQuality quality) //============================================================================= //============================================================================= -void LogConfigurator::initLogging(bool enable_file, bool enable_console, const string &log_file_name) +void LogConfigurator::initLogging() { - try - { - spdlog::init_thread_pool(8192, 1); - - vector<spdlog::sink_ptr> sinks; - - // attempt to create a rotating log files of size 10MB and 3 rotations - if (enable_file && !log_file_name.empty()) - sinks.push_back(make_shared<spdlog::sinks::rotating_file_sink_mt>(log_file_name, 1024 * 1024 * 10, 3)); - - if (enable_console) - sinks.push_back(make_shared<spdlog::sinks::stdout_color_sink_mt>()); + auto logger = spdlog::get(logging_utils::LibLoggerName); - if (sinks.empty()) - sinks.push_back(make_shared<spdlog::sinks::null_sink_mt>()); - - auto logger = make_shared<spdlog::async_logger>(LibLoggerName, - sinks.begin(), - sinks.end(), - spdlog::thread_pool(), - spdlog::async_overflow_policy::overrun_oldest); - - // set the logger as the default so it can be accessed all over the library - spdlog::register_logger(logger); - spdlog::flush_every(std::chrono::seconds(1)); - spdlog::flush_on(spdlog::level::warn); - spdlog::set_default_logger(logger); - - spdlog::debug("Initialised the logging system..."); + if (!logger) + { + try + { + spdlog::init_thread_pool(8192, 1); + + auto dist_sink = make_shared<spdlog::sinks::dist_sink_mt>(); + + auto logger = make_shared<spdlog::async_logger>(logging_utils::LibLoggerName, + dist_sink, + spdlog::thread_pool(), + spdlog::async_overflow_policy::overrun_oldest); + + // set the logger as the default so it can be accessed all over the library + spdlog::register_logger(logger); + spdlog::flush_every(std::chrono::seconds(1)); + spdlog::flush_on(spdlog::level::warn); + spdlog::set_default_logger(logger); + } + catch (const spdlog::spdlog_ex &ex) + { + string msg {"Failed to initialise the logging system, caught error: " + string(ex.what())}; + cout << msg << endl; + Tango::Except::throw_exception("Runtime Error", msg, LOCATION_INFO); + } + } +} - if (enable_file && !log_file_name.empty()) - spdlog::debug("File logging enabled. Log file at: {}", log_file_name); +//============================================================================= +//============================================================================= +void LogConfigurator::initSyslogLogging() +{ + try + { + auto logger = spdlog::get(logging_utils::LibLoggerName); + auto &sinks_tmp = dynamic_pointer_cast<spdlog::sinks::dist_sink_mt>(*(logger->sinks().begin()))->sinks(); + sinks_tmp.push_back(make_shared<spdlog::sinks::syslog_sink_mt>(logging_utils::SyslogIdent, 0, LOG_USER, false)); + } + catch (const spdlog::spdlog_ex &ex) + { + string msg {"Failed to initialise the syslog logging system, caught error: " + string(ex.what())}; + cout << msg << endl; + Tango::Except::throw_exception("Runtime Error", msg, LOCATION_INFO); + } +} - if (enable_console) - spdlog::debug("Console logging enabled."); +//============================================================================= +//============================================================================= +void LogConfigurator::initConsoleLogging() +{ + try + { + auto logger = spdlog::get(logging_utils::LibLoggerName); + auto &sinks_tmp = dynamic_pointer_cast<spdlog::sinks::dist_sink_mt>(*(logger->sinks().begin()))->sinks(); + sinks_tmp.push_back(make_shared<spdlog::sinks::stdout_color_sink_mt>()); } catch (const spdlog::spdlog_ex &ex) { - string msg {"Failed to initialise the logging system, caught error: " + string(ex.what())}; + string msg {"Failed to initialise the console logging system, caught error: " + string(ex.what())}; cout << msg << endl; Tango::Except::throw_exception("Runtime Error", msg, LOCATION_INFO); } @@ -182,17 +205,27 @@ void LogConfigurator::initLogging(bool enable_file, bool enable_console, const s //============================================================================= //============================================================================= -void LogConfigurator::initLoggingMetrics(bool enable_file, bool enable_console, const string &log_file_name) +void LogConfigurator::initFileLogging(const std::string &log_file_name) { - auto logger = spdlog::get(LibLoggerName); - if (!logger) initLogging(enable_file, enable_console, log_file_name); + try + { + auto logger = spdlog::get(logging_utils::LibLoggerName); + auto &sinks_tmp = dynamic_pointer_cast<spdlog::sinks::dist_sink_mt>(*(logger->sinks().begin()))->sinks(); + sinks_tmp.push_back(make_shared<spdlog::sinks::rotating_file_sink_mt>(log_file_name, 1024 * 1024 * 10, 3)); + } + catch (const spdlog::spdlog_ex &ex) + { + string msg {"Failed to initialise the file logging system, caught error: " + string(ex.what())}; + cout << msg << endl; + Tango::Except::throw_exception("Runtime Error", msg, LOCATION_INFO); + } } //============================================================================= //============================================================================= void LogConfigurator::shutdownLogging() { - auto logger = spdlog::get(LibLoggerName); + auto logger = spdlog::get(logging_utils::LibLoggerName); if (!logger) { diff --git a/src/LibUtils.hpp b/src/LibUtils.hpp index 266c7e0c7e11d1ad5248f04a526ddd59ad9cffc1..49dd0727fd7d1817710fba8682990876706c40cf 100644 --- a/src/LibUtils.hpp +++ b/src/LibUtils.hpp @@ -58,40 +58,44 @@ std::ostream &operator<<(std::ostream &os, Tango::AttrDataFormat format); std::ostream &operator<<(std::ostream &os, Tango::CmdArgType type); std::ostream &operator<<(std::ostream &os, Tango::AttrQuality quality); -// SPDLOG config and setup -const std::string LibLoggerName = "hdbpp"; - struct LogConfigurator { - static void initLogging(bool enable_file, bool enable_console, const std::string &log_file_name = ""); - - // this version is used for metrics testing, and ignores the call if the - // logger already exists - static void initLoggingMetrics(bool enable_file, bool enable_console, const std::string &log_file_name = ""); + static void initLogging(); + static void initSyslogLogging(); + static void initConsoleLogging(); + static void initFileLogging(const std::string &log_file_name); static void shutdownLogging(); static void setLoggingLevel(spdlog::level::level_enum level); }; -// get the file name from the __FILE__ variable for error messages -constexpr auto* getFileName(const char* const path) +namespace logging_utils { - const auto* start_position = path; + // SPDLOG config and setup + const std::string LibLoggerName = "hdbpp"; + const std::string SyslogIdent = "hdbpp-timescale"; - for (const auto* current_character = path; *current_character != '\0'; ++current_character) - if (*current_character == '\\' || *current_character == '/') - start_position = current_character; + // get the file name from the __FILE__ variable for error messages + constexpr auto* getFileName(const char* const path) + { + const auto* start_position = path; - if (start_position != path) - ++start_position; + for (const auto* current_character = path; *current_character != '\0'; ++current_character) + if (*current_character == '\\' || *current_character == '/') + start_position = current_character; - return start_position; -} + if (start_position != path) + ++start_position; + + return start_position; + } +} // namespace logging_utils // Macros to get the location for reporting errors #define S1(x) #x #define S2(x) S1(x) -#define LOCATION_INFO string(getFileName(__FILE__)) + ":" + string(__func__) + ":" S2(__LINE__) + +#define LOCATION_INFO std::string(logging_utils::getFileName(__FILE__)) + ":" + std::string(__func__) + ":" S2(__LINE__) }; // namespace hdbpp_internal #endif // _LIBUTILS_H diff --git a/src/PqxxExtension.hpp b/src/PqxxExtension.hpp index c64de39d04af85a21eb6ce8ad6345fed98c48a48..4f814ab726fd22e60c99b58ccfca717e02045e7e 100644 --- a/src/PqxxExtension.hpp +++ b/src/PqxxExtension.hpp @@ -176,9 +176,10 @@ public: } }; -// This specialisation is for string types to ensure the string is quoted -// for storage -template<> +// This specialisation is for string types. Unlike other types the string type requires +// the use of the ARRAY notation and dollar quoting to ensure the strings are stored +// without escape characters. +/*template<> struct string_traits<std::vector<std::string>> { public: @@ -217,12 +218,27 @@ public: } } + // unlike other types, when encoed to a string th static std::string to_string(const std::vector<std::string> &value) { - // simply use the pqxx utilities for this, rather than reinvent the wheel - return "{" + separated_list(",", value.begin(), value.end()) + "}"; + if (value.empty()) + return {}; + + auto iter = value.begin()l + auto result = "ARRAY[" + + result = "$$" + to_string((*iter)) + "$$"; + + for (++iter; iter != value.end(); ++iter) + { + result += ","; + result += "$$" + to_string((*iter)) + "$$"; + } + + result += "]" + return result; } -}; +};*/ // This specialisation is for bool, since it is not a normal container class, but // rather some kind of alien bitfield. We have to adjust the from_string to take into diff --git a/src/QueryBuilder.cpp b/src/QueryBuilder.cpp index eac7987d3527fa511ee17bfffe7e4019aa6e6b4a..32208b8e2973ddadb8f11823a69b439450e87f72 100644 --- a/src/QueryBuilder.cpp +++ b/src/QueryBuilder.cpp @@ -247,8 +247,8 @@ namespace pqxx_conn // cache the query string against the traits _data_event_error_queries.emplace(traits, query); - _logger->debug("Built new data event error query and cached it against traits: {}", traits); - _logger->debug("New data event error query is: {}", query); + spdlog::debug("Built new data event error query and cached it against traits: {}", traits); + spdlog::debug("New data event error query is: {}", query); // now return it (must dereference the map again to get the static version) return _data_event_error_queries[traits]; @@ -392,7 +392,7 @@ namespace pqxx_conn // add to the cache for future hits cache.emplace(traits, new_name); - _logger->debug("New query name: {} cached against traits:", new_name, traits); + spdlog::debug("New query name: {} cached against traits:", new_name, traits); return cache[traits]; } diff --git a/src/QueryBuilder.hpp b/src/QueryBuilder.hpp index 79cb042d34af857a38124de69cac861db0d85e1d..82bc533694190a73327d71138a7f6df59187cc4f 100644 --- a/src/QueryBuilder.hpp +++ b/src/QueryBuilder.hpp @@ -81,7 +81,6 @@ namespace pqxx_conn class QueryBuilder { public: - QueryBuilder() { _logger = spdlog::get(LibLoggerName); } // Non-static methods @@ -96,12 +95,12 @@ namespace pqxx_conn const std::string &storeDataEventQuery(const AttributeTraits &traits); const std::string &storeDataEventErrorQuery(const AttributeTraits &traits); - std::string tableName(const AttributeTraits &traits); void print(std::ostream &os) const noexcept; // Static methods + static std::string tableName(const AttributeTraits &traits); static const std::string &storeAttributeQuery(); static const std::string &storeHistoryEventQuery(); static const std::string &storeHistoryStringQuery(); @@ -128,11 +127,8 @@ namespace pqxx_conn std::map<AttributeTraits, std::string> _data_event_error_query_names; // cached insert query strings built from the traits object - map<AttributeTraits, std::string> _data_event_queries; - map<AttributeTraits, std::string> _data_event_error_queries; - - // logging subsystem - std::shared_ptr<spdlog::logger> _logger; + std::map<AttributeTraits, std::string> _data_event_queries; + std::map<AttributeTraits, std::string> _data_event_error_queries; }; //============================================================================= @@ -176,8 +172,8 @@ namespace pqxx_conn // cache the query string against the traits _data_event_queries.emplace(traits, query); - _logger->debug("Built new data event query and cached it against traits: {}", traits); - _logger->debug("New data event query is: {}", query); + spdlog::debug("Built new data event query and cached it against traits: {}", traits); + spdlog::debug("New data event query is: {}", query); // now return it (must dereference the map again to get the static version) return _data_event_queries[traits]; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 353a4276193140c384f27b8b4fea3a2d87a45d48..34869d53c93416e58e46bc6fccf9aac7342672be 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -37,4 +37,18 @@ if(DO_CLANG_TIDY) set_target_properties(unit-tests PROPERTIES CXX_CLANG_TIDY ${DO_CLANG_TIDY}) -endif(DO_CLANG_TIDY) \ No newline at end of file +endif(DO_CLANG_TIDY) + +add_executable(tester test.cpp) +target_compile_options(tester PRIVATE -Wall -Wextra -g) + +target_link_libraries(tester + PRIVATE libhdbpp_headers libhdbpp_timescale_shared_library Catch2 TangoInterfaceLibrary) + +target_include_directories(tester + PRIVATE ${CMAKE_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}) + +set_target_properties(tester + PROPERTIES + LINK_FLAGS "-Wl,--no-undefined" + CXX_STANDARD 14) diff --git a/test/HdbppTxDataEventTests.cpp b/test/HdbppTxDataEventTests.cpp index 8861eab72ed71e67a8d91444cf8ddc332b119607..5d1358f65f992abcd38e0da8bfbec10f456e1555 100644 --- a/test/HdbppTxDataEventTests.cpp +++ b/test/HdbppTxDataEventTests.cpp @@ -173,8 +173,10 @@ SCENARIO("Construct a valid HdbppTxDataEvent data event for storage", "[hdbpp-tx // ugly, how is this dealt with in Tango!?! struct timeval tv {}; + struct Tango::TimeVal tango_tv {}; + gettimeofday(&tv, nullptr); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; @@ -240,8 +242,10 @@ SCENARIO("An invalid quality results in an HdbppTxDataEvent event with no data", // ugly, how is this dealt with in Tango!?! struct timeval tv {}; + struct Tango::TimeVal tango_tv {}; + gettimeofday(&tv, nullptr); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; @@ -284,8 +288,10 @@ SCENARIO("A DeviceAttribute with no data results in an HdbppTxDataEvent event wi // ugly, how is this dealt with in Tango!?! struct timeval tv {}; + struct Tango::TimeVal tango_tv {}; + gettimeofday(&tv, nullptr); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; @@ -328,8 +334,10 @@ SCENARIO( // ugly, how is this dealt with in Tango!?! struct timeval tv {}; + struct Tango::TimeVal tango_tv {}; + gettimeofday(&tv, nullptr); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; @@ -409,8 +417,10 @@ TEST_CASE("Creating HdbppTxDataEvents for each tango type and storing them", "[d // ugly, how is this dealt with in Tango!?! struct timeval tv {}; + struct Tango::TimeVal tango_tv {}; + gettimeofday(&tv, nullptr); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; diff --git a/test/main.cpp b/test/main.cpp index 2be52fded2c5230c8a1c9ad15202fc2fd2689e6a..74ae4d91c7923d829e6af91490497b4f8d9f0301 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -24,7 +24,7 @@ int main(int argc, char *argv[]) { - hdbpp_internal::LogConfigurator::initLogging(false, true, "/tmp/hdb/test.log"); + hdbpp_internal::LogConfigurator::initLogging(); hdbpp_internal::LogConfigurator::setLoggingLevel(spdlog::level::err); int result = Catch::Session().run(argc, argv); diff --git a/thirdparty/spdlog b/thirdparty/spdlog index a7148b718ea2fabb8387cb90aee9bf448da63e65..1549ff12f1aa61ffc4d9a8727c519034724392a0 160000 --- a/thirdparty/spdlog +++ b/thirdparty/spdlog @@ -1 +1 @@ -Subproject commit a7148b718ea2fabb8387cb90aee9bf448da63e65 +Subproject commit 1549ff12f1aa61ffc4d9a8727c519034724392a0