diff --git a/CMakeLists.txt b/CMakeLists.txt index 25434fbf..1f0a9a70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -228,6 +228,7 @@ set(dxFeedGraalCxxApi_Isolated_Sources src/isolated/ipf/IsolatedInstrumentProfileReader.cpp src/isolated/ipf/live/IsolatedInstrumentProfileCollector.cpp src/isolated/ipf/live/IsolatedInstrumentProfileConnection.cpp + src/isolated/logging/IsolatedLogging.cpp src/isolated/model/IsolatedTxModelListener.cpp src/isolated/model/IsolatedIndexedTxModel.cpp src/isolated/model/IsolatedTimeSeriesTxModel.cpp @@ -267,6 +268,10 @@ set(dxFeedGraalCxxApi_Ipf_Sources src/ipf/live/IterableInstrumentProfile.cpp ) +set(dxFeedGraalCxxApi_Logging_Sources + src/logging/Logging.cpp +) + set(dxFeedGraalCxxApi_Model_Sources src/model/TxModelListener.cpp src/model/IndexedTxModel.cpp @@ -376,6 +381,7 @@ set(dxFeedGraalCxxApi_Sources ${dxFeedGraalCxxApi_ApiOsub_Sources} ${dxFeedGraalCxxApi_Auth_Sources} ${dxFeedGraalCxxApi_Ipf_Sources} + ${dxFeedGraalCxxApi_Logging_Sources} ${dxFeedGraalCxxApi_Model_Sources} ${dxFeedGraalCxxApi_OnDemand_Sources} ${dxFeedGraalCxxApi_Promise_Sources} diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 9961b519..96b2d738 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,3 +1,5 @@ +* **\[MDAPI-211]\[C++]** Implement logging management + * Added `Logging` class. * **\[MDAPI-82]\[C++]** Implement MarketDepthModel * Added `MarketDepthModel` class. * Added `MarketDepthModelListener` class. diff --git a/include/dxfeed_graal_cpp_api/api.hpp b/include/dxfeed_graal_cpp_api/api.hpp index ed4333ce..14e93ebb 100644 --- a/include/dxfeed_graal_cpp_api/api.hpp +++ b/include/dxfeed_graal_cpp_api/api.hpp @@ -75,6 +75,7 @@ DXFCXX_DISABLE_MSC_WARNINGS_PUSH(4251 4996) #include "isolated/ipf/live/IsolatedInstrumentProfileCollector.hpp" #include "isolated/ipf/live/IsolatedInstrumentProfileConnection.hpp" #include "isolated/ipf/IsolatedInstrumentProfileReader.hpp" +#include "isolated/logging/IsolatedLogging.hpp" #include "isolated/model/IsolatedTxModelListener.hpp" #include "isolated/model/IsolatedIndexedTxModel.hpp" #include "isolated/model/IsolatedTimeSeriesTxModel.hpp" @@ -84,6 +85,8 @@ DXFCXX_DISABLE_MSC_WARNINGS_PUSH(4251 4996) #include "isolated/schedule/IsolatedSession.hpp" #include "isolated/schedule/IsolatedSchedule.hpp" +#include "logging/Logging.hpp" + #include "ondemand/OnDemandService.hpp" #include "promise/Promise.hpp" diff --git a/include/dxfeed_graal_cpp_api/isolated/event/IsolatedEventType.hpp b/include/dxfeed_graal_cpp_api/isolated/event/IsolatedEventType.hpp index 313bc816..08ee7c41 100644 --- a/include/dxfeed_graal_cpp_api/isolated/event/IsolatedEventType.hpp +++ b/include/dxfeed_graal_cpp_api/isolated/event/IsolatedEventType.hpp @@ -7,6 +7,8 @@ #include "../../internal/Common.hpp" +#include + DXFCXX_DISABLE_MSC_WARNINGS_PUSH(4251) DXFCPP_BEGIN_NAMESPACE diff --git a/include/dxfeed_graal_cpp_api/isolated/logging/IsolatedLogging.hpp b/include/dxfeed_graal_cpp_api/isolated/logging/IsolatedLogging.hpp new file mode 100644 index 00000000..e34d6106 --- /dev/null +++ b/include/dxfeed_graal_cpp_api/isolated/logging/IsolatedLogging.hpp @@ -0,0 +1,38 @@ +// Copyright (c) 2024 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "../../internal/Conf.hpp" + +#include "../../logging/Logging.hpp" +#include "dxfeed_graal_cpp_api/internal/JavaObjectHandle.hpp" + +#include +#include +#include + +DXFCXX_DISABLE_MSC_WARNINGS_PUSH(4251) + +DXFCPP_BEGIN_NAMESPACE + +namespace isolated::logging::IsolatedLogging { + +// int32_t dxfg_logging_listener_new(graal_isolatethread_t *thread, dxfg_logging_listener_function_t user_func, +// void *user_data, DXFG_OUT dxfg_logging_listener_t **listener); +JavaObjectHandle createListener(void* userFunc, void* userData); + +// int32_t dxfg_logging_set_listener(graal_isolatethread_t *thread, dxfg_logging_listener_t *listener); +void setListener(const JavaObjectHandle& listener); + +//int32_t dxfg_logging_set_log_level(graal_isolatethread_t *thread, dxfg_logging_level_t level); +void setLogLevel(Logging::Level level); + +//int32_t dxfg_logging_set_err_level(graal_isolatethread_t *thread, dxfg_logging_level_t level); +void setErrFileLevel(Logging::Level level); + +} + +DXFCPP_END_NAMESPACE + +DXFCXX_DISABLE_MSC_WARNINGS_POP() \ No newline at end of file diff --git a/include/dxfeed_graal_cpp_api/logging/Logging.hpp b/include/dxfeed_graal_cpp_api/logging/Logging.hpp new file mode 100644 index 00000000..8bf76393 --- /dev/null +++ b/include/dxfeed_graal_cpp_api/logging/Logging.hpp @@ -0,0 +1,112 @@ +// Copyright (c) 2024 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "../internal/Conf.hpp" +#include "dxfeed_graal_cpp_api/internal/Handler.hpp" + +DXFCXX_DISABLE_MSC_WARNINGS_PUSH(4251) + +#include "../internal/Common.hpp" + +DXFCPP_BEGIN_NAMESPACE + +/** + * + */ +struct DXFCPP_EXPORT Logging final { + struct ListenerTag {}; + /** + * Defines a set of standard logging levels that can be used to control logging output. + */ + enum class Level { + /// Indicates that all messages should be logged. + ALL = 0, + + /// Indicates a highly detailed tracing message. + TRACE, + + /// Is a message level providing tracing debug information. + DEBUG, + + /// Is a message level for informational messages. + INFO, + + /// Is a message level indicating a potential problem. + WARN, + + /// Is a message level indicating a serious failure. + ERROR, + + /// Is a special level that can be used to turn off logging. + OFF, + }; + + static std::string levelToString(Level level); + + private: + static SimpleHandler + handler_; + + struct Impl; + + public: + /** + * Initializes logging and sets the logging level. + * + * This is equivalent to the following code: + * + * ```cpp + * System::setProperty("log.className", "com.devexperts.logging.InterceptableLogging"); + * System::setProperty("log.level", levelToString(level)); + * System::setProperty("err.level", levelToString(level)); + * ``` + * @param level The logging level. + */ + static void init(Level level = Level::OFF); + + /** + * Initializes logging, sets the path to the logging file and the logging level. + * + * This is equivalent to the following code: + * + * ```cpp + * System::setProperty("log.className", "com.devexperts.logging.InterceptableLogging"); + * System::setProperty("log.file", logFile); + * System::setProperty("log.level", levelToString(logLevel)); + * ``` + * + * @param logFile The logging file. + * @param logLevel The logging level. + */ + static void init(const std::string &logFile, Level logLevel = Level::INFO); + + /** + * Initializes logging, sets the path to the file for logging, to the file for outputting errors and warnings, and + * sets the logging level for both files. + * + * This is equivalent to the following code: + * + * ```cpp + * System::setProperty("log.className", "com.devexperts.logging.InterceptableLogging"); + * System::setProperty("log.file", logFile); + * System::setProperty("log.level", levelToString(logLevel)); + * System::setProperty("err.file", errFile); + * System::setProperty("err.level", levelToString(errFileLevel)); + * ``` + * + * @param logFile The logging file. + * @param errFile The err file. + * @param logLevel The logging level. + * @param errFileLevel The err file logging level. + */ + static void init(const std::string &logFile, const std::string &errFile, Level logLevel = Level::INFO, + Level errFileLevel = Level::WARN); +}; + +DXFCPP_END_NAMESPACE + +DXFCXX_DISABLE_MSC_WARNINGS_POP() \ No newline at end of file diff --git a/src/internal/JavaObjectHandle.cpp b/src/internal/JavaObjectHandle.cpp index d3e2751d..9f846490 100644 --- a/src/internal/JavaObjectHandle.cpp +++ b/src/internal/JavaObjectHandle.cpp @@ -101,4 +101,6 @@ template struct JavaObjectHandle; template struct JavaObjectHandle; +template struct JavaObjectHandle; + DXFCPP_END_NAMESPACE \ No newline at end of file diff --git a/src/isolated/logging/IsolatedLogging.cpp b/src/isolated/logging/IsolatedLogging.cpp new file mode 100644 index 00000000..e5a98308 --- /dev/null +++ b/src/isolated/logging/IsolatedLogging.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2024 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include +#include +#include + +DXFCPP_BEGIN_NAMESPACE + +namespace isolated::logging::IsolatedLogging { + +// int32_t dxfg_logging_listener_new(graal_isolatethread_t *thread, dxfg_logging_listener_function_t user_func, +// void *user_data, DXFG_OUT dxfg_logging_listener_t **listener); +JavaObjectHandle createListener(void *userFunc, void *userData) { + if (!userFunc) { + throw InvalidArgumentException("Unable to execute function `dxfg_logging_listener_new`. The " + "`userFunc` is nullptr"); + } + + dxfg_logging_listener_t *isolatedListener{}; + + runGraalFunctionAndThrowIfMinusOne(dxfg_logging_listener_new, + dxfcpp::bit_cast(userFunc), userData, + &isolatedListener); + + return JavaObjectHandle(isolatedListener); +} + +// int32_t dxfg_logging_set_listener(graal_isolatethread_t *thread, dxfg_logging_listener_t *listener); +void setListener(const JavaObjectHandle &listener) { + if (!listener) { + throw InvalidArgumentException("Unable to execute function `dxfg_logging_set_listener`. The " + "`listener` handle is invalid"); + } + + runGraalFunctionAndThrowIfMinusOne(dxfg_logging_set_listener, + dxfcpp::bit_cast(listener.get())); +} + +// int32_t dxfg_logging_set_log_level(graal_isolatethread_t *thread, dxfg_logging_level_t level); +void setLogLevel(Logging::Level level) { + runGraalFunctionAndThrowIfMinusOne(dxfg_logging_set_log_level, static_cast(level)); +} + +// int32_t dxfg_logging_set_err_level(graal_isolatethread_t *thread, dxfg_logging_level_t level); +void setErrFileLevel(Logging::Level level) { + runGraalFunctionAndThrowIfMinusOne(dxfg_logging_set_err_level, static_cast(level)); +} + +} // namespace isolated::logging::IsolatedLogging + +DXFCPP_END_NAMESPACE \ No newline at end of file diff --git a/src/logging/Logging.cpp b/src/logging/Logging.cpp new file mode 100644 index 00000000..aa8e75d7 --- /dev/null +++ b/src/logging/Logging.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2024 Devexperts LLC. +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + +#include + +#include +#include +#include +#include + +DXFCPP_BEGIN_NAMESPACE + +std::string Logging::levelToString(Level level) { + switch (level) { + case Level::ALL: + return "ALL"; + case Level::TRACE: + return "TRACE"; + case Level::DEBUG: + return "DEBUG"; + case Level::INFO: + return "INFO"; + case Level::WARN: + return "WARN"; + case Level::ERROR: + return "ERROR"; + case Level::OFF: + return "OFF"; + } + + return "UNKNOWN"; +} + +SimpleHandler + Logging::handler_{}; + +struct Logging::Impl { + static JavaObjectHandle listenerHandle; + + static void onLog(graal_isolatethread_t * /*thread*/, dxfg_logging_level_t level, int64_t timestamp, + const char *threadName, int64_t threadId, const char *loggerName, const char *message, + dxfg_exception_t * /*exception*/, const char * /*formattedMessage*/, void * /*userData*/) { + handler_(static_cast(level), timestamp, threadName, threadId, loggerName, message); + } +}; + +JavaObjectHandle Logging::Impl::listenerHandle{}; + +void Logging::init(Level level) { + System::setProperty("log.className", "com.devexperts.logging.InterceptableLogging"); + System::setProperty("log.level", levelToString(level)); + System::setProperty("err.level", levelToString(level)); +} + +void Logging::init(const std::string &logFile, Level logLevel) { + System::setProperty("log.className", "com.devexperts.logging.InterceptableLogging"); + System::setProperty("log.file", logFile); + System::setProperty("log.level", levelToString(logLevel)); +} + +void Logging::init(const std::string &logFile, const std::string &errFile, Level logLevel, Level errFileLevel) { + System::setProperty("log.className", "com.devexperts.logging.InterceptableLogging"); + System::setProperty("log.file", logFile); + System::setProperty("log.level", levelToString(logLevel)); + System::setProperty("err.file", errFile); + System::setProperty("err.level", levelToString(errFileLevel)); +} + +// void Logging::setListener(std::function +// listener) { +// static auto id = static_cast(-1); +// +// if (id != static_cast(-1)) { +// handler_ -= id; +// } +// +// id = handler_ += std::move(listener); +// +// if (!Impl::listenerHandle) { +// Impl::listenerHandle = +// isolated::logging::IsolatedLogging::createListener(dxfcpp::bit_cast(&Impl::onLog), nullptr); +// } +// } +// +// void Logging::setLogLevel(Level level) { +// isolated::logging::IsolatedLogging::setLogLevel(level); +// } +// +// void Logging::setErrFileLevel(Level level) { +// isolated::logging::IsolatedLogging::setErrFileLevel(level); +// } + +DXFCPP_END_NAMESPACE \ No newline at end of file