diff --git a/.github/workflows/run_post_merge_tests.yml b/.github/workflows/run_post_merge_tests.yml index 121d22821..d63e72ef2 100644 --- a/.github/workflows/run_post_merge_tests.yml +++ b/.github/workflows/run_post_merge_tests.yml @@ -140,5 +140,7 @@ jobs: if: contains(matrix.os, 'ubuntu') && !contains(matrix.compiler, 'intel') run: | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/install/lib && + export SR_LOG_FILE=smartredis_examples_log.txt && + export SR_LOG_LEVEL=INFO && make test-examples diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 6538ed963..0de02fc55 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -107,6 +107,8 @@ jobs: bash ../build-scripts/build-catch.sh && cd ../ && export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/install/lib && + export SR_LOG_FILE=smartredis_cicd_tests_log.txt && + export SR_LOG_LEVEL=INFO && make test-verbose - name: Run Python coverage tests @@ -163,5 +165,7 @@ jobs: - name: Run testing with redis cluster run: | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/install/lib && + export SR_LOG_FILE=smartredis_cicd_tests_log.txt && + export SR_LOG_LEVEL=INFO && make test-verbose diff --git a/.github/workflows/run_tests_uds.yml b/.github/workflows/run_tests_uds.yml index 206266c9b..2119a5d4d 100644 --- a/.github/workflows/run_tests_uds.yml +++ b/.github/workflows/run_tests_uds.yml @@ -145,5 +145,7 @@ jobs: - name: Run testing with UDS redis run: | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/install/lib && + export SR_LOG_FILE=smartredis_uds_tests_log.txt && + export SR_LOG_LEVEL=INFO && make test-verbose diff --git a/CMakeLists.txt b/CMakeLists.txt index 96373961a..82ec722e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ set(CLIENT_SRC src/c/c_client.cpp src/c/c_dataset.cpp src/c/c_error.cpp + src/c/c_logcontext.cpp src/cpp/address.cpp src/cpp/client.cpp src/cpp/dataset.cpp @@ -87,6 +88,9 @@ set(CLIENT_SRC src/cpp/stringfield.cpp src/cpp/pipelinereply.cpp src/cpp/threadpool.cpp + src/cpp/utility.cpp + src/cpp/logger.cpp + src/cpp/srobject.cpp ) include_directories(SYSTEM @@ -96,10 +100,12 @@ include_directories(SYSTEM if (BUILD_FORTRAN) set(FORTRAN_SRC - src/fortran/fortran_c_interop.F90 - src/fortran/dataset.F90 src/fortran/client.F90 + src/fortran/dataset.F90 src/fortran/errors.F90 + src/fortran/fortran_c_interop.F90 + src/fortran/logcontext.F90 + src/fortran/logger.F90 ) include_directories(src/fortran) # Note the following has to be before ANY add_library command) @@ -137,6 +143,8 @@ if(BUILD_PYTHON) add_library(smartredis_static STATIC ${CLIENT_SRC}) pybind11_add_module(smartredisPy + src/python/src/pysrobject.cpp + src/python/src/pylogcontext.cpp src/python/src/pyclient.cpp src/python/src/pydataset.cpp ${CLIENT_SRC} diff --git a/conftest.py b/conftest.py index d84d3fbd6..3fed36865 100644 --- a/conftest.py +++ b/conftest.py @@ -65,6 +65,10 @@ def mock_data(): def mock_model(): return MockTestModel +@pytest.fixture +def context(request): + return request.node.name + class MockTestData: @staticmethod diff --git a/doc/changelog.rst b/doc/changelog.rst index f94bf849d..4c9ee434d 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -14,6 +14,7 @@ Description - Improved error reporting capabilities for Fortran clients - Python error messages from SmartRedis contain more information +- Added logging functionality to the SmartRedis library - A bug related to thread pool initialization was fixed. - This version adds new functionality in the form of support for Unix Domain Sockets. - Fortran client can now be optionally built with the rest of the library @@ -23,6 +24,7 @@ Detailed Notes - Fortran clients can now access error text and source location (PR284_) - Add exception location information from CPP code to Python exceptions (PR283_) +- Added client activity and manual logging for developer use (PR281_) - Fix thread pool error (PR280_) - Update library linking instructions and update Fortran tester build process (PR277_) - Added `add_metadata_for_xarray` and `transform_to_xarray` methods in `DatasetConverter` class for initial support with Xarray (PR262_) @@ -32,6 +34,7 @@ Detailed Notes .. _PR280: https://github.com/CrayLabs/SmartRedis/pull/284 .. _PR280: https://github.com/CrayLabs/SmartRedis/pull/283 +.. _PR281: https://github.com/CrayLabs/SmartRedis/pull/281 .. _PR280: https://github.com/CrayLabs/SmartRedis/pull/280 .. _PR277: https://github.com/CrayLabs/SmartRedis/pull/277 .. _PR262: https://github.com/CrayLabs/SmartRedis/pull/262 diff --git a/doc/runtime.rst b/doc/runtime.rst index 37a584c65..896faf23e 100644 --- a/doc/runtime.rst +++ b/doc/runtime.rst @@ -42,6 +42,36 @@ location. However, the Python ``Client`` constructor also allows for the database location to be set as an input parameter. In this case, it sets SSDB from the input parameter. +Logging Environment Variables +============================= + +SmartRedis will log events to a file on behalf of a client. There +are two main environment variables that affect logging, ``SR_LOG_FILE`` +and ``SR_LOG_LEVEL``. + +``SR_LOG_FILE`` is the specifier for the location of the file that +receives logging information. Each entry in the file will be prefixed +with a timestamp and the identifier of the client that invoked the logging +message. The path may be relative or absolute, though a relative path risks +creation of multiple log files if the executables that instantiate SmartRedis +clients are run from different directories. If this variable is not set, +logging information will be sent to standard console output (STDOUT in C and +C++; output_unit in Fortran). + +``SR_LOG_LEVEL`` relates to the verbosity of information that wil be logged. +It may be one of three levels: ``QUIET`` disables logging altogether. +``INFO`` provides informational logging, such as exception events that +transpire within the SmartRedis library and creation or destruction of a +client object. ``DEBUG`` provides more verbose logging, including information +on the activities of the SmartRedis thread pool and API function entry and exit. +Debug level logging will also log the absence of an expected environment variable, +though this can happen only if the variables to set up logging are in place. If +this parameter is not set, a default logging level of ``INFO`` will be adopted. + +The runtime impact of log levels NONE or INFO should be minimal on +client performance; however, seting the log level to DEBUG may cause some +degradation. + Ensemble Environment Variables ============================== diff --git a/examples/parallel/cpp/smartredis_mnist.cpp b/examples/parallel/cpp/smartredis_mnist.cpp index e24a9b41e..3e348ef8f 100644 --- a/examples/parallel/cpp/smartredis_mnist.cpp +++ b/examples/parallel/cpp/smartredis_mnist.cpp @@ -100,10 +100,12 @@ int main(int argc, char* argv[]) { // Retrieve the MPI rank int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); + std::string logger_name("Client "); + logger_name += std::to_string(rank); // Initialize a Client object bool cluster_mode = true; // Set to false if not using a clustered database - SmartRedis::Client client(cluster_mode); + SmartRedis::Client client(cluster_mode, logger_name); // Set the model and script that will be used by all ranks // from MPI rank 0. diff --git a/examples/parallel/cpp/smartredis_put_get_3D.cpp b/examples/parallel/cpp/smartredis_put_get_3D.cpp index 8304401b1..3dfe5991f 100644 --- a/examples/parallel/cpp/smartredis_put_get_3D.cpp +++ b/examples/parallel/cpp/smartredis_put_get_3D.cpp @@ -50,13 +50,17 @@ int main(int argc, char* argv[]) { for(size_t i=0; i +#include #include #include #include #include #include #include +#include "srobject.h" #include "redisserver.h" #include "rediscluster.h" #include "redis.h" @@ -48,13 +49,12 @@ #include "tensorbase.h" #include "tensor.h" #include "sr_enums.h" +#include "logger.h" ///@file namespace SmartRedis { -class Client; - /*! * \brief The database response to a command */ @@ -65,7 +65,7 @@ typedef redisReply ReplyElem; * \brief The Client class is the primary user-facing * class for executing server commands. */ -class Client +class Client : public SRObject { public: @@ -73,10 +73,11 @@ class Client /*! * \brief Client constructor * \param cluster Flag for if a database cluster is being used + * \param logger_name Name to use for this client when logging * \throw SmartRedis::Exception if client connection or * object initialization fails */ - Client(bool cluster); + Client(bool cluster, const std::string& logger_name = "default"); /*! * \brief Client copy constructor is not available @@ -102,7 +103,7 @@ class Client /*! * \brief Client destructor */ - ~Client(); + virtual ~Client(); /*! * \brief Send a DataSet object to the database diff --git a/include/dataset.h b/include/dataset.h index ff9aa712e..948359056 100644 --- a/include/dataset.h +++ b/include/dataset.h @@ -29,9 +29,10 @@ #ifndef SMARTREDIS_DATASET_H #define SMARTREDIS_DATASET_H #ifdef __cplusplus -#include "stdlib.h" +#include #include #include +#include "srobject.h" #include "tensor.h" #include "tensorpack.h" #include "metadata.h" @@ -40,9 +41,7 @@ ///@file -namespace SmartRedis{ - -class DataSet; +namespace SmartRedis { ///@file /*! @@ -56,7 +55,7 @@ class DataSet; * the DataSet name * (e.g. {dataset_name}.tensor_name). */ -class DataSet +class DataSet : public SRObject { public: @@ -90,6 +89,11 @@ class DataSet */ DataSet& operator=(DataSet&& dataset) = default; + /*! + * \brief DataSet destructor + */ + virtual ~DataSet(); + /*! * \brief Add a tensor to the DataSet. * \param name The name used to reference the tensor diff --git a/include/dbnode.h b/include/dbnode.h index f573e4fa4..8bc714393 100644 --- a/include/dbnode.h +++ b/include/dbnode.h @@ -44,7 +44,7 @@ class DBNode; * \brief The DBNode class stores connection and hash slot * information for the database node. */ -class DBNode{ +class DBNode { public: diff --git a/include/enum_fortran.inc b/include/enum_fortran.inc index d5b38d6dc..e691dc9c9 100644 --- a/include/enum_fortran.inc +++ b/include/enum_fortran.inc @@ -77,3 +77,12 @@ enum, bind(c) enumerator :: SRInvalidError = -1 ! Uninitialized error variable enumerator :: SRTypeError = 9 ! Type mismatch end enum + +! SRLoggingLevel +enum, bind(c) + enumerator :: LLInvalid = -1 ! Invalid or uninitialized logging level + enumerator :: LLQuiet = 1 ! No logging at all + enumerator :: LLInfo = 2 ! Informational logging only + enumerator :: LLDebug = 3 ! Verbose logging for debugging purposes + enumerator :: LLDeveloper = 4 ! Extra verbose logging for internal use +end enum diff --git a/include/logcontext.h b/include/logcontext.h new file mode 100644 index 000000000..aa94be809 --- /dev/null +++ b/include/logcontext.h @@ -0,0 +1,68 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LOG_CONTEXT_H +#define LOG_CONTEXT_H +#ifdef __cplusplus +#include +#include "srobject.h" + +///@file + +namespace SmartRedis{ + +///@file +/*! +* \brief The LogContext is a user-defined context for logging +*/ +class LogContext : public SRObject +{ + public: + + /*! + * \brief LogContext constructor + * \param logging_name The name to prefix log entries with + * when logging with this as a context + */ + LogContext(const std::string& logging_name) + : SRObject(logging_name) + { + log_data(LLDebug, "New LogContext created"); + } + + /*! + * \brief LogContext default destructor + */ + virtual ~LogContext() = default; + +}; + +} // namespace SmartRedis + +#endif // __cplusplus +#endif // LOG_CONTEXT_H diff --git a/include/logger.h b/include/logger.h new file mode 100644 index 000000000..f9f3a5c42 --- /dev/null +++ b/include/logger.h @@ -0,0 +1,356 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SMARTREDIS_LOGGER_H +#define SMARTREDIS_LOGGER_H + +#ifdef __cplusplus // Skip C++ headers for C users +#include +#include "utility.h" +#endif // __cplusplus +#include +#include "sr_enums.h" +#include "srobject.h" + +///@file + +/* +* Redirect logging functions to exception-free variants for C and Fortran +*/ +#ifndef __cplusplus +#define log_data log_data_noexcept +#define log_warning log_warning_noexcept +#define log_error log_error_noexcept +#endif // __cplusplus + +#ifdef __cplusplus +namespace SmartRedis { + +class SRObject; + +/*! +* \brief The Logger class implements a logging facility for SmartRedis +*/ +class Logger { + + private: + + /*! + * \brief Logger constructor. Do not use! To obtain the reference + * to the singleton Logger, use the get_instance() method + */ + Logger() { _initialized = false; } + + /*! + * \brief Logger copy constructor unavailable + * \param logger The Logger to copy for construction + */ + Logger(const Logger& logger) = delete; + + /*! + * \brief Logger assignment operator unavailable + * \param logger The Logger to assign + */ + void operator=(const Logger& logger) = delete; + + /*! + * \brief Logger move assignment operator unavailable + * \param logger The Logger to move + */ + void operator=(Logger&& logger) = delete; + + /*! + * \brief Default Logger destructor + */ + ~Logger() = default; + + public: + + /*! + * \brief Retrieve the unique Logger instance + * \returns The actual logger instance + */ + static Logger& get_instance() + { + static Logger __instance; // instantiated on first acccess + return __instance; + } + + /*! + * \brief Set up logging for the current client + */ + void configure_logging(); + + /*! + * \brief Conditionally log data if the logging level is high enough + * \param context Logging context (string to prefix the log entry with) + * \param level Minimum logging level for data to be logged + * \param data Text of data to be logged + */ + void log_data( + const std::string& context, + SRLoggingLevel level, + const std::string& data); + + /*! + * \brief Conditionally log warning data if the logging level is + * high enough + * \param context Logging context (string to prefix the log entry with) + * \param level Minimum logging level for data to be logged + * \param data Text of data to be logged + */ + void log_warning( + const std::string& context, + SRLoggingLevel level, + const std::string& data) + { + log_data(context, level, "WARNING: " + data); + } + + /*! + * \brief Conditionally log error data if the logging level is + * high enough + * \param context Logging context (string to prefix the log entry with) + * \param level Minimum logging level for data to be logged + * \param data Text of data to be logged + */ + void log_error( + const std::string& context, + SRLoggingLevel level, + const std::string& data) + { + log_data(context, level, "ERROR: " + data); + } + + private: + + /*! + * \brief Track whether logging is initialized + */ + bool _initialized; + + /*! + * \brief The file to which to write log data + */ + std::string _logfile; + + /*! + * \brief The current logging level + */ + SRLoggingLevel _log_level; +}; + +/*! +* \brief Conditionally log data if the logging level is high enough +* \param context Logging context (string to prefix the log entry with) +* \param level Minimum logging level for data to be logged +* \param data Text of data to be logged +*/ +inline void log_data( + const std::string& context, + SRLoggingLevel level, + const std::string& data) +{ + Logger::get_instance().log_data(context, level, data); +} + +/*! +* \brief Conditionally log data if the logging level is high enough +* \param context Object containing the log context +* \param level Minimum logging level for data to be logged +* \param data Text of data to be logged +*/ +inline void log_data( + const SRObject* context, + SRLoggingLevel level, + const std::string& data) +{ + context->log_data(level, data); +} + +/*! +* \brief Conditionally log a warning if the logging level is high enough +* \param context Logging context (string to prefix the log entry with) +* \param level Minimum logging level for data to be logged +* \param data Text of data to be logged +*/ +inline void log_warning( + const std::string& context, + SRLoggingLevel level, + const std::string& data) +{ + Logger::get_instance().log_warning(context, level, data); +} + +/*! +* \brief Conditionally log a warning if the logging level is high enough +* \param context Object containing the log context +* \param level Minimum logging level for data to be logged +* \param data Text of data to be logged +*/ +inline void log_warning( + const SRObject* context, + SRLoggingLevel level, + const std::string& data) +{ + context->log_warning(level, data); +} + +/*! +* \brief Conditionally log an error if the logging level is high enough +* \param context Logging context (string to prefix the log entry with) +* \param level Minimum logging level for data to be logged +* \param data Text of data to be logged +*/ +inline void log_error( + const std::string& context, + SRLoggingLevel level, + const std::string& data) +{ + Logger::get_instance().log_error(context, level, data); +} + +/*! +* \brief Conditionally log an error if the logging level is high enough +* \param context Object containing the log context +* \param level Minimum logging level for data to be logged +* \param data Text of data to be logged +*/ +inline void log_error( + const SRObject* context, + SRLoggingLevel level, + const std::string& data) +{ + context->log_error(level, data); +} + + +///////////////////////////////////////////////////////// +// Stack-based function logger + +/*! +* \brief The FunctionLogger class logs entry and exit of an API function. +* The intended use is to create an instance of this class on the stack +* inside each API point via the LOG_API_FUNCTION macro, below. +*/ +class FunctionLogger { + public: + /*! + * \brief Logger constructor + * \param function_name The name of the function to track + */ + FunctionLogger(const SRObject* context, const char* function_name) + : _name(function_name), _context(context) + { + _context->log_data( + LLDebug, "API Function " + _name + " called"); + } + + /*! + * \brief Logger destructor + */ + ~FunctionLogger() + { + _context->log_data( + LLDebug, "API Function " + _name + " exited"); + } + private: + /*! + * \brief The name of the current function + */ + std::string _name; + + /*! + * \brief The logging context (enclosing object for the API function) + */ + const SRObject* _context; +}; + +/*! +* \brief Instantiate a FunctionLogger for the enclosing API function +*/ +#define LOG_API_FUNCTION() \ + FunctionLogger ___function_logger___(this, __func__) + + +} //namespace SmartRedis +#endif // __cplusplus + + +///////////////////////////////////////////////////////// +// Prototypes for C and Fortran logging methods + +/*! +* \brief Macro to establish C linkage in both C and C++ +*/ +#ifdef __cplusplus +#define C_API extern "C" +#else +#define C_API +#endif + +/*! +* \brief Conditionally log data if the logging level is high enough +* \param context Object containing the log context +* \param level Minimum logging level for data to be logged +* \param data Text of data to be logged +* \param data_len Length in characters of data to be logged +*/ +C_API void log_data_noexcept( + const void* context, + SRLoggingLevel level, + const char* data, + size_t data_len); + +/*! +* \brief Conditionally log a warning if the logging level is high enough +* \param context Object containing the log context +* \param level Minimum logging level for data to be logged +* \param data Text of data to be logged +* \param data_len Length in characters of data to be logged +*/ +C_API void log_warning_noexcept( + const void* context, + SRLoggingLevel level, + const char* data, + size_t data_len); + +/*! +* \brief Conditionally log an error if the logging level is high enough +* \param context Object containing the log context +* \param level Minimum logging level for data to be logged +* \param data Text of data to be logged +* \param data_len Length in characters of data to be logged +*/ +C_API void log_error_noexcept( + const void* context, + SRLoggingLevel level, + const char* data, + size_t data_len); + +#endif //SMARTREDIS_LOGGER_H diff --git a/include/pyclient.h b/include/pyclient.h index 18654cb8e..a7d33ea74 100644 --- a/include/pyclient.h +++ b/include/pyclient.h @@ -26,11 +26,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef PY_CLIENT_H #define PY_CLIENT_H -#ifdef __cplusplus -#include "client.h" -#include "pydataset.h" #include #include #include @@ -38,6 +36,9 @@ #include #include #include +#include "client.h" +#include "pydataset.h" +#include "pysrobject.h" ///@file @@ -45,14 +46,12 @@ namespace SmartRedis { namespace py = pybind11; -class PyClient; - /*! * \brief The PyClient class is a wrapper around the C++ client that is needed for the Python client. */ -class PyClient +class PyClient : public PySRObject { public: @@ -60,13 +59,16 @@ class PyClient * \brief PyClient constructor * \param cluster Flag to indicate if a database cluster * is being used + * \param logger_name Identifier for the current client */ - PyClient(bool cluster); + PyClient( + bool cluster, + const std::string& logger_name = std::string("default")); /*! * \brief PyClient destructor */ - ~PyClient(); + virtual ~PyClient(); /*! * \brief Put a tensor into the database diff --git a/include/pydataset.h b/include/pydataset.h index 233b9d0fb..323c9b094 100644 --- a/include/pydataset.h +++ b/include/pydataset.h @@ -26,11 +26,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "dataset.h" +#ifndef PYDATASET_H +#define PYDATASET_H + #include #include #include #include +#include "dataset.h" +#include "pysrobject.h" ///@file @@ -38,13 +42,11 @@ namespace py = pybind11; namespace SmartRedis { -class PyDataset; - /*! * \brief The PyDataset class is a wrapper around the C++ dataset abstraction class. */ -class PyDataset +class PyDataset : public PySRObject { public: @@ -68,7 +70,7 @@ class PyDataset /*! * \brief PyDataset destructor */ - ~PyDataset(); + virtual ~PyDataset(); /*! * \brief Add a tensor to the PyDataset @@ -147,4 +149,6 @@ class PyDataset DataSet* _dataset; }; -} //namespace SmartRedis \ No newline at end of file +} //namespace SmartRedis + +#endif // PYDATASET_H diff --git a/include/pylogcontext.h b/include/pylogcontext.h new file mode 100644 index 000000000..f9d9d5c54 --- /dev/null +++ b/include/pylogcontext.h @@ -0,0 +1,88 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PYLOGCONTEXT_H +#define PYLOGCONTEXT_H + +#include +#include +#include +#include "logcontext.h" +#include "pysrobject.h" + +///@file + +namespace py = pybind11; + +namespace SmartRedis { + +/*! +* \brief The PyLogContext class is a wrapper around the + C++ LogContext class. +*/ +class PyLogContext : public PySRObject +{ + public: + + /*! + * \brief PyLogContext constructor + * \param context The context to use for log messages + */ + PyLogContext(const std::string& context); + + /*! + * \brief PyLogContext constructor from a + * SmartRedis::LogContext object + * \param logcontext A SmartRedis::LogContext pointer to + * a SmartRedis::LogContext allocated on + * the heap. The SmartRedis::LogContext + * will be deleted upton PyLogContext + * deletion. + */ + PyLogContext(LogContext* logcontext); + + /*! + * \brief PyLogContext destructor + */ + virtual ~PyLogContext(); + + /*! + * \brief Retrieve a pointer to the underlying + * SmartRedis::LogContext object + * \returns LogContext pointer within PyLogContext + */ + LogContext* get(); + + private: + + LogContext* _logcontext; +}; + +} //namespace SmartRedis + +#endif // PYLOGCONTEXT_H diff --git a/include/pysrobject.h b/include/pysrobject.h new file mode 100644 index 000000000..82cb02b7b --- /dev/null +++ b/include/pysrobject.h @@ -0,0 +1,87 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PYSROBJECT_H +#define PYSROBJECT_H + +#include "srobject.h" +#include +#include +#include + +///@file + +namespace py = pybind11; + +namespace SmartRedis { + +/*! +* \brief The PySRObject class is a wrapper around the + C++ SRObject class. +*/ +class PySRObject +{ + public: + + /*! + * \brief PySRObject constructor + * \param context The context to use for log messages + */ + PySRObject(const std::string& context); + + /*! + * \brief PySRObject constructor from a + * SmartRedis::SRObject object + * \param logcontext A SmartRedis::SRObject pointer to + * a SmartRedis::SRObject allocated on + * the heap. The SmartRedis::SRObject + * will be deleted upton PySRObject + * deletion. + */ + PySRObject(SRObject* srobject); + + /*! + * \brief PySRObject destructor + */ + ~PySRObject(); + + /*! + * \brief Retrieve a pointer to the underlying + * SmartRedis::SRObject object + * \returns SRObject pointer within PySRObject + */ + SRObject* get(); + + private: + + SRObject* _srobject; +}; + +} //namespace SmartRedis + +#endif // PYSROBJECT_H diff --git a/include/redis.h b/include/redis.h index 0bccda933..7a99491b8 100644 --- a/include/redis.h +++ b/include/redis.h @@ -33,6 +33,8 @@ namespace SmartRedis { +class Client; + ///@file /*! @@ -44,16 +46,18 @@ class Redis : public RedisServer public: /*! * \brief Redis constructor. + * \param client The owning Client */ - Redis(); + Redis(const Client* client); /*! * \brief Redis constructor. * Uses address provided to constructor instead * of environment variables. + * \param client The owning Client * \param addr_spec The TCP or UDS server address */ - Redis(std::string addr_spec); + Redis(const Client* client, std::string addr_spec); /*! * \brief Redis copy constructor is not allowed diff --git a/include/rediscluster.h b/include/rediscluster.h index bf80809d0..b443ef0fc 100644 --- a/include/rediscluster.h +++ b/include/rediscluster.h @@ -43,7 +43,7 @@ namespace SmartRedis { ///@file -class RedisCluster; +class Client; /*! * \brief The RedisCluster class executes RedisServer @@ -55,16 +55,18 @@ class RedisCluster : public RedisServer /*! * \brief RedisCluster constructor. + * \param client The owning Client */ - RedisCluster(); + RedisCluster(const Client* client); /*! * \brief RedisCluster constructor. * Uses address provided to constructor instead * of environment variables. + * \param client The owning Client * \param address_spec The TCP or UDS address of the server */ - RedisCluster(std::string address_spec); + RedisCluster(const Client* client, std::string address_spec); /*! * \brief RedisCluster copy constructor is not allowed diff --git a/include/redisserver.h b/include/redisserver.h index a5639b815..f1eeb51b5 100644 --- a/include/redisserver.h +++ b/include/redisserver.h @@ -60,8 +60,7 @@ namespace SmartRedis { -class RedisServer; - +class Client; /*! * \brief Abstract class that defines interface for @@ -73,8 +72,9 @@ class RedisServer { /*! * \brief Default constructor + * \param client The owning Client */ - RedisServer(); + RedisServer(const Client* client); /*! * \brief Destructor @@ -562,6 +562,11 @@ class RedisServer { */ static constexpr int _DEFAULT_THREAD_COUNT = 4; + /*! + * \brief The owning Client + */ + const Client* _client; + /*! * \brief Seeding for the random number engine */ @@ -647,24 +652,6 @@ class RedisServer { */ void _check_ssdb_string(const std::string& env_str); - /*! - * \brief Initialize a variable of type integer from an environment - * variable. If the environment variable is not set, - * the default value is assigned. - * \param value Reference to a integer value which will be assigned - * a default value or environment variable value - * \param env_var std::string of the environment variable name - * \param default_value The default value to assign if the environment - * variable is not set. - * \throw SmartRedis::RuntimeException if environment variable - * retrieval fails, conversion to integer fails, or - * if the value of the environment value contains - * characters other than [0,9] or a negative sign ('-'). - */ - void _init_integer_from_env(int& value, - const std::string& env_var, - const int& default_value); - /*! * \brief This function checks that _connection_timeout, * _connection_interval, _command_timeout, and diff --git a/include/sr_enums.h b/include/sr_enums.h index 62ba98ee8..3535a5e82 100644 --- a/include/sr_enums.h +++ b/include/sr_enums.h @@ -72,4 +72,15 @@ typedef enum { SRTensorTypeUint16 = 8 // 16-bit unsigned integer tensor type } SRTensorType; +/*! +* \brief Enumeration for logging levels +*/ +typedef enum { + LLInvalid = 0, // Invalid or uninitialized logging level + LLQuiet = 1, // No logging at all + LLInfo = 2, // Informational logging only + LLDebug = 3, // Verbose logging for debugging purposes + LLDeveloper = 4 // Extra verbose logging for internal use +} SRLoggingLevel; + #endif // SMARTREDIS_ENUMS_H diff --git a/include/srexception.h b/include/srexception.h index 330c9cb40..d44ee51c6 100644 --- a/include/srexception.h +++ b/include/srexception.h @@ -26,11 +26,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SMARTREDIS_SRException_H -#define SMARTREDIS_SRException_H +#ifndef SMARTREDIS_SREXCEPTION_H +#define SMARTREDIS_SREXCEPTION_H #include #include +#include "sr_enums.h" +#include "logger.h" ///@file @@ -114,6 +116,9 @@ class Exception: public std::exception _loc(std::string("\"") + file + std::string("\", line ") + std::to_string(line)) { SRSetLastError(*this); + log_error( + "SmartRedis Library", LLInfo, + exception_class() + " at " + _loc + ": " + _msg); } /*! @@ -127,6 +132,9 @@ class Exception: public std::exception _loc(std::string("\"") + file + std::string("\", line ") + std::to_string(line)) { SRSetLastError(*this); + log_error( + "SmartRedis Library", LLInfo, + exception_class() + " at " + _loc + ": " + _msg); } /*! @@ -180,6 +188,14 @@ class Exception: public std::exception return SRInvalidError; } + /*! + * \brief Get a string representation of the exception class + * \returns Stringified version of the class name + */ + virtual std::string exception_class() { + return std::string("Exception"); + } + /*! * \brief Retrieve the message for an exception * \returns String of message data @@ -216,6 +232,7 @@ class Exception: public std::exception class BadAllocException: public Exception { public: + using Exception::Exception; /*! @@ -225,6 +242,14 @@ class BadAllocException: public Exception SRError to_error_code() const noexcept override { return SRBadAllocError; } + + /*! + * \brief Get a string representation of the exception class + * \returns Stringified version of the class name + */ + virtual std::string exception_class() { + return std::string("BadAllocException"); + } }; /*! @@ -248,6 +273,14 @@ class DatabaseException: public Exception SRError to_error_code() const noexcept override { return SRDatabaseError; } + + /*! + * \brief Get a string representation of the exception class + * \returns Stringified version of the class name + */ + virtual std::string exception_class() { + return std::string("DatabaseException"); + } }; /*! @@ -271,6 +304,14 @@ class RuntimeException: public Exception SRError to_error_code() const noexcept override { return SRRuntimeError; } + + /*! + * \brief Get a string representation of the exception class + * \returns Stringified version of the class name + */ + virtual std::string exception_class() { + return std::string("RuntimeException"); + } }; /*! @@ -294,6 +335,14 @@ class ParameterException: public Exception SRError to_error_code() const noexcept override { return SRParameterError; } + + /*! + * \brief Get a string representation of the exception class + * \returns Stringified version of the class name + */ + virtual std::string exception_class() { + return std::string("ParameterException"); + } }; /*! @@ -317,6 +366,14 @@ class TimeoutException: public Exception SRError to_error_code() const noexcept override { return SRTimeoutError; } + + /*! + * \brief Get a string representation of the exception class + * \returns Stringified version of the class name + */ + virtual std::string exception_class() { + return std::string("TimeoutException"); + } }; /*! @@ -340,6 +397,14 @@ class InternalException: public Exception SRError to_error_code() const noexcept override { return SRInternalError; } + + /*! + * \brief Get a string representation of the exception class + * \returns Stringified version of the class name + */ + virtual std::string exception_class() { + return std::string("InternalException"); + } }; /*! @@ -363,6 +428,14 @@ class KeyException: public Exception SRError to_error_code() const noexcept override { return SRKeyError; } + + /*! + * \brief Get a string representation of the exception class + * \returns Stringified version of the class name + */ + virtual std::string exception_class() { + return std::string("KeyException"); + } }; /*! @@ -385,6 +458,14 @@ class TypeException: public Exception SRError to_error_code() const noexcept override { return SRTypeError; } + + /*! + * \brief Get a string representation of the exception class + * \returns Stringified version of the class name + */ + virtual std::string exception_class() { + return std::string("TypeException"); + } }; /*! @@ -395,4 +476,4 @@ class TypeException: public Exception } // namespace SmartRedis #endif // __cplusplus -#endif // SMARTREDIS_SRException_H +#endif // SMARTREDIS_SREXCEPTION_H diff --git a/include/srobject.h b/include/srobject.h new file mode 100644 index 000000000..22a2d0c0d --- /dev/null +++ b/include/srobject.h @@ -0,0 +1,114 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SMARTREDIS_OBJECT_H +#define SMARTREDIS_OBJECT_H +#ifdef __cplusplus +#include +#include +#include +#include "sr_enums.h" + +///@file + +namespace SmartRedis{ + +///@file +/*! +* \brief The SRObject is a common base class for SmartRedis +* objects that can represent a context for logging +*/ +class SRObject +{ + public: + + /*! + * \brief SRObject constructor + * \param logging_name The name to prefix log entries with + * when logging with this as a context + */ + SRObject(const std::string& logging_name); + + /*! + * \brief SRObject default destructor + */ + virtual ~SRObject() = default; + + /*! + * \brief Conditionally log data if the logging level is high enough + * \param level Minimum logging level for data to be logged + * \param data Text of data to be logged + */ + virtual void log_data( + SRLoggingLevel level, const std::string& data) const; + + /*! + * \brief Conditionally log warning data if the logging level is + * high enough + * \param level Minimum logging level for data to be logged + * \param data Text of data to be logged + */ + virtual void log_warning( + SRLoggingLevel level, const std::string& data) const + { + log_data(level, "WARNING: " + data); + } + + /*! + * \brief Conditionally log error data if the logging level is + * high enough + * \param level Minimum logging level for data to be logged + * \param data Text of data to be logged + */ + virtual void log_error( + SRLoggingLevel level, const std::string& data) const + { + log_data(level, "ERROR: " + data); + } + + /*! + * \brief Retrieve the context for this object + * \returns The context for this object + */ + virtual std::string get_context() const + { + return _lname; + } + + private: + + /*! + * \brief The name prefix log entries with + */ + std::string _lname; +}; + +} // namespace SmartRedis + +#endif // __cplusplus +#endif // SMARTREDIS_OBJECT_H diff --git a/include/threadpool.h b/include/threadpool.h index 758490281..8dbd978c5 100644 --- a/include/threadpool.h +++ b/include/threadpool.h @@ -8,8 +8,11 @@ #include #ifndef SMARTREDIS_THREADPOOL_H +#define SMARTREDIS_THREADPOOL_H namespace SmartRedis { +class Client; + /*! * \brief A thread pool for concurrent execution of parallel jobs */ @@ -18,10 +21,11 @@ class ThreadPool public: /*! * \brief ThreadPool constructor + * \param client The owning client * \param num_threads The number of threads to create in the pool, * or 0 to use one thread per hardware context */ - ThreadPool(unsigned int num_threads=0); + ThreadPool(const Client* client, unsigned int num_threads=0); /*! * \brief ThreadPool destructor @@ -80,6 +84,11 @@ class ThreadPool * \brief Flag for if the thread pool shut down has completed. */ volatile bool shutdown_complete; + + /*! + * \brief Owning client object + */ + const Client* _client; }; } // namespace SmartRedis diff --git a/include/utility.h b/include/utility.h new file mode 100644 index 000000000..90caf0df8 --- /dev/null +++ b/include/utility.h @@ -0,0 +1,69 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SMARTREDIS_UTILITY_H +#define SMARTREDIS_UTILITY_H + +#include +#include + +///@file + +namespace SmartRedis { + +/*! +* \brief Initialize an integer from configuration, such as an +* environment variable +* \param value Receives the configuration value +* \param cfg_key The key to query for the configuration variable +* \param default_value Default if configuration key is not set +* \param suppress_warning Do not issue a warning if the variable +* is not set +*/ +void get_config_integer(int& value, + const std::string& cfg_key, + const int default_value, + bool suppress_warning = false); + +/*! +* \brief Initialize an string from configuration, such as an +* environment variable +* \param value Receives the configuration value +* \param cfg_key The key to query for the configuration variable +* \param default_value Default if configuration key is not set +* \param suppress_warning Do not issue a warning if the variable +* is not set +*/ +void get_config_string(std::string& value, + const std::string& cfg_key, + const std::string& default_value, + bool suppress_warning = false); + +} //namespace SmartRedis + +#endif //SMARTREDIS_UTILITY_H diff --git a/src/c/c_client.cpp b/src/c/c_client.cpp index c5fd68263..61a65cd7c 100644 --- a/src/c/c_client.cpp +++ b/src/c/c_client.cpp @@ -38,14 +38,19 @@ using namespace SmartRedis; // Return a pointer to a new Client. // The caller is responsible for deleting the client via DeleteClient(). extern "C" -SRError SmartRedisCClient(bool cluster, void** new_client) +SRError SmartRedisCClient( + bool cluster, + const char* logger_name, + const size_t logger_name_length, + void** new_client) { SRError result = SRNoError; try { // Sanity check params - SR_CHECK_PARAMS(new_client != NULL); + SR_CHECK_PARAMS(new_client != NULL && logger_name != NULL); - Client* s = new Client(cluster); + std::string _logger_name(logger_name, logger_name_length); + Client* s = new Client(cluster, _logger_name); *new_client = reinterpret_cast(s); } catch (const std::bad_alloc& e) { diff --git a/src/c/c_dataset.cpp b/src/c/c_dataset.cpp index 59d5c848f..6773a93e8 100644 --- a/src/c/c_dataset.cpp +++ b/src/c/c_dataset.cpp @@ -48,7 +48,7 @@ SRError CDataSet(const char* name, const size_t name_length, void** new_dataset) } catch (const std::bad_alloc& e) { *new_dataset = NULL; - SRSetLastError(SRBadAllocException("client allocation")); + SRSetLastError(SRBadAllocException("dataset allocation")); result = SRBadAllocError; } catch (const Exception& e) { diff --git a/src/c/c_error.cpp b/src/c/c_error.cpp index d6b70fd0a..546d3da00 100644 --- a/src/c/c_error.cpp +++ b/src/c/c_error.cpp @@ -39,24 +39,6 @@ static Exception __lastError = Exception("no error"); extern "C" void SRSetLastError(const Exception& last_error) { - // Check environment for debug level if we haven't done so yet - static bool __debug_level_verbose = false; - static bool __debug_level_checked = false; - if (!__debug_level_checked) - { - __debug_level_checked = true; - char *dbg_setting = getenv("SMARTREDIS_DEBUG_LEVEL"); - if (dbg_setting != NULL) { - std::string dbgLevel(dbg_setting); - __debug_level_verbose = dbgLevel.compare("VERBOSE") == 0; - } - } - - // Print out the error message if verbose - if (__debug_level_verbose && SRNoError != last_error.to_error_code()) { - printf("%s\n", last_error.what()); - } - // Store the last error __lastError = last_error; } diff --git a/src/c/c_logcontext.cpp b/src/c/c_logcontext.cpp new file mode 100644 index 000000000..ff5ac243a --- /dev/null +++ b/src/c/c_logcontext.cpp @@ -0,0 +1,95 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "c_logcontext.h" +#include "srexception.h" +#include "srassert.h" + +using namespace SmartRedis; + +// Create a new LogContext. +// The user is responsible for deallocating the LogContext +// via DeallocateeLogContext() +extern "C" +SRError SmartRedisCLogContext( + const char* context, + const size_t context_length, + void** new_logcontext) +{ + SRError result = SRNoError; + try { + // Sanity check params + SR_CHECK_PARAMS(context != NULL && new_logcontext != NULL); + + std::string context_str(context, context_length); + LogContext* logcontext = new LogContext(context_str); + *new_logcontext = reinterpret_cast(logcontext); + } + catch (const std::bad_alloc& e) { + *new_logcontext = NULL; + SRSetLastError(SRBadAllocException("logcontext allocation")); + result = SRBadAllocError; + } + catch (const Exception& e) { + *new_logcontext = NULL; + result = e.to_error_code(); + } + catch (...) { + *new_logcontext = NULL; + SRSetLastError(SRInternalException("Unknown exception occurred")); + result = SRInternalError; + } + + return result; +} + +// Deallocate a LogContext +extern "C" +SRError DeallocateLogContext(void** logcontext) +{ + SRError result = SRNoError; + try + { + // Sanity check params + SR_CHECK_PARAMS(logcontext != NULL); + + LogContext* lc = reinterpret_cast(*logcontext); + delete lc; + *logcontext = NULL; + } + catch (const Exception& e) { + SRSetLastError(e); + result = e.to_error_code(); + } + catch (...) { + SRSetLastError(SRInternalException("Unknown exception occurred")); + result = SRInternalError; + } + + return result; +} diff --git a/src/cpp/client.cpp b/src/cpp/client.cpp index 9c8ebd276..1795dcdca 100644 --- a/src/cpp/client.cpp +++ b/src/cpp/client.cpp @@ -29,20 +29,29 @@ #include #include "client.h" #include "srexception.h" +#include "logger.h" +#include "utility.h" using namespace SmartRedis; // Constructor -Client::Client(bool cluster) - : _redis_cluster(cluster ? new RedisCluster() : NULL), - _redis(cluster ? NULL : new Redis()) +Client::Client(bool cluster, const std::string& logger_name) + : SRObject(logger_name) { + // Log that a new client has been instantiated + log_data(LLDebug, "New client created"); + + // Set up Redis server connection // A std::bad_alloc exception on the initializer will be caught // by the call to new for the client + _redis_cluster = (cluster ? new RedisCluster(this) : NULL); + _redis = (cluster ? NULL : new Redis(this)); if (cluster) _redis_server = _redis_cluster; else _redis_server = _redis; + + // Initialize key prefixing _set_prefixes_from_env(); _use_tensor_prefix = true; _use_model_prefix = false; @@ -63,11 +72,17 @@ Client::~Client() _redis = NULL; } _redis_server = NULL; + + // Log Client destruction + log_data(LLDebug, "Client destroyed"); } // Put a DataSet object into the database void Client::put_dataset(DataSet& dataset) { + // Track calls to this API function + LOG_API_FUNCTION(); + CommandList cmds; _append_dataset_metadata_commands(cmds, dataset); _append_dataset_tensor_commands(cmds, dataset); @@ -78,6 +93,9 @@ void Client::put_dataset(DataSet& dataset) // Retrieve a DataSet object from the database DataSet Client::get_dataset(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Get the metadata message and construct DataSet CommandReply reply = _get_dataset_metadata(name); @@ -108,6 +126,9 @@ DataSet Client::get_dataset(const std::string& name) void Client::rename_dataset(const std::string& old_name, const std::string& new_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + copy_dataset(old_name, new_name); delete_dataset(old_name); } @@ -116,6 +137,9 @@ void Client::rename_dataset(const std::string& old_name, void Client::copy_dataset(const std::string& src_name, const std::string& dest_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Get the metadata message and construct DataSet CommandReply reply = _get_dataset_metadata(src_name); if (reply.n_elements() == 0) { @@ -149,6 +173,9 @@ void Client::copy_dataset(const std::string& src_name, // All tensors and metdata in the DataSet will be deleted. void Client::delete_dataset(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + CommandReply reply = _get_dataset_metadata(name); if (reply.n_elements() == 0) { throw SRRuntimeException("The requested DataSet " + @@ -184,6 +211,9 @@ void Client::put_tensor(const std::string& name, const SRTensorType type, const SRMemoryLayout mem_layout) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string key = _build_tensor_key(name, false); TensorBase* tensor = NULL; @@ -240,6 +270,9 @@ void Client::get_tensor(const std::string& name, SRTensorType& type, const SRMemoryLayout mem_layout) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Retrieve the TensorBase from the database TensorBase* ptr = _get_tensorbase_obj(name); @@ -263,6 +296,8 @@ void Client::get_tensor(const std::string& name, SRTensorType& type, const SRMemoryLayout mem_layout) { + // Track calls to this API function + LOG_API_FUNCTION(); std::vector dims_vec; get_tensor(name, data, dims_vec, type, mem_layout); @@ -287,6 +322,9 @@ void Client::unpack_tensor(const std::string& name, const SRTensorType type, const SRMemoryLayout mem_layout) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (mem_layout == SRMemLayoutContiguous && dims.size() > 1) { throw SRRuntimeException("The destination memory space "\ "dimension vector should only "\ @@ -405,6 +443,9 @@ void Client::unpack_tensor(const std::string& name, void Client::rename_tensor(const std::string& old_name, const std::string& new_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string old_key = _build_tensor_key(old_name, true); std::string new_key = _build_tensor_key(new_name, false); CommandReply reply = _redis_server->rename_tensor(old_key, new_key); @@ -415,6 +456,9 @@ void Client::rename_tensor(const std::string& old_name, // Delete a tensor from the database void Client::delete_tensor(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string key = _build_tensor_key(name, true); CommandReply reply = _redis_server->delete_tensor(key); if (reply.has_error()) @@ -425,6 +469,9 @@ void Client::delete_tensor(const std::string& name) void Client::copy_tensor(const std::string& src_name, const std::string& dest_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string src_key = _build_tensor_key(src_name, true); std::string dest_key = _build_tensor_key(dest_name, false); CommandReply reply = _redis_server->copy_tensor(src_key, dest_key); @@ -443,6 +490,9 @@ void Client::set_model_from_file(const std::string& name, const std::vector& inputs, const std::vector& outputs) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (model_file.size() == 0) { throw SRParameterException("model_file is a required " "parameter of set_model_from_file."); @@ -471,6 +521,9 @@ void Client::set_model_from_file_multigpu(const std::string& name, const std::vector& inputs, const std::vector& outputs) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (model_file.size() == 0) { throw SRParameterException("model_file is a required " "parameter of set_model_from_file_multigpu."); @@ -497,6 +550,9 @@ void Client::set_model(const std::string& name, const std::vector& inputs, const std::vector& outputs) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (name.size() == 0) { throw SRParameterException("name is a required parameter of set_model."); } @@ -551,6 +607,9 @@ void Client::set_model_multigpu(const std::string& name, const std::vector& inputs, const std::vector& outputs) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (name.size() == 0) { throw SRParameterException("name is a required parameter of set_model."); } @@ -596,6 +655,9 @@ void Client::set_model_multigpu(const std::string& name, // Retrieve the model from the database std::string_view Client::get_model(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string get_key = _build_model_key(name, true); CommandReply reply = _redis_server->get_model(get_key); if (reply.has_error()) @@ -613,6 +675,9 @@ void Client::set_script_from_file(const std::string& name, const std::string& device, const std::string& script_file) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Read the script from the file std::ifstream fin(script_file); std::ostringstream ostream; @@ -632,6 +697,9 @@ void Client::set_script_from_file_multigpu(const std::string& name, int first_gpu, int num_gpus) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Read the script from the file std::ifstream fin(script_file); std::ostringstream ostream; @@ -649,6 +717,9 @@ void Client::set_script(const std::string& name, const std::string& device, const std::string_view& script) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (device.size() == 0) { throw SRParameterException("device is a required " "parameter of set_script."); @@ -668,6 +739,9 @@ void Client::set_script_multigpu(const std::string& name, int first_gpu, int num_gpus) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (first_gpu < 0) { throw SRParameterException("first_gpu must be a non-negative integer."); } @@ -696,6 +770,9 @@ void Client::run_model(const std::string& name, std::vector inputs, std::vector outputs) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string key = _build_model_key(name, true); if (_use_tensor_prefix) { @@ -714,6 +791,9 @@ void Client::run_model_multigpu(const std::string& name, int first_gpu, int num_gpus) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (first_gpu < 0) { throw SRParameterException("first_gpu must be a non-negative integer."); } @@ -737,6 +817,9 @@ void Client::run_script(const std::string& name, std::vector inputs, std::vector outputs) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string key = _build_model_key(name, true); if (_use_tensor_prefix) { @@ -756,6 +839,9 @@ void Client::run_script_multigpu(const std::string& name, int first_gpu, int num_gpus) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (first_gpu < 0) { throw SRParameterException("first_gpu must be a non-negative integer"); } @@ -776,6 +862,9 @@ void Client::run_script_multigpu(const std::string& name, // Delete a model from the database void Client::delete_model(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string key = _build_model_key(name, true); CommandReply reply = _redis_server->delete_model(key); @@ -787,6 +876,9 @@ void Client::delete_model(const std::string& name) void Client::delete_model_multigpu( const std::string& name, int first_gpu, int num_gpus) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (first_gpu < 0) { throw SRParameterException("first_gpu must be a non-negative integer"); } @@ -801,6 +893,9 @@ void Client::delete_model_multigpu( // Delete a script from the database void Client::delete_script(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string key = _build_model_key(name, true); CommandReply reply = _redis_server->delete_script(key); @@ -812,6 +907,9 @@ void Client::delete_script(const std::string& name) void Client::delete_script_multigpu( const std::string& name, int first_gpu, int num_gpus) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (first_gpu < 0) { throw SRParameterException("first_gpu must be a non-negative integer"); } @@ -826,12 +924,18 @@ void Client::delete_script_multigpu( // Check if the key exists in the database bool Client::key_exists(const std::string& key) { + // Track calls to this API function + LOG_API_FUNCTION(); + return _redis_server->key_exists(key); } // Check if the tensor (or the dataset) exists in the database bool Client::tensor_exists(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string key = _build_tensor_key(name, true); return _redis_server->key_exists(key); } @@ -839,6 +943,9 @@ bool Client::tensor_exists(const std::string& name) // Check if the dataset exists in the database bool Client::dataset_exists(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string key = _build_dataset_ack_key(name, true); return _redis_server->hash_field_exists(key, _DATASET_ACK_FIELD); } @@ -846,6 +953,9 @@ bool Client::dataset_exists(const std::string& name) // Check if the model (or the script) exists in the database bool Client::model_exists(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::string key = _build_model_key(name, true); return _redis_server->model_key_exists(key); } @@ -855,6 +965,9 @@ bool Client::poll_key(const std::string& key, int poll_frequency_ms, int num_tries) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Check for the key however many times requested for (int i = 0; i < num_tries; i++) { if (key_exists(key)) @@ -871,6 +984,9 @@ bool Client::poll_model(const std::string& name, int poll_frequency_ms, int num_tries) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Check for the model/script however many times requested for (int i = 0; i < num_tries; i++) { if (model_exists(name)) @@ -887,6 +1003,9 @@ bool Client::poll_tensor(const std::string& name, int poll_frequency_ms, int num_tries) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Check for the tensor however many times requested for (int i = 0; i < num_tries; i++) { if (tensor_exists(name)) @@ -903,6 +1022,9 @@ bool Client::poll_dataset(const std::string& name, int poll_frequency_ms, int num_tries) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Check for the dataset however many times requested for (int i = 0; i < num_tries; i++) { if (dataset_exists(name)) @@ -917,6 +1039,9 @@ bool Client::poll_dataset(const std::string& name, // Establish a datasource void Client::set_data_source(std::string source_id) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Validate the source prefix bool valid_prefix = false; size_t num_prefix = _get_key_prefixes.size(); @@ -948,6 +1073,9 @@ void Client::set_data_source(std::string source_id) // prefix model and script keys. void Client::use_model_ensemble_prefix(bool use_prefix) { + // Track calls to this API function + LOG_API_FUNCTION(); + _use_model_prefix = use_prefix; } @@ -959,6 +1087,9 @@ void Client::use_model_ensemble_prefix(bool use_prefix) // aggregation list keys. void Client::use_list_ensemble_prefix(bool use_prefix) { + // Track calls to this API function + LOG_API_FUNCTION(); + _use_list_prefix = use_prefix; } @@ -972,12 +1103,18 @@ void Client::use_list_ensemble_prefix(bool use_prefix) // environment variables. void Client::use_tensor_ensemble_prefix(bool use_prefix) { + // Track calls to this API function + LOG_API_FUNCTION(); + _use_tensor_prefix = use_prefix; } // Returns information about the given database node parsed_reply_nested_map Client::get_db_node_info(std::string address) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Run an INFO EVERYTHING command to get node info DBInfoCommand cmd; SRAddress db_address(address); @@ -995,6 +1132,9 @@ parsed_reply_nested_map Client::get_db_node_info(std::string address) // Returns the CLUSTER INFO command reply addressed to a single cluster node. parsed_reply_map Client::get_db_cluster_info(std::string address) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (_redis_cluster == NULL) throw SRRuntimeException("Cannot run on non-cluster environment"); @@ -1017,6 +1157,9 @@ parsed_reply_map Client::get_ai_info(const std::string& address, const std::string& key, const bool reset_stat) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Run the command CommandReply reply = _redis_server->get_model_script_ai_info(address, key, reset_stat); @@ -1058,6 +1201,9 @@ parsed_reply_map Client::get_ai_info(const std::string& address, // Delete all the keys of the given database void Client::flush_db(std::string address) { + // Track calls to this API function + LOG_API_FUNCTION(); + AddressAtCommand cmd; SRAddress db_address(address); cmd.set_exec_address(db_address); @@ -1072,6 +1218,9 @@ void Client::flush_db(std::string address) std::unordered_map Client::config_get(std::string expression, std::string address) { + // Track calls to this API function + LOG_API_FUNCTION(); + AddressAtCommand cmd; SRAddress db_address(address); cmd.set_exec_address(db_address); @@ -1094,6 +1243,9 @@ Client::config_get(std::string expression, std::string address) // Reconfigure the server void Client::config_set(std::string config_param, std::string value, std::string address) { + // Track calls to this API function + LOG_API_FUNCTION(); + AddressAtCommand cmd; SRAddress db_address(address); cmd.set_exec_address(db_address); @@ -1106,6 +1258,9 @@ void Client::config_set(std::string config_param, std::string value, std::string void Client::save(std::string address) { + // Track calls to this API function + LOG_API_FUNCTION(); + AddressAtCommand cmd; SRAddress db_address(address); cmd.set_exec_address(db_address); @@ -1120,6 +1275,9 @@ void Client::save(std::string address) void Client::append_to_list(const std::string& list_name, const DataSet& dataset) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Build the list key std::string list_key = _build_list_key(list_name, false); @@ -1140,6 +1298,9 @@ void Client::append_to_list(const std::string& list_name, // Delete an aggregation list void Client::delete_list(const std::string& list_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Build the list key std::string list_key = _build_list_key(list_name, true); @@ -1157,6 +1318,9 @@ void Client::delete_list(const std::string& list_name) void Client::copy_list(const std::string& src_name, const std::string& dest_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Check for empty string inputs if (src_name.size() == 0) { throw SRParameterException("The src_name parameter cannot "\ @@ -1244,6 +1408,9 @@ void Client::copy_list(const std::string& src_name, void Client::rename_list(const std::string& src_name, const std::string& dest_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (src_name.size() == 0) { throw SRParameterException("The src_name parameter cannot "\ "be an empty string."); @@ -1265,6 +1432,9 @@ void Client::rename_list(const std::string& src_name, // Get the length of the list int Client::get_list_length(const std::string& list_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Build the list key std::string list_key = _build_list_key(list_name, false); @@ -1297,6 +1467,9 @@ int Client::get_list_length(const std::string& list_name) bool Client::poll_list_length(const std::string& name, int list_length, int poll_frequency_ms, int num_tries) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Enforce positive list length if (list_length < 0) { throw SRParameterException("A positive value for list_length "\ @@ -1311,6 +1484,9 @@ bool Client::poll_list_length(const std::string& name, int list_length, bool Client::poll_list_length_gte(const std::string& name, int list_length, int poll_frequency_ms, int num_tries) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Enforce positive list length if (list_length < 0) { throw SRParameterException("A positive value for list_length "\ @@ -1325,6 +1501,9 @@ bool Client::poll_list_length_gte(const std::string& name, int list_length, bool Client::poll_list_length_lte(const std::string& name, int list_length, int poll_frequency_ms, int num_tries) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Enforce positive list length if (list_length < 0) { throw SRParameterException("A positive value for list_length "\ @@ -1340,6 +1519,9 @@ bool Client::poll_list_length_lte(const std::string& name, int list_length, // Retrieve datasets in aggregation list std::vector Client::get_datasets_from_list(const std::string& list_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (list_name.size() == 0) { throw SRParameterException("The list name must have length "\ "greater than zero"); @@ -1353,6 +1535,9 @@ std::vector Client::get_dataset_list_range(const std::string& list_name const int start_index, const int end_index) { + // Track calls to this API function + LOG_API_FUNCTION(); + if (list_name.size() == 0) { throw SRParameterException("The list name must have length "\ "greater than zero"); @@ -1366,17 +1551,19 @@ std::vector Client::get_dataset_list_range(const std::string& list_name void Client::_set_prefixes_from_env() { // Establish set prefix - const char* keyout_p = std::getenv("SSKEYOUT"); - if (keyout_p != NULL) - _put_key_prefix = keyout_p; + std::string put_key_prefix; + get_config_string(put_key_prefix, "SSKEYOUT", ""); + if (put_key_prefix.length() > 0) + _put_key_prefix = put_key_prefix; else _put_key_prefix.clear(); // Establish get prefix(es) - char* keyin_p = std::getenv("SSKEYIN"); - if (keyin_p != NULL) { - char* a = keyin_p; - char* b = a; + std::string get_key_prefixes; + get_config_string(get_key_prefixes, "SSKEYIN", ""); + if (get_key_prefixes.length() > 0) { + const char* a = get_key_prefixes.c_str(); + const char* b = a; char parse_char = ','; while (*b != '\0') { if (*b == parse_char) { diff --git a/src/cpp/dataset.cpp b/src/cpp/dataset.cpp index b15d06413..270037010 100644 --- a/src/cpp/dataset.cpp +++ b/src/cpp/dataset.cpp @@ -29,14 +29,23 @@ #include #include "dataset.h" #include "srexception.h" +#include "logger.h" using namespace SmartRedis; // DataSet constructor DataSet::DataSet(const std::string& name) - : _dsname(name) + : SRObject(name), _dsname(name) { - // NOP + // Log that a new DataSet has been instantiated + log_data(LLDebug, "New DataSet created"); +} + +// DataSet Destructor +DataSet::~DataSet() +{ + // Log DataSet destruction + log_data(LLDebug, "DataSet destroyed"); } // Add a tensor to the DataSet. @@ -46,6 +55,9 @@ void DataSet::add_tensor(const std::string& name, const SRTensorType type, SRMemoryLayout mem_layout) { + // Track calls to this API function + LOG_API_FUNCTION(); + _add_to_tensorpack(name, data, dims, type, mem_layout); _metadata.add_string(".tensor_names", name); } @@ -57,6 +69,9 @@ void DataSet::add_meta_scalar(const std::string& name, const void* data, const SRMetaDataType type) { + // Track calls to this API function + LOG_API_FUNCTION(); + _metadata.add_scalar(name, data, type); } @@ -66,6 +81,9 @@ void DataSet::add_meta_scalar(const std::string& name, void DataSet::add_meta_string(const std::string& name, const std::string& data) { + // Track calls to this API function + LOG_API_FUNCTION(); + _metadata.add_string(name, data); } @@ -78,6 +96,9 @@ void DataSet::get_tensor(const std::string& name, SRTensorType& type, SRMemoryLayout mem_layout) { + // Track calls to this API function + LOG_API_FUNCTION(); + // Clone the tensor in the DataSet TensorBase* tensor = _get_tensorbase_obj(name); if (tensor == NULL) { @@ -99,6 +120,9 @@ void DataSet::get_tensor(const std::string& name, SRTensorType& type, SRMemoryLayout mem_layout) { + // Track calls to this API function + LOG_API_FUNCTION(); + std::vector dims_vec; get_tensor(name, data, dims_vec, type, mem_layout); @@ -124,6 +148,9 @@ void DataSet::unpack_tensor(const std::string& name, const SRTensorType type, SRMemoryLayout mem_layout) { + // Track calls to this API function + LOG_API_FUNCTION(); + _enforce_tensor_exists(name); _tensorpack.get_tensor(name)->fill_mem_space(data, dims, mem_layout); } @@ -138,6 +165,9 @@ void DataSet::get_meta_scalars(const std::string& name, size_t& length, SRMetaDataType& type) { + // Track calls to this API function + LOG_API_FUNCTION(); + _metadata.get_scalar_values(name, data, length, type); } @@ -151,24 +181,36 @@ void DataSet::get_meta_strings(const std::string& name, size_t& n_strings, size_t*& lengths) { + // Track calls to this API function + LOG_API_FUNCTION(); + _metadata.get_string_values(name, data, n_strings, lengths); } // Check if the DataSet has a field bool DataSet::has_field(const std::string& field_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + return _metadata.has_field(field_name); } // Clear all entries in a DataSet field. void DataSet::clear_field(const std::string& field_name) { + // Track calls to this API function + LOG_API_FUNCTION(); + _metadata.clear_field(field_name); } // Retrieve the names of the tensors in the DataSet std::vector DataSet::get_tensor_names() { + // Track calls to this API function + LOG_API_FUNCTION(); + if (_metadata.has_field(".tensor_names")) return _metadata.get_string_values(".tensor_names"); else @@ -181,12 +223,18 @@ std::vector DataSet::get_tensor_names() // std::vectorstd::string. std::vector DataSet::get_meta_strings(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + return _metadata.get_string_values(name); } // Get the Tensor type of the Tensor SRTensorType DataSet::get_tensor_type(const std::string& name) { + // Track calls to this API function + LOG_API_FUNCTION(); + return _tensorpack.get_tensor(name)->type(); } diff --git a/src/cpp/logger.cpp b/src/cpp/logger.cpp new file mode 100644 index 000000000..de6f7d9be --- /dev/null +++ b/src/cpp/logger.cpp @@ -0,0 +1,236 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utility.h" +#include "logger.h" +#include "srexception.h" +#include "srassert.h" +#include "srobject.h" + +using namespace SmartRedis; + +// Convert a std::string to lower case +void str_to_lower(std::string& str) +{ + std::transform( + str.begin(), str.end(), str.begin(), + [](unsigned char c) { return std::tolower(c); } + ); +} + +// Set up logging for the current client +void Logger::configure_logging() +{ + // Mark ourselves as initialized now + _initialized = true; + + // Get the logfile + get_config_string(_logfile, "SR_LOG_FILE", "", true); + std::string requestedLogfile(_logfile); + bool missingLogFile = _logfile.length() == 0; + + // Make sure it can be written to + bool badLogFile = false; + if (_logfile.length() > 0) { + std::ofstream logstream; + logstream.open(_logfile, std::ios_base::app); + badLogFile = !logstream.good(); + + // Switch to STDOUT if we can't write to the specified file + if (badLogFile) + _logfile = ""; + } + + // Get the logging level + std::string level; + get_config_string(level, "SR_LOG_LEVEL", "", true); + bool missingLogLevel = level.length() == 0; + bool badLogLevel = false; + if (level.length() > 0) { + str_to_lower(level); + if (level.compare("quiet") == 0) + _log_level = LLQuiet; + else if (level.compare("info") == 0) + _log_level = LLInfo; + else if (level.compare("debug") == 0) + _log_level = LLDebug; + else if (level.compare("developer") == 0) + _log_level = LLDeveloper; + else { + // Don't recognize the requested level; use default + badLogLevel = true; + _log_level = LLInfo; + } + } + else { + _log_level = LLInfo; + } + + // Now that everything is configured, issue warning and + // error messages. By deferring them, we may be able to + // issue them to the customer-requested logfile instead + // of to console, depending on what went wrong + if (missingLogFile) { + log_warning( + "SmartRedis Library", + LLInfo, + "Environment variable SR_LOG_FILE is not set. " + "Defaulting to stdout" + ); + } + if (missingLogLevel) { + log_warning( + "SmartRedis Library", + LLInfo, + "Environment variable SR_LOG_LEVEL is not set. " + "Defaulting to INFO" + ); + } + if (badLogFile) { + throw SRRuntimeException( + "Cannot write to file: " + requestedLogfile); + } + if (badLogLevel) { + throw SRRuntimeException( + "Unrecognized logging level: " + level); + } +} + +// Conditionally log data if the logging level is high enough +void Logger::log_data( + const std::string& context, + SRLoggingLevel level, + const std::string& data) +{ + // If we're not initialized, configure logging as a default + // client. This can happen if a caller invokes a Dataset API point + // without initializing a Client object + if (!_initialized) + configure_logging(); + + // Silently ignore logging more verbose than requested + if (level > _log_level) + return; + + // Get the current timestamp + auto t = std::time(NULL); + auto tm = *std::localtime(&t); + auto timestamp = std::put_time(&tm, "%H-%M-%S"); + + // write the log data + bool writingFile = (_logfile.length() > 0); + std::ofstream logstream; + std::ostream& log_target(writingFile ? logstream : std::cout); + if (_logfile.length() > 0) { + logstream.open(_logfile, std::ios_base::app); + if (!logstream.good()) { + // The logfile is no longer writable! + // Switch to console and emit an error + _logfile = ""; + log_error( + "SmartRedis Library", + LLInfo, + "Logfile no longer writeable. Switching to console logging"); + // Re-log this message since the current attempt has failed + log_data(context, level, data); + // Bail -- we're done here + return; + } + } + log_target << context << "@" << timestamp << ":" << data + << std::endl; +} + +// Conditionally log data if the logging level is high enough +// (exception-free variant) +extern "C" void log_data_noexcept( + const void* context, + SRLoggingLevel level, + const char* data, + size_t data_len) +{ + try { + SR_CHECK_PARAMS(context != NULL); + const SRObject* ctxt = reinterpret_cast(context); + std::string strData(data, data_len); + ctxt->log_data(level, strData); + } + catch (Exception& e) { + std::cout << "Logging failure: " << e.where() + << ": " << e.what() << std::endl; + } +} + +// Conditionally log a warning if the logging level is high enough +// (exception-free variant) +extern "C" void log_warning_noexcept( + const void* context, + SRLoggingLevel level, + const char* data, + size_t data_len) +{ + try { + SR_CHECK_PARAMS(context != NULL); + const SRObject* ctxt = reinterpret_cast(context); + std::string strData(data, data_len); + ctxt->log_warning(level, strData); + } + catch (Exception& e) { + std::cout << "Logging failure: " << e.where() + << ": " << e.what() << std::endl; + } +} + +// Conditionally log an error if the logging level is high enough +// (exception-free variant) +extern "C" void log_error_noexcept( + const void* context, + SRLoggingLevel level, + const char* data, + size_t data_len) +{ + try { + SR_CHECK_PARAMS(context != NULL); + const SRObject* ctxt = reinterpret_cast(context); + std::string strData(data, data_len); + ctxt->log_error(level, strData); + } + catch (Exception& e) { + std::cout << "Logging failure: " << e.where() + << ": " << e.what() << std::endl; + } +} diff --git a/src/cpp/redis.cpp b/src/cpp/redis.cpp index 5f6221754..922abda58 100644 --- a/src/cpp/redis.cpp +++ b/src/cpp/redis.cpp @@ -28,11 +28,14 @@ #include "redis.h" #include "srexception.h" +#include "utility.h" +#include "client.h" using namespace SmartRedis; // Redis constructor. -Redis::Redis() : RedisServer() +Redis::Redis(const Client* client) + : RedisServer(client) { SRAddress db_address(_get_ssdb()); // Remember whether it's a unix domain socket for later @@ -42,7 +45,8 @@ Redis::Redis() : RedisServer() } // Redis constructor. Uses address provided to constructor instead of environment variables -Redis::Redis(std::string addr_spec) : RedisServer() +Redis::Redis(const Client* client, std::string addr_spec) + : RedisServer(client) { SRAddress db_address(addr_spec); _add_to_address_map(db_address); @@ -387,8 +391,8 @@ CommandReply Redis::run_model(const std::string& key, { // Check for a non-default timeout setting int run_timeout; - _init_integer_from_env(run_timeout, _MODEL_TIMEOUT_ENV_VAR, - _DEFAULT_MODEL_TIMEOUT); + get_config_integer(run_timeout, _MODEL_TIMEOUT_ENV_VAR, + _DEFAULT_MODEL_TIMEOUT); // Build the command CompoundCommand cmd; diff --git a/src/cpp/rediscluster.cpp b/src/cpp/rediscluster.cpp index c9d1c72f4..c66232106 100644 --- a/src/cpp/rediscluster.cpp +++ b/src/cpp/rediscluster.cpp @@ -30,11 +30,14 @@ #include "nonkeyedcommand.h" #include "keyedcommand.h" #include "srexception.h" +#include "utility.h" +#include "client.h" using namespace SmartRedis; // RedisCluster constructor -RedisCluster::RedisCluster() : RedisServer() +RedisCluster::RedisCluster(const Client* client) + : RedisServer(client) { SRAddress db_address(_get_ssdb()); if (!db_address._is_tcp) { @@ -52,7 +55,8 @@ RedisCluster::RedisCluster() : RedisServer() // RedisCluster constructor. Uses address provided to constructor instead of // environment variables -RedisCluster::RedisCluster(std::string address_spec) : RedisServer() +RedisCluster::RedisCluster(const Client* client, std::string address_spec) + : RedisServer(client) { SRAddress db_address(address_spec); _connect(db_address); @@ -615,8 +619,8 @@ CommandReply RedisCluster::run_model(const std::string& model_name, { // Check for a non-default timeout setting int run_timeout; - _init_integer_from_env(run_timeout, _MODEL_TIMEOUT_ENV_VAR, - _DEFAULT_MODEL_TIMEOUT); + get_config_integer(run_timeout, _MODEL_TIMEOUT_ENV_VAR, + _DEFAULT_MODEL_TIMEOUT); /* For this version of run model, we have to copy all input and output tensors, so we will randomly select diff --git a/src/cpp/redisserver.cpp b/src/cpp/redisserver.cpp index 72430498f..b0f61ae5f 100644 --- a/src/cpp/redisserver.cpp +++ b/src/cpp/redisserver.cpp @@ -29,23 +29,25 @@ #include #include "redisserver.h" #include "srexception.h" +#include "utility.h" +#include "client.h" using namespace SmartRedis; // RedisServer constructor -RedisServer::RedisServer() - : _gen(_rd()) +RedisServer::RedisServer(const Client* client) + : _client(client), _gen(_rd()) { - _init_integer_from_env(_connection_timeout, _CONN_TIMEOUT_ENV_VAR, - _DEFAULT_CONN_TIMEOUT); - _init_integer_from_env(_connection_interval, _CONN_INTERVAL_ENV_VAR, - _DEFAULT_CONN_INTERVAL); - _init_integer_from_env(_command_timeout, _CMD_TIMEOUT_ENV_VAR, - _DEFAULT_CMD_TIMEOUT); - _init_integer_from_env(_command_interval, _CMD_INTERVAL_ENV_VAR, - _DEFAULT_CMD_INTERVAL); - _init_integer_from_env(_thread_count, _TP_THREAD_COUNT, - _DEFAULT_THREAD_COUNT); + get_config_integer(_connection_timeout, _CONN_TIMEOUT_ENV_VAR, + _DEFAULT_CONN_TIMEOUT); + get_config_integer(_connection_interval, _CONN_INTERVAL_ENV_VAR, + _DEFAULT_CONN_INTERVAL); + get_config_integer(_command_timeout, _CMD_TIMEOUT_ENV_VAR, + _DEFAULT_CMD_TIMEOUT); + get_config_integer(_command_interval, _CMD_INTERVAL_ENV_VAR, + _DEFAULT_CMD_INTERVAL); + get_config_integer(_thread_count, _TP_THREAD_COUNT, + _DEFAULT_THREAD_COUNT); _check_runtime_variables(); @@ -55,7 +57,7 @@ RedisServer::RedisServer() _command_attempts = (_command_timeout * 1000) / _command_interval + 1; - _tp = new ThreadPool(_thread_count); + _tp = new ThreadPool(_client, _thread_count); } // RedisServer destructor @@ -72,29 +74,29 @@ RedisServer::~RedisServer() SRAddress RedisServer::_get_ssdb() { // Retrieve the environment variable - char* env_char = getenv("SSDB"); - if (env_char == NULL) + std::string db_spec; + get_config_string(db_spec, "SSDB", ""); + if (db_spec.length() == 0) throw SRRuntimeException("The environment variable SSDB "\ "must be set to use the client."); - std::string env_str = std::string(env_char); - _check_ssdb_string(env_str); + _check_ssdb_string(db_spec); // Parse the data in it std::vector address_choices; const char delim = ','; size_t i_pos = 0; - size_t j_pos = env_str.find(delim); + size_t j_pos = db_spec.find(delim); while (j_pos != std::string::npos) { - std::string substr = env_str.substr(i_pos, j_pos - i_pos); + std::string substr = db_spec.substr(i_pos, j_pos - i_pos); SRAddress addr_spec(substr); address_choices.push_back(addr_spec); i_pos = j_pos + 1; - j_pos = env_str.find(delim, i_pos); + j_pos = db_spec.find(delim, i_pos); } // Catch the last value that does not have a trailing ',' - if (i_pos < env_str.size()) { - std::string substr = env_str.substr(i_pos, j_pos - i_pos); + if (i_pos < db_spec.size()) { + std::string substr = db_spec.substr(i_pos, j_pos - i_pos); SRAddress addr_spec(substr); address_choices.push_back(addr_spec); } @@ -115,49 +117,6 @@ void RedisServer::_check_ssdb_string(const std::string& env_str) { } } -//Initialize variable of type integer from environment variable -void RedisServer::_init_integer_from_env(int& value, - const std::string& env_var, - const int& default_value) -{ - value = default_value; - - char* env_char = getenv(env_var.c_str()); - - if (env_char != NULL && strlen(env_char) > 0) { - // Enforce that all characters are digits because std::stoi - // will truncate a string like "10xy" to 10. - // We want to guard users from input errors they might have. - char* c = env_char; - while (*c != '\0') { - if (!isdigit(*c) && !(*c == '-' && c == env_char)) { - throw SRParameterException("The value of " + env_var + - " must be a valid number."); - } - c++; - } - - try { - value = std::stoi(env_char); - } - catch (std::invalid_argument& e) { - throw SRParameterException("The value of " + env_var + " could "\ - "not be converted to type integer."); - } - catch (std::out_of_range& e) { - throw SRParameterException("The value of " + env_var + " is too "\ - "large to be stored as an integer "\ - "value."); - } - catch (std::exception& e) { - throw SRInternalException("An unexpected error occurred "\ - "while attempting to convert the "\ - "environment variable " + env_var + - " to an integer."); - } - } -} - // Check that runtime variables are within valid ranges inline void RedisServer::_check_runtime_variables() { @@ -172,7 +131,8 @@ inline void RedisServer::_check_runtime_variables() } if (_command_timeout <= 0) { - throw SRParameterException(_CMD_TIMEOUT_ENV_VAR + + throw SRParameterException(_CMD_TIMEOUT_ENV_VAR + " " + + std::to_string(_command_timeout) + " must be greater than 0."); } @@ -188,7 +148,8 @@ inline void RedisServer::_check_runtime_variables() } if (_command_timeout > (INT_MAX / 1000)) { - throw SRParameterException(_CMD_TIMEOUT_ENV_VAR + + throw SRParameterException(_CMD_TIMEOUT_ENV_VAR + " " + + std::to_string(_command_timeout) + " must be less than " + std::to_string(INT_MAX / 1000)); } diff --git a/src/cpp/srobject.cpp b/src/cpp/srobject.cpp new file mode 100644 index 000000000..b5ae9c49d --- /dev/null +++ b/src/cpp/srobject.cpp @@ -0,0 +1,55 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srobject.h" +#include "logger.h" + +using namespace SmartRedis; + +// SRObject constructor +SRObject::SRObject(const std::string& logging_name) + : _lname(logging_name) +{ + // NOP +} + + +// Conditionally log data if the logging level is high enough +void SRObject::log_data(SRLoggingLevel level, const std::string& data) const +{ + Logger::get_instance().log_data(_lname, level, data); +} diff --git a/src/cpp/threadpool.cpp b/src/cpp/threadpool.cpp index 3486fefa2..e5d7c48b6 100644 --- a/src/cpp/threadpool.cpp +++ b/src/cpp/threadpool.cpp @@ -7,18 +7,28 @@ #include "threadpool.h" #include "srexception.h" +#include "logger.h" +#include "client.h" using namespace SmartRedis; using namespace std::chrono_literals; -// TIME_THREADPOOL -// Enable this flag to display timings for the threadpool activity. -// Currently, this will be to screen, but eventually it will go to -// the SmartRedis log -#undef TIME_THREADPOOL +// Log safely, even if our _client pointer is NULL +// This is a workaround for the direct instantiation of Redis and RedisCluster +// objects in our unit tests that do not currently include a Client*. Once those +// usages are updated, this function can go away and all calls replaced with +// direct invocations of _client->log_data() +void safelog(const Client* client, SRLoggingLevel level, const std::string& msg) +{ + if (client != NULL) + client->log_data(level, msg); + else + log_data("Threadpool", level, msg); +} // Constructor -ThreadPool::ThreadPool(unsigned int num_threads) +ThreadPool::ThreadPool(const Client* client, unsigned int num_threads) + : _client(client) { // Flags that we're initializing and not shutting down initialization_complete = false; @@ -31,9 +41,8 @@ ThreadPool::ThreadPool(unsigned int num_threads) // Create worker threads if (num_threads < 1) num_threads = 1; // Force a minimum of 1 thread for (unsigned int i = 0; i < num_threads; i++) { - #ifdef TIME_THREADPOOL - std::cout << "Kicking off thread " + std::to_string(i) << std::endl; - #endif + safelog(_client, + LLDeveloper, "Kicking off thread " + std::to_string(i)); threads.push_back(std::thread(&ThreadPool::perform_jobs, this, i)); } @@ -58,49 +67,41 @@ void ThreadPool::shutdown() while (!initialization_complete) ; // Spin - #ifdef TIME_THREADPOOL - std::cout << "Shutting down thread pool" << std::endl; - #endif + safelog(_client, LLDeveloper, "Shutting down thread pool"); // We're closed for business shutting_down = true; // Wait for worker threads to finish up - #ifdef TIME_THREADPOOL int i = 0; size_t num_threads = threads.size(); - #endif for (std::thread& thr : threads) { cv.notify_all(); // Wake up all the threads - #ifdef TIME_THREADPOOL - std::cout << "Waiting for a thread to terminate (" << std::to_string(i++) << " of " - << std::to_string(num_threads) << ")" << std::endl; - #endif + std::string message = + "Waiting for thread to terminate (" + + std::to_string(i++) + " of " + + std::to_string(num_threads) + ")"; + safelog(_client, LLDeveloper, message); thr.join(); // Blocks until the thread finishes execution } // Done - #ifdef TIME_THREADPOOL - std::cout << "Shutdown complete" << std::endl; - #endif + safelog(_client, LLDeveloper, "Shutdown complete"); shutdown_complete = true; } // Worker thread main loop to acquire and perform jobs void ThreadPool::perform_jobs(unsigned int tid) { - #ifdef TIME_THREADPOOL int jobid = 0; - std::cout << "Thread " << std::to_string(tid) << " reporting for duty" << std::endl; - #endif + safelog(_client, + LLDebug, "Thread " + std::to_string(tid) + " reporting for duty"); // Loop forever processing jobs until we get killed std::function job; while (!shutting_down) { - #ifdef TIME_THREADPOOL auto start = std::chrono::steady_clock::now(); - #endif // Get a job, blocking until one appears if none immediately available do { @@ -122,25 +123,26 @@ void ThreadPool::perform_jobs(unsigned int tid) } // End scope and release lock while (!shutting_down); - #ifdef TIME_THREADPOOL auto have_job = std::chrono::steady_clock::now(); - #endif + // Perform the job if (!shutting_down) { job(); - #ifdef TIME_THREADPOOL auto job_done = std::chrono::steady_clock::now(); std::chrono::duration get_job = have_job - start; std::chrono::duration execute_job = job_done - have_job; - std::cout << "Thread " << std::to_string(tid) << " " - << "time to get job " << std::to_string(jobid++) << ": " << get_job.count() << " s; " - << "time to execute job: " << execute_job.count() << " s" << std::endl; - #endif + std::string message = + "Thread " + std::to_string(tid) + + " time to get job " + std::to_string(jobid++) + + ": " + std::to_string(get_job.count()) + " s; " + + "time to execute job: " + + std::to_string(execute_job.count()) + " s"; + safelog(_client, LLDeveloper, message); } } - #ifdef TIME_THREADPOOL - std::cout << "Thread " << std::to_string(tid) << " shutting down" << std::endl; - #endif + + safelog(_client, + LLDeveloper, "Thread " + std::to_string(tid) + " shutting down"); } // Submit a job to threadpool for execution diff --git a/src/cpp/utility.cpp b/src/cpp/utility.cpp new file mode 100644 index 000000000..359deca48 --- /dev/null +++ b/src/cpp/utility.cpp @@ -0,0 +1,148 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include "srexception.h" +#include "utility.h" +#include "logger.h" + +namespace SmartRedis { + +/*! +* \brief Initialize an integer from configuration, such as an +* environment variable +* \param value Receives the configuration value +* \param cfg_key The key to query for the configuration variable +* \param default_value Default if configuration key is not set +* \param suppress_warning Do not issue a warning if the variable +* is not set +*/ +void get_config_integer(int& value, + const std::string& cfg_key, + const int default_value, + bool suppress_warning /*= false*/) +{ + value = default_value; + std::string message = "Getting value for " + cfg_key; + log_data("SmartRedis Library", LLDebug, message); + + char* cfg_val = std::getenv(cfg_key.c_str()); + message = "Retrieved value \""; + message += cfg_val == NULL ? "" : cfg_val; + message += "\""; + if (NULL == cfg_val) + message += ". Using default value of " + std::to_string(default_value); + log_data("SmartRedis Library", LLDebug, message); + + if (cfg_val != NULL && std::strlen(cfg_val) > 0) { + // Enforce that all characters are digits because std::stoi + // will truncate a string like "10xy" to 10. + // We want to guard users from input errors they might have. + for (char* c = cfg_val; *c != '\0'; c++) { + if (!isdigit(*c) && !(*c == '-' && c == cfg_val)) { + throw SRParameterException("The value of " + cfg_key + + " must be a valid number."); + } + } + + try { + value = std::stoi(cfg_val); + } + catch (std::invalid_argument& e) { + throw SRParameterException("The value of " + cfg_key + " could "\ + "not be converted to type integer."); + } + catch (std::out_of_range& e) { + throw SRParameterException("The value of " + cfg_key + " is too "\ + "large to be stored as an integer "\ + "value."); + } + catch (std::exception& e) { + throw SRInternalException("An unexpected error occurred "\ + "while attempting to convert the "\ + "environment variable " + cfg_key + + " to an integer."); + } + } + else if (!suppress_warning) { + log_warning( + "SmartRedis Library", + LLDebug, + "Configuration variable " + cfg_key + " not set" + ); + } + + message = "Exiting with value \"" + std::to_string(value) + "\""; + log_data("SmartRedis Library", LLDebug, message); +} + +/*! +* \brief Initialize an string from configuration, such as an +* environment variable +* \param value Receives the configuration value +* \param cfg_key The key to query for the configuration variable +* \param default_value Default if configuration key is not set +* \param suppress_warning Do not issue a warning if the variable +* is not set +*/ +void get_config_string(std::string& value, + const std::string& cfg_key, + const std::string& default_value, + bool suppress_warning /*= false*/) +{ + value = default_value; + std::string message = "Getting value for " + cfg_key; + log_data("SmartRedis Library", LLDebug, message); + + char* cfg_val = std::getenv(cfg_key.c_str()); + message = "Retrieved value \""; + message += cfg_val == NULL ? "" : cfg_val; + message += "\""; + if (NULL == cfg_val) + message += ". Using default value of \"" + default_value + "\""; + log_data("SmartRedis Library", LLDebug, message); + + if (cfg_val != NULL && std::strlen(cfg_val) > 0) + value = cfg_val; + else if (!suppress_warning) { + log_warning( + "SmartRedis Library", + LLDebug, + "Configuration variable " + cfg_key + " not set" + ); + } + + message = "Exiting with value \"" + value + "\""; + log_data("SmartRedis Library", LLDebug, message); +} + +} // namespace SmartRedis { diff --git a/src/fortran/client.F90 b/src/fortran/client.F90 index 69d62f283..e56715c75 100644 --- a/src/fortran/client.F90 +++ b/src/fortran/client.F90 @@ -79,6 +79,8 @@ module smartredis_client procedure :: isinitialized !> Destructs a new instance of the SmartRedis client procedure :: destructor + !> Access the raw C pointer for the client + procedure :: get_c_pointer !> Check the database for the existence of a specific model procedure :: model_exists !> Check the database for the existence of a specific tensor @@ -232,14 +234,27 @@ function SR_error_parser(self, response_code) result(is_error) end function SR_error_parser !> Initializes a new instance of a SmartRedis client -function initialize_client(self, cluster) - integer(kind=enum_kind) :: initialize_client - class(client_type), intent(inout) :: self !< Receives the initialized client - logical, optional, intent(in ) :: cluster !< If true, client uses a database cluster (Default: .false.) +function initialize_client(self, cluster, logger_name) + integer(kind=enum_kind) :: initialize_client + class(client_type), intent(inout) :: self !< Receives the initialized client + logical, optional, intent(in ) :: cluster !< If true, client uses a database cluster (Default: .false.) + character(len=*), optional, intent(in ) :: logger_name !< Identifier for the current client + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_logger_name + integer(kind=c_size_t) :: logger_name_length + + if (present(logger_name)) then + c_logger_name = logger_name + else + c_logger_name = 'default' + endif + logger_name_length = len_trim(c_logger_name) if (present(cluster)) self%cluster = cluster - initialize_client = c_constructor(self%cluster, self%client_ptr) + initialize_client = c_constructor(self%cluster, c_logger_name, logger_name_length, self%client_ptr) self%is_initialized = initialize_client .eq. SRNoError + if (allocated(c_logger_name)) deallocate(c_logger_name) end function initialize_client !> Check whether the client has been initialized @@ -257,6 +272,13 @@ function destructor(self) self%client_ptr = C_NULL_PTR end function destructor +!> Access the raw C pointer for the client +function get_c_pointer(self) + type(c_ptr) :: get_c_pointer + class(client_type), intent(in) :: self + get_c_pointer = self%client_ptr +end function get_c_pointer + !> Check if the specified key exists in the database function key_exists(self, key, exists) class(client_type), intent(in) :: self !< The client diff --git a/src/fortran/client/client_interfaces.inc b/src/fortran/client/client_interfaces.inc index 9e7697892..35318ce37 100644 --- a/src/fortran/client/client_interfaces.inc +++ b/src/fortran/client/client_interfaces.inc @@ -25,12 +25,15 @@ ! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. interface - function c_constructor(cluster, new_client) bind(c, name="SmartRedisCClient") - use iso_c_binding, only : c_ptr, c_bool + function c_constructor(cluster, logger_name, logger_name_length, new_client) bind(c, name="SmartRedisCClient") + use iso_c_binding, only : c_ptr, c_bool, c_char, c_size_t import :: enum_kind - integer(kind=enum_kind) :: c_constructor - logical(kind=c_bool), value :: cluster !< True if a database cluster is being used - type(c_ptr) :: new_client !< Receives the newly constructed client + integer(kind=enum_kind) :: c_constructor + logical(kind=c_bool), value :: cluster !< True if a database cluster is being used + character(kind=c_char) :: logger_name(*) + integer(kind=c_size_t), value :: logger_name_length + + type(c_ptr) :: new_client !< Receives the newly constructed client end function c_constructor end interface diff --git a/src/fortran/dataset.F90 b/src/fortran/dataset.F90 index aa19bd5d9..be489279b 100644 --- a/src/fortran/dataset.F90 +++ b/src/fortran/dataset.F90 @@ -52,6 +52,8 @@ module smartredis_dataset !> Initialize a new dataset with a given name procedure :: initialize => initialize_dataset + !> Access the raw C pointer for the client + procedure :: get_c_pointer !> Add metadata to the dataset with a given field and string procedure :: add_meta_string ! procedure :: get_meta_strings ! Not supported currently @@ -110,6 +112,13 @@ function initialize_dataset(self, name) result(code) code = dataset_constructor(c_name, name_length, self%dataset_ptr) end function initialize_dataset +!> Access the raw C pointer for the dataset +function get_c_pointer(self) + type(c_ptr) :: get_c_pointer + class(dataset_type), intent(in) :: self + get_c_pointer = self%dataset_ptr +end function get_c_pointer + !> Add a tensor to a dataset whose Fortran type is the equivalent 'int8' C-type function add_tensor_i8(self, name, data, dims) result(code) integer(kind=c_int8_t), dimension(..), target, intent(in) :: data !< Data to be sent diff --git a/src/fortran/logcontext.F90 b/src/fortran/logcontext.F90 new file mode 100644 index 000000000..a9488457f --- /dev/null +++ b/src/fortran/logcontext.F90 @@ -0,0 +1,81 @@ +! BSD 2-Clause License +! +! Copyright (c) 2021-2022, Hewlett Packard Enterprise +! All rights reserved. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, this +! list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +module smartredis_logcontext + +use iso_c_binding, only : c_ptr, c_null_ptr, c_char, c_int, c_size_t +use iso_c_binding, only : c_loc, c_f_pointer +use fortran_c_interop, only : enum_kind + +implicit none; private + +include 'enum_fortran.inc' +include 'logcontext/logcontext_interfaces.inc' + +public :: enum_kind !< The kind of integer equivalent to a C enum. According to C an Fortran + !! standards this should be c_int, but is renamed here to ensure that + !! users do not have to import the iso_c_binding module into their + !! programs + +!> Contains a context against to emit log messages +type, public :: logcontext_type + type(c_ptr) :: logcontext_ptr !< A pointer to the initialized logcontext object + + contains + + !> Initialize a new dataset with a given name + procedure :: initialize => initialize_logcontext + !> Access the raw C pointer for the client + procedure :: get_c_pointer + +end type logcontext_type + +contains + +!> Initialize the logcontext +function initialize_logcontext(self, context) result(code) + class(logcontext_type), intent(inout) :: self !< Receives the logcontext + character(len=*), intent(in) :: context !< Context for the logcontext + integer(kind=enum_kind) :: code !< Result of the operation + + ! Local variables + integer(kind=c_size_t) :: context_length + character(kind=c_char, len=len_trim(context)) :: c_context + + context_length = len_trim(context) + c_context = trim(context) + + code = logcontext_constructor(c_context, context_length, self%logcontext_ptr) +end function initialize_logcontext + +!> Access the raw C pointer for the logcontext +function get_c_pointer(self) + type(c_ptr) :: get_c_pointer + class(logcontext_type), intent(in) :: self + get_c_pointer = self%logcontext_ptr +end function get_c_pointer + +end module smartredis_logcontext diff --git a/src/fortran/logcontext/logcontext_interfaces.inc b/src/fortran/logcontext/logcontext_interfaces.inc new file mode 100644 index 000000000..463c71ccc --- /dev/null +++ b/src/fortran/logcontext/logcontext_interfaces.inc @@ -0,0 +1,37 @@ +! BSD 2-Clause License +! +! Copyright (c) 2021-2022, Hewlett Packard Enterprise +! All rights reserved. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, this +! list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +interface + function logcontext_constructor( context, context_length, logcontext ) & + bind(c, name="SmartRedisCLogContext") + use iso_c_binding, only : c_ptr, c_char, c_size_t + import :: enum_kind + integer(kind=enum_kind) :: logcontext_constructor + character(kind=c_char) :: context !< Context for logging + integer(kind=c_size_t), value :: context_length !< How many characters in context + type(c_ptr) :: logcontext !< Receives the constructed logcontext + end function logcontext_constructor +end interface diff --git a/src/fortran/logger.F90 b/src/fortran/logger.F90 new file mode 100644 index 000000000..27d5752a0 --- /dev/null +++ b/src/fortran/logger.F90 @@ -0,0 +1,237 @@ +! BSD 2-Clause License +! +! Copyright (c) 2021-2022, Hewlett Packard Enterprise +! All rights reserved. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, this +! list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +module smartredis_logger + +use iso_c_binding, only : c_ptr, c_char, c_size_t +use iso_c_binding, only : c_int8_t, c_int16_t, c_int32_t, c_int64_t, c_float, c_double +use iso_c_binding, only : c_loc, c_f_pointer +use fortran_c_interop, only : convert_char_array_to_c, enum_kind, C_MAX_STRING + +use, intrinsic :: iso_fortran_env, only: stderr => error_unit +use smartredis_client, only : client_type +use smartredis_dataset, only : dataset_type +use smartredis_logcontext, only : logcontext_type + +implicit none; private + +#include "enum_fortran.inc" +#include "logger/logger_interfaces.inc" + +interface log_data + module procedure log_data_client, log_data_dataset, log_data_logcontext +end interface log_data + +interface log_warning + module procedure log_warning_client, log_warning_dataset, log_warning_logcontext +end interface log_warning + +interface log_error + module procedure log_error_client, log_error_dataset, log_error_logcontext +end interface log_error + +public :: enum_kind !< The kind of integer equivalent to a C enum. According to C an Fortran + !! standards this should be c_int, but is renamed here to ensure that + !! users do not have to import the iso_c_binding module into their + !! programs + +public :: log_data, log_warning, log_error + +contains + +! log_data overloads +! ================== + +!> Log data to the SmartRedis log against a Client object +subroutine log_data_client(context, level, data) + type(client_type), intent(in) :: context + integer (kind=enum_kind), intent(in) :: level !< Minimum logging level to log the data at + character(len=*), intent(in) :: data !< Data to be logged + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_data + integer(kind=c_size_t) :: c_data_length + + allocate(character(kind=c_char, len=len_trim(data)) :: c_data) + c_data = data + c_data_length = len_trim(data) + + call c_log_data(context%get_c_pointer(), level, c_data, c_data_length) + deallocate(c_data) +end subroutine log_data_client + +!> Log data to the SmartRedis log against a Dataset object +subroutine log_data_dataset(context, level, data) + type(dataset_type), intent(in) :: context + integer (kind=enum_kind), intent(in) :: level !< Minimum logging level to log the data at + character(len=*), intent(in) :: data !< Data to be logged + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_data + integer(kind=c_size_t) :: c_data_length + + allocate(character(kind=c_char, len=len_trim(data)) :: c_data) + c_data = data + c_data_length = len_trim(data) + + call c_log_data(context%get_c_pointer(), level, c_data, c_data_length) + deallocate(c_data) +end subroutine log_data_dataset + +!> Log data to the SmartRedis log against a Logcontext object +subroutine log_data_logcontext(context, level, data) + type(logcontext_type), intent(in) :: context + integer (kind=enum_kind), intent(in) :: level !< Minimum logging level to log the data at + character(len=*), intent(in) :: data !< Data to be logged + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_data + integer(kind=c_size_t) :: c_data_length + + allocate(character(kind=c_char, len=len_trim(data)) :: c_data) + c_data = data + c_data_length = len_trim(data) + + call c_log_data(context%get_c_pointer(), level, c_data, c_data_length) + deallocate(c_data) +end subroutine log_data_logcontext + +! log_warning overloads +! ===================== + +!> Log a warning to the SmartRedis log against a Client object +subroutine log_warning_client(context, level, data) + type(client_type), intent(in) :: context + integer (kind=enum_kind), intent(in) :: level !< Minimum logging level to log the data at + character(len=*), intent(in) :: data !< Data to be logged + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_data + integer(kind=c_size_t) :: c_data_length + + allocate(character(kind=c_char, len=len_trim(data)) :: c_data) + c_data = data + c_data_length = len_trim(data) + + call c_log_warning(context%get_c_pointer(), level, c_data, c_data_length) + deallocate(c_data) +end subroutine log_warning_client + +!> Log a warning to the SmartRedis log against a Dataset object +subroutine log_warning_dataset(context, level, data) + type(dataset_type), intent(in) :: context + integer (kind=enum_kind), intent(in) :: level !< Minimum logging level to log the data at + character(len=*), intent(in) :: data !< Data to be logged + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_data + integer(kind=c_size_t) :: c_data_length + + allocate(character(kind=c_char, len=len_trim(data)) :: c_data) + c_data = data + c_data_length = len_trim(data) + + call c_log_warning(context%get_c_pointer(), level, c_data, c_data_length) + deallocate(c_data) +end subroutine log_warning_dataset + +!> Log a warning to the SmartRedis log against a LogContext object +subroutine log_warning_logcontext(context, level, data) + type(logcontext_type), intent(in) :: context + integer (kind=enum_kind), intent(in) :: level !< Minimum logging level to log the data at + character(len=*), intent(in) :: data !< Data to be logged + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_data + integer(kind=c_size_t) :: c_data_length + + allocate(character(kind=c_char, len=len_trim(data)) :: c_data) + c_data = data + c_data_length = len_trim(data) + + call c_log_warning(context%get_c_pointer(), level, c_data, c_data_length) + deallocate(c_data) +end subroutine log_warning_logcontext + +! log_error overloads +! =================== + +!> Log an error to the SmartRedis log against a Client object +subroutine log_error_client(context, level, data) + type(client_type), intent(in) :: context + integer (kind=enum_kind), intent(in) :: level !< Minimum logging level to log the data at + character(len=*), intent(in) :: data !< Data to be logged + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_data + integer(kind=c_size_t) :: c_data_length + + allocate(character(kind=c_char, len=len_trim(data)) :: c_data) + c_data = data + c_data_length = len_trim(data) + + call c_log_error(context%get_c_pointer(), level, c_data, c_data_length) + deallocate(c_data) +end subroutine log_error_client + +!> Log an error to the SmartRedis log against a Dataset object +subroutine log_error_dataset(context, level, data) + type(dataset_type), intent(in) :: context + integer (kind=enum_kind), intent(in) :: level !< Minimum logging level to log the data at + character(len=*), intent(in) :: data !< Data to be logged + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_data + integer(kind=c_size_t) :: c_data_length + + allocate(character(kind=c_char, len=len_trim(data)) :: c_data) + c_data = data + c_data_length = len_trim(data) + + call c_log_error(context%get_c_pointer(), level, c_data, c_data_length) + deallocate(c_data) +end subroutine log_error_dataset + +!> Log an error to the SmartRedis log against a LogContext object +subroutine log_error_logcontext(context, level, data) + type(logcontext_type), intent(in) :: context + integer (kind=enum_kind), intent(in) :: level !< Minimum logging level to log the data at + character(len=*), intent(in) :: data !< Data to be logged + + ! Local variables + character(kind=c_char, len=:), allocatable :: c_data + integer(kind=c_size_t) :: c_data_length + + allocate(character(kind=c_char, len=len_trim(data)) :: c_data) + c_data = data + c_data_length = len_trim(data) + + call c_log_error(context%get_c_pointer(), level, c_data, c_data_length) + deallocate(c_data) +end subroutine log_error_logcontext + +end module smartredis_logger + diff --git a/src/fortran/logger/logger_interfaces.inc b/src/fortran/logger/logger_interfaces.inc new file mode 100644 index 000000000..0c6ae2843 --- /dev/null +++ b/src/fortran/logger/logger_interfaces.inc @@ -0,0 +1,58 @@ +! BSD 2-Clause License +! +! Copyright (c) 2021-2022, Hewlett Packard Enterprise +! All rights reserved. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, this +! list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +interface + subroutine c_log_data(context, level, data, data_length) bind(c, name="log_data_noexcept") + use iso_c_binding, only : c_ptr, c_char, c_size_t + import :: enum_kind + type(c_ptr), value :: context + integer(kind=enum_kind), value :: level + character(kind=c_char) :: data(*) + integer(kind=c_size_t), value :: data_length + end subroutine c_log_data +end interface + +interface + subroutine c_log_warning(context, level, data, data_length) bind(c, name="log_warning_noexcept") + use iso_c_binding, only : c_ptr, c_char, c_size_t + import :: enum_kind + type(c_ptr), value :: context + integer(kind=enum_kind), value :: level + character(kind=c_char) :: data(*) + integer(kind=c_size_t), value :: data_length + end subroutine c_log_warning +end interface + +interface + subroutine c_log_error(context, level, data, data_length) bind(c, name="log_error_noexcept") + use iso_c_binding, only : c_ptr, c_char, c_size_t + import :: enum_kind + type(c_ptr), value :: context + integer(kind=enum_kind), value :: level + character(kind=c_char) :: data(*) + integer(kind=c_size_t), value :: data_length + end subroutine c_log_error +end interface diff --git a/src/python/bindings/bind.cpp b/src/python/bindings/bind.cpp index b5e36f87b..f504f92f0 100644 --- a/src/python/bindings/bind.cpp +++ b/src/python/bindings/bind.cpp @@ -26,8 +26,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "pysrobject.h" #include "pyclient.h" +#include "pydataset.h" +#include "pylogcontext.h" #include "srexception.h" +#include "logger.h" +//#include "srobject.h" +//#include "logcontext.h" using namespace SmartRedis; namespace py = pybind11; @@ -36,9 +42,17 @@ namespace py = pybind11; PYBIND11_MODULE(smartredisPy, m) { m.doc() = "smartredis client"; // optional module docstring - // Python client bindings - py::class_(m, "PyClient") - .def(py::init()) + // Python SRObject class + py::class_(m, "PySRObject") + .def(py::init()); + + // Python LogContext class + py::class_(m, "PyLogContext") + .def(py::init()); + + // Python client class + py::class_(m, "PyClient") + .def(py::init()) .def("put_tensor", &PyClient::put_tensor) .def("get_tensor", &PyClient::get_tensor) .def("delete_tensor", &PyClient::delete_tensor) @@ -98,7 +112,7 @@ PYBIND11_MODULE(smartredisPy, m) { .def("get_dataset_list_range", &PyClient::get_dataset_list_range); // Python Dataset class - py::class_(m, "PyDataset") + py::class_(m, "PyDataset") .def(py::init()) .def("add_tensor", &PyDataset::add_tensor) .def("get_tensor", &PyDataset::get_tensor) @@ -108,6 +122,22 @@ PYBIND11_MODULE(smartredisPy, m) { .def("get_meta_strings", &PyDataset::get_meta_strings) .def("get_name", &PyDataset::get_name); + // Logging functions + m.def("cpp_log_data", py::overload_cast(&log_data)) + .def("cpp_log_data", py::overload_cast(&log_data)) + .def("cpp_log_warning", py::overload_cast(&log_warning)) + .def("cpp_log_warning", py::overload_cast(&log_warning)) + .def("cpp_log_error", py::overload_cast(&log_error)) + .def("cpp_log_error", py::overload_cast(&log_error)); + + // Logging levels + py::enum_(m, "SRLoggingLevel") + .value("LLQuiet", LLQuiet) + .value("LLInfo", LLInfo) + .value("LLDebug", LLDebug) + .value("LLDeveloper", LLDeveloper) + .export_values(); + // Error management routines m.def("c_get_last_error_location", &SRGetLastErrorLocation); diff --git a/src/python/module/smartredis/__init__.py b/src/python/module/smartredis/__init__.py index 0ec061281..0fc804c6b 100644 --- a/src/python/module/smartredis/__init__.py +++ b/src/python/module/smartredis/__init__.py @@ -24,6 +24,29 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +__all__ = [ + "Client", + "Dataset", + "SRObject", + "LogContext", + "DatasetConverter", + "log_data", + "log_warning", + "log_error", + "LLQuiet", + "LLInfo", + "LLDebug", + "LLDeveloper", +] + from .client import Client from .dataset import Dataset -from .dataset_utils import DatasetConverter \ No newline at end of file +from .dataset_utils import DatasetConverter +from .logger import ( + log_data, log_warning, log_error +) +from .srobject import SRObject +from .logcontext import LogContext +from .smartredisPy import ( + LLQuiet, LLInfo, LLDebug, LLDeveloper +) diff --git a/src/python/module/smartredis/client.py b/src/python/module/smartredis/client.py index 1391eb616..a7b5cfd0e 100644 --- a/src/python/module/smartredis/client.py +++ b/src/python/module/smartredis/client.py @@ -39,7 +39,7 @@ from .smartredisPy import RedisReplyError as PybindRedisReplyError class Client(PyClient): - def __init__(self, address=None, cluster=False): + def __init__(self, address=None, cluster=False, logger_name="default"): """Initialize a RedisAI client For clusters, the address can be a single tcp/ip address and port @@ -52,6 +52,8 @@ def __init__(self, address=None, cluster=False): :param address: Address of the database :param cluster: True if connecting to a redis cluster, defaults to False :type cluster: bool, optional + :param logger_name: Identifier for the current client + :type logger_name: str :raises RedisConnectionError: if connection initialization fails """ if address: @@ -59,7 +61,7 @@ def __init__(self, address=None, cluster=False): if "SSDB" not in os.environ: raise RedisConnectionError("Could not connect to database. $SSDB not set") try: - super().__init__(cluster) + super().__init__(cluster, logger_name) except PybindRedisReplyError as e: raise RedisConnectionError(str(e)) from None except RuntimeError as e: diff --git a/src/python/module/smartredis/dataset.py b/src/python/module/smartredis/dataset.py index 096eafc46..c98086f02 100644 --- a/src/python/module/smartredis/dataset.py +++ b/src/python/module/smartredis/dataset.py @@ -42,7 +42,7 @@ def __init__(self, name): """ typecheck(name, "name", str) self._data = PyDataset(name) - + @staticmethod def from_pybind(dataset): diff --git a/src/python/module/smartredis/logcontext.py b/src/python/module/smartredis/logcontext.py new file mode 100644 index 000000000..42d702f87 --- /dev/null +++ b/src/python/module/smartredis/logcontext.py @@ -0,0 +1,79 @@ +# BSD 2-Clause License +# +# Copyright (c) 2021-2022, Hewlett Packard Enterprise +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from .smartredisPy import PyLogContext +from .util import exception_handler, typecheck + +from .error import * + +class LogContext: + def __init__(self, context): + """Initialize a LogContext object + + :param context: logging context + :type name: str + """ + typecheck(context, "context", str) + self._name = context + self._data = PyLogContext(context) + + + @staticmethod + def from_pybind(logcontext): + """Initialize a LogContext object from + a PyLogContext object + + :param logcontext: The pybind PyLogContext object + to use for construction + :type logcontext: PyLogContext + :return: The newly constructor LogContext from + the PyLogContext + :rtype: LogContext + """ + typecheck(logcontext, "logcontext", PyLogContext) + new_logcontext = LogContext(logcontext._name) + new_logcontext.set_data(logcontext) + return new_logcontext + + @exception_handler + def get_data(self): + """Return the PyLogContext attribute + + :return: The PyLogContext attribute containing + the logcontext information + :rtype: PyLogContext + """ + return self._data + + @exception_handler + def set_data(self, logcontext): + """Set the PyLogContext attribute + + :param logcontext: The PyLogContext object + :type logcontext: PyLogContext + """ + typecheck(logcontext, "logcontext", PyLogContext) + self._data = logcontext diff --git a/src/python/module/smartredis/logger.py b/src/python/module/smartredis/logger.py new file mode 100644 index 000000000..ce47244e5 --- /dev/null +++ b/src/python/module/smartredis/logger.py @@ -0,0 +1,86 @@ +# BSD 2-Clause License +# +# Copyright (c) 2021-2022, Hewlett Packard Enterprise +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from .smartredisPy import cpp_log_data, cpp_log_warning, cpp_log_error, SRLoggingLevel +from .util import exception_handler, typecheck + +# Logging levels +#LLQuiet = 1 # No logging at all +#LLInfo = 2 # Informational logging only +#LLDebug = 3 # Verbose logging for debugging purposes +#LLDeveloper = 4 # Extra verbose logging for internal use + +@exception_handler +def log_data(context, level, data): + """Log data to the SmartRedis logfile + + :param context: Logging context (string to prefix the log entry with) + :type context: str + :param level: minimum logging level for data to be logged with + :type name: enum + :param data: message data to log + :type data: str + :raises RedisReplyError: if logging fails + """ + typecheck(context, "context", str) + typecheck(level, "level", SRLoggingLevel) + typecheck(data, "data", str) + cpp_log_data(context, level, data) + +@exception_handler +def log_warning(context, level, data): + """Log a warning to the SmartRedis logfile + + :param context: Logging context (string to prefix the log entry with) + :type context: str + :param level: minimum logging level for data to be logged with + :type name: enum + :param data: message data to log + :type data: str + :raises RedisReplyError: if logging fails + """ + typecheck(context, "context", str) + typecheck(level, "level", SRLoggingLevel) + typecheck(data, "data", str) + cpp_log_warning(context, level, data) + +@exception_handler +def log_error(context, level, data): + """Log an error to the SmartRedis logfile + + :param context: Logging context (string to prefix the log entry with) + :type context: str + :param level: minimum logging level for data to be logged with + :type name: enum + :param data: message data to log + :type data: str + :raises RedisReplyError: if logging fails + """ + typecheck(context, "context", str) + typecheck(level, "level", SRLoggingLevel) + typecheck(data, "data", str) + cpp_log_error(context, level, data) + diff --git a/src/python/module/smartredis/srobject.py b/src/python/module/smartredis/srobject.py new file mode 100644 index 000000000..1ed2acecc --- /dev/null +++ b/src/python/module/smartredis/srobject.py @@ -0,0 +1,79 @@ +# BSD 2-Clause License +# +# Copyright (c) 2021-2022, Hewlett Packard Enterprise +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from .smartredisPy import PySRObject +from .util import exception_handler, typecheck + +from .error import * + +class SRObject: + def __init__(self, context): + """Initialize a SRObject object + + :param context: logging context + :type name: str + """ + typecheck(context, "context", str) + self._name = context + self._data = PySRObject(context) + + + @staticmethod + def from_pybind(srobject): + """Initialize a SRObject object from + a PySRObject object + + :param srobject: The pybind PySRObject object + to use for construction + :type srobject: PySRObject + :return: The newly constructor SRObject from + the PySRObject + :rtype: SRObject + """ + typecheck(srobject, "srobject", PySRObject) + new_srobject = SRObject(srobject._name) + new_srobject.set_data(srobject) + return new_srobject + + @exception_handler + def get_data(self): + """Return the PySRObject attribute + + :return: The PySRObject attribute containing + the srobject information + :rtype: PySRObject + """ + return self._data + + @exception_handler + def set_data(self, srobject): + """Set the PySRObject attribute + + :param srobject: The PySRObject object + :type srobject: PySRObject + """ + typecheck(srobject, "srobject", PySRObject) + self._data = srobject diff --git a/src/python/src/pyclient.cpp b/src/python/src/pyclient.cpp index bd971dba8..95980f018 100644 --- a/src/python/src/pyclient.cpp +++ b/src/python/src/pyclient.cpp @@ -35,12 +35,12 @@ using namespace SmartRedis; namespace py = pybind11; -PyClient::PyClient(bool cluster) +PyClient::PyClient(bool cluster, const std::string& logger_name) + : PySRObject(logger_name) { -// throw SRRuntimeException("Test"); _client = NULL; try { - _client = new Client(cluster); + _client = new Client(cluster, logger_name); } catch (Exception& e) { // exception is already prepared for caller diff --git a/src/python/src/pydataset.cpp b/src/python/src/pydataset.cpp index 35280b52a..10e274c68 100644 --- a/src/python/src/pydataset.cpp +++ b/src/python/src/pydataset.cpp @@ -29,12 +29,14 @@ #include "pydataset.h" #include "srexception.h" +#include "logger.h" using namespace SmartRedis; namespace py = pybind11; PyDataset::PyDataset(const std::string& name) + : PySRObject(name) { _dataset = NULL; try { @@ -56,6 +58,7 @@ PyDataset::PyDataset(const std::string& name) } PyDataset::PyDataset(DataSet* dataset) + : PySRObject(dataset->get_context()) { _dataset = dataset; } diff --git a/src/python/src/pylogcontext.cpp b/src/python/src/pylogcontext.cpp new file mode 100644 index 000000000..f8af470be --- /dev/null +++ b/src/python/src/pylogcontext.cpp @@ -0,0 +1,76 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "pylogcontext.h" +#include "logcontext.h" +#include "srexception.h" + +using namespace SmartRedis; + +namespace py = pybind11; + +PyLogContext::PyLogContext(const std::string& context) + : PySRObject(context) +{ + _logcontext = NULL; + try { + _logcontext = new LogContext(context); + } + catch (Exception& e) { + // exception is already prepared for caller + throw; + } + catch (std::exception& e) { + // should never happen + throw SRInternalException(e.what()); + } + catch (...) { + // should never happen + throw SRInternalException("A non-standard exception was encountered "\ + "during dataset construction."); + } +} + +PyLogContext::PyLogContext(LogContext* logcontext) + : PySRObject(logcontext->get_context()) +{ + _logcontext = logcontext; +} + +PyLogContext::~PyLogContext() +{ + if (_logcontext != NULL) { + delete _logcontext; + _logcontext = NULL; + } +} + +LogContext* PyLogContext::get() { + return _logcontext; +} diff --git a/src/python/src/pysrobject.cpp b/src/python/src/pysrobject.cpp new file mode 100644 index 000000000..e8f6b3934 --- /dev/null +++ b/src/python/src/pysrobject.cpp @@ -0,0 +1,75 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "pysrobject.h" +#include "srobject.h" +#include "srexception.h" +#include "logger.h" + +using namespace SmartRedis; + +namespace py = pybind11; + +PySRObject::PySRObject(const std::string& context) +{ + _srobject = NULL; + try { + _srobject = new SRObject(context); + } + catch (Exception& e) { + // exception is already prepared for caller + throw; + } + catch (std::exception& e) { + // should never happen + throw SRInternalException(e.what()); + } + catch (...) { + // should never happen + throw SRInternalException("A non-standard exception was encountered "\ + "during dataset construction."); + } +} + +PySRObject::PySRObject(SRObject* srobject) +{ + _srobject = srobject; +} + +PySRObject::~PySRObject() +{ + if (_srobject != NULL) { + delete _srobject; + _srobject = NULL; + } +} + +SRObject* PySRObject::get() { + return _srobject; +} diff --git a/tests/c/CMakeLists.txt b/tests/c/CMakeLists.txt index db284d93e..180670ac7 100644 --- a/tests/c/CMakeLists.txt +++ b/tests/c/CMakeLists.txt @@ -56,6 +56,14 @@ target_link_libraries(client_test_dataset_exists ${SR_LIB} ) +add_executable(client_test_logging + client_test_logging.c +) + +target_link_libraries(client_test_logging + ${SR_LIB} +) + add_executable(client_test_put_unpack_1D client_test_put_unpack_1D.c ) diff --git a/tests/c/client_test_dataset_aggregation.c b/tests/c/client_test_dataset_aggregation.c index 93ab166dd..e650c6afb 100644 --- a/tests/c/client_test_dataset_aggregation.c +++ b/tests/c/client_test_dataset_aggregation.c @@ -130,10 +130,13 @@ int main(int argc, char* argv[]) char* dataset_name[] = {"agg_dataset_0", "agg_dataset_1", "agg_dataset_2", "agg_dataset_2", "agg_dataset_3"}; char* list_name = "my_aggregation"; void** datasets = NULL; + const char* logger_name = "test_dataset_aggregation"; + size_t cid_len = strlen(logger_name); // Initialize client void *client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client) || NULL == client) { + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client) || + NULL == client) { printf("Failed to initialize client!\n"); printf("Test passed: NO\n"); return -1; diff --git a/tests/c/client_test_dataset_exists.c b/tests/c/client_test_dataset_exists.c index f1842d554..11bfba6ce 100644 --- a/tests/c/client_test_dataset_exists.c +++ b/tests/c/client_test_dataset_exists.c @@ -41,7 +41,9 @@ bool cluster = true; int missing_dataset(char *dataset_name, size_t dataset_name_len) { void *client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "missing_dataset"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; bool exists = false; @@ -67,9 +69,11 @@ int present_dataset(char *dataset_name, size_t dataset_name_len) uint16_t ***tensor = NULL; int i, j, k; bool exists = false; + const char* logger_name = "present_dataset"; + size_t cid_len = strlen(logger_name); // Initialize client and dataset - if (SRNoError != SmartRedisCClient(use_cluster(), &client) || NULL == client) + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client) || NULL == client) return -1; if (SRNoError != CDataSet(dataset_name, dataset_name_len, &dataset) || NULL == dataset) return -1; diff --git a/tests/c/client_test_logging.c b/tests/c/client_test_logging.c new file mode 100644 index 000000000..58e5f54f6 --- /dev/null +++ b/tests/c/client_test_logging.c @@ -0,0 +1,160 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "c_client.h" +#include "c_dataset.h" +#include "c_logcontext.h" +#include "c_client_test_utils.h" +#include +#include +#include +#include "stdint.h" +#include "srexception.h" +#include "logger.h" + +bool cluster = true; + +#define TEST_LOG(logtype, context, loglevel, logmessage) \ +log_##logtype(context, loglevel, logmessage, strlen(logmessage)) + +int main(int argc, char* argv[]) +{ + int result = 0; + void* client = NULL; + void* dataset = NULL; + void* logcontext = NULL; + const char* ctx_client = "client_test_logging (client)"; + const char* ctx_dataset = "client_test_logging (dataset)"; + const char* ctx_logcontext = "client_test_logging (logcontext)"; + size_t ctx_client_len = strlen(ctx_client); + size_t ctx_dataset_len = strlen(ctx_dataset); + size_t ctx_logcontext_len = strlen(ctx_logcontext); + + // Initialize client, dataset, logcontext + if (SRNoError != SmartRedisCClient( + use_cluster(), ctx_client, ctx_client_len, + &client) || NULL == client) { + return -1; + } + if (SRNoError != CDataSet( + ctx_dataset, ctx_dataset_len, &dataset) || NULL == dataset) { + return -1; + } + if (SRNoError != SmartRedisCLogContext( + ctx_logcontext, ctx_logcontext_len, &logcontext) || NULL == logcontext) { + return -1; + } + + // Log stuff against a client + TEST_LOG(data, client, LLQuiet, + "This is data logged against the client at the Quiet level"); + TEST_LOG(data, client, LLInfo, + "This is data logged against the client at the Info level"); + TEST_LOG(data, client, LLDebug, + "This is data logged against the client at the Debug level"); + TEST_LOG(data, client, LLDeveloper, + "This is data logged against the client at the Developer level"); + + TEST_LOG(warning, client, LLQuiet, + "This is a warning logged against the client at the Quiet level"); + TEST_LOG(warning, client, LLInfo, + "This is a warning logged against the client at the Info level"); + TEST_LOG(warning, client, LLDebug, + "This is a warning logged against the client at the Debug level"); + TEST_LOG(warning, client, LLDeveloper, + "This is a warning logged against the client at the Developer level"); + + TEST_LOG(error, client, LLQuiet, + "This is an error logged against the client at the Quiet level"); + TEST_LOG(error, client, LLInfo, + "This is an error logged against the client at the Info level"); + TEST_LOG(error, client, LLDebug, + "This is an error logged against the client at the Debug level"); + TEST_LOG(error, client, LLDeveloper, + "This is an error logged against the client at the Developer level"); + + // Log stuff against a dataset + TEST_LOG(data, dataset, LLQuiet, + "This is data logged against the dataset at the Quiet level"); + TEST_LOG(data, dataset, LLInfo, + "This is data logged against the dataset at the Info level"); + TEST_LOG(data, dataset, LLDebug, + "This is data logged against the dataset at the Debug level"); + TEST_LOG(data, dataset, LLDeveloper, + "This is data logged against the dataset at the Developer level"); + + TEST_LOG(warning, dataset, LLQuiet, + "This is a warning logged against the dataset at the Quiet level"); + TEST_LOG(warning, dataset, LLInfo, + "This is a warning logged against the dataset at the Info level"); + TEST_LOG(warning, dataset, LLDebug, + "This is a warning logged against the dataset at the Debug level"); + TEST_LOG(warning, dataset, LLDeveloper, + "This is a warning logged against the dataset at the Developer level"); + + TEST_LOG(error, dataset, LLQuiet, + "This is an error logged against the dataset at the Quiet level"); + TEST_LOG(error, dataset, LLInfo, + "This is an error logged against the dataset at the Info level"); + TEST_LOG(error, dataset, LLDebug, + "This is an error logged against the dataset at the Debug level"); + TEST_LOG(error, dataset, LLDeveloper, + "This is an error logged against the dataset at the Developer level"); + + // Log stuff against a logcontext + TEST_LOG(data, logcontext, LLQuiet, + "This is data logged against the logcontext at the Quiet level"); + TEST_LOG(data, logcontext, LLInfo, + "This is data logged against the logcontext at the Info level"); + TEST_LOG(data, logcontext, LLDebug, + "This is data logged against the logcontext at the Debug level"); + TEST_LOG(data, logcontext, LLDeveloper, + "This is data logged against the logcontext at the Developer level"); + + TEST_LOG(warning, logcontext, LLQuiet, + "This is a warning logged against the logcontext at the Quiet level"); + TEST_LOG(warning, logcontext, LLInfo, + "This is a warning logged against the logcontext at the Info level"); + TEST_LOG(warning, logcontext, LLDebug, + "This is a warning logged against the logcontext at the Debug level"); + TEST_LOG(warning, logcontext, LLDeveloper, + "This is a warning logged against the logcontext at the Developer level"); + + TEST_LOG(error, logcontext, LLQuiet, + "This is an error logged against the logcontext at the Quiet level"); + TEST_LOG(error, logcontext, LLInfo, + "This is an error logged against the logcontext at the Info level"); + TEST_LOG(error, logcontext, LLDebug, + "This is an error logged against the logcontext at the Debug level"); + TEST_LOG(error, logcontext, LLDeveloper, + "This is an error logged against the logcontexts at the Developer level"); + + // Done + printf("Test passed: %s\n", result == 0 ? "YES" : "NO"); + return result; +} diff --git a/tests/c/client_test_put_get_1D.c b/tests/c/client_test_put_get_1D.c index c67c804c6..f2b7759af 100644 --- a/tests/c/client_test_put_get_1D.c +++ b/tests/c/client_test_put_get_1D.c @@ -117,7 +117,9 @@ int put_get_1D_tensor_double(size_t* dims, size_t n_dims, char* key_suffix, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_1D_tensor_double"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; double* tensor = (double*)malloc(dims[0]*sizeof(double)); @@ -152,7 +154,9 @@ int put_get_1D_tensor_float(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_1D_tensor_float"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; float* tensor = (float*)malloc(dims[0]*sizeof(float)); @@ -182,7 +186,9 @@ int put_get_1D_tensor_i8(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_1D_tensor_i8"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int8_t* tensor = (int8_t*)malloc(dims[0]*sizeof(int8_t)); @@ -220,7 +226,9 @@ int put_get_1D_tensor_i16(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_1D_tensor_i16"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int16_t* tensor = (int16_t*)malloc(dims[0]*sizeof(int16_t)); @@ -258,7 +266,9 @@ int put_get_1D_tensor_i32(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_1D_tensor_i32"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int32_t* tensor = (int32_t*)malloc(dims[0]*sizeof(int32_t)); @@ -296,7 +306,9 @@ int put_get_1D_tensor_i64(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_1D_tensor_i64"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int64_t* tensor = (int64_t*)malloc(dims[0]*sizeof(int64_t)); @@ -334,7 +346,9 @@ int put_get_1D_tensor_ui8(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_1D_tensor_ui8"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; uint8_t* tensor = (uint8_t*)malloc(dims[0]*sizeof(uint8_t)); @@ -369,7 +383,9 @@ int put_get_1D_tensor_ui16(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_1D_tensor_ui16"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; uint16_t* tensor = (uint16_t*)malloc(dims[0]*sizeof(uint16_t)); diff --git a/tests/c/client_test_put_get_2D.c b/tests/c/client_test_put_get_2D.c index 5208bbc10..f3a752cbc 100644 --- a/tests/c/client_test_put_get_2D.c +++ b/tests/c/client_test_put_get_2D.c @@ -118,7 +118,9 @@ int put_get_2D_tensor_double(size_t* dims, int n_dims, char* key_suffix, int key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_2D_tensor_double"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; double** tensor = (double**)malloc(dims[0]*sizeof(double*)); @@ -165,7 +167,9 @@ int put_get_2D_tensor_float(size_t* dims, int n_dims, int key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_2D_tensor_float"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; float** tensor = (float**)malloc(dims[0]*sizeof(float*)); @@ -212,7 +216,9 @@ int put_get_2D_tensor_i8(size_t* dims, int n_dims, int key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_2D_tensor_i8"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int8_t** tensor = (int8_t**)malloc(dims[0]*sizeof(int8_t*)); @@ -263,7 +269,9 @@ int put_get_2D_tensor_i16(size_t* dims, int n_dims, int key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_2D_tensor_i16"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int16_t** tensor = (int16_t**)malloc(dims[0]*sizeof(int16_t*)); @@ -314,7 +322,9 @@ int put_get_2D_tensor_i32(size_t* dims, int n_dims, int key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_2D_tensor_i32"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int32_t** tensor = (int32_t**)malloc(dims[0]*sizeof(int32_t*)); @@ -365,7 +375,9 @@ int put_get_2D_tensor_i64(size_t* dims, int n_dims, int key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_2D_tensor_i64"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int64_t** tensor = (int64_t**)malloc(dims[0]*sizeof(int64_t*)); @@ -416,7 +428,9 @@ int put_get_2D_tensor_ui8(size_t* dims, int n_dims, int key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_2D_tensor_ui8"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; uint8_t** tensor = (uint8_t**)malloc(dims[0]*sizeof(uint8_t*)); @@ -465,7 +479,9 @@ int put_get_2D_tensor_ui16(size_t* dims, int n_dims, int key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_2D_tensor_ui16"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; uint16_t** tensor = (uint16_t**)malloc(dims[0]*sizeof(uint16_t*)); diff --git a/tests/c/client_test_put_get_3D.c b/tests/c/client_test_put_get_3D.c index 7b235899a..4f5e1888e 100644 --- a/tests/c/client_test_put_get_3D.c +++ b/tests/c/client_test_put_get_3D.c @@ -118,7 +118,9 @@ int put_get_3D_tensor_double(size_t* dims, size_t n_dims, char* key_suffix, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_3D_tensor_double"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; double*** tensor = (double***)malloc(dims[0]*sizeof(double**)); @@ -175,7 +177,9 @@ int put_get_3D_tensor_float(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_3D_tensor_float"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; float*** tensor = (float***)malloc(dims[0]*sizeof(float**)); @@ -231,7 +235,9 @@ int put_get_3D_tensor_i8(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_3D_tensor_i8"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int8_t*** tensor = (int8_t***)malloc(dims[0]*sizeof(int8_t**)); @@ -291,7 +297,9 @@ int put_get_3D_tensor_i16(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_3D_tensor_i16"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int16_t*** tensor = (int16_t***)malloc(dims[0]*sizeof(int16_t**)); @@ -352,7 +360,9 @@ int put_get_3D_tensor_i32(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_3D_tensor_i32"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int32_t*** tensor = (int32_t***)malloc(dims[0]*sizeof(int32_t**)); @@ -413,7 +423,9 @@ int put_get_3D_tensor_i64(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_3D_tensor_i64"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; int64_t*** tensor = (int64_t***)malloc(dims[0]*sizeof(int64_t**)); @@ -474,7 +486,9 @@ int put_get_3D_tensor_ui8(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_3D_tensor_ui8"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; uint8_t*** tensor = (uint8_t***)malloc(dims[0]*sizeof(uint8_t**)); @@ -535,7 +549,9 @@ int put_get_3D_tensor_ui16(size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_get_3D_tensor_ui16"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; uint16_t*** tensor = (uint16_t***)malloc(dims[0]*sizeof(uint16_t**)); diff --git a/tests/c/client_test_put_unpack_1D.c b/tests/c/client_test_put_unpack_1D.c index fe83210d0..2aa4ff5a9 100644 --- a/tests/c/client_test_put_unpack_1D.c +++ b/tests/c/client_test_put_unpack_1D.c @@ -47,7 +47,9 @@ int put_unpack_1D_tensor(void* tensor, size_t* dims, size_t n_dims, size_t key_suffix_length) { void* client = NULL; - if (SRNoError != SmartRedisCClient(use_cluster(), &client)) + const char* logger_name = "put_unpack_1D_tensor"; + size_t cid_len = strlen(logger_name); + if (SRNoError != SmartRedisCClient(use_cluster(), logger_name, cid_len, &client)) return -1; char* prefix_str = "1D_tensor_test"; diff --git a/tests/cpp/client_test_copy_dataset.cpp b/tests/cpp/client_test_copy_dataset.cpp index 8db2764f6..91a82c109 100644 --- a/tests/cpp/client_test_copy_dataset.cpp +++ b/tests/cpp/client_test_copy_dataset.cpp @@ -52,7 +52,7 @@ void put_and_copy_dataset( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSet - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_copy_dataset"); SmartRedis::DataSet source_dataset(dataset_name); //Add metadata to the DataSet diff --git a/tests/cpp/client_test_dataset.cpp b/tests/cpp/client_test_dataset.cpp index e19a000a5..9927998f7 100644 --- a/tests/cpp/client_test_dataset.cpp +++ b/tests/cpp/client_test_dataset.cpp @@ -52,7 +52,7 @@ void put_get_dataset( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSet - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_dataset"); SmartRedis::DataSet sent_dataset(dataset_name); //Add metadata to the DataSet diff --git a/tests/cpp/client_test_dataset_aggregation.cpp b/tests/cpp/client_test_dataset_aggregation.cpp index 47a4af46c..91bca8b09 100644 --- a/tests/cpp/client_test_dataset_aggregation.cpp +++ b/tests/cpp/client_test_dataset_aggregation.cpp @@ -112,7 +112,7 @@ void check_dataset(SmartRedis::DataSet& dataset_1, int main(int argc, char* argv[]) { // Create client for dataset and aggregation list actions - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_dataset_aggregation"); // Set a fill function for dataset creation void (*fill_function)(double***, int, int, int) = diff --git a/tests/cpp/client_test_dataset_copy_assignment.cpp b/tests/cpp/client_test_dataset_copy_assignment.cpp index b35dd009a..a3df18150 100644 --- a/tests/cpp/client_test_dataset_copy_assignment.cpp +++ b/tests/cpp/client_test_dataset_copy_assignment.cpp @@ -52,7 +52,7 @@ void copy_assignment( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSets - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_dataset_copy_assignment"); SmartRedis::DataSet* dataset = new SmartRedis::DataSet(dataset_name); //Add tensors to the DataSet diff --git a/tests/cpp/client_test_dataset_copy_constructor.cpp b/tests/cpp/client_test_dataset_copy_constructor.cpp index 638796238..8e62d675d 100644 --- a/tests/cpp/client_test_dataset_copy_constructor.cpp +++ b/tests/cpp/client_test_dataset_copy_constructor.cpp @@ -52,7 +52,7 @@ void copy_constructor( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSet - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_dataset_copy_constructor"); SmartRedis::DataSet* dataset = new SmartRedis::DataSet(dataset_name); //Add tensors to the DataSet diff --git a/tests/cpp/client_test_dataset_empty.cpp b/tests/cpp/client_test_dataset_empty.cpp index c70002339..a096a2508 100644 --- a/tests/cpp/client_test_dataset_empty.cpp +++ b/tests/cpp/client_test_dataset_empty.cpp @@ -35,7 +35,7 @@ void put_get_empty_dataset(std::string dataset_name) { //Create Client and DataSet - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_dataset_empty"); SmartRedis::DataSet sent_dataset(dataset_name); //Put the DataSet into the database diff --git a/tests/cpp/client_test_dataset_move_assignment.cpp b/tests/cpp/client_test_dataset_move_assignment.cpp index 7e52c213c..c5c68a4af 100644 --- a/tests/cpp/client_test_dataset_move_assignment.cpp +++ b/tests/cpp/client_test_dataset_move_assignment.cpp @@ -52,7 +52,7 @@ void put_get_3D_array( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSets - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_dataset_move_assignment"); SmartRedis::DataSet* dataset = new SmartRedis::DataSet(dataset_name); //Add tensors to the DataSet diff --git a/tests/cpp/client_test_dataset_move_constructor.cpp b/tests/cpp/client_test_dataset_move_constructor.cpp index c33b5c06b..22119702e 100644 --- a/tests/cpp/client_test_dataset_move_constructor.cpp +++ b/tests/cpp/client_test_dataset_move_constructor.cpp @@ -52,7 +52,7 @@ void move_constructor( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSets - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_dataet_move_constructor"); SmartRedis::DataSet* dataset = new SmartRedis::DataSet(dataset_name); //Add tensors to the DataSet @@ -125,11 +125,11 @@ void move_constructor( &DATASET_TEST_UTILS::ui32_meta_1, SRMetadataTypeUint32); - //Move the DataSet half way through metadata additions to - //test that we can continue adding new fields to the old fields - SmartRedis::DataSet moved_dataset = SmartRedis::DataSet(std::move(*dataset)); + //Move the DataSet half way through metadata additions to + //test that we can continue adding new fields to the old fields + SmartRedis::DataSet moved_dataset = SmartRedis::DataSet(std::move(*dataset)); - moved_dataset.add_meta_scalar("ui32_field_1", + moved_dataset.add_meta_scalar("ui32_field_1", &DATASET_TEST_UTILS::ui32_meta_2, SRMetadataTypeUint32); moved_dataset.add_meta_scalar("ui32_field_2", diff --git a/tests/cpp/client_test_dataset_multiple_get_tensor.cpp b/tests/cpp/client_test_dataset_multiple_get_tensor.cpp index bf82e403b..148c6dca8 100644 --- a/tests/cpp/client_test_dataset_multiple_get_tensor.cpp +++ b/tests/cpp/client_test_dataset_multiple_get_tensor.cpp @@ -52,7 +52,7 @@ void get_multiple_tensors( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSet - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_dataset_multiple_get_tensor"); SmartRedis::DataSet sent_dataset(dataset_name); //Add metadata to the DataSet diff --git a/tests/cpp/client_test_dataset_no_meta.cpp b/tests/cpp/client_test_dataset_no_meta.cpp index 9e28a65b0..573164380 100644 --- a/tests/cpp/client_test_dataset_no_meta.cpp +++ b/tests/cpp/client_test_dataset_no_meta.cpp @@ -52,7 +52,7 @@ void put_get_dataset( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSet - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_dataset_no_meta"); SmartRedis::DataSet sent_dataset(dataset_name); //Add tensors to the DataSet diff --git a/tests/cpp/client_test_dataset_no_tensors.cpp b/tests/cpp/client_test_dataset_no_tensors.cpp index 9014e7b55..7b726d6fb 100644 --- a/tests/cpp/client_test_dataset_no_tensors.cpp +++ b/tests/cpp/client_test_dataset_no_tensors.cpp @@ -34,7 +34,7 @@ void put_dataset_no_tensors(std::string dataset_name) { //Create Client and DataSet - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_dataset_no_tensors"); SmartRedis::DataSet sent_dataset(dataset_name); //Add metadata to the DataSet diff --git a/tests/cpp/client_test_delete_dataset.cpp b/tests/cpp/client_test_delete_dataset.cpp index 7e6f3df8c..dd5c00109 100644 --- a/tests/cpp/client_test_delete_dataset.cpp +++ b/tests/cpp/client_test_delete_dataset.cpp @@ -52,7 +52,7 @@ void dataset_delete( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSets - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_delete_dataset"); SmartRedis::DataSet* dataset = new SmartRedis::DataSet(dataset_name); //Add tensors to the DataSet diff --git a/tests/cpp/client_test_ensemble.cpp b/tests/cpp/client_test_ensemble.cpp index 2743d203f..c5c6ddc42 100644 --- a/tests/cpp/client_test_ensemble.cpp +++ b/tests/cpp/client_test_ensemble.cpp @@ -58,7 +58,7 @@ void produce( std::string keyout="", std::string keyin="") { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_ensemble::producer"); client.use_model_ensemble_prefix(true); // Tensors @@ -128,7 +128,7 @@ void consume(std::vector dims, std::string keyout="", std::string keyin="") { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_ensemble::consumer"); client.use_model_ensemble_prefix(true); // Tensors @@ -216,50 +216,35 @@ int main(int argc, char* argv[]) { const char* old_keyin = std::getenv("SSKEYIN"); const char* old_keyout = std::getenv("SSKEYOUT"); - char keyin_env_put[] = "SSKEYIN=producer_0,producer_1"; - char keyout_env_put[] = "SSKEYOUT=producer_0"; - putenv( keyin_env_put ); - putenv( keyout_env_put ); + char keyin_env_put[] = "producer_0,producer_1"; + char keyout_env_put[] = "producer_0"; + setenv("SSKEYIN", keyin_env_put, (NULL != old_keyin)); + setenv("SSKEYOUT", keyout_env_put, (NULL != old_keyout)); size_t dim1 = 10; std::vector dims = {dim1}; - produce(dims, - std::string("producer_0"), - std::string("producer_0")); + produce(dims, std::string("producer_0"), std::string("producer_0")); - char keyin_env_get[] = "SSKEYIN=producer_1,producer_0"; - char keyout_env_get[] = "SSKEYOUT=producer_1"; - putenv(keyin_env_get); - putenv(keyout_env_get); - consume(dims, - std::string("producer_1"), - std::string("producer_0")); + char keyin_env_get[] = "producer_1,producer_0"; + char keyout_env_get[] = "producer_1"; + setenv("SSKEYIN", keyin_env_get, 1); + setenv("SSKEYOUT", keyout_env_get, 1); + + consume(dims, std::string("producer_1"), std::string("producer_0")); if (old_keyin != nullptr) { - std::string reset_keyin = std::string("SSKEYIN=") + std::string(old_keyin); - char* reset_keyin_c = new char[reset_keyin.size() + 1]; - std::copy(reset_keyin.begin(), reset_keyin.end(), reset_keyin_c); - reset_keyin_c[reset_keyin.size()] = '\0'; - putenv( reset_keyin_c); - delete[] reset_keyin_c; + setenv("SSKEYIN", old_keyin, 1); } else { unsetenv("SSKEYIN"); } if (old_keyout != nullptr) { - std::string reset_keyout = std::string("SSKEYOUT=") + std::string(old_keyout); - char* reset_keyout_c = new char[reset_keyout.size() + 1]; - std::copy(reset_keyout.begin(), reset_keyout.end(), reset_keyout_c); - reset_keyout_c[reset_keyout.size()] = '\0'; - putenv( reset_keyout_c); - delete[] reset_keyout_c; + setenv("SSKEYOUT", old_keyout, 1); } else { unsetenv("SSKEYOUT"); } - std::cout<<"Ensemble test complete"< dims({10,10,2}); - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_ensemble_dataset"); client.use_tensor_ensemble_prefix(true); double*** t_send_1 = @@ -137,7 +137,7 @@ void add_to_aggregation_list(std::string keyout) { std::vector dims({10,10,2}); - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_ensemble_dataset"); client.use_tensor_ensemble_prefix(true); client.use_list_ensemble_prefix(true); @@ -244,40 +244,28 @@ int main(int argc, char* argv[]) { const char* old_keyin = std::getenv("SSKEYIN"); const char* old_keyout = std::getenv("SSKEYOUT"); - char keyin_env_put[] = "SSKEYIN=producer_0,producer_1"; - char keyout_env_put[] = "SSKEYOUT=producer_0"; - putenv( keyin_env_put ); - putenv( keyout_env_put ); + const char* keyin_env_put = "producer_0,producer_1"; + const char* keyout_env_put = "producer_0"; + setenv("SSKEYIN", keyin_env_put, (NULL != old_keyin)); + setenv("SSKEYOUT", keyout_env_put, (NULL != old_keyout)); rename_dataset("producer_0"); add_to_aggregation_list("producer_0"); if (old_keyin != nullptr) { - std::string reset_keyin = std::string("SSKEYIN=") + std::string(old_keyin); - char* reset_keyin_c = new char[reset_keyin.size() + 1]; - std::copy(reset_keyin.begin(), reset_keyin.end(), reset_keyin_c); - reset_keyin_c[reset_keyin.size()] = '\0'; - putenv( reset_keyin_c); - delete [] reset_keyin_c; + setenv("SSKEYIN", old_keyin, 1); } else { unsetenv("SSKEYIN"); } if (old_keyout != nullptr) { - std::string reset_keyout = std::string("SSKEYOUT=") + std::string(old_keyout); - char* reset_keyout_c = new char[reset_keyout.size() + 1]; - std::copy(reset_keyout.begin(), reset_keyout.end(), reset_keyout_c); - reset_keyout_c[reset_keyout.size()] = '\0'; - putenv( reset_keyout_c); - delete [] reset_keyout_c; + setenv("SSKEYOUT", old_keyout, 1); } else { unsetenv("SSKEYOUT"); } - std::cout<<"Ensemble test complete"<(1,1,28,28); float** result = allocate_2D_array(1, 10); @@ -78,7 +78,7 @@ void run_mnist(const std::string& model_name, int main(int argc, char* argv[]) { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_mnist"); std::string model_key = "mnist_model"; std::string model_file = "./../mnist_data/mnist_cnn.pt"; client.set_model_from_file(model_key, model_file, "TORCH", "CPU"); diff --git a/tests/cpp/client_test_mnist_dataset.cpp b/tests/cpp/client_test_mnist_dataset.cpp index 68c48faff..6cbbdd204 100644 --- a/tests/cpp/client_test_mnist_dataset.cpp +++ b/tests/cpp/client_test_mnist_dataset.cpp @@ -52,7 +52,7 @@ void load_mnist_image_to_array(float**** img) void run_mnist(const std::string& model_name, const std::string& script_name) { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_mnist_dataset"); float**** array = allocate_4D_array(1,1,28,28); float** result = allocate_2D_array(1, 10); @@ -82,7 +82,7 @@ void run_mnist(const std::string& model_name, int main(int argc, char* argv[]) { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_mnist_dataset"); std::string model_key = "mnist_model"; std::string model_file = "./../mnist_data/mnist_cnn.pt"; client.set_model_from_file(model_key, model_file, "TORCH", "CPU"); diff --git a/tests/cpp/client_test_put_get_1D.cpp b/tests/cpp/client_test_put_get_1D.cpp index 649bf5b0e..5815016ff 100644 --- a/tests/cpp/client_test_put_get_1D.cpp +++ b/tests/cpp/client_test_put_get_1D.cpp @@ -38,7 +38,7 @@ void put_get_1D_array( SRTensorType type, std::string key_suffix="") { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_put_get_1D"); //Allocate and fill arrays T_send* array = (T_send*)malloc(dims[0]*sizeof(T_send)); diff --git a/tests/cpp/client_test_put_get_2D.cpp b/tests/cpp/client_test_put_get_2D.cpp index 38fd33bbb..5d4abc583 100644 --- a/tests/cpp/client_test_put_get_2D.cpp +++ b/tests/cpp/client_test_put_get_2D.cpp @@ -39,7 +39,7 @@ void put_get_2D_array( std::string key_suffix="") { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_put_get_2D"); //Allocate and fill arrays T_send** array = allocate_2D_array(dims[0], dims[1]); diff --git a/tests/cpp/client_test_put_get_3D.cpp b/tests/cpp/client_test_put_get_3D.cpp index 11ab5be0a..12cc913ef 100644 --- a/tests/cpp/client_test_put_get_3D.cpp +++ b/tests/cpp/client_test_put_get_3D.cpp @@ -38,7 +38,7 @@ void put_get_3D_array( SRTensorType type, std::string key_suffix="") { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_put_get_3D"); //Allocate and fill arrays T_send*** array = allocate_3D_array(dims[0], dims[1], dims[2]); diff --git a/tests/cpp/client_test_put_get_3D_static_values.cpp b/tests/cpp/client_test_put_get_3D_static_values.cpp index 9e2af64d9..4c89a2420 100644 --- a/tests/cpp/client_test_put_get_3D_static_values.cpp +++ b/tests/cpp/client_test_put_get_3D_static_values.cpp @@ -37,7 +37,7 @@ void put_get_3D_array( SRTensorType type, std::string key_suffix="") { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_put_get_3D_static_values"); //Allocate and fill arrays T_send*** array = allocate_3D_array(dims[0], dims[1], dims[2]); diff --git a/tests/cpp/client_test_put_get_contiguous_3D.cpp b/tests/cpp/client_test_put_get_contiguous_3D.cpp index 242c62e56..e800b813f 100644 --- a/tests/cpp/client_test_put_get_contiguous_3D.cpp +++ b/tests/cpp/client_test_put_get_contiguous_3D.cpp @@ -38,7 +38,7 @@ void put_get_3D_array( SRTensorType type, std::string key_suffix="") { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_put_get_contiguous_3D"); //Allocate and fill arrays T_send* array = diff --git a/tests/cpp/client_test_put_get_transpose_3D.cpp b/tests/cpp/client_test_put_get_transpose_3D.cpp index 9ef5435b8..4344ebcfb 100644 --- a/tests/cpp/client_test_put_get_transpose_3D.cpp +++ b/tests/cpp/client_test_put_get_transpose_3D.cpp @@ -60,7 +60,7 @@ void put_get_3D_array( SRMemoryLayout send_direction = SRMemLayoutContiguous, SRMemoryLayout recv_direction = SRMemLayoutContiguous) { - SmartRedis::Client client(use_cluster()); + SmartRedis::Client client(use_cluster(), "client_test_put_get_transpose_3D"); //Allocate and fill arrays T_send* array = (T_send*)malloc(dims[0]*dims[1]*dims[2]*sizeof(T_send)); diff --git a/tests/cpp/client_test_rename_dataset.cpp b/tests/cpp/client_test_rename_dataset.cpp index dc301de57..4a6eba2b9 100644 --- a/tests/cpp/client_test_rename_dataset.cpp +++ b/tests/cpp/client_test_rename_dataset.cpp @@ -52,7 +52,7 @@ void rename_dataset( fill_array(t_send_3, dims[0], dims[1], dims[2]); //Create Client and DataSet - DATASET_TEST_UTILS::DatasetTestClient client(use_cluster()); + DATASET_TEST_UTILS::DatasetTestClient client(use_cluster(), "client_test_rename_dataset"); SmartRedis::DataSet sent_dataset(dataset_name); //Add metadata to the DataSet diff --git a/tests/cpp/client_test_utils.h b/tests/cpp/client_test_utils.h index 4ab667049..78f126382 100644 --- a/tests/cpp/client_test_utils.h +++ b/tests/cpp/client_test_utils.h @@ -39,7 +39,7 @@ using namespace SmartRedis; class RedisClusterTestObject : public RedisCluster { public: - RedisClusterTestObject() : RedisCluster() {}; + RedisClusterTestObject() : RedisCluster(NULL) {}; std::string get_crc16_prefix(uint64_t hash_slot) { return _get_crc16_prefix(hash_slot); diff --git a/tests/cpp/dataset_test_utils.h b/tests/cpp/dataset_test_utils.h index 0fc4b4167..8667c214d 100644 --- a/tests/cpp/dataset_test_utils.h +++ b/tests/cpp/dataset_test_utils.h @@ -43,7 +43,8 @@ namespace DATASET_TEST_UTILS { class DatasetTestClient : public SmartRedis::Client { public: - DatasetTestClient(bool cluster) : Client(cluster) {}; + DatasetTestClient(bool cluster, const std::string& name) + : Client(cluster, name) {}; bool hash_field_exists(const std::string& key, const std::string& field) { diff --git a/tests/cpp/unit-tests/CMakeLists.txt b/tests/cpp/unit-tests/CMakeLists.txt index 9add6f647..3b1ed1dfe 100644 --- a/tests/cpp/unit-tests/CMakeLists.txt +++ b/tests/cpp/unit-tests/CMakeLists.txt @@ -64,7 +64,7 @@ set(SOURCES ../../../src/cpp/metadatafield.cpp ../../../src/cpp/multikeycommand.cpp ../../../src/cpp/nonkeyedcommand.cpp - ../../../src/cpp/pipelinereply.cpp + ../../../src/cpp/pipelinereply.cpp ../../../src/cpp/redis.cpp ../../../src/cpp/rediscluster.cpp ../../../src/cpp/redisserver.cpp @@ -73,12 +73,15 @@ set(SOURCES ../../../src/cpp/tensorbase.cpp ../../../src/cpp/tensorpack.cpp ../../../src/cpp/threadpool.cpp + ../../../src/cpp/utility.cpp + ../../../src/cpp/logger.cpp + ../../../src/cpp/srobject.cpp ) # The unit tests to be executed set(UNIT_TESTS main.cpp - test_aggregation_list.cpp + test_aggregation_list.cpp test_client.cpp test_client_ensemble.cpp test_dataset.cpp @@ -88,7 +91,6 @@ set(UNIT_TESTS test_tensor.cpp test_metadata.cpp test_stringfield.cpp - test_ssdb.cpp test_commandreply.cpp test_singlekeycommand.cpp test_addressatcommand.cpp @@ -97,7 +99,9 @@ set(UNIT_TESTS test_addressanycommand.cpp test_dbinfocommand.cpp test_clusterinfocommand.cpp - test_redisserver.cpp + test_redisserver.cpp + test_logger.cpp + test_ssdb.cpp ) # Compare the number of source files given above to the number of source CPP diff --git a/tests/cpp/unit-tests/test_addressanycommand.cpp b/tests/cpp/unit-tests/test_addressanycommand.cpp index 050eebec8..31f5f9548 100644 --- a/tests/cpp/unit-tests/test_addressanycommand.cpp +++ b/tests/cpp/unit-tests/test_addressanycommand.cpp @@ -29,6 +29,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "addressanycommand.h" #include "redisserver.h" +#include "logger.h" unsigned long get_time_offset(); @@ -37,6 +38,8 @@ using namespace SmartRedis; SCENARIO("Testing assignment operator for AddressAnyCommand", "[AddressAnyCommand]") { std::cout << std::to_string(get_time_offset()) << ": Testing assignment operator for AddressAnyCommand" << std::endl; + std::string context("test_addressanycommand"); + log_data(context, LLDebug, "***Beginning AddressAnyCommand testing***"); GIVEN("An AddressAnyCommand object") { AddressAnyCommand cmd; @@ -100,11 +103,14 @@ SCENARIO("Testing assignment operator for AddressAnyCommand", "[AddressAnyComman } } } + log_data(context, LLDebug, "***End AddressAnyCommand testing***"); } SCENARIO("Testing AddressAnyCommand member variables", "[AddressAnyCommand]") { std::cout << std::to_string(get_time_offset()) << ": Testing AddressAnyCommand member variables" << std::endl; + std::string context("test_addressanycommand"); + log_data(context, LLDebug, "***Beginning AddressAnyCommand member testing***"); GIVEN("An AddressAnyCommand object and a db node address and port") { AddressAnyCommand cmd; @@ -121,4 +127,5 @@ SCENARIO("Testing AddressAnyCommand member variables", "[AddressAnyCommand]") } } } + log_data(context, LLDebug, "***End AddressAnyCommand member testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_addressatcommand.cpp b/tests/cpp/unit-tests/test_addressatcommand.cpp index 4d63d7181..bf2934065 100644 --- a/tests/cpp/unit-tests/test_addressatcommand.cpp +++ b/tests/cpp/unit-tests/test_addressatcommand.cpp @@ -28,6 +28,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "addressatcommand.h" +#include "logger.h" unsigned long get_time_offset(); @@ -36,6 +37,9 @@ using namespace SmartRedis; SCENARIO("Ensuring the iterators for an AddressAtCommand are correct", "[AddressAtCommand]") { std::cout << std::to_string(get_time_offset()) << ": Ensuring the iterators for an AddressAtCommand are correct" << std::endl; + std::string context("test_addressatcommand"); + log_data(context, LLDebug, "***Beginning AddressAtCommand testing***"); + GIVEN("An AddressAtCommand with a single field") { AddressAtCommand cmd; @@ -59,11 +63,15 @@ SCENARIO("Ensuring the iterators for an AddressAtCommand are correct", "[Address } } } + log_data(context, LLDebug, "***End AddressAtCommand testing***"); } SCENARIO("Testing assignment operator for AddressAtCommand on heap", "[AddressAtCommand]") { std::cout << std::to_string(get_time_offset()) << ": Testing assignment operator for AddressAtCommand on heap" << std::endl; + std::string context("test_addressatcommand"); + log_data(context, LLDebug, "***Beginning AddressAtCommand heap testing***"); + GIVEN("An AddressAtCommand object on the heap") { AddressAtCommand* cmd = new AddressAtCommand; @@ -130,11 +138,14 @@ SCENARIO("Testing assignment operator for AddressAtCommand on heap", "[AddressAt } } } + log_data(context, LLDebug, "***End AddressAtCommand heap testing***"); } SCENARIO("Testing AddressAtCommand member variables", "[AddressAtCommand]") { std::cout << std::to_string(get_time_offset()) << ": Testing AddressAnyCommand member variables" << std::endl; + std::string context("test_addressatcommand"); + log_data(context, LLDebug, "***Beginning AddressAtCommand variable testing***"); GIVEN("An AddressAtCommand object") { AddressAtCommand* cmd = new AddressAtCommand; @@ -148,4 +159,5 @@ SCENARIO("Testing AddressAtCommand member variables", "[AddressAtCommand]") } } } + log_data(context, LLDebug, "***End AddressAtCommand variable testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_aggregation_list.cpp b/tests/cpp/unit-tests/test_aggregation_list.cpp index 119b4a936..a82bd5a0b 100644 --- a/tests/cpp/unit-tests/test_aggregation_list.cpp +++ b/tests/cpp/unit-tests/test_aggregation_list.cpp @@ -33,6 +33,7 @@ #include "../dataset_test_utils.h" #include "srexception.h" #include +#include "logger.h" unsigned long get_time_offset(); @@ -146,9 +147,12 @@ bool is_same_dataset(DataSet& dataset_1, DataSet& dataset_2) SCENARIO("Testing Dataset aggregation via our client", "[List]") { std::cout << std::to_string(get_time_offset()) << ": Testing Dataset aggregation via our client" << std::endl; + std::string context("test_aggregation_list"); + log_data(context, LLDebug, "***Beginning DataSet Aggregation testing***"); + GIVEN("A Client object and vector of DataSet objects") { - Client client(use_cluster()); + Client client(use_cluster(), "test_aggregation_list"); std::vector datasets; @@ -500,4 +504,5 @@ SCENARIO("Testing Dataset aggregation via our client", "[List]") } } } + log_data(context, LLDebug, "***End DataSet Aggregation testing***"); } diff --git a/tests/cpp/unit-tests/test_client.cpp b/tests/cpp/unit-tests/test_client.cpp index 2e936d55c..4edc6189b 100644 --- a/tests/cpp/unit-tests/test_client.cpp +++ b/tests/cpp/unit-tests/test_client.cpp @@ -32,6 +32,7 @@ #include "../client_test_utils.h" #include "srexception.h" #include +#include "logger.h" unsigned long get_time_offset(); @@ -95,9 +96,11 @@ void check_all_data(size_t length, std::vector& original_datas, SCENARIO("Testing Dataset Functions on Client Object", "[Client]") { std::cout << std::to_string(get_time_offset()) << ": Testing Dataset Functions on Client Object" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client testing***"); GIVEN("A Client object") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); THEN("get, rename, and copy DataSet called on " "a nonexistent DataSet throws errors") @@ -186,14 +189,17 @@ SCENARIO("Testing Dataset Functions on Client Object", "[Client]") } } } + log_data(context, LLDebug, "***End Client testing***"); } SCENARIO("Testing Tensor Functions on Client Object", "[Client]") { std::cout << std::to_string(get_time_offset()) << ": Testing Tensor Functions on Client Object" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client tensor testing***"); GIVEN("A Client object") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); AND_WHEN("Tensors of each type are created and put into the Client") { @@ -466,14 +472,17 @@ SCENARIO("Testing Tensor Functions on Client Object", "[Client]") } } } + log_data(context, LLDebug, "***End Client tensor testing***"); } SCENARIO("Testing INFO Functions on Client Object", "[Client]") { std::cout << std::to_string(get_time_offset()) << ": Testing INFO Functions on Client Object" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client INFO testing***"); GIVEN("A Client object") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); WHEN("INFO or CLUSTER INFO is called on database with " "an invalid address") @@ -515,14 +524,17 @@ SCENARIO("Testing INFO Functions on Client Object", "[Client]") } } } + log_data(context, LLDebug, "***End Client tensor testing***"); } SCENARIO("Testing AI.INFO Functions on Client Object", "[Client]") { std::cout << std::to_string(get_time_offset()) << ": Testing AI.INFO Functions on Client Object" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client AI.INFO testing***"); GIVEN("A Client object") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); WHEN("AI.INFO called on database with an invalid address") { @@ -564,14 +576,18 @@ SCENARIO("Testing AI.INFO Functions on Client Object", "[Client]") } } } + log_data(context, LLDebug, "***End Client AI.INFO testing***"); } SCENARIO("Testing FLUSHDB on empty Client Object", "[Client][FLUSHDB]") { std::cout << std::to_string(get_time_offset()) << ": Testing FLUSHDB on empty Client Object" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client empty FLIUSHDB testing***"); + GIVEN("An empty non-cluster Client object") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); WHEN("FLUSHDB is called on database with " "an invalid address") @@ -599,11 +615,15 @@ SCENARIO("Testing FLUSHDB on empty Client Object", "[Client][FLUSHDB]") } } } + log_data(context, LLDebug, "***End Client empty FLUSHDB testing***"); } SCENARIO("Testing FLUSHDB on Client Object", "[Client][FLUSHDB]") { std::cout << std::to_string(get_time_offset()) << ": Testing FLUSHDB on Client Object" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client FLUSHDB testing***"); + GIVEN("A non-cluster Client object") { // From within the testing framework, there is no way of knowing @@ -611,7 +631,7 @@ SCENARIO("Testing FLUSHDB on Client Object", "[Client][FLUSHDB]") if (use_cluster()) return; - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); std::string dataset_name = "test_dataset_name"; DataSet dataset(dataset_name); dataset.add_meta_string("meta_string_name", "meta_string_val"); @@ -640,14 +660,18 @@ SCENARIO("Testing FLUSHDB on Client Object", "[Client][FLUSHDB]") } } } + log_data(context, LLDebug, "***End Client FLUSHDB testing***"); } SCENARIO("Testing CONFIG GET and CONFIG SET on Client Object", "[Client]") { std::cout << std::to_string(get_time_offset()) << ": Testing CONFIG GET and CONFIG SET on Client Object" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client config get/set testing***"); + GIVEN("A Client object") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); WHEN("CONFIG GET or CONFIG SET are called on databases with " "invalid addresses ") @@ -685,14 +709,18 @@ SCENARIO("Testing CONFIG GET and CONFIG SET on Client Object", "[Client]") } } } + log_data(context, LLDebug, "***End Client config get/set testing***"); } SCENARIO("Test CONFIG GET on an unsupported command", "[Client]") { std::cout << std::to_string(get_time_offset()) << ": Test CONFIG GET on an unsupported command" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client config get unsupported testing***"); + GIVEN("A client object") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); std::string address = parse_SSDB(std::getenv("SSDB")); WHEN("CONFIG GET is called with an unsupported command") @@ -706,14 +734,18 @@ SCENARIO("Test CONFIG GET on an unsupported command", "[Client]") } } } + log_data(context, LLDebug, "***End Client config get unsupported testing***"); } SCENARIO("Test CONFIG SET on an unsupported command", "[Client]") { std::cout << std::to_string(get_time_offset()) << ": Test CONFIG SET on an unsupported command" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client config set unsupported testing***"); + GIVEN("A client object") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); std::string address = parse_SSDB(std::getenv("SSDB")); WHEN("CONFIG SET is called with an unsupported command") @@ -727,14 +759,18 @@ SCENARIO("Test CONFIG SET on an unsupported command", "[Client]") } } } + log_data(context, LLDebug, "***End Client config get unsupported testing***"); } SCENARIO("Testing SAVE command on Client Object", "[!mayfail][Client][SAVE]") { std::cout << std::to_string(get_time_offset()) << ": Testing SAVE command on Client Object" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client SAVE unsupported testing***"); + GIVEN("A client object and some data") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); std::string dataset_name = "test_save_dataset"; DataSet dataset(dataset_name); dataset.add_meta_string("meta_string_save_name", "meta_string_val"); @@ -767,11 +803,15 @@ SCENARIO("Testing SAVE command on Client Object", "[!mayfail][Client][SAVE]") } } } + log_data(context, LLDebug, "***End Client SAVE unsupported testing***"); } SCENARIO("Test that prefixing covers all hash slots of a cluster", "[Client]") { std::cout << std::to_string(get_time_offset()) << ": Test that prefixing covers all hash slots of a cluster" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client prefix coverage testing***"); + if(use_cluster()==false) return; @@ -804,16 +844,19 @@ SCENARIO("Test that prefixing covers all hash slots of a cluster", "[Client]") RuntimeException); } } - } + log_data(context, LLDebug, "***End Client prefix coverage testing***"); } SCENARIO("Testing Multi-GPU Function error cases", "[Client]") { std::cout << std::to_string(get_time_offset()) << ": Testing Multi-GPU Function error cases" << std::endl; + std::string context("test_client"); + log_data(context, LLDebug, "***Beginning Client multigpu error testing***"); + GIVEN("A Client object, a script, and a model") { - Client client(use_cluster()); + Client client(use_cluster(), "test_client"); std::string model_key = "a_model"; std::string model_file = "./../../mnist_data/mnist_cnn.pt"; std::string script_key = "a_script"; @@ -953,4 +996,5 @@ SCENARIO("Testing Multi-GPU Function error cases", "[Client]") } } } + log_data(context, LLDebug, "***End Client multigpu error testing***"); } diff --git a/tests/cpp/unit-tests/test_client_ensemble.cpp b/tests/cpp/unit-tests/test_client_ensemble.cpp index 5da09beeb..d1968f4ed 100644 --- a/tests/cpp/unit-tests/test_client_ensemble.cpp +++ b/tests/cpp/unit-tests/test_client_ensemble.cpp @@ -29,6 +29,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "client.h" #include "dataset.h" +#include "logger.h" #include "../client_test_utils.h" unsigned long get_time_offset(); @@ -40,25 +41,13 @@ using namespace SmartRedis; void reset_env_vars(const char* old_keyin, const char* old_keyout) { if (old_keyin != nullptr) { - std::string reset_keyin = - std::string("SSKEYIN=") + std::string(old_keyin); - char* reset_keyin_c = new char[reset_keyin.size() + 1]; - std::copy(reset_keyin.begin(), reset_keyin.end(), reset_keyin_c); - reset_keyin_c[reset_keyin.size()] = '\0'; - putenv( reset_keyin_c); - delete [] reset_keyin_c; + setenv("SSKEYIN", old_keyin, 1); } else { unsetenv("SSKEYIN"); } if (old_keyout != nullptr) { - std::string reset_keyout = - std::string("SSKEYOUT=") + std::string(old_keyout); - char* reset_keyout_c = new char[reset_keyout.size() + 1]; - std::copy(reset_keyout.begin(), reset_keyout.end(), reset_keyout_c); - reset_keyout_c[reset_keyout.size()] = '\0'; - putenv( reset_keyout_c); - delete [] reset_keyout_c; + setenv("SSKEYOUT", old_keyout, 1); } else { unsetenv("SSKEYOUT"); @@ -90,14 +79,17 @@ void load_mnist_image_to_array(float**** img) SCENARIO("Testing Client ensemble using a producer/consumer paradigm") { std::cout << std::to_string(get_time_offset()) << ": Testing Client ensemble using a producer/consumer paradigm" << std::endl; + std::string context("test_client_ensemble"); + log_data(context, LLDebug, "***Beginning Client Ensemble testing***"); + GIVEN("Variables that will be used by the producer and consumer") { const char* old_keyin = std::getenv("SSKEYIN"); const char* old_keyout = std::getenv("SSKEYOUT"); - char keyin_env_put[] = "SSKEYIN=producer_0,producer_1"; - char keyout_env_put[] = "SSKEYOUT=producer_0"; - char keyin_env_get[] = "SSKEYIN=producer_1,producer_0"; - char keyout_env_get[] = "SSKEYOUT=producer_1"; + char keyin_env_put[] = "producer_0,producer_1"; + char keyout_env_put[] = "producer_0"; + char keyin_env_get[] = "producer_1,producer_0"; + char keyout_env_get[] = "producer_1"; size_t dim1 = 10; std::vector dims = {dim1}; std::string producer_keyout = "producer_0"; @@ -133,11 +125,13 @@ SCENARIO("Testing Client ensemble using a producer/consumer paradigm") THEN("The Client ensemble can be tested with " "a producer/consumer relationship") { + //////////////////////////////////////////////////////////// // do producer stuff - putenv(keyin_env_put); - putenv(keyout_env_put); + log_data(context, LLDebug, "***Beginning producer operations***"); + setenv("SSKEYIN", keyin_env_put, (old_keyin != NULL)); + setenv("SSKEYOUT", keyout_env_put, (old_keyout != NULL)); - Client producer_client(use_cluster()); + Client producer_client(use_cluster(), "test_client_ensemble::producer"); producer_client.use_model_ensemble_prefix(true); // Tensors @@ -182,13 +176,15 @@ SCENARIO("Testing Client ensemble using a producer/consumer paradigm") producer_client.run_model(model_name, {script_out_key_ds}, {out_key_ds}); free_4D_array(mnist_array, 1, 1, 28); + log_data(context, LLDebug, "***End producer operations***"); - + //////////////////////////////////////////////////////////// // do consumer stuff - putenv(keyin_env_get); - putenv(keyout_env_get); + log_data(context, LLDebug, "***Beginning consumer operations***"); + setenv("SSKEYIN", keyin_env_get, 1); + setenv("SSKEYOUT", keyout_env_get, 1); - Client consumer_client(use_cluster()); + Client consumer_client(use_cluster(), "test_client_ensemble::consumer"); consumer_client.use_model_ensemble_prefix(true); // Tensors @@ -251,6 +247,8 @@ SCENARIO("Testing Client ensemble using a producer/consumer paradigm") // reset environment variables to their original state reset_env_vars(old_keyin, old_keyout); + log_data(context, LLDebug, "***End consumer operations***"); } } + log_data(context, LLDebug, "***End Client Ensemble testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_clusterinfocommand.cpp b/tests/cpp/unit-tests/test_clusterinfocommand.cpp index 565b37b0a..69c22ff0b 100644 --- a/tests/cpp/unit-tests/test_clusterinfocommand.cpp +++ b/tests/cpp/unit-tests/test_clusterinfocommand.cpp @@ -28,6 +28,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "clusterinfocommand.h" +#include "logger.h" unsigned long get_time_offset(); @@ -36,6 +37,9 @@ using namespace SmartRedis; SCENARIO("Parsing an empty string for cluster info") { std::cout << std::to_string(get_time_offset()) << ": Parsing an empty string for cluster info" << std::endl; + std::string context("test_clusterinfocommand"); + log_data(context, LLDebug, "***Beginning ClusterInfoCommand testing***"); + GIVEN("A ClusterInfoCommand and an empty string") { ClusterInfoCommand cmd; @@ -49,4 +53,5 @@ SCENARIO("Parsing an empty string for cluster info") } } } + log_data(context, LLDebug, "***End ClusterInfoCommand testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_commandlist.cpp b/tests/cpp/unit-tests/test_commandlist.cpp index 9d785c3ff..84892f27f 100644 --- a/tests/cpp/unit-tests/test_commandlist.cpp +++ b/tests/cpp/unit-tests/test_commandlist.cpp @@ -34,6 +34,7 @@ #include "addressatcommand.h" #include "addressanycommand.h" #include "client.h" +#include "logger.h" unsigned long get_time_offset(); @@ -42,6 +43,8 @@ using namespace SmartRedis; SCENARIO("Testing CommandList object", "[CommandList]") { std::cout << std::to_string(get_time_offset()) << ": Testing CommandList objectinfo" << std::endl; + std::string context("test_commandlist"); + log_data(context, LLDebug, "***Beginning CommandList testing***"); GIVEN("A CommandList object") { CommandList cmd_lst; @@ -285,11 +288,15 @@ SCENARIO("Testing CommandList object", "[CommandList]") } } } + log_data(context, LLDebug, "***End CommandList testing***"); } SCENARIO("Testing CommandList object on heap", "[CommandList]") { std::cout << std::to_string(get_time_offset()) << ": Testing CommandList object on heap" << std::endl; + std::string context("test_commandlist"); + log_data(context, LLDebug, "***Beginning CommandList heap testing***"); + GIVEN("A CommandList object on the heap with three Commands") { CommandList* cmd_lst = new CommandList; @@ -334,4 +341,5 @@ SCENARIO("Testing CommandList object on heap", "[CommandList]") } } } + log_data(context, LLDebug, "***End CommandList heap testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_commandreply.cpp b/tests/cpp/unit-tests/test_commandreply.cpp index 7bc932c2d..b7e4c3c63 100644 --- a/tests/cpp/unit-tests/test_commandreply.cpp +++ b/tests/cpp/unit-tests/test_commandreply.cpp @@ -29,6 +29,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "commandreply.h" #include "srexception.h" +#include "logger.h" unsigned long get_time_offset(); @@ -104,6 +105,9 @@ void fill_reply_array(redisReply*& reply, int num_of_children) SCENARIO("Testing CommandReply object", "[CommandReply]") { std::cout << std::to_string(get_time_offset()) << ": Testing CommandReply object" << std::endl; + std::string context("test_commandreply"); + log_data(context, LLDebug, "***Beginning CommandReply object testing***"); + GIVEN("A CommandReply object with type REDIS_REPLY_INTEGER") { redisReply* reply = new redisReply; @@ -159,12 +163,16 @@ SCENARIO("Testing CommandReply object", "[CommandReply]") CHECK_THROWS_AS(cmd_reply.redis_reply_type(), RuntimeException); } } + log_data(context, LLDebug, "***End CommandReply object testing***"); } SCENARIO("Test CommandReply copy assignment operator and copy " "constructor on simple REDIS_REPLY_TYPES", "[CommandReply]") { std::cout << std::to_string(get_time_offset()) << ": Test CommandReply copy assignment operator and copy" << std::endl; + std::string context("test_commandreply"); + log_data(context, LLDebug, "***Beginning CommandReply copy testing***"); + GIVEN("A CommandReply") { redisReply* reply = new redisReply; @@ -205,11 +213,15 @@ SCENARIO("Test CommandReply copy assignment operator and copy " } } } + log_data(context, LLDebug, "***End CommandReply copy testing***"); } SCENARIO("Test CommandReply::has_error", "[CommandReply]") { std::cout << std::to_string(get_time_offset()) << ": Test CommandReply::has_error" << std::endl; + std::string context("test_commandreply"); + log_data(context, LLDebug, "***Beginning CommandReply has_error testing***"); + GIVEN("A parent and child redisReply") { char const* str = "ERR"; @@ -230,13 +242,16 @@ SCENARIO("Test CommandReply::has_error", "[CommandReply]") } } } - + log_data(context, LLDebug, "***End CommandReply has_error testing***"); } SCENARIO("CommandReply copy assignment operator preserves the state of the " "rvalue and the lvalue when one of the objects are deleted", "[CommandReply]") { std::cout << std::to_string(get_time_offset()) << ": CommandReply copy assignment operator preserves the state of the" << std::endl; + std::string context("test_commandreply"); + log_data(context, LLDebug, "***Beginning CommandReply delete testing***"); + GIVEN("Two dynamically allocated CommandReply. One with a complex " "redisReply, and the other with a simple redisReply") { @@ -320,11 +335,15 @@ SCENARIO("CommandReply copy assignment operator preserves the state of the " } } } + log_data(context, LLDebug, "***End CommandReply delete testing***"); } SCENARIO("Simple tests on CommandReply constructors that use redisReply*", "[CommandReply]") { std::cout << std::to_string(get_time_offset()) << ": Simple tests on CommandReply constructors that use redisReply*" << std::endl; + std::string context("test_commandreply"); + log_data(context, LLDebug, "***Beginning CommandReply redisReply testing***"); + GIVEN("A redisReply") { char const* str = "100.0"; @@ -366,11 +385,15 @@ SCENARIO("Simple tests on CommandReply constructors that use redisReply*", "[Com } } } + log_data(context, LLDebug, "***End CommandReply redisReply testing***"); } SCENARIO("Test CommandReply copy constructor with an inconsistent redisReply", "[CommandReply]") { std::cout << std::to_string(get_time_offset()) << ": Test CommandReply copy constructor with an inconsistent redisReply" << std::endl; + std::string context("test_commandreply"); + log_data(context, LLDebug, "***Beginning CommandReply inconsistent redisReply testing***"); + GIVEN("An inconsistent redisReply where its 'elements' doesn't "\ "correspond to its 'element'") { @@ -392,11 +415,14 @@ SCENARIO("Test CommandReply copy constructor with an inconsistent redisReply", " } } } + log_data(context, LLDebug, "***End CommandReply inconsistent redisReply testing***"); } SCENARIO("Test CommandReply's redisReply deep copy on a shallow copy", "[CommandReply]") { std::cout << std::to_string(get_time_offset()) << ": Test CommandReply's redisReply deep copy on a shallow copy" << std::endl; + std::string context("test_commandreply"); + log_data(context, LLDebug, "***Beginning CommandReply deep copy testing***"); GIVEN("A CommandReply with redisReply type REDIS_REPLY_ARRAY") { char const* strs[] = {"zero", "one"}; @@ -431,11 +457,15 @@ SCENARIO("Test CommandReply's redisReply deep copy on a shallow copy", "[Command } } } + log_data(context, LLDebug, "***End CommandReply deep copy testing***"); } SCENARIO("Test CommandReply string retrieval for non REDIS_REPLY_STRING", "[CommandReply]") { std::cout << std::to_string(get_time_offset()) << ": Test CommandReply string retrieval for non REDIS_REPLY_STRING" << std::endl; + std::string context("test_commandreply"); + log_data(context, LLDebug, "***Beginning CommandReply string retrieval testing***"); + char const* strs[] = {"OK", "42.5", "99999999999999999999", "Verbatim string"}; int lens[] = {3, 5, 21, 16}; int types[] = {REDIS_REPLY_STATUS, REDIS_REPLY_DOUBLE, REDIS_REPLY_BIGNUM, REDIS_REPLY_VERB}; @@ -469,12 +499,16 @@ SCENARIO("Test CommandReply string retrieval for non REDIS_REPLY_STRING", "[Comm } delete cmd_reply; } + log_data(context, LLDebug, "***End CommandReply string retrieval testing***"); } SCENARIO("Test REDIS_REPLY_ERROR retrieval from a CommandReply", "[CommandReply]") { std::cout << std::to_string(get_time_offset()) << ": Test REDIS_REPLY_ERROR retrieval from a CommandReply" << std::endl; + std::string context("test_commandreply"); + log_data(context, LLDebug, "***Beginning CommandReply REDIS_REPLY_ERROR retrieval testing***"); + /* CommanReply (ARRAY) LEVEL 0 / | \ @@ -517,4 +551,5 @@ SCENARIO("Test REDIS_REPLY_ERROR retrieval from a CommandReply", "[CommandReply] } } } + log_data(context, LLDebug, "***End CommandReply REDIS_REPLY_ERROR retrieval testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_compoundcommand.cpp b/tests/cpp/unit-tests/test_compoundcommand.cpp index cbc5d417f..72d5d29bd 100644 --- a/tests/cpp/unit-tests/test_compoundcommand.cpp +++ b/tests/cpp/unit-tests/test_compoundcommand.cpp @@ -28,6 +28,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "compoundcommand.h" +#include "logger.h" unsigned long get_time_offset(); @@ -36,6 +37,9 @@ using namespace SmartRedis; SCENARIO("Testing copy constructor and deep copy operator for CompoundCommand", "[CompoundCommand]") { std::cout << std::to_string(get_time_offset()) << ": Testing copy constructor and deep copy operator for CompoundCommand" << std::endl; + std::string context("test_compoundcommand"); + log_data(context, LLDebug, "***Beginning CompoundCommand testing***"); + GIVEN("A CompoundCommand object") { CompoundCommand cmd; @@ -133,4 +137,5 @@ SCENARIO("Testing copy constructor and deep copy operator for CompoundCommand", } } } + log_data(context, LLDebug, "***End CompoundCommand testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_dataset.cpp b/tests/cpp/unit-tests/test_dataset.cpp index e4e16c209..cbfa9d293 100644 --- a/tests/cpp/unit-tests/test_dataset.cpp +++ b/tests/cpp/unit-tests/test_dataset.cpp @@ -30,20 +30,22 @@ #include "dataset.h" #include "srexception.h" #include +#include "logger.h" unsigned long get_time_offset(); using namespace SmartRedis; const char *currentExceptionTypeName() { - int status; -// return abi::__cxa_demangle(abi::__cxa_current_exception_type()->name(), 0, 0, &status); return abi::__cxa_current_exception_type()->name(); } SCENARIO("Testing DataSet object", "[DataSet]") { std::cout << std::to_string(get_time_offset()) << ": Testing DataSet object" << std::endl; + std::string context("test_dataset"); + log_data(context, LLDebug, "***Beginning DataSet testing***"); + GIVEN("A DataSet object") { std::string dataset_name; @@ -208,4 +210,5 @@ SCENARIO("Testing DataSet object", "[DataSet]") } } } + log_data(context, LLDebug, "***End DataSet testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_dbinfocommand.cpp b/tests/cpp/unit-tests/test_dbinfocommand.cpp index 736fcd6ab..1b57b73ea 100644 --- a/tests/cpp/unit-tests/test_dbinfocommand.cpp +++ b/tests/cpp/unit-tests/test_dbinfocommand.cpp @@ -28,6 +28,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "dbinfocommand.h" +#include "logger.h" unsigned long get_time_offset(); @@ -36,6 +37,9 @@ using namespace SmartRedis; SCENARIO("Parsing an empty string for db info") { std::cout << std::to_string(get_time_offset()) << ": Parsing an empty string for db info" << std::endl; + std::string context("test_dbinfocommand"); + log_data(context, LLDebug, "***Beginning DBInfoCommand testing***"); + GIVEN("A DBInfoCommand and an empty string") { DBInfoCommand cmd; @@ -49,4 +53,5 @@ SCENARIO("Parsing an empty string for db info") } } } + log_data(context, LLDebug, "***End DBInfoCommand testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_dbnode.cpp b/tests/cpp/unit-tests/test_dbnode.cpp index c78093c99..f0970805e 100644 --- a/tests/cpp/unit-tests/test_dbnode.cpp +++ b/tests/cpp/unit-tests/test_dbnode.cpp @@ -29,6 +29,7 @@ #include #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "dbnode.h" +#include "logger.h" unsigned long get_time_offset(); @@ -37,6 +38,9 @@ using namespace SmartRedis; SCENARIO("Testing DBNode object", "[DBNode]") { std::cout << std::to_string(get_time_offset()) << ": Testing DBNode object" << std::endl; + std::string context("test_dbnode"); + log_data(context, LLDebug, "***Beginning DBNode testing***"); + GIVEN("Two DBNode objects created with the default contructor") { DBNode node_1; @@ -92,4 +96,5 @@ SCENARIO("Testing DBNode object", "[DBNode]") CHECK(node_1 < node_2); } } + log_data(context, LLDebug, "***End DBNode testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_logger.cpp b/tests/cpp/unit-tests/test_logger.cpp new file mode 100644 index 000000000..d47b2719b --- /dev/null +++ b/tests/cpp/unit-tests/test_logger.cpp @@ -0,0 +1,63 @@ +/* + * BSD 2-Clause License + * + * Copyright (c) 2021-2022, Hewlett Packard Enterprise + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../../../third-party/catch/single_include/catch2/catch.hpp" +#include "../client_test_utils.h" +#include "redis.h" +#include "client.h" +#include "address.h" +#include "logger.h" + +unsigned long get_time_offset(); + +using namespace SmartRedis; + +SCENARIO("Additional Testing for logging", "[LOG]") +{ + std::cout << std::to_string(get_time_offset()) << ": Additional Testing for logging" << std::endl; + std::string context("test_logger"); + log_data(context, LLDebug, "***Beginning Logger testing***"); + + GIVEN("A Client object") + { + Client client(use_cluster(), "test_logger"); + + THEN("Logging should be able to be done") + { + // log_data() + log_data(context, LLInfo, "This is data logged at the Info level"); + + // log_warning() + log_warning(context, LLInfo, "This is a warning logged at the Info level"); + + // log_error() + log_error(context, LLInfo, "This is an error logged at the Info level"); + } + } + log_data(context, LLDebug, "***End Logger testing***"); +} \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_metadata.cpp b/tests/cpp/unit-tests/test_metadata.cpp index 8b710b133..b21531ab3 100644 --- a/tests/cpp/unit-tests/test_metadata.cpp +++ b/tests/cpp/unit-tests/test_metadata.cpp @@ -29,6 +29,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "metadata.h" #include "srexception.h" +#include "logger.h" unsigned long get_time_offset(); @@ -131,6 +132,8 @@ void check_metadata_copied_correctly(MetaData metadata, MetaData metadata_cpy) SCENARIO("Test MetaData", "[MetaData]") { std::cout << std::to_string(get_time_offset()) << ": Test MetaData" << std::endl; + std::string context("test_metadata"); + log_data(context, LLDebug, "***Beginning Metadata testing***"); GIVEN("A MetaData object") { MetaData metadata; @@ -357,4 +360,5 @@ SCENARIO("Test MetaData", "[MetaData]") } } } + log_data(context, LLDebug, "***End DBNode testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_multikeycommand.cpp b/tests/cpp/unit-tests/test_multikeycommand.cpp index 7d0d15e45..051ca87b9 100644 --- a/tests/cpp/unit-tests/test_multikeycommand.cpp +++ b/tests/cpp/unit-tests/test_multikeycommand.cpp @@ -28,6 +28,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "multikeycommand.h" +#include "logger.h" unsigned long get_time_offset(); @@ -36,6 +37,9 @@ using namespace SmartRedis; SCENARIO("Adding fields of different types", "[MultiKeyCommand]") { std::cout << std::to_string(get_time_offset()) << ": Adding fields of different types" << std::endl; + std::string context("test_multikeycommand"); + log_data(context, LLDebug, "***Beginning MultiKeyCommand testing***"); + GIVEN("A MultiKeyCommand object") { MultiKeyCommand cmd; @@ -74,4 +78,5 @@ SCENARIO("Adding fields of different types", "[MultiKeyCommand]") } } } + log_data(context, LLDebug, "***End MultiKeyCommand testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_redisserver.cpp b/tests/cpp/unit-tests/test_redisserver.cpp index 1b269a5e0..9b5230e14 100644 --- a/tests/cpp/unit-tests/test_redisserver.cpp +++ b/tests/cpp/unit-tests/test_redisserver.cpp @@ -35,6 +35,8 @@ #include "rediscluster.h" #include "redis.h" #include "srexception.h" +#include "logger.h" +#include "client.h" unsigned long get_time_offset(); @@ -54,6 +56,7 @@ using namespace SmartRedis; class RedisTest : public Redis { public: + RedisTest(Client* c) : Redis(c) {} int get_connection_timeout() {return _connection_timeout;} int get_connection_interval() {return _connection_interval;} int get_command_timeout() {return _command_timeout;} @@ -69,6 +72,7 @@ class RedisTest : public Redis class RedisClusterTest : public RedisCluster { public: + RedisClusterTest(Client* c) : RedisCluster(c) {} int get_connection_timeout() {return _connection_timeout;} int get_connection_interval() {return _connection_interval;} int get_command_timeout() {return _command_timeout;} @@ -93,10 +97,10 @@ const char* CMD_INTERVAL_ENV_VAR = "SR_CMD_INTERVAL"; void invoke_constructor() { if (use_cluster()) { - RedisClusterTest cluster_obj; + RedisClusterTest cluster_obj(NULL); } else { - RedisTest non_cluster_obj; + RedisTest non_cluster_obj(NULL); } } @@ -110,6 +114,44 @@ void unset_all_env_vars() unsetenv(CMD_INTERVAL_ENV_VAR); } +// Helper function to retrieve original versions of environment vars +void save_env_vars( + char** conn_timeout, + char** conn_interval, + char** cmd_timeout, + char** cmd_interval) +{ + *conn_timeout = getenv(CONN_TIMEOUT_ENV_VAR); + *conn_interval = getenv(CONN_INTERVAL_ENV_VAR); + *cmd_timeout = getenv(CMD_TIMEOUT_ENV_VAR); + *cmd_interval = getenv(CMD_INTERVAL_ENV_VAR); +} + +// Helper function to restore environment vars +void restore_env_vars( + char* conn_timeout, + char* conn_interval, + char* cmd_timeout, + char* cmd_interval) +{ + if (conn_timeout != NULL) + setenv(CONN_TIMEOUT_ENV_VAR, conn_timeout, 1); + else + unsetenv(CONN_TIMEOUT_ENV_VAR); + if (conn_interval != NULL) + setenv(CONN_INTERVAL_ENV_VAR, conn_interval, 1); + else + unsetenv(CONN_INTERVAL_ENV_VAR); + if (cmd_timeout != NULL) + setenv(CMD_TIMEOUT_ENV_VAR, cmd_timeout, 1); + else + unsetenv(CMD_TIMEOUT_ENV_VAR); + if (cmd_interval != NULL) + setenv(CMD_INTERVAL_ENV_VAR, cmd_interval, 1); + else + unsetenv(CMD_INTERVAL_ENV_VAR); +} + // Helper function to check that all default values being used template void check_all_defaults(T& server) @@ -130,18 +172,27 @@ void check_all_defaults(T& server) SCENARIO("Test runtime settings are initialized correctly", "[RedisServer]") { std::cout << std::to_string(get_time_offset()) << ": Test runtime settings are initialized correctly" << std::endl; + std::string context("test_redisserver"); + log_data(context, LLDebug, "***Beginning RedisServer testing***"); + + char* __conn_timeout; + char* __conn_interval; + char* __cmd_timeout; + char* __cmd_interval; + save_env_vars(&__conn_timeout, &__conn_interval, &__cmd_timeout, &__cmd_interval); + GIVEN("A Redis derived object created with all environment variables unset") { unset_all_env_vars(); if (use_cluster()) { - RedisClusterTest redis_server; + RedisClusterTest redis_server(NULL); THEN("Default member variable values are used") { check_all_defaults(redis_server); } } else { - RedisTest redis_server; + RedisTest redis_server(NULL); THEN("Default member variable values are used") { check_all_defaults(redis_server); @@ -157,14 +208,14 @@ SCENARIO("Test runtime settings are initialized correctly", "[RedisServer]") setenv(CMD_INTERVAL_ENV_VAR, "", true); if (use_cluster()) { - RedisClusterTest redis_server; + RedisClusterTest redis_server(NULL); THEN("Default member variable values are used") { check_all_defaults(redis_server); } } else { - RedisTest redis_server; + RedisTest redis_server(NULL); THEN("Default member variable values are used") { check_all_defaults(redis_server); @@ -187,7 +238,7 @@ SCENARIO("Test runtime settings are initialized correctly", "[RedisServer]") setenv(CMD_INTERVAL_ENV_VAR, std::to_string(cmd_interval).c_str(), true); if (use_cluster()) { - RedisClusterTest redis_server; + RedisClusterTest redis_server(NULL); THEN("Environment variables are used for member variables") { CHECK(redis_server.get_connection_timeout() == @@ -206,7 +257,7 @@ SCENARIO("Test runtime settings are initialized correctly", "[RedisServer]") } } else { - RedisTest redis_server; + RedisTest redis_server(NULL); THEN("Environment variables are used for member variables") { CHECK(redis_server.get_connection_timeout() == @@ -305,4 +356,6 @@ SCENARIO("Test runtime settings are initialized correctly", "[RedisServer]") CHECK_THROWS_AS(invoke_constructor(), ParameterException); } } + restore_env_vars(__conn_timeout, __conn_interval, __cmd_timeout, __cmd_interval); + log_data(context, LLDebug, "***End RedisServer testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_singlekeycommand.cpp b/tests/cpp/unit-tests/test_singlekeycommand.cpp index 98738936a..ed8c0f9d5 100644 --- a/tests/cpp/unit-tests/test_singlekeycommand.cpp +++ b/tests/cpp/unit-tests/test_singlekeycommand.cpp @@ -29,6 +29,7 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "singlekeycommand.h" #include "srexception.h" +#include "logger.h" unsigned long get_time_offset(); @@ -37,6 +38,9 @@ using namespace SmartRedis; SCENARIO("Retrieve field to empty SingleKeyCommand", "[SingleKeyCommand]") { std::cout << std::to_string(get_time_offset()) << ": Retrieve field to empty SingleKeyCommand" << std::endl; + std::string context("test_singlekeycommand"); + log_data(context, LLDebug, "***Beginning SingleKeyCommand empty testing***"); + GIVEN("An empty SingleKeyCommand object") { SingleKeyCommand cmd; @@ -50,12 +54,15 @@ SCENARIO("Retrieve field to empty SingleKeyCommand", "[SingleKeyCommand]") } } } - + log_data(context, LLDebug, "***End SingleKeyCommand empty testing***"); } SCENARIO("Testing copy constructor for SingleKeyCommand on heap", "[SingleKeyCommand]") { std::cout << std::to_string(get_time_offset()) << ": Testing copy constructor for SingleKeyCommand on heap" << std::endl; + std::string context("test_singlekeycommand"); + log_data(context, LLDebug, "***Beginning SingleKeyCommand copy testing***"); + GIVEN("A SingleKeyCommand object on the heap") { SingleKeyCommand* cmd = new SingleKeyCommand; @@ -120,4 +127,5 @@ SCENARIO("Testing copy constructor for SingleKeyCommand on heap", "[SingleKeyCom delete cmd_cpy; } } + log_data(context, LLDebug, "***End SingleKeyCommand copy testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_ssdb.cpp b/tests/cpp/unit-tests/test_ssdb.cpp index 4ce96d05a..abfa8a356 100644 --- a/tests/cpp/unit-tests/test_ssdb.cpp +++ b/tests/cpp/unit-tests/test_ssdb.cpp @@ -30,6 +30,7 @@ #include "redis.h" #include "client.h" #include "address.h" +#include "logger.h" unsigned long get_time_offset(); @@ -39,7 +40,7 @@ using namespace SmartRedis; class TestSSDB : public Redis { public: - TestSSDB() : Redis() {} + TestSSDB() : Redis(NULL) {} SRAddress get_ssdb() { @@ -59,13 +60,16 @@ void setenv_ssdb(const char* ssdb) SCENARIO("Additional Testing for various SSDBs", "[SSDB]") { std::cout << std::to_string(get_time_offset()) << ": Additional Testing for various SSDBs" << std::endl; + std::string context("test_ssdb"); + log_data(context, LLDebug, "***Beginning SSDB testing***"); + GIVEN("A TestSSDB object") { const char* old_ssdb = std::getenv("SSDB"); - INFO("SSDB must be set to a valid host and "\ - "port before running this test."); - REQUIRE(old_ssdb != NULL); + INFO("SSDB must be set to a valid host and "\ + "port before running this test."); + REQUIRE(old_ssdb != NULL); TestSSDB test_ssdb; Client* c = NULL; @@ -87,9 +91,10 @@ SCENARIO("Additional Testing for various SSDBs", "[SSDB]") // SSDB points to a unix domain socket and we're using clustered Redis setenv_ssdb ("unix://127.0.0.1:6349"); - CHECK_THROWS_AS(c = new Client(true), SmartRedis::RuntimeException); + CHECK_THROWS_AS(c = new Client(true, "test_ssdb"), SmartRedis::RuntimeException); setenv_ssdb(old_ssdb); } } + log_data(context, LLDebug, "***End SSDB testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_stringfield.cpp b/tests/cpp/unit-tests/test_stringfield.cpp index 6ba3c9766..5467ab3e8 100644 --- a/tests/cpp/unit-tests/test_stringfield.cpp +++ b/tests/cpp/unit-tests/test_stringfield.cpp @@ -28,12 +28,16 @@ #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "stringfield.h" +#include "logger.h" unsigned long get_time_offset(); SCENARIO("Test StringField", "[StringField]") { std::cout << std::to_string(get_time_offset()) << ": Test StringField" << std::endl; + std::string context("test_stringfield"); + log_data(context, LLDebug, "***Beginning StringField testing***"); + GIVEN("A StringField object constructed with the string field name") { std::string name_1 = "stringfield_name_1"; @@ -95,4 +99,5 @@ SCENARIO("Test StringField", "[StringField]") } // TODO: Test serializing the StringField } + log_data(context, LLDebug, "***End StringField testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_tensor.cpp b/tests/cpp/unit-tests/test_tensor.cpp index 19f7749be..4446c8363 100644 --- a/tests/cpp/unit-tests/test_tensor.cpp +++ b/tests/cpp/unit-tests/test_tensor.cpp @@ -29,6 +29,7 @@ #include #include "../../../third-party/catch/single_include/catch2/catch.hpp" #include "tensor.h" +#include "logger.h" unsigned long get_time_offset(); @@ -37,6 +38,9 @@ using namespace SmartRedis; SCENARIO("Testing Tensor", "[Tensor]") { std::cout << std::to_string(get_time_offset()) << ": Testing Tensor" << std::endl; + std::string context("test_tensor"); + log_data(context, LLDebug, "***Beginning Tensor testing***"); + GIVEN("Two Tensors") { // Create first tensor @@ -135,4 +139,5 @@ SCENARIO("Testing Tensor", "[Tensor]") } } } + log_data(context, LLDebug, "***End Tensor testing***"); } \ No newline at end of file diff --git a/tests/cpp/unit-tests/test_tensorbase.cpp b/tests/cpp/unit-tests/test_tensorbase.cpp index 411f5f503..5671dbfbe 100644 --- a/tests/cpp/unit-tests/test_tensorbase.cpp +++ b/tests/cpp/unit-tests/test_tensorbase.cpp @@ -39,6 +39,9 @@ using namespace SmartRedis; SCENARIO("Testing TensorBase through TensorPack", "[TensorBase]") { std::cout << std::to_string(get_time_offset()) << ": Testing TensorBase through TensorPack" << std::endl; + std::string context("test_tensorbase"); + log_data(context, LLDebug, "***Beginning TensorBase testing***"); + SRTensorType tensor_type = GENERATE(SRTensorTypeDouble, SRTensorTypeFloat, SRTensorTypeInt64, SRTensorTypeInt32, SRTensorTypeInt16, SRTensorTypeInt8, @@ -234,4 +237,5 @@ SCENARIO("Testing TensorBase through TensorPack", "[TensorBase]") } } } + log_data(context, LLDebug, "***End TensorBase testing***"); } \ No newline at end of file diff --git a/tests/docker/c/test_docker.c b/tests/docker/c/test_docker.c index 0083d092f..2f4b35085 100644 --- a/tests/docker/c/test_docker.c +++ b/tests/docker/c/test_docker.c @@ -36,9 +36,11 @@ int main(int argc, char* argv[]) { void* client = NULL; + const char* logger_name = "test_docker"; + size_t cid_len = strlen(logger_name); SRError return_code = SRNoError; - return_code = SmartRedisCClient(false, &client); + return_code = SmartRedisCClient(false, logger_name, cid_len, &client); if (return_code != SRNoError) { return -1; diff --git a/tests/docker/cpp/docker_test.cpp b/tests/docker/cpp/docker_test.cpp index a65b09740..7ab614467 100644 --- a/tests/docker/cpp/docker_test.cpp +++ b/tests/docker/cpp/docker_test.cpp @@ -30,7 +30,7 @@ int main(int argc, char* argv[]) { - SmartRedis::Client client(false); + SmartRedis::Client client(false, __FILE__); std::vector data = {1.0, 2.0, 3.0}; std::vector dims = {3}; diff --git a/tests/docker/fortran/test_docker.F90 b/tests/docker/fortran/test_docker.F90 index 7da5a689c..06baf3f28 100644 --- a/tests/docker/fortran/test_docker.F90 +++ b/tests/docker/fortran/test_docker.F90 @@ -38,7 +38,7 @@ program main real(kind=8), dimension(dim1) :: tensor real(kind=8), dimension(dim1) :: returned - result = client%initialize(.FALSE.) + result = client%initialize(.FALSE., "test_docker.F90") if (result .ne. SRNoError) stop call random_number(tensor) diff --git a/tests/fortran/CMakeLists.txt b/tests/fortran/CMakeLists.txt index 826af7e42..94a7a0aaa 100644 --- a/tests/fortran/CMakeLists.txt +++ b/tests/fortran/CMakeLists.txt @@ -69,6 +69,7 @@ list(APPEND EXECUTABLES client_test_put_get_2D client_test_put_get_3D client_test_put_get_unpack_dataset + client_test_logging client_test_errors ) diff --git a/tests/fortran/client_test_dataset.F90 b/tests/fortran/client_test_dataset.F90 index 9cb428188..b6c259c90 100644 --- a/tests/fortran/client_test_dataset.F90 +++ b/tests/fortran/client_test_dataset.F90 @@ -164,7 +164,7 @@ program main if (.not. all(meta_int64_recv == meta_int64_vec)) error stop 'meta_int64: FAILED' ! test dataset_existence - result = client%initialize(use_cluster()) + result = client%initialize(use_cluster(), "client_test_dataset") if (result .ne. SRNoError) error stop result = client%dataset_exists("nonexistent", exists) if (result .ne. SRNoError) error stop diff --git a/tests/fortran/client_test_dataset_aggregation.F90 b/tests/fortran/client_test_dataset_aggregation.F90 index 91be63cdb..38bf1bedb 100644 --- a/tests/fortran/client_test_dataset_aggregation.F90 +++ b/tests/fortran/client_test_dataset_aggregation.F90 @@ -51,7 +51,8 @@ program main character(len=12) :: dataset_name integer :: result - result = client%initialize(use_cluster()) + result = client%initialize(use_cluster(), & + "client_test_dataset_aggregation") if (result .ne. SRNoError) error stop call random_number(true_vectors) diff --git a/tests/fortran/client_test_ensemble.F90 b/tests/fortran/client_test_ensemble.F90 index 70c09a47e..290682c99 100644 --- a/tests/fortran/client_test_ensemble.F90 +++ b/tests/fortran/client_test_ensemble.F90 @@ -49,7 +49,7 @@ program main call setenv("SSKEYIN", "producer_0,producer_1") call setenv("SSKEYOUT", ensemble_keyout) -result = client%initialize(use_cluster()) +result = client%initialize(use_cluster(), "client_test_ensemble") if (result .ne. SRNoError) error stop result = client%use_model_ensemble_prefix(.true.) if (result .ne. SRNoError) error stop @@ -103,7 +103,7 @@ program main call setenv("SSKEYIN", "producer_1,producer_0") call setenv("SSKEYOUT", ensemble_keyout) -result = client%initialize(use_cluster()) +result = client%initialize(use_cluster(), "client_test_ensemble") if (result .ne. SRNoError) error stop result = client%use_model_ensemble_prefix(.true.) if (result .ne. SRNoError) error stop diff --git a/tests/fortran/client_test_initialized.F90 b/tests/fortran/client_test_initialized.F90 index 48e1d8124..caec3be22 100644 --- a/tests/fortran/client_test_initialized.F90 +++ b/tests/fortran/client_test_initialized.F90 @@ -40,7 +40,7 @@ program main if (client%isinitialized()) error stop 'client not initialized' - result = client%initialize(use_cluster()) + result = client%initialize(use_cluster(), "client_test_initialized") if (result .ne. SRNoError) error stop if (.not. client%isinitialized()) error stop 'client is initialized' diff --git a/tests/fortran/client_test_logging.F90 b/tests/fortran/client_test_logging.F90 new file mode 100644 index 000000000..7ce1e3903 --- /dev/null +++ b/tests/fortran/client_test_logging.F90 @@ -0,0 +1,142 @@ +! BSD 2-Clause License +! +! Copyright (c) 2021-2022, Hewlett Packard Enterprise +! All rights reserved. +! +! Redistribution and use in source and binary forms, with or without +! modification, are permitted provided that the following conditions are met: +! +! 1. Redistributions of source code must retain the above copyright notice, this +! list of conditions and the following disclaimer. +! +! 2. Redistributions in binary form must reproduce the above copyright notice, +! this list of conditions and the following disclaimer in the documentation +! and/or other materials provided with the distribution. +! +! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +program main + use smartredis_client, only : client_type + use smartredis_dataset, only : dataset_type + use smartredis_logcontext, only : logcontext_type + use smartredis_logger, only : log_data, log_warning, log_error + use test_utils, only : use_cluster + use iso_fortran_env, only : STDERR => error_unit + use iso_c_binding, only : c_ptr, c_bool, c_null_ptr, c_char, c_int + use iso_c_binding, only : c_int8_t, c_int16_t, c_int32_t, c_int64_t, c_float, c_double, c_size_t + + implicit none + +#include "enum_fortran.inc" + + type(client_type) :: client + type(dataset_type) :: dataset + type(logcontext_type) :: logcontext + integer :: result + + result = logcontext%initialize("client_test_logging (logcontext)") + if (result .ne. SRNoError) error stop + result = client%initialize(use_cluster(), "client_test_logging (client)") + if (result .ne. SRNoError) error stop + result = dataset%initialize("client_test_logging (dataset)") + if (result .ne. SRNoError) error stop + + ! Logging against the Client + ! ========================== + call log_data(client, LLQuiet, & + "This is data logged against the Client at the Quiet level") + call log_data(client, LLInfo, & + "This is data logged against the Client at the Info level") + call log_data(client, LLDebug, & + "This is data logged against the Client at the Debug level") + call log_data(client, LLDeveloper, & + "This is data logged against the Client at the Developer level") + + call log_warning(client, LLQuiet, & + "This is a warning logged against the Client at the Quiet level") + call log_warning(client, LLInfo, & + "This is a warning logged against the Client at the Info level") + call log_warning(client, LLDebug, & + "This is a warning logged against the Client at the Debug level") + call log_warning(client, LLDeveloper, & + "This is a warning logged against the Client at the Developer level") + + call log_error(client, LLQuiet, & + "This is an error logged against the Client at the Quiet level") + call log_error(client, LLInfo, & + "This is an error logged against the Client at the Info level") + call log_error(client, LLDebug, & + "This is an error logged against the Client at the Debug level") + call log_error(client, LLDeveloper, & + "This is an error logged against the Client at the Developer level") + + ! Logging against the Dataset + ! =========================== + call log_data(dataset, LLQuiet, & + "This is data logged against the Dataset at the Quiet level") + call log_data(dataset, LLInfo, & + "This is data logged against the Dataset at the Info level") + call log_data(dataset, LLDebug, & + "This is data logged against the Dataset at the Debug level") + call log_data(dataset, LLDeveloper, & + "This is data logged against the Dataset at the Developer level") + + call log_warning(dataset, LLQuiet, & + "This is a warning logged against the Dataset at the Quiet level") + call log_warning(dataset, LLInfo, & + "This is a warning logged against the Dataset at the Info level") + call log_warning(dataset, LLDebug, & + "This is a warning logged against the Dataset at the Debug level") + call log_warning(dataset, LLDeveloper, & + "This is a warning logged against the Dataset at the Developer level") + + call log_error(dataset, LLQuiet, & + "This is an error logged against the Dataset at the Quiet level") + call log_error(dataset, LLInfo, & + "This is an error logged against the Dataset at the Info level") + call log_error(dataset, LLDebug, & + "This is an error logged against the Dataset at the Debug level") + call log_error(dataset, LLDeveloper, & + "This is an error logged against the Dataset at the Developer level") + + ! Logging against the LogContext + ! ============================== + call log_data(logcontext, LLQuiet, & + "This is data logged against the LogContext at the Quiet level") + call log_data(logcontext, LLInfo, & + "This is data logged against the LogContext at the Info level") + call log_data(logcontext, LLDebug, & + "This is data logged against the LogContext at the Debug level") + call log_data(logcontext, LLDeveloper, & + "This is data logged against the LogContext at the Developer level") + + call log_warning(logcontext, LLQuiet, & + "This is a warning logged against the LogContext at the Quiet level") + call log_warning(logcontext, LLInfo, & + "This is a warning logged against the LogContext at the Info level") + call log_warning(logcontext, LLDebug, & + "This is a warning logged against the LogContext at the Debug level") + call log_warning(logcontext, LLDeveloper, & + "This is a warning logged against the LogContext at the Developer level") + + call log_error(logcontext, LLQuiet, & + "This is an error logged against the LogContext at the Quiet level") + call log_error(logcontext, LLInfo, & + "This is an error logged against the LogContext at the Info level") + call log_error(logcontext, LLDebug, & + "This is an error logged against the LogContext at the Debug level") + call log_error(logcontext, LLDeveloper, & + "This is an error logged against the LogContext at the Developer level") + + ! Done + write(*,*) "client logging: passed" +end program main diff --git a/tests/fortran/client_test_misc_tensor.F90 b/tests/fortran/client_test_misc_tensor.F90 index 842d488ba..23ce54af0 100644 --- a/tests/fortran/client_test_misc_tensor.F90 +++ b/tests/fortran/client_test_misc_tensor.F90 @@ -46,7 +46,7 @@ program main integer :: result logical(kind=c_bool) :: exists - result = client%initialize(use_cluster()) + result = client%initialize(use_cluster(), "client_test_misc_tensor") if (result .ne. SRNoError) error stop print *, "Putting tensor" diff --git a/tests/fortran/client_test_mnist.F90 b/tests/fortran/client_test_mnist.F90 index 85cae2890..c42aba28c 100644 --- a/tests/fortran/client_test_mnist.F90 +++ b/tests/fortran/client_test_mnist.F90 @@ -44,7 +44,7 @@ program mnist_test character(len=2) :: key_suffix integer :: result - result = client%initialize(use_cluster()) + result = client%initialize(use_cluster(), "client_test_mnist") if (result .ne. SRNoError) error stop result = client%set_model_from_file(model_key, model_file, "TORCH", "CPU") diff --git a/tests/fortran/client_test_mnist_multigpu.F90 b/tests/fortran/client_test_mnist_multigpu.F90 index d62bde5ce..e0e2b47e0 100644 --- a/tests/fortran/client_test_mnist_multigpu.F90 +++ b/tests/fortran/client_test_mnist_multigpu.F90 @@ -47,7 +47,8 @@ program mnist_test character(len=2) :: key_suffix integer :: sr_return_code - sr_return_code = client%initialize(use_cluster()) + sr_return_code = client%initialize(use_cluster(), & + "client_test_mnist_multigpu") if (sr_return_code .ne. SRNoError) error stop sr_return_code = client%set_model_from_file_multigpu(model_key, model_file, "TORCH", first_gpu, num_gpus) diff --git a/tests/fortran/client_test_put_get_1D.F90 b/tests/fortran/client_test_put_get_1D.F90 index eac4739e8..f54fc583e 100644 --- a/tests/fortran/client_test_put_get_1D.F90 +++ b/tests/fortran/client_test_put_get_1D.F90 @@ -73,7 +73,7 @@ program main recv_array_integer_64(i) = irand() enddo - result = client%initialize(use_cluster()) + result = client%initialize(use_cluster(), "client_test_put_get_1D") if (result .ne. SRNoError) error stop result = client%put_tensor("true_array_real_32", true_array_real_32, shape(true_array_real_32)) diff --git a/tests/fortran/client_test_put_get_2D.F90 b/tests/fortran/client_test_put_get_2D.F90 index 9326fb0df..d52d9b9e3 100644 --- a/tests/fortran/client_test_put_get_2D.F90 +++ b/tests/fortran/client_test_put_get_2D.F90 @@ -74,7 +74,7 @@ program main recv_array_integer_64(i,j) = irand() enddo; enddo - result = client%initialize(use_cluster()) + result = client%initialize(use_cluster(), "client_test_put_get_2D") if (result .ne. SRNoError) error stop result = client%put_tensor("true_array_real_32", true_array_real_32, shape(true_array_real_32)) diff --git a/tests/fortran/client_test_put_get_3D.F90 b/tests/fortran/client_test_put_get_3D.F90 index 3f2e970a7..c5407074f 100644 --- a/tests/fortran/client_test_put_get_3D.F90 +++ b/tests/fortran/client_test_put_get_3D.F90 @@ -76,7 +76,7 @@ program main recv_array_integer_64(i,j,k) = irand() enddo; enddo; enddo - result = client%initialize(use_cluster()) + result = client%initialize(use_cluster(), "client_test_put_get_3D") if (result .ne. SRNoError) error stop result = client%put_tensor("true_array_real_32", true_array_real_32, shape(true_array_real_32)) diff --git a/tests/fortran/client_test_put_get_unpack_dataset.F90 b/tests/fortran/client_test_put_get_unpack_dataset.F90 index 7ee155751..c86a9f430 100644 --- a/tests/fortran/client_test_put_get_unpack_dataset.F90 +++ b/tests/fortran/client_test_put_get_unpack_dataset.F90 @@ -61,7 +61,8 @@ program main integer :: err_code - result = client%initialize(use_cluster()) + result = client%initialize(use_cluster(), & + "client_test_put_get_unpack_dataset") if (result .ne. SRNoError) error stop call random_number(true_array_real_32) diff --git a/tests/python/test_address.py b/tests/python/test_address.py index c26693ab6..764ef230c 100644 --- a/tests/python/test_address.py +++ b/tests/python/test_address.py @@ -29,13 +29,13 @@ from smartredis import Client -def test_address(use_cluster): +def test_address(use_cluster, context): # get env var to set through client init ssdb = os.environ["SSDB"] del os.environ["SSDB"] # client init should fail if SSDB not set - c = Client(address=ssdb, cluster=use_cluster) + c = Client(address=ssdb, cluster=use_cluster, logger_name=context) # check if SSDB was set anyway assert os.environ["SSDB"] == ssdb diff --git a/tests/python/test_dataset_aggregation.py b/tests/python/test_dataset_aggregation.py index 4477fbcd0..8ab6833e5 100644 --- a/tests/python/test_dataset_aggregation.py +++ b/tests/python/test_dataset_aggregation.py @@ -29,23 +29,28 @@ import pytest from smartredis import Client, Dataset from smartredis.error import * +from smartredis import * -def test_aggregation(use_cluster): +def test_aggregation(use_cluster, context): num_datasets = 4 - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) + log_data(context, LLDebug, "Initialization complete") # Build datasets original_datasets = [create_dataset(f"dataset_{i}") for i in range(num_datasets)] + log_data(context, LLDebug, "DataSets built") # Make sure the list is cleared list_name = "dataset_test_list" client.delete_list(list_name) + log_data(context, LLDebug, "list cleared") # Put datasets into the list for i in range(num_datasets): client.put_dataset(original_datasets[i]) client.append_to_list(list_name, original_datasets[i]) + log_data(context, LLDebug, "DataSets added to list") # Confirm that poll for list length works correctly actual_length = num_datasets @@ -54,12 +59,14 @@ def test_aggregation(use_cluster): raise RuntimeError( f"Polling for list length of {actual_length} returned " f"False for known length of {actual_length}.") + log_data(context, LLDebug, "Polling 1") poll_result = client.poll_list_length(list_name, actual_length + 1, 100, 5) if (poll_result == True): raise RuntimeError( f"Polling for list length of {actual_length + 1} returned " f"True for known length of {actual_length}.") + log_data(context, LLDebug, "Polling 2") # Confirm that poll for greater than or equal list length works correctly poll_result = client.poll_list_length_gte(list_name, actual_length - 1, 100, 5) @@ -67,18 +74,21 @@ def test_aggregation(use_cluster): raise RuntimeError( f"Polling for list length greater than or equal to {actual_length - 1} " f"returned False for known length of {actual_length}.") + log_data(context, LLDebug, "Polling 3") poll_result = client.poll_list_length_gte(list_name, actual_length, 100, 5) if (poll_result == False): raise RuntimeError( f"Polling for list length greater than or equal to {actual_length} " f"returned False for known length of {actual_length}.") + log_data(context, LLDebug, "Polling 4") poll_result = client.poll_list_length_gte(list_name, actual_length + 1, 100, 5) if (poll_result == True): raise RuntimeError( f"Polling for list length greater than or equal to {actual_length + 1} " f"returned True for known length of {actual_length}.") + log_data(context, LLDebug, "Polling 5") # Confirm that poll for less than or equal list length works correctly poll_result = client.poll_list_length_lte(list_name, actual_length - 1, 100, 5) @@ -86,26 +96,29 @@ def test_aggregation(use_cluster): raise RuntimeError( f"Polling for list length less than or equal to {actual_length - 1} " f"returned True for known length of {actual_length}.") + log_data(context, LLDebug, "Polling 6") poll_result = client.poll_list_length_lte(list_name, actual_length, 100, 5) if (poll_result == False): raise RuntimeError( f"Polling for list length less than or equal to {actual_length} " f"returned False for known length of {actual_length}.") + log_data(context, LLDebug, "Polling 7") poll_result = client.poll_list_length_lte(list_name, actual_length + 1, 100, 5) if (poll_result == False): raise RuntimeError( f"Polling for list length less than or equal to {actual_length + 1} " f"returned False for known length of {actual_length}.") + log_data(context, LLDebug, "Polling 8") # Check the list length list_length = client.get_list_length(list_name) - if (list_length != actual_length): raise RuntimeError( f"The list length of {list_length} does not match expected " f"value of {actual_length}.") + log_data(context, LLDebug, "List length check") # Retrieve datasets via the aggregation list datasets = client.get_datasets_from_list(list_name) @@ -115,6 +128,7 @@ def test_aggregation(use_cluster): f"does not match expected value of {list_length}.") for ds in datasets: check_dataset(ds) + log_data(context, LLDebug, "DataSet retrieval") # ------------ helper functions --------------------------------- diff --git a/tests/python/test_dataset_ops.py b/tests/python/test_dataset_ops.py index 474bd18b5..8abf868fb 100644 --- a/tests/python/test_dataset_ops.py +++ b/tests/python/test_dataset_ops.py @@ -31,12 +31,12 @@ from smartredis.error import * -def test_copy_dataset(use_cluster): +def test_copy_dataset(use_cluster, context): # test copying dataset from one key to another dataset = create_dataset("test_dataset_copy") - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) client.put_dataset(dataset) client.copy_dataset("test_dataset_copy", "test_dataset_copied") @@ -67,12 +67,12 @@ def test_copy_dataset(use_cluster): ) -def test_rename_dataset(use_cluster): +def test_rename_dataset(use_cluster, context): # test renaming a dataset in the database dataset = create_dataset("dataset_rename") - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) client.put_dataset(dataset) client.rename_dataset("dataset_rename", "dataset_renamed") @@ -105,12 +105,12 @@ def test_rename_dataset(use_cluster): ) -def test_delete_dataset(use_cluster): +def test_delete_dataset(use_cluster, context): # test renaming a dataset in the database dataset = create_dataset("dataset_delete") - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) client.put_dataset(dataset) client.delete_dataset( @@ -123,25 +123,25 @@ def test_delete_dataset(use_cluster): # ----------- Error handling ------------------------------------ -def test_rename_nonexisting_dataset(use_cluster): +def test_rename_nonexisting_dataset(use_cluster, context): - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) with pytest.raises(RedisReplyError): client.rename_dataset("not-a-tensor", "still-not-a-tensor") -def test_copy_nonexistant_dataset(use_cluster): +def test_copy_nonexistant_dataset(use_cluster, context): - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) with pytest.raises(RedisReplyError): client.copy_dataset("not-a-tensor", "still-not-a-tensor") -def test_copy_not_dataset(use_cluster): +def test_copy_not_dataset(use_cluster, context): def test_func(param): print(param) - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) client.set_function("test_func_dataset", test_func) with pytest.raises(RedisReplyError): client.copy_dataset("test_func_dataset", "test_fork_dataset") diff --git a/tests/python/test_errors.py b/tests/python/test_errors.py index 16f798274..627af2a00 100644 --- a/tests/python/test_errors.py +++ b/tests/python/test_errors.py @@ -28,55 +28,55 @@ import numpy as np import pytest -from smartredis import Client, Dataset +from smartredis import * from smartredis.error import * -def test_SSDB_not_set(use_cluster): +def test_SSDB_not_set(use_cluster, context): ssdb = os.environ["SSDB"] del os.environ["SSDB"] with pytest.raises(RedisConnectionError): - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) os.environ["SSDB"] = ssdb -def test_bad_SSDB(use_cluster): +def test_bad_SSDB(use_cluster, context): ssdb = os.environ["SSDB"] del os.environ["SSDB"] os.environ["SSDB"] = "not-an-address:6379;" with pytest.raises(RedisConnectionError): - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) os.environ["SSDB"] = ssdb -def test_bad_get_tensor(use_cluster): - c = Client(None, use_cluster) +def test_bad_get_tensor(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(RedisReplyError): c.get_tensor("not-a-key") -def test_bad_get_dataset(use_cluster): - c = Client(None, use_cluster) +def test_bad_get_dataset(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(RedisKeyError): c.get_dataset("not-a-key") -def test_bad_script_file(use_cluster): - c = Client(None, use_cluster) +def test_bad_script_file(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(FileNotFoundError): c.set_script_from_file("key", "not-a-file") -def test_get_non_existant_script(use_cluster): - c = Client(None, use_cluster) +def test_get_non_existant_script(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(RedisReplyError): script = c.get_script("not-a-script") -def test_bad_function_execution(use_cluster): +def test_bad_function_execution(use_cluster, context): """Error raised inside function""" - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.set_function("bad-function", bad_function) data = np.array([1, 2, 3, 4]) c.put_tensor("bad-func-tensor", data) @@ -84,10 +84,10 @@ def test_bad_function_execution(use_cluster): c.run_script("bad-function", "bad_function", ["bad-func-tensor"], ["output"]) -def test_missing_script_function(use_cluster): +def test_missing_script_function(use_cluster, context): """User requests to run a function not in the script""" - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.set_function("bad-function", bad_function) with pytest.raises(RedisReplyError): c.run_script( @@ -95,27 +95,27 @@ def test_missing_script_function(use_cluster): ) -def test_wrong_model_name(mock_data, mock_model, use_cluster): +def test_wrong_model_name(mock_data, mock_model, use_cluster, context): """User requests to run a model that is not there""" data = mock_data.create_data(1) model = mock_model.create_torch_cnn() - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.set_model("simple_cnn", model, "TORCH", "CPU") c.put_tensor("input", data[0]) with pytest.raises(RedisReplyError): c.run_model("wrong_cnn", ["input"], ["output"]) -def test_wrong_model_name_from_file(mock_data, mock_model, use_cluster): +def test_wrong_model_name_from_file(mock_data, mock_model, use_cluster, context): """User requests to run a model that is not there that was loaded from file.""" try: data = mock_data.create_data(1) mock_model.create_torch_cnn(filepath="./torch_cnn.pt") - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.set_model_from_file("simple_cnn_from_file", "./torch_cnn.pt", "TORCH", "CPU") c.put_tensor("input", data[0]) with pytest.raises(RedisReplyError): @@ -124,16 +124,16 @@ def test_wrong_model_name_from_file(mock_data, mock_model, use_cluster): os.remove("torch_cnn.pt") -def test_bad_device(use_cluster): - c = Client(None, use_cluster) +def test_bad_device(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.set_script("key", "some_script", device="not-a-gpu") ##### # Test type errors from bad parameter types to Client API calls -def test_bad_type_put_tensor(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_put_tensor(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) array = np.array([1, 2, 3, 4]) with pytest.raises(TypeError): c.put_tensor(42, array) @@ -141,79 +141,79 @@ def test_bad_type_put_tensor(use_cluster): c.put_tensor("key", [1, 2, 3, 4]) -def test_unsupported_type_put_tensor(use_cluster): +def test_unsupported_type_put_tensor(use_cluster, context): """test an unsupported numpy type""" - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) data = np.array([1, 2, 3, 4]).astype(np.uint64) with pytest.raises(TypeError): c.put_tensor("key", data) -def test_bad_type_get_tensor(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_tensor(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.get_tensor(42) -def test_bad_type_delete_tensor(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_delete_tensor(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.delete_tensor(42) -def test_bad_type_copy_tensor(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_copy_tensor(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.copy_tensor(42, "newname") with pytest.raises(TypeError): c.copy_tensor("oldname", 42) -def test_bad_type_rename_tensor(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_rename_tensor(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.rename_tensor(42, "newname") with pytest.raises(TypeError): c.rename_tensor("oldname", 42) -def test_bad_type_put_dataset(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_put_dataset(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) array = np.array([1, 2, 3, 4]) with pytest.raises(TypeError): c.put_dataset(array) -def test_bad_type_get_dataset(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_dataset(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.get_dataset(42) -def test_bad_type_delete_dataset(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_delete_dataset(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.delete_dataset(42) -def test_bad_type_copy_dataset(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_copy_dataset(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.copy_dataset(42, "dest") with pytest.raises(TypeError): c.copy_dataset("src", 42) -def test_bad_type_rename_dataset(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_rename_dataset(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.rename_dataset(42, "oldkey") with pytest.raises(TypeError): c.rename_dataset("newkey", 42) -def test_bad_type_set_function(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_set_function(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.set_function(42, bad_function) with pytest.raises(TypeError): @@ -221,8 +221,8 @@ def test_bad_type_set_function(use_cluster): with pytest.raises(TypeError): c.set_function("key", bad_function, 42) -def test_bad_type_set_function_multigpu(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_set_function_multigpu(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.set_function_multigpu(42, bad_function, 0, 1) with pytest.raises(TypeError): @@ -236,8 +236,8 @@ def test_bad_type_set_function_multigpu(use_cluster): with pytest.raises(ValueError): c.set_function_multigpu("key", bad_function, 0, 0) # invalid num GPUs -def test_bad_type_set_script(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_set_script(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) key = "key_for_script" script = "bad script but correct parameter type" device = "CPU" @@ -248,8 +248,8 @@ def test_bad_type_set_script(use_cluster): with pytest.raises(TypeError): c.set_script(key, script, 42) -def test_bad_type_set_script_multigpu(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_set_script_multigpu(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) key = "key_for_script" script = "bad script but correct parameter type" first_gpu = 0 @@ -267,8 +267,8 @@ def test_bad_type_set_script_multigpu(use_cluster): with pytest.raises(ValueError): c.set_script_multigpu(key, script, first_gpu, 0) -def test_bad_type_set_script_from_file(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_set_script_from_file(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) key = "key_for_script" scriptfile = "bad filename but correct parameter type" device = "CPU" @@ -279,8 +279,8 @@ def test_bad_type_set_script_from_file(use_cluster): with pytest.raises(TypeError): c.set_script_from_file(key, scriptfile, 42) -def test_bad_type_set_script_from_file_multigpu(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_set_script_from_file_multigpu(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) key = "key_for_script" scriptfile = "bad filename but correct parameter type" first_gpu = 0 @@ -294,14 +294,14 @@ def test_bad_type_set_script_from_file_multigpu(use_cluster): with pytest.raises(TypeError): c.set_script_from_file_multigpu(key, scriptfile, first_gpu, "not an integer") -def test_bad_type_get_script(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_script(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.get_script(42) -def test_bad_type_run_script(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_run_script(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) key = "my_script" fn_name = "phred" inputs = ["list", "of", "strings"] @@ -316,8 +316,8 @@ def test_bad_type_run_script(use_cluster): c.run_script(key, fn_name, inputs, 42) -def test_bad_type_run_script_multigpu(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_run_script_multigpu(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) key = "my_script" fn_name = "phred" inputs = ["list", "of", "strings"] @@ -345,15 +345,15 @@ def test_bad_type_run_script_multigpu(use_cluster): c.run_script_multigpu(key, fn_name, inputs, outputs, offset, first_gpu, 0) -def test_bad_type_get_model(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_model(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.get_model(42) -def test_bad_type_set_model(mock_model, use_cluster): +def test_bad_type_set_model(mock_model, use_cluster, context): model = mock_model.create_torch_cnn() - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.set_model(42, model, "TORCH", "CPU") with pytest.raises(TypeError): @@ -371,10 +371,10 @@ def test_bad_type_set_model(mock_model, use_cluster): with pytest.raises(TypeError): c.set_model("simple_cnn", model, "TORCH", "CPU", tag=42) -def test_bad_type_set_model_multigpu(mock_model, use_cluster): - c = Client(None, use_cluster) +def test_bad_type_set_model_multigpu(mock_model, use_cluster, context): + c = Client(None, use_cluster, logger_name=context) model = mock_model.create_torch_cnn() - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.set_model_multigpu(42, model, "TORCH", 0, 1) with pytest.raises(TypeError): @@ -397,9 +397,9 @@ def test_bad_type_set_model_multigpu(mock_model, use_cluster): c.set_model_multigpu("simple_cnn", model, "TORCH", 0, 1, tag=42) -def test_bad_type_set_model_from_file(use_cluster): +def test_bad_type_set_model_from_file(use_cluster, context): modelfile = "bad filename but right parameter type" - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.set_model_from_file(42, modelfile, "TORCH", "CPU") with pytest.raises(TypeError): @@ -419,9 +419,9 @@ def test_bad_type_set_model_from_file(use_cluster): with pytest.raises(TypeError): c.set_model_from_file("simple_cnn", modelfile, "TORCH", "CPU", tag=42) -def test_bad_type_set_model_from_file_multigpu(use_cluster): +def test_bad_type_set_model_from_file_multigpu(use_cluster, context): modelfile = "bad filename but right parameter type" - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.set_model_from_file_multigpu(42, modelfile, "TORCH", 0, 1) with pytest.raises(TypeError): @@ -441,14 +441,14 @@ def test_bad_type_set_model_from_file_multigpu(use_cluster): with pytest.raises(TypeError): c.set_model_from_file_multigpu("simple_cnn", modelfile, "TORCH", 0, 1, tag=42) -def test_bad_type_run_model(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_run_model(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.run_model(42) -def test_bad_type_run_model_multigpu(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_run_model_multigpu(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.run_model_multigpu(42, 0, 0, 1) with pytest.raises(TypeError): @@ -462,8 +462,8 @@ def test_bad_type_run_model_multigpu(use_cluster): with pytest.raises(ValueError): c.run_model_multigpu("simple_cnn", 0, 0, 0) -def test_bad_type_delete_model_multigpu(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_delete_model_multigpu(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.delete_model_multigpu(42, 0, 1) with pytest.raises(TypeError): @@ -475,8 +475,8 @@ def test_bad_type_delete_model_multigpu(use_cluster): with pytest.raises(ValueError): c.delete_model_multigpu("simple_cnn", 0, 0) -def test_bad_type_delete_script_multigpu(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_delete_script_multigpu(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) script_name = "my_script" with pytest.raises(TypeError): c.delete_script_multigpu(42, 0, 1) @@ -489,32 +489,32 @@ def test_bad_type_delete_script_multigpu(use_cluster): with pytest.raises(ValueError): c.delete_script_multigpu(script_name, 0, 0) -def test_bad_type_tensor_exists(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_tensor_exists(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.tensor_exists(42) -def test_bad_type_dataset_exists(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_dataset_exists(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.dataset_exists(42) -def test_bad_type_model_exists(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_model_exists(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.model_exists(42) -def test_bad_type_key_exists(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_key_exists(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.key_exists(42) -def test_bad_type_poll_key(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_poll_key(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) name = "some_key" freq = 42 num_tries = 42 @@ -527,8 +527,8 @@ def test_bad_type_poll_key(use_cluster): c.poll_key(name, freq, bogus) -def test_bad_type_poll_tensor(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_poll_tensor(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) name = "some_key" freq = 42 num_tries = 42 @@ -541,8 +541,8 @@ def test_bad_type_poll_tensor(use_cluster): c.poll_tensor(name, freq, bogus) -def test_bad_type_poll_dataset(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_poll_dataset(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) name = "some_key" freq = 42 num_tries = 42 @@ -555,8 +555,8 @@ def test_bad_type_poll_dataset(use_cluster): c.poll_dataset(name, freq, bogus) -def test_bad_type_poll_model(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_poll_model(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) name = "some_key" freq = 42 num_tries = 42 @@ -569,44 +569,44 @@ def test_bad_type_poll_model(use_cluster): c.poll_model(name, freq, bogus) -def test_bad_type_set_data_source(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_set_data_source(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.set_data_source(42) -def test_bad_type_use_model_ensemble_prefix(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_use_model_ensemble_prefix(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.use_model_ensemble_prefix("not a boolean") -def test_bad_type_use_list_ensemble_prefix(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_use_list_ensemble_prefix(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.use_list_ensemble_prefix("not a boolean") -def test_bad_type_use_tensor_ensemble_prefix(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_use_tensor_ensemble_prefix(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.use_tensor_ensemble_prefix("not a boolean") -def test_bad_type_get_db_node_info(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_db_node_info(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.get_db_node_info("not a list") -def test_bad_type_get_db_cluster_info(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_db_cluster_info(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.get_db_cluster_info("not a list") -def test_bad_type_get_ai_info(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_ai_info(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) address = ["list", "of", "str"] key = "ai.info.key" with pytest.raises(TypeError): @@ -617,22 +617,22 @@ def test_bad_type_get_ai_info(use_cluster): c.get_ai_info(address, key, "not a boolean") -def test_bad_type_flush_db(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_flush_db(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.flush_db("not a list") -def test_bad_type_config_get(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_config_get(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.config_get("timeout", 42) with pytest.raises(TypeError): c.config_get(42, "address") -def test_bad_type_config_set(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_config_set(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) param = "timeout" value = "never" address = "127.0.0.1:6379" @@ -644,42 +644,42 @@ def test_bad_type_config_set(use_cluster): c.config_set(param, value, 42) -def test_bad_type_save(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_save(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.save("not a list") -def test_bad_type_append_to_list(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_append_to_list(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.append_to_list(42, 42) -def test_bad_type_delete_list(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_delete_list(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.delete_list(42) -def test_bad_type_copy_list(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_copy_list(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.copy_list(42, "dest") with pytest.raises(TypeError): c.copy_list("src", 42) -def test_bad_type_rename_list(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_rename_list(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.rename_list(42, "dest") with pytest.raises(TypeError): c.rename_list("src", 42) -def test_bad_type_get_list_length(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_list_length(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.get_list_length(42) -def test_bad_type_poll_list_length(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_poll_list_length(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) name = "mylist" len = 42 pollfreq = 42 @@ -693,8 +693,8 @@ def test_bad_type_poll_list_length(use_cluster): with pytest.raises(TypeError): c.poll_list_length(name, len, pollfreq, "not an integer") -def test_bad_type_poll_list_length_gte(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_poll_list_length_gte(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) name = "mylist" len = 42 pollfreq = 42 @@ -708,8 +708,8 @@ def test_bad_type_poll_list_length_gte(use_cluster): with pytest.raises(TypeError): c.poll_list_length_gte(name, len, pollfreq, "not an integer") -def test_bad_type_poll_list_length_lte(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_poll_list_length_lte(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) name = "mylist" len = 42 pollfreq = 42 @@ -723,13 +723,13 @@ def test_bad_type_poll_list_length_lte(use_cluster): with pytest.raises(TypeError): c.poll_list_length_lte(name, len, pollfreq, "not an integer") -def test_bad_type_get_datasets_from_list(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_datasets_from_list(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) with pytest.raises(TypeError): c.get_datasets_from_list(42) -def test_bad_type_get_dataset_list_range(use_cluster): - c = Client(None, use_cluster) +def test_bad_type_get_dataset_list_range(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) listname = "my_list" start_index = 0 end_index = 42 @@ -740,6 +740,18 @@ def test_bad_type_get_dataset_list_range(use_cluster): with pytest.raises(TypeError): c.get_dataset_list_range(listname, start_index, "not an integer") +@pytest.mark.parametrize("log_fn", [ + (log_data,), (log_warning,), (log_error,) +]) +def test_bad_type_log_function(use_cluster, context, log_fn): + c = Client(None, use_cluster, logger_name=context) + with pytest.raises(TypeError): + log_fn(42, LLInfo, "Data to be logged") + with pytest.raises(TypeError): + log_fn("test_bad_type_log_function", "Not a logging level", "Data to be logged") + with pytest.raises(TypeError): + log_fn("test_bad_type_log_function", LLInfo, 42) + ##### # Test type errors from bad parameter types to Dataset API calls diff --git a/tests/python/test_logging.py b/tests/python/test_logging.py new file mode 100644 index 000000000..a87d5f9b8 --- /dev/null +++ b/tests/python/test_logging.py @@ -0,0 +1,39 @@ +# BSD 2-Clause License +# +# Copyright (c) 2021-2022, Hewlett Packard Enterprise +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from smartredis import * +from smartredis.error import * +import pytest + +@pytest.mark.parametrize("log_level", [ + LLQuiet, LLInfo, LLDebug, LLDeveloper +]) +def test_logging(use_cluster, context, log_level): + c = Client(None, use_cluster, logger_name=context) + log_data(context, log_level, f"This is data logging ({log_level.name})") + log_warning(context, log_level, f"This is a warning ({log_level.name})") + log_error(context, log_level, f"This is an error ({log_level.name})") + diff --git a/tests/python/test_model_methods_torch.py b/tests/python/test_model_methods_torch.py index 6a6d65c73..78445b71f 100644 --- a/tests/python/test_model_methods_torch.py +++ b/tests/python/test_model_methods_torch.py @@ -30,18 +30,18 @@ from smartredis import Client -def test_set_model(mock_model, use_cluster): +def test_set_model(mock_model, use_cluster, context): model = mock_model.create_torch_cnn() - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.set_model("simple_cnn", model, "TORCH", "CPU") returned_model = c.get_model("simple_cnn") assert model == returned_model -def test_set_model_from_file(mock_model, use_cluster): +def test_set_model_from_file(mock_model, use_cluster, context): try: mock_model.create_torch_cnn(filepath="./torch_cnn.pt") - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.set_model_from_file("file_cnn", "./torch_cnn.pt", "TORCH", "CPU") assert c.model_exists("file_cnn") returned_model = c.get_model("file_cnn") @@ -54,10 +54,10 @@ def test_set_model_from_file(mock_model, use_cluster): os.remove("torch_cnn.pt") -def test_torch_inference(mock_model, use_cluster): +def test_torch_inference(mock_model, use_cluster, context): # get model and set into database model = mock_model.create_torch_cnn() - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.set_model("torch_cnn", model, "TORCH") # setup input tensor diff --git a/tests/python/test_nonkeyed_cmd.py b/tests/python/test_nonkeyed_cmd.py index 54d2bce4c..76f014b6d 100644 --- a/tests/python/test_nonkeyed_cmd.py +++ b/tests/python/test_nonkeyed_cmd.py @@ -32,26 +32,26 @@ from smartredis.error import * -def test_dbnode_info_command(use_cluster): +def test_dbnode_info_command(use_cluster, context): # get env var to set through client init ssdb = os.environ["SSDB"] db_info_addr = [ssdb] del os.environ["SSDB"] - client = Client(address=ssdb, cluster=use_cluster) + client = Client(address=ssdb, cluster=use_cluster, logger_name=context) info = client.get_db_node_info(db_info_addr) assert len(info) > 0 -def test_dbcluster_info_command(mock_model, use_cluster): +def test_dbcluster_info_command(mock_model, use_cluster, context): # get env var to set through client init ssdb = os.environ["SSDB"] address = [ssdb] del os.environ["SSDB"] - client = Client(address=ssdb, cluster=use_cluster) + client = Client(address=ssdb, cluster=use_cluster, logger_name=context) if use_cluster: info = client.get_db_cluster_info(address) @@ -67,7 +67,7 @@ def test_dbcluster_info_command(mock_model, use_cluster): del os.environ["SSDB"] # Init client - client = Client(address=ssdb, cluster=use_cluster) + client = Client(address=ssdb, cluster=use_cluster, logger_name=context) # Get a mock model model = mock_model.create_torch_cnn() @@ -87,7 +87,7 @@ def test_dbcluster_info_command(mock_model, use_cluster): with pytest.raises(RedisRuntimeError): client.get_ai_info(address, "bad_key") -def test_flushdb_command(use_cluster): +def test_flushdb_command(use_cluster, context): # from within the testing framework, there is no way # of knowing each db node that is being used, so skip # if on cluster @@ -98,7 +98,7 @@ def test_flushdb_command(use_cluster): address = [ssdb] del os.environ["SSDB"] - client = Client(address=ssdb, cluster=use_cluster) + client = Client(address=ssdb, cluster=use_cluster, logger_name=context) # add key to client via put_tensor tensor = np.array([1, 2]) @@ -109,13 +109,13 @@ def test_flushdb_command(use_cluster): assert not client.tensor_exists("test_copy") -def test_config_set_get_command(use_cluster): +def test_config_set_get_command(use_cluster, context): # get env var to set through client init ssdb = os.environ["SSDB"] del os.environ["SSDB"] - client = Client(address=ssdb, cluster=use_cluster) + client = Client(address=ssdb, cluster=use_cluster, logger_name=context) value = "6000" client.config_set("lua-time-limit", value, ssdb) @@ -125,33 +125,33 @@ def test_config_set_get_command(use_cluster): assert get_reply["lua-time-limit"] == value -def test_config_set_command_DNE(use_cluster): +def test_config_set_command_DNE(use_cluster, context): # get env var to set through client init ssdb = os.environ["SSDB"] del os.environ["SSDB"] - client = Client(address=ssdb, cluster=use_cluster) + client = Client(address=ssdb, cluster=use_cluster, logger_name=context) # The CONFIG parameter "config_param_DNE" is unsupported with pytest.raises(RedisReplyError): client.config_set("config_param_DNE", "10", ssdb) -def test_config_get_command_DNE(use_cluster): +def test_config_get_command_DNE(use_cluster, context): # get env var to set through client init ssdb = os.environ["SSDB"] del os.environ["SSDB"] - client = Client(address=ssdb, cluster=use_cluster) + client = Client(address=ssdb, cluster=use_cluster, logger_name=context) # CONFIG GET returns an empty dictionary if the config_param is unsupported get_reply = client.config_get("config_param_DNE", ssdb) assert get_reply == dict() -def test_save_command(use_cluster, mock_data): +def test_save_command(use_cluster, mock_data, context): # get env var to set through client init ssdb = os.environ["SSDB"] if use_cluster: @@ -161,7 +161,7 @@ def test_save_command(use_cluster, mock_data): del os.environ["SSDB"] # client init should fail if SSDB not set - client = Client(address=ssdb, cluster=use_cluster) + client = Client(address=ssdb, cluster=use_cluster, logger_name=context) # for each address, check that the timestamp of the last SAVE increases after calling Client::save for address in addresses: diff --git a/tests/python/test_put_get_dataset.py b/tests/python/test_put_get_dataset.py index 0e0ad6bcd..9b349d655 100644 --- a/tests/python/test_put_get_dataset.py +++ b/tests/python/test_put_get_dataset.py @@ -30,7 +30,7 @@ from smartredis import Client, Dataset -def test_put_get_dataset(mock_data, use_cluster): +def test_put_get_dataset(mock_data, use_cluster, context): """test sending and recieving a dataset with 2D tensors of every datatype """ @@ -43,7 +43,7 @@ def test_put_get_dataset(mock_data, use_cluster): key = f"tensor_{str(index)}" dataset.add_tensor(key, tensor) - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) assert not client.dataset_exists( "nonexistent-dataset" @@ -64,7 +64,7 @@ def test_put_get_dataset(mock_data, use_cluster): ) -def test_augment_dataset(mock_data, use_cluster): +def test_augment_dataset(mock_data, use_cluster, context): """Test sending, receiving, altering, and sending a Dataset. """ @@ -75,7 +75,7 @@ def test_augment_dataset(mock_data, use_cluster): dataset_name = "augment-dataset" # Initialize a client - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) # Create a dataset to put into the database dataset = Dataset(dataset_name) diff --git a/tests/python/test_put_get_tensor.py b/tests/python/test_put_get_tensor.py index 4f6416dd4..a086d20ad 100644 --- a/tests/python/test_put_get_tensor.py +++ b/tests/python/test_put_get_tensor.py @@ -31,28 +31,28 @@ # ----- Tests ----------------------------------------------------------- -def test_1D_put_get(mock_data, use_cluster): +def test_1D_put_get(mock_data, use_cluster, context): """Test put/get_tensor for 1D numpy arrays""" - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) data = mock_data.create_data(10) send_get_arrays(client, data) -def test_2D_put_get(mock_data, use_cluster): +def test_2D_put_get(mock_data, use_cluster, context): """Test put/get_tensor for 2D numpy arrays""" - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) data = mock_data.create_data((10, 10)) send_get_arrays(client, data) -def test_3D_put_get(mock_data, use_cluster): +def test_3D_put_get(mock_data, use_cluster, context): """Test put/get_tensor for 3D numpy arrays""" - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) data = mock_data.create_data((10, 10, 10)) send_get_arrays(client, data) diff --git a/tests/python/test_script_methods.py b/tests/python/test_script_methods.py index e021240c7..e0c1efd72 100644 --- a/tests/python/test_script_methods.py +++ b/tests/python/test_script_methods.py @@ -34,25 +34,25 @@ file_path = osp.dirname(osp.abspath(__file__)) -def test_set_get_function(use_cluster): - c = Client(None, use_cluster) +def test_set_get_function(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) c.set_function("test-set-function", one_to_one) script = c.get_script("test-set-function") sent_script = inspect.getsource(one_to_one) assert script == sent_script -def test_set_get_script(use_cluster): - c = Client(None, use_cluster) +def test_set_get_script(use_cluster, context): + c = Client(None, use_cluster, logger_name=context) sent_script = read_script_from_file() c.set_script("test-set-script", sent_script) script = c.get_script("test-set-script") assert sent_script == script -def test_set_script_from_file(use_cluster): +def test_set_script_from_file(use_cluster, context): sent_script = read_script_from_file() - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.set_script_from_file( "test-script-file", osp.join(file_path, "./data_processing_script.txt") ) @@ -63,10 +63,10 @@ def test_set_script_from_file(use_cluster): assert not c.model_exists("test-script-file") -def test_run_script(use_cluster): +def test_run_script(use_cluster, context): data = np.array([[1, 2, 3, 4, 5]]) - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.put_tensor("script-test-data", data) c.set_function("one-to-one", one_to_one) c.run_script("one-to-one", "one_to_one", ["script-test-data"], ["script-test-out"]) @@ -74,11 +74,11 @@ def test_run_script(use_cluster): assert out == 5 -def test_run_script_multi(use_cluster): +def test_run_script_multi(use_cluster, context): data = np.array([[1, 2, 3, 4]]) data_2 = np.array([[5, 6, 7, 8]]) - c = Client(None, use_cluster) + c = Client(None, use_cluster, logger_name=context) c.put_tensor("srpt-multi-out-data-1", data) c.put_tensor("srpt-multi-out-data-2", data_2) c.set_function("two-to-one", two_to_one) diff --git a/tests/python/test_tensor_ops.py b/tests/python/test_tensor_ops.py index bea38cdb2..1fb37e403 100644 --- a/tests/python/test_tensor_ops.py +++ b/tests/python/test_tensor_ops.py @@ -31,10 +31,10 @@ from smartredis.error import RedisReplyError -def test_copy_tensor(use_cluster): +def test_copy_tensor(use_cluster, context): # test copying tensor - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) tensor = np.array([1, 2]) client.put_tensor("test_copy", tensor) @@ -46,10 +46,10 @@ def test_copy_tensor(use_cluster): assert np.array_equal(tensor, returned) -def test_rename_tensor(use_cluster): +def test_rename_tensor(use_cluster, context): # test renaming tensor - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) tensor = np.array([1, 2]) client.put_tensor("test_rename", tensor) @@ -61,10 +61,10 @@ def test_rename_tensor(use_cluster): assert np.array_equal(tensor, returned) -def test_delete_tensor(use_cluster): +def test_delete_tensor(use_cluster, context): # test renaming tensor - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) tensor = np.array([1, 2]) client.put_tensor("test_delete", tensor) @@ -76,25 +76,25 @@ def test_delete_tensor(use_cluster): # --------------- Error handling ---------------------- -def test_rename_nonexisting_key(use_cluster): +def test_rename_nonexisting_key(use_cluster, context): - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) with pytest.raises(RedisReplyError): client.rename_tensor("not-a-tensor", "still-not-a-tensor") -def test_copy_nonexistant_key(use_cluster): +def test_copy_nonexistant_key(use_cluster, context): - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) with pytest.raises(RedisReplyError): client.copy_tensor("not-a-tensor", "still-not-a-tensor") -def test_copy_not_tensor(use_cluster): +def test_copy_not_tensor(use_cluster, context): def test_func(param): print(param) - client = Client(None, use_cluster) + client = Client(None, use_cluster, logger_name=context) client.set_function("test_func", test_func) with pytest.raises(RedisReplyError): client.copy_tensor("test_func", "test_fork")