已合并
[RFC]: MindIE-LLM 日志模块整改,包括 CPP 和 Python两侧——第二部分(支持动态调整日志等级、封装CPP接口) #384
KaiMa创建于 2月9日
[RFC]: MindIE-LLM 日志模块整改,包括 CPP 和 Python两侧——第二部分(支持动态调整日志等级、封装CPP接口) #384
已合并
共 15 个文件变更+916-94
| @@ -39,7 +39,7 @@ function fn_build() | |||
| 39 | rm -rf "$CODE_ROOT/llm_debug_symbols" | 39 | rm -rf "$CODE_ROOT/llm_debug_symbols" |
| 40 | fi | 40 | fi |
| 41 | 41 | ||
| 42 | - mkdir -p $OUTPUT_DIR $CACHE_DIR $BUILD_DIR | 42 | + mkdir -p $OUTPUT_DIR $CACHE_DIR $BUILD_DIR $MINDIE_LLM_LIB_DIR |
| 43 | 43 | ||
| 44 | if [ "$CMAKE_CXX_COMPILER_LAUNCHER" == "" ] && command -v ccache &> /dev/null;then | 44 | if [ "$CMAKE_CXX_COMPILER_LAUNCHER" == "" ] && command -v ccache &> /dev/null;then |
| 45 | COMPILE_OPTIONS="${COMPILE_OPTIONS} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" | 45 | COMPILE_OPTIONS="${COMPILE_OPTIONS} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" |
| @@ -54,6 +54,7 @@ function fn_build() | |||
| 54 | fn_build_version_info | 54 | fn_build_version_info |
| 55 | fn_build_third_party | 55 | fn_build_third_party |
| 56 | fn_build_src | 56 | fn_build_src |
| 57 | + cp $OUTPUT_DIR/lib/libfoundation.so $MINDIE_LLM_LIB_DIR/foundation.so | ||
| 57 | if [ "$build_type" = "release" ]; then | 58 | if [ "$build_type" = "release" ]; then |
| 58 | fn_extract_debug_symbols $OUTPUT_DIR "$CODE_ROOT/llm_debug_symbols" | 59 | fn_extract_debug_symbols $OUTPUT_DIR "$CODE_ROOT/llm_debug_symbols" |
| 59 | fi | 60 | fi |
| @@ -6,6 +6,8 @@ CACHE_DIR=$CODE_ROOT/.cache | |||
| 6 | BUILD_DIR=$CODE_ROOT/build | 6 | BUILD_DIR=$CODE_ROOT/build |
| 7 | OUTPUT_DIR=$CODE_ROOT/output | 7 | OUTPUT_DIR=$CODE_ROOT/output |
| 8 | RELEASE_DIR=$CODE_ROOT/release | 8 | RELEASE_DIR=$CODE_ROOT/release |
| 9 | +MINDIE_LLM_DIR=$CODE_ROOT/mindie_llm | ||
| 10 | +MINDIE_LLM_LIB_DIR=$MINDIE_LLM_DIR/lib | ||
| 9 | LOG_PATH="/var/log/mindie_log/" | 11 | LOG_PATH="/var/log/mindie_log/" |
| 10 | LOG_NAME="mindie_llm_install.log" | 12 | LOG_NAME="mindie_llm_install.log" |
| 11 | ARCH="aarch64" | 13 | ARCH="aarch64" |
| @@ -1,4 +1,5 @@ | |||
| 1 | add_subdirectory(utils) | 1 | add_subdirectory(utils) |
| 2 | +add_subdirectory(py_interface) | ||
| 2 | add_subdirectory(config_manager) | 3 | add_subdirectory(config_manager) |
| 3 | add_subdirectory(block_manager) | 4 | add_subdirectory(block_manager) |
| 4 | add_subdirectory(engine) | 5 | add_subdirectory(engine) |
| @@ -34,6 +34,7 @@ const std::string DEFAULT_MINDIE_LOG_VERBOSE = "1"; | |||
| 34 | const std::string DEFAULT_MINDIE_LOG_ROTATE = "-fs 20 -r 10"; // Rotating log files, 20 MB each, keep 10 files. | 34 | const std::string DEFAULT_MINDIE_LOG_ROTATE = "-fs 20 -r 10"; // Rotating log files, 20 MB each, keep 10 files. |
| 35 | const std::string DEFAULT_CHECK_PERM = ""; | 35 | const std::string DEFAULT_CHECK_PERM = ""; |
| 36 | 36 | ||
| 37 | +const std::string& GetDefaultMindIELLMHomePath(); | ||
| 37 | 38 | ||
| 38 | class EnvVar { | 39 | class EnvVar { |
| 39 | public: | 40 | public: |
| @@ -27,23 +27,28 @@ | |||
| 27 | 27 | ||
| 28 | 28 | ||
| 29 | 29 | ||
| 30 | + | ||
| 30 | 31 | ||
| 31 | namespace mindie_llm { | 32 | namespace mindie_llm { |
| 32 | -// AUDIT: mindie_llm::LogLine mandatory logging. | ||
| 33 | -enum class LogSeverity: uint8_t { AUDIT = 0, DEBUG, INFO, WARN, ERROR, CRITICAL, __COUNT__ }; | ||
| 34 | -// Logs of each type go into separate files. | ||
| 35 | -enum class LogType: uint8_t { GENERAL = 0, REQUEST, TOKEN, __COUNT__ }; | ||
| 36 | - | ||
| 37 | static const std::string ALL_COMPONENT = "__all__"; | 33 | static const std::string ALL_COMPONENT = "__all__"; |
| 34 | + | ||
| 35 | +// AUDIT: mindie_llm::LogLine mandatory logging. | ||
| 36 | +enum class LogSeverity: uint8_t { DEBUG, INFO, WARN, ERROR, CRITICAL, AUDIT, __COUNT__ }; | ||
| 37 | +const std::array<std::string, static_cast<uint8_t>(LogSeverity::__COUNT__) - 1>& GetLogSeverityNameArray(); | ||
| 38 | +const std::unordered_set<std::string>& GetAllLogSeverity(); | ||
| 39 | +bool String2LogSeverity(const std::string& level, LogSeverity& out); | ||
| 40 | + | ||
| 41 | +// Logs of each type go into separate files. | ||
| 42 | +enum class LogType: uint8_t { GENERAL = 0, REQUEST, TOKEN, TOKENIZER, __COUNT__ }; | ||
| 43 | +const std::array<std::string, static_cast<uint8_t>(LogType::__COUNT__)>& GetLogTypeNameArray(); | ||
| 44 | +bool String2LogType(const std::string& s, LogType& out); | ||
| 45 | + | ||
| 38 | // Component enumeration object supporting independent control, with all component logs falling into same file by PID | 46 | // Component enumeration object supporting independent control, with all component logs falling into same file by PID |
| 39 | enum class LogComponent: uint8_t { LLM = 0, LLMMODELS, SERVER, __COUNT__ }; | 47 | enum class LogComponent: uint8_t { LLM = 0, LLMMODELS, SERVER, __COUNT__ }; |
| 40 | -inline const std::string& ComponentToString(LogComponent c) | 48 | +const std::array<std::string, static_cast<uint8_t>(LogComponent::__COUNT__)>& GetComponentNameArray(); |
| 41 | -{ | 49 | +const std::string& Component2String(LogComponent c); |
| 42 | - static const std::array<std::string, static_cast<uint8_t>(LogComponent::__COUNT__)> compNames = { | 50 | +bool String2Component(const std::string& s, LogComponent& out); |
| 43 | - "llm", "llmmodels", "server" | 51 | + |
| 44 | - }; | ||
| 45 | - return compNames[static_cast<uint8_t>(c)]; | ||
| 46 | -} | ||
| 47 | // Buffer-pushing struct for async writer thread consumption | 52 | // Buffer-pushing struct for async writer thread consumption |
| 48 | struct MsgPkg { | 53 | struct MsgPkg { |
| 49 | LogComponent component; | 54 | LogComponent component; |
| @@ -65,8 +70,6 @@ struct LogSink { | |||
| 65 | size_t curSize; | 70 | size_t curSize; |
| 66 | }; | 71 | }; |
| 67 | 72 | ||
| 68 | -bool String2LogLevel(const std::string& level, LogSeverity& out); | ||
| 69 | - | ||
| 70 | // ================= LogManager ================= | 73 | // ================= LogManager ================= |
| 71 | 74 | ||
| 72 | class LogManager { | 75 | class LogManager { |
| @@ -97,7 +100,7 @@ public: | |||
| 97 | auto kv = ParseKeyValueString(val, validValues, ALL_COMPONENT, ';', ':'); | 100 | auto kv = ParseKeyValueString(val, validValues, ALL_COMPONENT, ';', ':'); |
| 98 | for (size_t i = 0; i < componentCfgs_.size(); ++i) { | 101 | for (size_t i = 0; i < componentCfgs_.size(); ++i) { |
| 99 | auto& cfg = componentCfgs_[i]; | 102 | auto& cfg = componentCfgs_[i]; |
| 100 | - const std::string& componentName = ComponentToString(static_cast<LogComponent>(i)); | 103 | + const std::string& componentName = Component2String(static_cast<LogComponent>(i)); |
| 101 | if (kv.count(ALL_COMPONENT)) { | 104 | if (kv.count(ALL_COMPONENT)) { |
| 102 | const T value = parser(kv.at(ALL_COMPONENT)); | 105 | const T value = parser(kv.at(ALL_COMPONENT)); |
| 103 | setter(cfg, value); | 106 | setter(cfg, value); |
| @@ -119,10 +122,12 @@ private: | |||
| 119 | void GetLogRotate(); | 122 | void GetLogRotate(); |
| 120 | void GetLogDirs(); | 123 | void GetLogDirs(); |
| 121 | void OpenLogFiles(); | 124 | void OpenLogFiles(); |
| 122 | - void RenewLogFilePath(LogType type); | 125 | + void CreateLogFilePath(LogType type); |
| 123 | 126 | ||
| 124 | void Writer(); | 127 | void Writer(); |
| 125 | void FlushLoop(); | 128 | void FlushLoop(); |
| 129 | + uint32_t GetLogFileSizeCutOff(LogType type) const; | ||
| 130 | + uint32_t GetLogFileNumCutOff(LogType type) const; | ||
| 126 | void RotateLogs(LogType type); | 131 | void RotateLogs(LogType type); |
| 127 | void Stop(); | 132 | void Stop(); |
| 128 | 133 | ||
| @@ -155,7 +160,7 @@ public: | |||
| 155 | return *this; | 160 | return *this; |
| 156 | } | 161 | } |
| 157 | 162 | ||
| 158 | - void AssembleAndPush(LogType type, const char* file, size_t line); | 163 | + void AssembleAndPush(LogType type, const char* file, size_t line, std::string& stack); |
| 159 | void Reset(); | 164 | void Reset(); |
| 160 | 165 | ||
| 161 | private: | 166 | private: |
| @@ -191,24 +196,91 @@ public: | |||
| 191 | return *this; | 196 | return *this; |
| 192 | } | 197 | } |
| 193 | 198 | ||
| 199 | +private: | ||
| 200 | + std::string BuildStackTrace(); | ||
| 201 | + | ||
| 194 | private: | 202 | private: |
| 195 | Logger& logger_; | 203 | Logger& logger_; |
| 196 | bool enabled_{false}; | 204 | bool enabled_{false}; |
| 197 | LogType type_{LogType::GENERAL}; | 205 | LogType type_{LogType::GENERAL}; |
| 198 | const char* file_; | 206 | const char* file_; |
| 199 | size_t line_; | 207 | size_t line_; |
| 208 | + std::string stack_; | ||
| 200 | }; | 209 | }; |
| 201 | 210 | ||
| 202 | // ================= GetThreadLogger ================= | 211 | // ================= GetThreadLogger ================= |
| 203 | 212 | ||
| 204 | Logger& GetThreadLogger(LogComponent comp, LogSeverity level); | 213 | Logger& GetThreadLogger(LogComponent comp, LogSeverity level); |
| 205 | 214 | ||
| 215 | +// ================= DynamicLogManager ================= | ||
| 216 | + | ||
| 217 | +struct DynamicLogConfig { | ||
| 218 | + std::string logSeverity; | ||
| 219 | + int validHours{2}; | ||
| 220 | + std::string validTimeStamp; | ||
| 221 | +}; | ||
| 222 | + | ||
| 223 | +struct DynamicLogDiff { | ||
| 224 | + bool logSeverityChanged{false}; | ||
| 225 | + bool validHoursChanged{false}; | ||
| 226 | + bool validTimeStampChanged{false}; | ||
| 227 | +}; | ||
| 228 | + | ||
| 229 | +class DynamicLogManager { | ||
| 230 | +public: | ||
| 231 | + static DynamicLogManager& GetInstance(); | ||
| 232 | + | ||
| 233 | +private: | ||
| 234 | + DynamicLogManager(); | ||
| 235 | + ~DynamicLogManager(); | ||
| 236 | + | ||
| 237 | + void Init(); | ||
| 238 | + void Stop(); | ||
| 239 | + void GetDefaultLogSeverity(); | ||
| 240 | + void Monitor(); | ||
| 241 | + void GetAndSetLogConfig(); | ||
| 242 | + std::string GetConfigPath() const; | ||
| 243 | + DynamicLogConfig LoadLogConfig(const std::string& configPath); | ||
| 244 | + std::string GetLogSeverity(const Json& logConfig) const; | ||
| 245 | + int GetTimeInterval(const Json& logConfig, int lastHours) const; | ||
| 246 | + std::string GetTimeStamp(const Json& logConfig, const std::string& lastTs) const; | ||
| 247 | + bool IsValidTimeFormat(const std::string& timeStr) const; | ||
| 248 | + bool ParseTime(const std::string& s, std::time_t& out) const; | ||
| 249 | + bool IsGreaterThanNow(const std::string& timeStr) const; | ||
| 250 | + | ||
| 251 | + DynamicLogDiff DiffConfig(const DynamicLogConfig& current, const DynamicLogConfig& last); | ||
| 252 | + void ResetToDefaultLogSeverity(); | ||
| 253 | + void ApplyLogSeverity(const std::string& severity); | ||
| 254 | + void UpdateValidTimeStamp(DynamicLogConfig& cfg); | ||
| 255 | + bool IsWithinValidRange(const DynamicLogConfig& cfg) const; | ||
| 256 | + | ||
| 257 | +private: | ||
| 258 | + const std::string keyLogConfig = "LogConfig"; | ||
| 259 | + const std::string keyLogSeverity = "dynamicLogLevel"; | ||
| 260 | + const std::string keyTimeInterval = "dynamicLogLevelValidHours"; | ||
| 261 | + const std::string keyTimeStamp = "dynamicLogLevelValidTime"; | ||
| 262 | + | ||
| 263 | +private: | ||
| 264 | + std::atomic<bool> isRunning_{false}; | ||
| 265 | + std::thread monitorThread_; | ||
| 266 | + std::mutex mtx_; | ||
| 267 | + static constexpr uint8_t monitorInterval_{5}; | ||
| 268 | + static constexpr int defaultHours_{2}; | ||
| 269 | + | ||
| 270 | + std::string defaultLogSeverity_{"info"}; | ||
| 271 | + std::string lastLogSeverity_; | ||
| 272 | + int lastValidHours_{defaultHours_}; | ||
| 273 | + std::string lastValidTimeStamp_; | ||
| 274 | +}; | ||
| 275 | + | ||
| 276 | +// ================= InitSystemLog ================= | ||
| 277 | + | ||
| 278 | +void InitSystemLog(); | ||
| 279 | + | ||
| 206 | } // namespace mindie_llm | 280 | } // namespace mindie_llm |
| 207 | 281 | ||
| 208 | // ================= Macro ================= | 282 | // ================= Macro ================= |
| 209 | // llm log interface | 283 | // llm log interface |
| 210 | - | ||
| 211 | - mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__) | ||
| 212 | 284 | ||
| 213 | mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::DEBUG, __FILE__, __LINE__) | 285 | mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::DEBUG, __FILE__, __LINE__) |
| 214 | 286 | ||
| @@ -219,9 +291,9 @@ Logger& GetThreadLogger(LogComponent comp, LogSeverity level); | |||
| 219 | mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::ERROR, __FILE__, __LINE__) | 291 | mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::ERROR, __FILE__, __LINE__) |
| 220 | 292 | ||
| 221 | mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::CRITICAL, __FILE__, __LINE__) | 293 | mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::CRITICAL, __FILE__, __LINE__) |
| 294 | + | ||
| 295 | + mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__) | ||
| 222 | // model log interface | 296 | // model log interface |
| 223 | - | ||
| 224 | - mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__) | ||
| 225 | 297 | ||
| 226 | mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::DEBUG, __FILE__, __LINE__) | 298 | mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::DEBUG, __FILE__, __LINE__) |
| 227 | 299 | ||
| @@ -232,9 +304,9 @@ Logger& GetThreadLogger(LogComponent comp, LogSeverity level); | |||
| 232 | mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::ERROR, __FILE__, __LINE__) | 304 | mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::ERROR, __FILE__, __LINE__) |
| 233 | 305 | ||
| 234 | mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::CRITICAL, __FILE__, __LINE__) | 306 | mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::CRITICAL, __FILE__, __LINE__) |
| 307 | + | ||
| 308 | + mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__) | ||
| 235 | // server log interface | 309 | // server log interface |
| 236 | - | ||
| 237 | - mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__) | ||
| 238 | 310 | ||
| 239 | mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::DEBUG, __FILE__, __LINE__) | 311 | mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::DEBUG, __FILE__, __LINE__) |
| 240 | 312 | ||
| @@ -245,5 +317,7 @@ Logger& GetThreadLogger(LogComponent comp, LogSeverity level); | |||
| 245 | mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::ERROR, __FILE__, __LINE__) | 317 | mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::ERROR, __FILE__, __LINE__) |
| 246 | 318 | ||
| 247 | mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::CRITICAL, __FILE__, __LINE__) | 319 | mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::CRITICAL, __FILE__, __LINE__) |
| 320 | + | ||
| 321 | + mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__) | ||
| 248 | 322 | ||
| 249 | 323 | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | +file(GLOB_RECURSE SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/*.cpp") | ||
| 2 | + | ||
| 3 | +add_library(foundation SHARED ${SOURCE_FILES}) | ||
| 4 | + | ||
| 5 | +target_link_libraries(foundation | ||
| 6 | + system_log | ||
| 7 | +) | ||
| 8 | + | ||
| 9 | +install(TARGETS foundation DESTINATION lib) | ||
| @@ -0,0 +1,53 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2026. All rights reserved. | ||
| 3 | + * MindIE is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +namespace FOUNDATION { | ||
| 18 | + PyDoc_STRVAR(InterfaceModuleDoc, "The part of the MindIE-LLM module that is implemented in CXX."); | ||
| 19 | + | ||
| 20 | + static PyModuleDef g_InterfaceModule = { | ||
| 21 | + PyModuleDef_HEAD_INIT, | ||
| 22 | + "foundation", // m_name | ||
| 23 | + InterfaceModuleDoc, // m_doc | ||
| 24 | + -1, // m_size | ||
| 25 | + nullptr, // m_methods | ||
| 26 | + nullptr, // m_slots | ||
| 27 | + nullptr, // m_traverse | ||
| 28 | + nullptr, // m_clear | ||
| 29 | + nullptr // m_free | ||
| 30 | + }; | ||
| 31 | + | ||
| 32 | + PyMODINIT_FUNC PyInit_foundation(void) | ||
| 33 | + { | ||
| 34 | + PyObject* m = PyModule_Create(&g_InterfaceModule); | ||
| 35 | + if (!m) { | ||
| 36 | + return nullptr; | ||
| 37 | + } | ||
| 38 | + PyObject* pyLog = GetLogModule(); | ||
| 39 | + if (!pyLog) { | ||
| 40 | + PyErr_SetString(PyExc_ImportError, "Failed to create log submodule."); | ||
| 41 | + Py_DECREF(m); | ||
| 42 | + return nullptr; | ||
| 43 | + } | ||
| 44 | + Py_INCREF(pyLog); | ||
| 45 | + if (PyModule_AddObject(m, "log", pyLog) < 0) { | ||
| 46 | + PyErr_SetString(PyExc_ImportError, "Failed to add log submodule."); | ||
| 47 | + Py_DECREF(pyLog); | ||
| 48 | + Py_DECREF(m); | ||
| 49 | + return nullptr; | ||
| 50 | + } | ||
| 51 | + return m; | ||
| 52 | + } | ||
| 53 | +} // namespace FOUNDATION | ||
| @@ -0,0 +1,159 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2026. All rights reserved. | ||
| 3 | + * MindIE is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +using namespace mindie_llm; | ||
| 21 | + | ||
| 22 | +namespace FOUNDATION { | ||
| 23 | + PyDoc_STRVAR(LogModuleDoc, "C++ logging bindings based on system_log."); | ||
| 24 | + | ||
| 25 | + static void LogLineForPyObject(LogComponent comp, LogSeverity level, LogType type, const char* msg) | ||
| 26 | + { | ||
| 27 | + auto& mgr = LogManager::GetInstance(); | ||
| 28 | + if (!mgr.IsPrintLog(comp, level)) { | ||
| 29 | + return; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + const char* filename = ""; | ||
| 33 | + size_t lineNum = 0; | ||
| 34 | + PyGILState_STATE gil = PyGILState_Ensure(); | ||
| 35 | + PyFrameObject* frame = PyEval_GetFrame(); | ||
| 36 | + PyFrameObject* caller = frame ? PyFrame_GetBack(frame) : nullptr; | ||
| 37 | + PyCodeObject* code = caller ? PyFrame_GetCode(caller) : nullptr; | ||
| 38 | + if (code) { | ||
| 39 | + PyObject* pyFile = code->co_filename; | ||
| 40 | + if (pyFile) filename = PyUnicode_AsUTF8(pyFile); | ||
| 41 | + lineNum = static_cast<size_t>(PyFrame_GetLineNumber(caller)); | ||
| 42 | + } | ||
| 43 | + Py_XDECREF(code); | ||
| 44 | + Py_XDECREF(caller); | ||
| 45 | + PyGILState_Release(gil); | ||
| 46 | + | ||
| 47 | + LogLine line(comp, level, filename, lineNum); | ||
| 48 | + line.SetType(type) << msg; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + static PyObject* PyLog(PyObject*, PyObject* args, LogSeverity level) | ||
| 52 | + { | ||
| 53 | + const char* msg; | ||
| 54 | + const char* compStr; | ||
| 55 | + const char* typeStr = "general"; | ||
| 56 | + | ||
| 57 | + if (!PyArg_ParseTuple(args, "ss|s", &msg, &compStr, &typeStr)) { | ||
| 58 | + return nullptr; | ||
| 59 | + } | ||
| 60 | + LogComponent comp; | ||
| 61 | + if (!String2Component(compStr, comp)) { | ||
| 62 | + PyErr_Format(PyExc_ValueError, "invalid component: '%s'", compStr); | ||
| 63 | + return nullptr; | ||
| 64 | + } | ||
| 65 | + LogType type; | ||
| 66 | + if (!String2LogType(typeStr, type)) { | ||
| 67 | + PyErr_Format(PyExc_ValueError, "invalid log type: '%s'", typeStr); | ||
| 68 | + return nullptr; | ||
| 69 | + } | ||
| 70 | + LogLineForPyObject(comp, level, type, msg); | ||
| 71 | + Py_RETURN_NONE; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + static PyObject* PyDebug(PyObject* s, PyObject* a) | ||
| 75 | + { | ||
| 76 | + return PyLog(s, a, LogSeverity::DEBUG); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + static PyObject* PyInfo(PyObject* s, PyObject* a) | ||
| 80 | + { | ||
| 81 | + return PyLog(s, a, LogSeverity::INFO); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + static PyObject* PyWarn(PyObject* s, PyObject* a) | ||
| 85 | + { | ||
| 86 | + return PyLog(s, a, LogSeverity::WARN); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + static PyObject* PyError(PyObject* s, PyObject* a) | ||
| 90 | + { | ||
| 91 | + return PyLog(s, a, LogSeverity::ERROR); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + static PyObject* PyCritical(PyObject* s, PyObject* a) | ||
| 95 | + { | ||
| 96 | + return PyLog(s, a, LogSeverity::CRITICAL); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + static PyObject* PyAudit(PyObject* s, PyObject* a) | ||
| 100 | + { | ||
| 101 | + return PyLog(s, a, LogSeverity::AUDIT); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + static PyObject* PySetLogLevel(PyObject*, PyObject* arg) | ||
| 105 | + { | ||
| 106 | + if (!PyUnicode_Check(arg)) { | ||
| 107 | + PyErr_SetString(PyExc_TypeError, "set_log_level(level: str), e.g. 'debug', 'llm:info;llmmodels:warn'" | ||
| 108 | + ); | ||
| 109 | + return nullptr; | ||
| 110 | + } | ||
| 111 | + const char* levelStr = PyUnicode_AsUTF8(arg); | ||
| 112 | + if (levelStr == nullptr) { | ||
| 113 | + return nullptr; | ||
| 114 | + } | ||
| 115 | + const std::string level(levelStr); | ||
| 116 | + LogManager::GetInstance().LoadByComponentByString<LogSeverity>(level, GetAllLogSeverity(), | ||
| 117 | + [](const std::string& s) { | ||
| 118 | + LogSeverity lvl; | ||
| 119 | + if (!String2LogSeverity(s, lvl)) { | ||
| 120 | + return LogSeverity::INFO; | ||
| 121 | + } | ||
| 122 | + return lvl; | ||
| 123 | + }, | ||
| 124 | + [](ComponentConfig& c, LogSeverity v) { | ||
| 125 | + c.minLevel = v; | ||
| 126 | + } | ||
| 127 | + ); | ||
| 128 | + Py_RETURN_NONE; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + static PyMethodDef LogMethods[] = { | ||
| 132 | + {"debug", PyDebug, METH_VARARGS, "debug(msg, comp, 'general')"}, | ||
| 133 | + {"info", PyInfo, METH_VARARGS, "info(msg, comp, 'general')"}, | ||
| 134 | + {"warn", PyWarn, METH_VARARGS, "warn(msg, comp, 'general')"}, | ||
| 135 | + {"error", PyError, METH_VARARGS, "error(msg, comp, 'general')"}, | ||
| 136 | + {"critical", PyCritical, METH_VARARGS, "critical(msg, comp, 'general')"}, | ||
| 137 | + {"audit", PyAudit, METH_VARARGS, "audit(msg, comp, 'general')"}, | ||
| 138 | + {"set_log_level", PySetLogLevel, METH_O, "set_log_level(level: str)"}, | ||
| 139 | + {nullptr, nullptr, 0, nullptr} | ||
| 140 | + }; | ||
| 141 | + | ||
| 142 | + static PyModuleDef g_LogModule = { | ||
| 143 | + PyModuleDef_HEAD_INIT, | ||
| 144 | + "foundation.log", // m_name | ||
| 145 | + LogModuleDoc, // m_doc | ||
| 146 | + -1, // m_size | ||
| 147 | + LogMethods, // m_methods | ||
| 148 | + nullptr, // m_slots | ||
| 149 | + nullptr, // m_traverse | ||
| 150 | + nullptr, // m_clear | ||
| 151 | + nullptr // m_free | ||
| 152 | + }; | ||
| 153 | + | ||
| 154 | + PyObject* GetLogModule() | ||
| 155 | + { | ||
| 156 | + return PyModule_Create(&g_LogModule); | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | +} // namespace FOUNDATION | ||
| @@ -0,0 +1,22 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2026. All rights reserved. | ||
| 3 | + * MindIE is licensed under Mulan PSL v2. | ||
| 4 | + * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 5 | + * You may obtain a copy of Mulan PSL v2 at: | ||
| 6 | + * http://license.coscl.org.cn/MulanPSL2 | ||
| 7 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 8 | + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 9 | + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 10 | + * See the Mulan PSL v2 for more details. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +namespace FOUNDATION { | ||
| 19 | + PyObject *GetLogModule(); | ||
| 20 | +} // namespace FOUNDATION | ||
| 21 | + | ||
| 22 | + | ||
| @@ -32,6 +32,7 @@ target_link_libraries(${TARGET_NAME} | |||
| 32 | mindieservice_endpoint | 32 | mindieservice_endpoint |
| 33 | config_manager | 33 | config_manager |
| 34 | mindie_llm_utils | 34 | mindie_llm_utils |
| 35 | + system_log | ||
| 35 | pybind11::embed | 36 | pybind11::embed |
| 36 | ) | 37 | ) |
| 37 | 38 | ||
| @@ -30,6 +30,7 @@ | |||
| 30 | 30 | ||
| 31 | 31 | ||
| 32 | 32 | ||
| 33 | + | ||
| 33 | 34 | ||
| 34 | using namespace mindie_llm; | 35 | using namespace mindie_llm; |
| 35 | static std::mutex g_exitMtx; | 36 | static std::mutex g_exitMtx; |
| @@ -315,9 +316,7 @@ void RegisterSignal(void) | |||
| 315 | 316 | ||
| 316 | void RunEP(std::unordered_map<std::string, std::string> commandLineArgsMap) | 317 | void RunEP(std::unordered_map<std::string, std::string> commandLineArgsMap) |
| 317 | { | 318 | { |
| 318 | - Py_Initialize(); | ||
| 319 | PyEval_SaveThread(); | 319 | PyEval_SaveThread(); |
| 320 | - | ||
| 321 | pthread_setname_np(pthread_self(), "RunEP"); | 320 | pthread_setname_np(pthread_self(), "RunEP"); |
| 322 | std::string fileNamePrefix = "mindie-server"; | 321 | std::string fileNamePrefix = "mindie-server"; |
| 323 | EndPoint ep; | 322 | EndPoint ep; |
| @@ -390,6 +389,8 @@ bool ParseCommandLineArgs(int &argc, char **argv, std::unordered_map<std::string | |||
| 390 | 389 | ||
| 391 | int main(int argc, char *argv[]) | 390 | int main(int argc, char *argv[]) |
| 392 | { | 391 | { |
| 392 | + Py_Initialize(); | ||
| 393 | + InitSystemLog(); | ||
| 393 | static_assert(std::atomic<bool>::is_always_lock_free, "Bool type should be lock-free."); | 394 | static_assert(std::atomic<bool>::is_always_lock_free, "Bool type should be lock-free."); |
| 394 | g_mainPid = getpid(); | 395 | g_mainPid = getpid(); |
| 395 | std::cerr << "g_mainPid = " << g_mainPid << std::endl; | 396 | std::cerr << "g_mainPid = " << g_mainPid << std::endl; |
| @@ -11,8 +11,49 @@ | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 14 | namespace mindie_llm { | 18 | namespace mindie_llm { |
| 15 | 19 | ||
| 20 | +static std::string GetSitePackagesPath() | ||
| 21 | +{ | ||
| 22 | + PyGILState_STATE gil = PyGILState_Ensure(); | ||
| 23 | + std::string result; | ||
| 24 | + PyObject* site_module = PyImport_ImportModule("site"); | ||
| 25 | + if (!site_module) { | ||
| 26 | + PyErr_Print(); | ||
| 27 | + PyGILState_Release(gil); | ||
| 28 | + return ""; | ||
| 29 | + } | ||
| 30 | + PyObject* func = PyObject_GetAttrString(site_module, "getsitepackages"); | ||
| 31 | + if (func && PyCallable_Check(func)) { | ||
| 32 | + PyObject* list = PyObject_CallObject(func, nullptr); | ||
| 33 | + if (list && PyList_Size(list) > 0) { | ||
| 34 | + PyObject* item = PyList_GetItem(list, 0); | ||
| 35 | + if (PyUnicode_Check(item)) { | ||
| 36 | + result = PyUnicode_AsUTF8(item); | ||
| 37 | + } | ||
| 38 | + } | ||
| 39 | + Py_XDECREF(list); | ||
| 40 | + } | ||
| 41 | + Py_XDECREF(func); | ||
| 42 | + Py_DECREF(site_module); | ||
| 43 | + PyGILState_Release(gil); | ||
| 44 | + return result; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +const std::string& GetDefaultMindIELLMHomePath() | ||
| 48 | +{ | ||
| 49 | + static std::string path; | ||
| 50 | + static std::once_flag once; | ||
| 51 | + std::call_once(once, [] { | ||
| 52 | + path = GetSitePackagesPath() + "/mindie_llm/"; | ||
| 53 | + }); | ||
| 54 | + return path; | ||
| 55 | +} | ||
| 56 | + | ||
| 16 | EnvVar& EnvVar::GetInstance() | 57 | EnvVar& EnvVar::GetInstance() |
| 17 | { | 58 | { |
| 18 | static EnvVar instance; | 59 | static EnvVar instance; |
| @@ -12,9 +12,9 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 15 | 16 | ||
| 16 | 17 | ||
| 17 | - | ||
| 18 | 18 | ||
| 19 | namespace mindie_llm { | 19 | namespace mindie_llm { |
| 20 | 20 | ||
| @@ -68,23 +68,21 @@ bool CheckJsonDepthCallbackNoLogger(int depth, Json::parse_event_t ev, Json& obj | |||
| 68 | bool CheckJsonDepthCallback(int depth, Json::parse_event_t ev, Json& obj) | 68 | bool CheckJsonDepthCallback(int depth, Json::parse_event_t ev, Json& obj) |
| 69 | { | 69 | { |
| 70 | return CheckJsonDepthWithLogger(depth, ev, [depth, &obj]() { | 70 | return CheckJsonDepthWithLogger(depth, ev, [depth, &obj]() { |
| 71 | - MINDIE_LLM_LOG_ERROR("Failed to parse json: depth is " << depth << ", object is " << sizeof(obj)); | 71 | + std::cerr << "Failed to parse json: depth is " << depth << ", object is " << sizeof(obj); |
| 72 | }); | 72 | }); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | bool CheckJsonDepthCallbackUlog(int depth, Json::parse_event_t ev, Json& obj) | 75 | bool CheckJsonDepthCallbackUlog(int depth, Json::parse_event_t ev, Json& obj) |
| 76 | { | 76 | { |
| 77 | return CheckJsonDepthWithLogger(depth, ev, [depth, &obj]() { | 77 | return CheckJsonDepthWithLogger(depth, ev, [depth, &obj]() { |
| 78 | - ULOG_ERROR(SUBMODLE_NAME_ENDPOINT, GenerateEndpointErrCode(ERROR, SUBMODLE_FEATURE_SINGLE_INFERENCE, | 78 | + std::cerr << "Failed to parse json: depth is " << depth << ", object is " << sizeof(obj); |
| 79 | - JSON_PARSE_ERROR), "Failed to parse json: depth is " << depth << ", object is " << sizeof(obj)); | ||
| 80 | }); | 79 | }); |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | bool CheckOrderedJsonDepthCallback(int depth, OrderedJson::parse_event_t ev, OrderedJson& obj) | 82 | bool CheckOrderedJsonDepthCallback(int depth, OrderedJson::parse_event_t ev, OrderedJson& obj) |
| 84 | { | 83 | { |
| 85 | return CheckJsonDepthWithLogger(depth, ev, [depth, &obj]() { | 84 | return CheckJsonDepthWithLogger(depth, ev, [depth, &obj]() { |
| 86 | - ULOG_ERROR(SUBMODLE_NAME_ENDPOINT, GenerateEndpointErrCode(ERROR, SUBMODLE_FEATURE_SINGLE_INFERENCE, | 85 | + std::cerr << "Failed to parse json: depth is " << depth << ", object is " << sizeof(obj); |
| 87 | - JSON_PARSE_ERROR), "Failed to parse json: depth is " << depth << ", object is " << sizeof(obj)); | ||
| 88 | }); | 86 | }); |
| 89 | } | 87 | } |
| 90 | 88 | ||
| @@ -1,10 +1,7 @@ | |||
| 1 | -cmake_minimum_required(VERSION 3.19.0) | ||
| 2 | -project("system_log") | ||
| 3 | -set(CMAKE_CXX_STANDARD 17) | ||
| 4 | - | ||
| 5 | file(GLOB SOURCE_FILES | 1 | file(GLOB SOURCE_FILES |
| 6 | "${CMAKE_CURRENT_LIST_DIR}/system_log.cpp" | 2 | "${CMAKE_CURRENT_LIST_DIR}/system_log.cpp" |
| 7 | "${CMAKE_CURRENT_LIST_DIR}/../safe_path.cpp" | 3 | "${CMAKE_CURRENT_LIST_DIR}/../safe_path.cpp" |
| 4 | + "${CMAKE_CURRENT_LIST_DIR}/../safe_io.cpp" | ||
| 8 | "${CMAKE_CURRENT_LIST_DIR}/../safe_envvar.cpp" | 5 | "${CMAKE_CURRENT_LIST_DIR}/../safe_envvar.cpp" |
| 9 | "${CMAKE_CURRENT_LIST_DIR}/../safe_result.cpp" | 6 | "${CMAKE_CURRENT_LIST_DIR}/../safe_result.cpp" |
| 10 | "${CMAKE_CURRENT_LIST_DIR}/../string_utils.cpp" | 7 | "${CMAKE_CURRENT_LIST_DIR}/../string_utils.cpp" |
| @@ -22,6 +22,11 @@ | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 25 | 30 | ||
| 26 | 31 | ||
| 27 | 32 | ||
| @@ -30,46 +35,104 @@ | |||
| 30 | namespace mindie_llm { | 35 | namespace mindie_llm { |
| 31 | 36 | ||
| 32 | const std::string LLM = "llm"; | 37 | const std::string LLM = "llm"; |
| 33 | -constexpr int DECIMAL_BASE = 10; | 38 | +static constexpr size_t DECIMAL_BASE = 10; |
| 34 | -constexpr size_t BUFFER_SIZE_32 = 32; | 39 | +static constexpr size_t BUFFER_SIZE_32 = 32; |
| 40 | +static constexpr size_t BUFFER_SIZE_256 = 256; | ||
| 41 | +static constexpr size_t BUFFER_SIZE_512 = 512; | ||
| 42 | +static constexpr size_t BUFFER_SIZE_2048 = 2048; | ||
| 35 | 43 | ||
| 36 | -static const std::unordered_map<std::string, LogSeverity> str2LogLevelMap = { | 44 | +const std::array<std::string, static_cast<uint8_t>(LogSeverity::__COUNT__) - 1>& GetLogSeverityNameArray() |
| 37 | - {"debug", LogSeverity::DEBUG}, | 45 | +{ |
| 38 | - {"info", LogSeverity::INFO}, | 46 | + // Note: levelNames are in the same order as LogSeverity. |
| 39 | - {"warn", LogSeverity::WARN}, | 47 | + static const std::array<std::string, static_cast<uint8_t>(LogSeverity::__COUNT__) - 1> levelNames = { |
| 40 | - {"error", LogSeverity::ERROR}, | 48 | + "debug", "info", "warn", "error", "critical" |
| 41 | - {"critical", LogSeverity::CRITICAL} | 49 | + }; |
| 42 | -}; | 50 | + return levelNames; |
| 51 | +} | ||
| 43 | 52 | ||
| 44 | -static const std::unordered_map<LogSeverity, std::string> logLevel2StrMap = { | 53 | +const std::unordered_set<std::string>& GetAllLogSeverity() |
| 45 | - {LogSeverity::AUDIT, "AUDIT"}, | 54 | +{ |
| 46 | - {LogSeverity::DEBUG, "DEBUG"}, | 55 | + static const std::unordered_set<std::string> values = [] { |
| 47 | - {LogSeverity::INFO, "INFO"}, | 56 | + std::unordered_set<std::string> s; |
| 48 | - {LogSeverity::WARN, "WARN"}, | 57 | + for (const auto& name : GetLogSeverityNameArray()) { |
| 49 | - {LogSeverity::ERROR, "ERROR"}, | 58 | + s.insert(name); |
| 50 | - {LogSeverity::CRITICAL, "CRITICAL"} | 59 | + } |
| 51 | -}; | 60 | + return s; |
| 61 | + }(); | ||
| 62 | + return values; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +const std::array<std::string, static_cast<uint8_t>(LogType::__COUNT__)>& GetLogTypeNameArray() | ||
| 66 | +{ | ||
| 67 | + // Note: typeNames are in the same order as LogType. | ||
| 68 | + static const std::array<std::string, static_cast<uint8_t>(LogType::__COUNT__)> typeNames = { | ||
| 69 | + "general", "request", "token", "tokenizer" | ||
| 70 | + }; | ||
| 71 | + return typeNames; | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +bool String2LogType(const std::string& s, LogType& out) | ||
| 75 | +{ | ||
| 76 | + const auto& typeNames = GetLogTypeNameArray(); | ||
| 77 | + for (uint8_t i = 0; i < typeNames.size(); ++i) { | ||
| 78 | + if (typeNames[i] == s) { | ||
| 79 | + out = static_cast<LogType>(i); | ||
| 80 | + return true; | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | + return false; | ||
| 84 | +} | ||
| 52 | 85 | ||
| 53 | static const std::unordered_map<LogType, std::string> logType2StrMap = { | 86 | static const std::unordered_map<LogType, std::string> logType2StrMap = { |
| 54 | - {LogType::GENERAL, "xxxmindie-llm"}, | 87 | + {LogType::GENERAL, "mindie-llm"}, |
| 55 | - {LogType::REQUEST, "xxxmindie-llm-request"}, | 88 | + {LogType::REQUEST, "mindie-llm-request"}, |
| 56 | - {LogType::TOKEN, "xxxmindie-llm-token"} | 89 | + {LogType::TOKEN, "mindie-llm-token"}, |
| 90 | + {LogType::TOKENIZER, "mindie-llm-tokenizer"} | ||
| 57 | }; | 91 | }; |
| 58 | 92 | ||
| 93 | +const std::array<std::string, static_cast<uint8_t>(LogComponent::__COUNT__)>& GetComponentNameArray() | ||
| 94 | +{ | ||
| 95 | + // Note: compNames are in the same order as LogComponent. | ||
| 96 | + static const std::array<std::string, static_cast<uint8_t>(LogComponent::__COUNT__)> compNames = { | ||
| 97 | + "llm", "llmmodels", "server" | ||
| 98 | + }; | ||
| 99 | + return compNames; | ||
| 100 | +} | ||
| 101 | + | ||
| 102 | +const std::string& Component2String(LogComponent c) | ||
| 103 | +{ | ||
| 104 | + const auto& compNames = GetComponentNameArray(); | ||
| 105 | + return compNames[static_cast<uint8_t>(c)]; | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +bool String2Component(const std::string& s, LogComponent& out) | ||
| 109 | +{ | ||
| 110 | + const auto& compNames = GetComponentNameArray(); | ||
| 111 | + for (uint8_t i = 0; i < compNames.size(); ++i) { | ||
| 112 | + if (compNames[i] == s) { | ||
| 113 | + out = static_cast<LogComponent>(i); | ||
| 114 | + return true; | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + return false; | ||
| 118 | +} | ||
| 119 | + | ||
| 59 | enum class TimestampFormat { READABLE, TIGHT }; | 120 | enum class TimestampFormat { READABLE, TIGHT }; |
| 60 | 121 | ||
| 61 | // ================= Log utils ================= | 122 | // ================= Log utils ================= |
| 62 | 123 | ||
| 63 | -bool String2LogLevel(const std::string& in, LogSeverity& out) | 124 | +bool String2LogSeverity(const std::string& in, LogSeverity& out) |
| 64 | { | 125 | { |
| 65 | std::string key = in; | 126 | std::string key = in; |
| 66 | ToLower(key); | 127 | ToLower(key); |
| 67 | - auto it = str2LogLevelMap.find(key); | 128 | + const auto& names = GetLogSeverityNameArray(); |
| 68 | - if (it == str2LogLevelMap.end()) { | 129 | + for (uint8_t i = 0; i < names.size(); ++i) { |
| 69 | - return false; | 130 | + if (names[i] == key) { |
| 131 | + out = static_cast<LogSeverity>(i); | ||
| 132 | + return true; | ||
| 133 | + } | ||
| 70 | } | 134 | } |
| 71 | - out = it->second; | 135 | + return false; |
| 72 | - return true; | ||
| 73 | } | 136 | } |
| 74 | 137 | ||
| 75 | void AppendCurTimestamp(std::string& out, TimestampFormat format) | 138 | void AppendCurTimestamp(std::string& out, TimestampFormat format) |
| @@ -102,7 +165,7 @@ void AppendCurTimestamp(std::string& out, TimestampFormat format) | |||
| 102 | 165 | ||
| 103 | std::string GetTightTimestamp() | 166 | std::string GetTightTimestamp() |
| 104 | { | 167 | { |
| 105 | - constexpr size_t tightTimestampLength = 17; // YYYYMMDDHHMMSSmmm | 168 | + static constexpr size_t tightTimestampLength = 17; // YYYYMMDDHHMMSSmmm |
| 106 | std::string ts; | 169 | std::string ts; |
| 107 | ts.reserve(tightTimestampLength); | 170 | ts.reserve(tightTimestampLength); |
| 108 | AppendCurTimestamp(ts, TimestampFormat::TIGHT); | 171 | AppendCurTimestamp(ts, TimestampFormat::TIGHT); |
| @@ -145,10 +208,18 @@ inline void AppendTid(std::string& out) | |||
| 145 | out.push_back(']'); | 208 | out.push_back(']'); |
| 146 | } | 209 | } |
| 147 | 210 | ||
| 148 | -inline std::string LogLevelToString(LogSeverity level) | 211 | +inline std::string LogSeverity2String(LogSeverity level) |
| 149 | { | 212 | { |
| 150 | - auto it = logLevel2StrMap.find(level); | 213 | + static const std::unordered_map<LogSeverity, std::string> logSeverity2StrMap = { |
| 151 | - if (it != logLevel2StrMap.end()) { | 214 | + {LogSeverity::DEBUG, "DEBUG"}, |
| 215 | + {LogSeverity::INFO, "INFO"}, | ||
| 216 | + {LogSeverity::WARN, "WARN"}, | ||
| 217 | + {LogSeverity::ERROR, "ERROR"}, | ||
| 218 | + {LogSeverity::CRITICAL, "CRITICAL"}, | ||
| 219 | + {LogSeverity::AUDIT, "AUDIT"} | ||
| 220 | + }; | ||
| 221 | + auto it = logSeverity2StrMap.find(level); | ||
| 222 | + if (it != logSeverity2StrMap.end()) { | ||
| 152 | return it->second; | 223 | return it->second; |
| 153 | } | 224 | } |
| 154 | return "INFO"; | 225 | return "INFO"; |
| @@ -157,17 +228,17 @@ inline std::string LogLevelToString(LogSeverity level) | |||
| 157 | inline void AppendLevel(std::string& out, LogSeverity level) | 228 | inline void AppendLevel(std::string& out, LogSeverity level) |
| 158 | { | 229 | { |
| 159 | out.append(" ["); | 230 | out.append(" ["); |
| 160 | - out.append(LogLevelToString(level)); | 231 | + out.append(LogSeverity2String(level)); |
| 161 | out.push_back(']'); | 232 | out.push_back(']'); |
| 162 | } | 233 | } |
| 163 | 234 | ||
| 164 | inline void FilterAndAppend(std::string& out, const char* input, size_t length) | 235 | inline void FilterAndAppend(std::string& out, const char* input, size_t length) |
| 165 | { | 236 | { |
| 166 | - constexpr unsigned char kAsciiControlMin = 0x00; | 237 | + static constexpr unsigned char kAsciiControlMin = 0x00; |
| 167 | - constexpr unsigned char kAsciiControlMax = 0x1F; | 238 | + static constexpr unsigned char kAsciiControlMax = 0x1F; |
| 168 | - constexpr unsigned char kAsciiDelete = 0x7F; | 239 | + static constexpr unsigned char kAsciiDelete = 0x7F; |
| 169 | - constexpr unsigned char kLineFeed = '\n'; | 240 | + static constexpr unsigned char kLineFeed = '\n'; |
| 170 | - constexpr unsigned char kCarriageReturn = '\r'; | 241 | + static constexpr unsigned char kCarriageReturn = '\r'; |
| 171 | const char* cursor = input; | 242 | const char* cursor = input; |
| 172 | const char* end = input + length; | 243 | const char* end = input + length; |
| 173 | while (cursor < end) { | 244 | while (cursor < end) { |
| @@ -202,8 +273,8 @@ void ParseRotateArgs(const std::string& argsStr, uint32_t& outLogFileSize, uint3 | |||
| 202 | if (!r.IsOk()) { | 273 | if (!r.IsOk()) { |
| 203 | throw std::runtime_error(r.message()); | 274 | throw std::runtime_error(r.message()); |
| 204 | } | 275 | } |
| 205 | - constexpr size_t fileNumLimit1 = 1; | 276 | + static constexpr size_t fileNumLimit1 = 1; |
| 206 | - constexpr size_t fileNumLimit64 = 64; | 277 | + static constexpr size_t fileNumLimit64 = 64; |
| 207 | if (outLogFileNum < fileNumLimit1 || outLogFileNum > fileNumLimit64) { | 278 | if (outLogFileNum < fileNumLimit1 || outLogFileNum > fileNumLimit64) { |
| 208 | throw std::runtime_error("Log file count must be between " + std::to_string(fileNumLimit1) + " and " + | 279 | throw std::runtime_error("Log file count must be between " + std::to_string(fileNumLimit1) + " and " + |
| 209 | std::to_string(fileNumLimit64) + ", got: " + std::to_string(outLogFileNum)); | 280 | std::to_string(fileNumLimit64) + ", got: " + std::to_string(outLogFileNum)); |
| @@ -228,16 +299,101 @@ static std::string MakeRotateName(const std::string& base, int idx) | |||
| 228 | return base + buf; | 299 | return base + buf; |
| 229 | } | 300 | } |
| 230 | 301 | ||
| 302 | +static std::string GetStackTrace(size_t skip) | ||
| 303 | +{ | ||
| 304 | + void* buffer[BUFFER_SIZE_32]; | ||
| 305 | + int nptrs = ::backtrace(buffer, BUFFER_SIZE_32) - 3; | ||
| 306 | + if (nptrs <= 0 || nptrs <= static_cast<int>(skip)) { | ||
| 307 | + return ""; | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + std::ostringstream oss; | ||
| 311 | + oss << "\nStack trace:\n"; | ||
| 312 | + for (int i = skip; i < nptrs; ++i) { | ||
| 313 | + Dl_info info{}; | ||
| 314 | + if (!dladdr(buffer[i], &info)) { | ||
| 315 | + oss << "#" << (i - skip) << " ??\n"; | ||
| 316 | + continue; | ||
| 317 | + } | ||
| 318 | + std::string function = "??"; | ||
| 319 | + if (info.dli_sname) { | ||
| 320 | + int status = 0; | ||
| 321 | + std::unique_ptr<char, void(*)(void*)> demangled( | ||
| 322 | + abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status), | ||
| 323 | + std::free | ||
| 324 | + ); | ||
| 325 | + if (status == 0 && demangled) { | ||
| 326 | + function = demangled.get(); | ||
| 327 | + } else { | ||
| 328 | + function = info.dli_sname; | ||
| 329 | + } | ||
| 330 | + } | ||
| 331 | + oss << "#" << (i - skip) << " " << function << "\n"; | ||
| 332 | + } | ||
| 333 | + return oss.str(); | ||
| 334 | +} | ||
| 335 | + | ||
| 336 | +static std::string GetPythonStackTrace() | ||
| 337 | +{ | ||
| 338 | + std::string result; | ||
| 339 | + PyGILState_STATE gil = PyGILState_Ensure(); | ||
| 340 | + | ||
| 341 | + PyObject* traceback = PyImport_ImportModule("traceback"); | ||
| 342 | + if (!traceback) { | ||
| 343 | + PyGILState_Release(gil); | ||
| 344 | + return result; | ||
| 345 | + } | ||
| 346 | + | ||
| 347 | + PyObject* formatFunc = PyObject_GetAttrString(traceback, "format_stack"); | ||
| 348 | + if (!formatFunc) { | ||
| 349 | + Py_DECREF(traceback); | ||
| 350 | + PyGILState_Release(gil); | ||
| 351 | + return result; | ||
| 352 | + } | ||
| 353 | + | ||
| 354 | + PyObject* stackList = PyObject_CallObject(formatFunc, nullptr); | ||
| 355 | + if (!stackList) { | ||
| 356 | + Py_DECREF(formatFunc); | ||
| 357 | + Py_DECREF(traceback); | ||
| 358 | + PyGILState_Release(gil); | ||
| 359 | + return result; | ||
| 360 | + } | ||
| 361 | + | ||
| 362 | + PyObject* sep = PyUnicode_FromString(""); | ||
| 363 | + if (!sep) { | ||
| 364 | + Py_DECREF(stackList); | ||
| 365 | + Py_DECREF(formatFunc); | ||
| 366 | + Py_DECREF(traceback); | ||
| 367 | + PyGILState_Release(gil); | ||
| 368 | + return result; | ||
| 369 | + } | ||
| 370 | + | ||
| 371 | + PyObject* joined = PyUnicode_Join(sep, stackList); | ||
| 372 | + if (joined) { | ||
| 373 | + result = PyUnicode_AsUTF8(joined); | ||
| 374 | + Py_DECREF(joined); | ||
| 375 | + } | ||
| 376 | + | ||
| 377 | + Py_DECREF(sep); | ||
| 378 | + Py_DECREF(stackList); | ||
| 379 | + Py_DECREF(formatFunc); | ||
| 380 | + Py_DECREF(traceback); | ||
| 381 | + PyGILState_Release(gil); | ||
| 382 | + return result; | ||
| 383 | +} | ||
| 384 | + | ||
| 231 | // ================= LogManager ================= | 385 | // ================= LogManager ================= |
| 232 | 386 | ||
| 233 | LogManager& LogManager::GetInstance() | 387 | LogManager& LogManager::GetInstance() |
| 234 | { | 388 | { |
| 235 | static LogManager inst; | 389 | static LogManager inst; |
| 236 | - inst.Init(); | ||
| 237 | return inst; | 390 | return inst; |
| 238 | } | 391 | } |
| 239 | 392 | ||
| 240 | -LogManager::LogManager() = default; | 393 | +LogManager::LogManager() |
| 394 | +{ | ||
| 395 | + Init(); | ||
| 396 | +} | ||
| 241 | 397 | ||
| 242 | LogManager::~LogManager() | 398 | LogManager::~LogManager() |
| 243 | { | 399 | { |
| @@ -270,11 +426,10 @@ void LogManager::Init() | |||
| 270 | 426 | ||
| 271 | void LogManager::LoadComponentConfigs() | 427 | void LogManager::LoadComponentConfigs() |
| 272 | { | 428 | { |
| 273 | - LoadByComponentByEnv<LogSeverity>(MINDIE_LOG_LEVEL, DEFAULT_MINDIE_LOG_LEVEL, | 429 | + LoadByComponentByEnv<LogSeverity>(MINDIE_LOG_LEVEL, DEFAULT_MINDIE_LOG_LEVEL, GetAllLogSeverity(), |
| 274 | - {"debug", "info", "warn", "error", "critical"}, | ||
| 275 | [](const std::string& s) { | 430 | [](const std::string& s) { |
| 276 | LogSeverity lvl; | 431 | LogSeverity lvl; |
| 277 | - if (!String2LogLevel(s, lvl)) { | 432 | + if (!String2LogSeverity(s, lvl)) { |
| 278 | return LogSeverity::INFO; | 433 | return LogSeverity::INFO; |
| 279 | } | 434 | } |
| 280 | return lvl; | 435 | return lvl; |
| @@ -326,11 +481,12 @@ ComponentConfig& LogManager::GetComponentConfig(LogComponent comp) | |||
| 326 | 481 | ||
| 327 | bool LogManager::IsPrintLog(LogComponent comp, LogSeverity level) | 482 | bool LogManager::IsPrintLog(LogComponent comp, LogSeverity level) |
| 328 | { | 483 | { |
| 329 | - if (level == LogSeverity::AUDIT) { | 484 | + if (level != LogSeverity::AUDIT) { |
| 485 | + auto& cfg = GetComponentConfig(comp); | ||
| 486 | + return isRunning_ && level >= cfg.minLevel && (cfg.toStdout || cfg.toFile); | ||
| 487 | + } else { | ||
| 330 | return isRunning_; | 488 | return isRunning_; |
| 331 | } | 489 | } |
| 332 | - auto& cfg = GetComponentConfig(comp); | ||
| 333 | - return isRunning_ && level >= cfg.minLevel && (cfg.toStdout || cfg.toFile); | ||
| 334 | } | 490 | } |
| 335 | 491 | ||
| 336 | void LogManager::GetLogRotate() | 492 | void LogManager::GetLogRotate() |
| @@ -375,7 +531,7 @@ void LogManager::OpenLogFiles() | |||
| 375 | { | 531 | { |
| 376 | for (size_t i = 0; i < static_cast<size_t>(LogType::__COUNT__); ++i) { | 532 | for (size_t i = 0; i < static_cast<size_t>(LogType::__COUNT__); ++i) { |
| 377 | LogType type = static_cast<LogType>(i); | 533 | LogType type = static_cast<LogType>(i); |
| 378 | - RenewLogFilePath(type); | 534 | + CreateLogFilePath(type); |
| 379 | auto& sink = sinks_[i]; | 535 | auto& sink = sinks_[i]; |
| 380 | sink.ofs.open(sink.filePath, std::ios::app); | 536 | sink.ofs.open(sink.filePath, std::ios::app); |
| 381 | if (sink.ofs.is_open()) { | 537 | if (sink.ofs.is_open()) { |
| @@ -386,7 +542,7 @@ void LogManager::OpenLogFiles() | |||
| 386 | } | 542 | } |
| 387 | } | 543 | } |
| 388 | 544 | ||
| 389 | -void LogManager::RenewLogFilePath(LogType type) | 545 | +void LogManager::CreateLogFilePath(LogType type) |
| 390 | { | 546 | { |
| 391 | const size_t idx = static_cast<size_t>(type); | 547 | const size_t idx = static_cast<size_t>(type); |
| 392 | auto& sink = sinks_[idx]; | 548 | auto& sink = sinks_[idx]; |
| @@ -448,7 +604,7 @@ void LogManager::Writer() | |||
| 448 | } | 604 | } |
| 449 | } | 605 | } |
| 450 | if (cfg.toFile && sink.ofs.is_open()) { | 606 | if (cfg.toFile && sink.ofs.is_open()) { |
| 451 | - if (sink.curSize + m.msg.size() + 1 >= logFileSize_) { | 607 | + if (sink.curSize + m.msg.size() + 1 >= GetLogFileSizeCutOff(static_cast<LogType>(i))) { |
| 452 | RotateLogs(static_cast<LogType>(i)); | 608 | RotateLogs(static_cast<LogType>(i)); |
| 453 | } | 609 | } |
| 454 | sink.ofs << m.msg << '\n'; | 610 | sink.ofs << m.msg << '\n'; |
| @@ -462,6 +618,25 @@ void LogManager::Writer() | |||
| 462 | } | 618 | } |
| 463 | } | 619 | } |
| 464 | 620 | ||
| 621 | +uint32_t LogManager::GetLogFileSizeCutOff(LogType type) const | ||
| 622 | +{ | ||
| 623 | + if (type != LogType::TOKEN) { | ||
| 624 | + return logFileSize_; | ||
| 625 | + } else { | ||
| 626 | + return SIZE_1MB; | ||
| 627 | + } | ||
| 628 | +} | ||
| 629 | + | ||
| 630 | +uint32_t LogManager::GetLogFileNumCutOff(LogType type) const | ||
| 631 | +{ | ||
| 632 | + if (type != LogType::TOKEN) { | ||
| 633 | + return logFileNum_; | ||
| 634 | + } else { | ||
| 635 | + uint32_t maxFileNumForTokenType = 2; | ||
| 636 | + return maxFileNumForTokenType; | ||
| 637 | + } | ||
| 638 | +} | ||
| 639 | + | ||
| 465 | void LogManager::RotateLogs(LogType type) | 640 | void LogManager::RotateLogs(LogType type) |
| 466 | { | 641 | { |
| 467 | // RotateLogs only called in flush thread. RotateLogs <- Writer <- FlushLoop | 642 | // RotateLogs only called in flush thread. RotateLogs <- Writer <- FlushLoop |
| @@ -473,9 +648,9 @@ void LogManager::RotateLogs(LogType type) | |||
| 473 | 648 | ||
| 474 | const std::string& base = sink.basePath; | 649 | const std::string& base = sink.basePath; |
| 475 | std::error_code ec; | 650 | std::error_code ec; |
| 476 | - fs::remove(MakeRotateName(base, logFileNum_), ec); | 651 | + fs::remove(MakeRotateName(base, GetLogFileNumCutOff(type)), ec); |
| 477 | ec.clear(); | 652 | ec.clear(); |
| 478 | - for (int i = static_cast<int>(logFileNum_) - 1; i >= 1; --i) { | 653 | + for (int i = static_cast<int>(GetLogFileNumCutOff(type)) - 1; i >= 1; --i) { |
| 479 | fs::rename(MakeRotateName(base, i), MakeRotateName(base, i + 1), ec); | 654 | fs::rename(MakeRotateName(base, i), MakeRotateName(base, i + 1), ec); |
| 480 | ec.clear(); | 655 | ec.clear(); |
| 481 | } | 656 | } |
| @@ -498,27 +673,28 @@ bool Logger::ShouldLog() const | |||
| 498 | return LogManager::GetInstance().IsPrintLog(component_, level_); | 673 | return LogManager::GetInstance().IsPrintLog(component_, level_); |
| 499 | } | 674 | } |
| 500 | 675 | ||
| 501 | -void Logger::AssembleAndPush(LogType type, const char* file, size_t line) | 676 | +void Logger::AssembleAndPush(LogType type, const char* file, size_t line, std::string& stack) |
| 502 | { | 677 | { |
| 503 | if (stream_.tellp() == std::streampos(0)) { | 678 | if (stream_.tellp() == std::streampos(0)) { |
| 504 | return; | 679 | return; |
| 505 | } | 680 | } |
| 506 | - constexpr size_t maxLogMsgLength = 2048UL; | ||
| 507 | - constexpr size_t prefixMaxLength = 256; | ||
| 508 | std::string msg = stream_.str(); | 681 | std::string msg = stream_.str(); |
| 509 | - const size_t length = std::min(msg.size(), maxLogMsgLength); | 682 | + const size_t length = std::min(msg.size(), BUFFER_SIZE_2048); |
| 510 | std::string out; | 683 | std::string out; |
| 511 | - out.reserve(length + prefixMaxLength); | 684 | + out.reserve(length + BUFFER_SIZE_256); |
| 512 | AppendCurTimestamp(out, TimestampFormat::READABLE); | 685 | AppendCurTimestamp(out, TimestampFormat::READABLE); |
| 513 | auto& cfg = LogManager::GetInstance().GetComponentConfig(component_); | 686 | auto& cfg = LogManager::GetInstance().GetComponentConfig(component_); |
| 514 | if (cfg.verbose) { | 687 | if (cfg.verbose) { |
| 515 | AppendPid(out); | 688 | AppendPid(out); |
| 516 | AppendTid(out); | 689 | AppendTid(out); |
| 517 | - AppendComponent(out, ComponentToString(component_)); | 690 | + AppendComponent(out, Component2String(component_)); |
| 518 | } | 691 | } |
| 519 | AppendLevel(out, level_); | 692 | AppendLevel(out, level_); |
| 520 | AppendFileLine(out, file, line); | 693 | AppendFileLine(out, file, line); |
| 521 | FilterAndAppend(out, msg.data(), length); | 694 | FilterAndAppend(out, msg.data(), length); |
| 695 | + if (!stack.empty()) { | ||
| 696 | + out.append(stack); | ||
| 697 | + } | ||
| 522 | LogManager::GetInstance().Push(component_, type, std::move(out)); | 698 | LogManager::GetInstance().Push(component_, type, std::move(out)); |
| 523 | } | 699 | } |
| 524 | 700 | ||
| @@ -529,11 +705,15 @@ void Logger::Reset() | |||
| 529 | } | 705 | } |
| 530 | 706 | ||
| 531 | // ================= LogLine ================= | 707 | // ================= LogLine ================= |
| 708 | + | ||
| 532 | LogLine::LogLine(LogComponent comp, LogSeverity level, const char* file, size_t line) | 709 | LogLine::LogLine(LogComponent comp, LogSeverity level, const char* file, size_t line) |
| 533 | : logger_(GetThreadLogger(comp, level)), enabled_(false), file_(file), line_(line) | 710 | : logger_(GetThreadLogger(comp, level)), enabled_(false), file_(file), line_(line) |
| 534 | { | 711 | { |
| 535 | logger_.Reset(); | 712 | logger_.Reset(); |
| 536 | enabled_ = logger_.ShouldLog(); | 713 | enabled_ = logger_.ShouldLog(); |
| 714 | + if (enabled_ && (level == LogSeverity::ERROR || level == LogSeverity::CRITICAL)) { | ||
| 715 | + stack_ = BuildStackTrace(); | ||
| 716 | + } | ||
| 537 | } | 717 | } |
| 538 | 718 | ||
| 539 | LogLine::~LogLine() | 719 | LogLine::~LogLine() |
| @@ -541,10 +721,28 @@ LogLine::~LogLine() | |||
| 541 | if (!enabled_) { | 721 | if (!enabled_) { |
| 542 | return; | 722 | return; |
| 543 | } | 723 | } |
| 544 | - logger_.AssembleAndPush(type_, file_, line_); | 724 | + logger_.AssembleAndPush(type_, file_, line_, stack_); |
| 545 | logger_.Reset(); | 725 | logger_.Reset(); |
| 546 | } | 726 | } |
| 547 | 727 | ||
| 728 | +std::string LogLine::BuildStackTrace() | ||
| 729 | +{ | ||
| 730 | + static constexpr size_t kSkip = 3; | ||
| 731 | + std::string result; | ||
| 732 | + | ||
| 733 | + if (Py_IsInitialized()) { | ||
| 734 | + PyGILState_STATE gil = PyGILState_Ensure(); | ||
| 735 | + PyFrameObject* frame = PyEval_GetFrame(); | ||
| 736 | + if (frame != nullptr) { | ||
| 737 | + result += GetPythonStackTrace(); | ||
| 738 | + } | ||
| 739 | + PyGILState_Release(gil); | ||
| 740 | + } | ||
| 741 | + | ||
| 742 | + result += GetStackTrace(kSkip); | ||
| 743 | + return result; | ||
| 744 | +} | ||
| 745 | + | ||
| 548 | // ================= thread_local Logger ================= | 746 | // ================= thread_local Logger ================= |
| 549 | 747 | ||
| 550 | Logger& GetThreadLogger(LogComponent comp, LogSeverity level) | 748 | Logger& GetThreadLogger(LogComponent comp, LogSeverity level) |
| @@ -566,4 +764,268 @@ Logger& GetThreadLogger(LogComponent comp, LogSeverity level) | |||
| 566 | return loggers[static_cast<uint8_t>(comp)][static_cast<uint8_t>(level)]; | 764 | return loggers[static_cast<uint8_t>(comp)][static_cast<uint8_t>(level)]; |
| 567 | } | 765 | } |
| 568 | 766 | ||
| 767 | +// ================= DynamicLogManager ================= | ||
| 768 | + | ||
| 769 | +DynamicLogManager &DynamicLogManager::GetInstance() | ||
| 770 | +{ | ||
| 771 | + static DynamicLogManager inst; | ||
| 772 | + return inst; | ||
| 773 | +} | ||
| 774 | + | ||
| 775 | +DynamicLogManager::DynamicLogManager() | ||
| 776 | +{ | ||
| 777 | + Init(); | ||
| 778 | +} | ||
| 779 | + | ||
| 780 | +void DynamicLogManager::Init() | ||
| 781 | +{ | ||
| 782 | + GetDefaultLogSeverity(); | ||
| 783 | + isRunning_ = true; | ||
| 784 | + monitorThread_ = std::thread(&DynamicLogManager::Monitor, this); | ||
| 785 | + pthread_setname_np(monitorThread_.native_handle(), "DynamicLogMonitorThread"); | ||
| 786 | +} | ||
| 787 | + | ||
| 788 | +DynamicLogManager::~DynamicLogManager() | ||
| 789 | +{ | ||
| 790 | + Stop(); | ||
| 791 | +} | ||
| 792 | + | ||
| 793 | +void DynamicLogManager::Stop() | ||
| 794 | +{ | ||
| 795 | + if (!isRunning_) { | ||
| 796 | + return; | ||
| 797 | + } | ||
| 798 | + isRunning_ = false; | ||
| 799 | + if (monitorThread_.joinable()) { | ||
| 800 | + monitorThread_.join(); | ||
| 801 | + } | ||
| 802 | +} | ||
| 803 | + | ||
| 804 | +void DynamicLogManager::GetDefaultLogSeverity() | ||
| 805 | +{ | ||
| 806 | + EnvVar::GetInstance().Get(MINDIE_LOG_LEVEL, DEFAULT_MINDIE_LOG_LEVEL, defaultLogSeverity_); | ||
| 807 | +} | ||
| 808 | + | ||
| 809 | +void DynamicLogManager::Monitor() | ||
| 810 | +{ | ||
| 811 | + while (isRunning_) { | ||
| 812 | + try { | ||
| 813 | + GetAndSetLogConfig(); | ||
| 814 | + } catch (const std::exception& e) { | ||
| 815 | + std::cout << "DynamicLogManager exception: " << e.what() << std::endl; | ||
| 816 | + } | ||
| 817 | + std::this_thread::sleep_for(std::chrono::seconds(monitorInterval_)); | ||
| 818 | + } | ||
| 819 | +} | ||
| 820 | + | ||
| 821 | +void DynamicLogManager::GetAndSetLogConfig() | ||
| 822 | +{ | ||
| 823 | + std::lock_guard<std::mutex> guard(mtx_); | ||
| 824 | + const std::string configPath = GetConfigPath(); | ||
| 825 | + DynamicLogConfig newCfg = LoadLogConfig(configPath); | ||
| 826 | + if (newCfg.logSeverity.empty() && !lastLogSeverity_.empty()) { | ||
| 827 | + ResetToDefaultLogSeverity(); | ||
| 828 | + return; | ||
| 829 | + } | ||
| 830 | + DynamicLogConfig lastCfg { lastLogSeverity_, lastValidHours_, lastValidTimeStamp_ }; | ||
| 831 | + auto diff = DiffConfig(newCfg, lastCfg); | ||
| 832 | + | ||
| 833 | + UpdateValidTimeStamp(newCfg); | ||
| 834 | + if (!IsWithinValidRange(newCfg)) { | ||
| 835 | + ResetToDefaultLogSeverity(); | ||
| 836 | + return; | ||
| 837 | + } | ||
| 838 | + | ||
| 839 | + if (!diff.logSeverityChanged && !diff.validHoursChanged) { | ||
| 840 | + return; | ||
| 841 | + } | ||
| 842 | + ApplyLogSeverity(newCfg.logSeverity); | ||
| 843 | + lastLogSeverity_ = newCfg.logSeverity; | ||
| 844 | + lastValidHours_ = newCfg.validHours; | ||
| 845 | + lastValidTimeStamp_ = newCfg.validTimeStamp; | ||
| 846 | +} | ||
| 847 | + | ||
| 848 | +std::string DynamicLogManager::GetConfigPath() const | ||
| 849 | +{ | ||
| 850 | + std::string configPath; | ||
| 851 | + Result r = EnvVar::GetInstance().Get(MINDIE_LLM_HOME_PATH, GetDefaultMindIELLMHomePath(), configPath); | ||
| 852 | + if (!r.IsOk()) { | ||
| 853 | + throw std::runtime_error(r.message()); | ||
| 854 | + } | ||
| 855 | + configPath += "/conf/config.json"; | ||
| 856 | + return configPath; | ||
| 857 | +} | ||
| 858 | + | ||
| 859 | +DynamicLogConfig DynamicLogManager::LoadLogConfig(const std::string& configPath) | ||
| 860 | +{ | ||
| 861 | + Json configJsonData; | ||
| 862 | + Result r = LoadJson(configPath, configJsonData); | ||
| 863 | + if (!r.IsOk()) { | ||
| 864 | + throw std::runtime_error(r.message()); | ||
| 865 | + } | ||
| 866 | + const Json& cfgJson = configJsonData.value(keyLogConfig, Json::object()); | ||
| 867 | + std::string logSeverity = GetLogSeverity(cfgJson); | ||
| 868 | + int timeInterval = GetTimeInterval(cfgJson, lastValidHours_); | ||
| 869 | + std::string timeStamp = GetTimeStamp(cfgJson, lastValidTimeStamp_); | ||
| 870 | + return {logSeverity, timeInterval, timeStamp}; | ||
| 871 | +} | ||
| 872 | + | ||
| 873 | +std::string DynamicLogManager::GetLogSeverity(const Json& logConfig) const | ||
| 874 | +{ | ||
| 875 | + if (!logConfig.contains(keyLogSeverity) || !logConfig[keyLogSeverity].is_string()) { | ||
| 876 | + return ""; | ||
| 877 | + } | ||
| 878 | + return logConfig[keyLogSeverity].get<std::string>(); | ||
| 879 | +} | ||
| 880 | + | ||
| 881 | +int DynamicLogManager::GetTimeInterval(const Json& logConfig, int lastHours) const | ||
| 882 | +{ | ||
| 883 | + static constexpr int minValidHours = 1; | ||
| 884 | + static constexpr int maxValidHours = 168; // A week (7 * 24) | ||
| 885 | + if (!logConfig.contains(keyTimeInterval) || !logConfig[keyTimeInterval].is_number_integer()) { | ||
| 886 | + return lastHours; | ||
| 887 | + } | ||
| 888 | + int hours = logConfig[keyTimeInterval].get<int>(); | ||
| 889 | + if (hours < minValidHours || hours > maxValidHours) { | ||
| 890 | + return lastHours; | ||
| 891 | + } | ||
| 892 | + return hours; | ||
| 893 | +} | ||
| 894 | + | ||
| 895 | +std::string DynamicLogManager::GetTimeStamp(const Json& logConfig, const std::string& lastTs) const | ||
| 896 | +{ | ||
| 897 | + if (!logConfig.contains(keyTimeStamp) || !logConfig[keyTimeStamp].is_string()) { | ||
| 898 | + return lastTs; | ||
| 899 | + } | ||
| 900 | + const std::string ts = logConfig[keyTimeStamp].get<std::string>(); | ||
| 901 | + if (!IsValidTimeFormat(ts)) { | ||
| 902 | + return lastTs; | ||
| 903 | + } | ||
| 904 | + if (!ts.empty() && IsGreaterThanNow(ts)) { | ||
| 905 | + return ""; | ||
| 906 | + } | ||
| 907 | + return ts; | ||
| 908 | +} | ||
| 909 | + | ||
| 910 | +DynamicLogDiff DynamicLogManager::DiffConfig(const DynamicLogConfig& current, const DynamicLogConfig& last) | ||
| 911 | +{ | ||
| 912 | + return { | ||
| 913 | + current.logSeverity != last.logSeverity, | ||
| 914 | + current.validHours != last.validHours, | ||
| 915 | + current.validTimeStamp != last.validTimeStamp | ||
| 916 | + }; | ||
| 917 | +} | ||
| 918 | + | ||
| 919 | +bool DynamicLogManager::IsValidTimeFormat(const std::string& timeStr) const | ||
| 920 | +{ | ||
| 921 | + static constexpr size_t strLen = 19; | ||
| 922 | + static constexpr int maxHour = 23; | ||
| 923 | + static constexpr int maxMinute = 59; | ||
| 924 | + static constexpr int maxSecond = 59; | ||
| 925 | + static constexpr int maxMonth = 11; | ||
| 926 | + static constexpr int maxDay = 31; | ||
| 927 | + if (timeStr.empty()) { | ||
| 928 | + return true; | ||
| 929 | + } | ||
| 930 | + if (timeStr.length() != strLen) { // "YYYY-MM-DD HH:MM:SS" | ||
| 931 | + return false; | ||
| 932 | + } | ||
| 933 | + std::tm tm = {}; | ||
| 934 | + std::istringstream iss(timeStr); | ||
| 935 | + iss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); | ||
| 936 | + if (iss.fail()) { | ||
| 937 | + return false; | ||
| 938 | + } | ||
| 939 | + if (tm.tm_hour < 0 || tm.tm_hour > maxHour || tm.tm_min < 0 || tm.tm_min > maxMinute || | ||
| 940 | + tm.tm_sec < 0 || tm.tm_sec > maxSecond || tm.tm_mon < 0 || tm.tm_mon > maxMonth || | ||
| 941 | + tm.tm_mday < 1 || tm.tm_mday > maxDay) { | ||
| 942 | + return false; | ||
| 943 | + } | ||
| 944 | + std::tm tm_copy = tm; | ||
| 945 | + std::time_t t = std::mktime(&tm_copy); | ||
| 946 | + if (t == -1) { | ||
| 947 | + return false; | ||
| 948 | + } | ||
| 949 | + return tm.tm_year == tm_copy.tm_year && tm.tm_mon == tm_copy.tm_mon && tm.tm_mday == tm_copy.tm_mday && | ||
| 950 | + tm.tm_hour == tm_copy.tm_hour && tm.tm_min == tm_copy.tm_min && tm.tm_sec == tm_copy.tm_sec; | ||
| 951 | +} | ||
| 952 | + | ||
| 953 | +bool DynamicLogManager::ParseTime(const std::string& s, std::time_t& out) const | ||
| 954 | +{ | ||
| 955 | + std::tm tm {}; | ||
| 956 | + tm.tm_isdst = -1; | ||
| 957 | + std::istringstream iss(s); | ||
| 958 | + iss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); | ||
| 959 | + if (iss.fail()) { | ||
| 960 | + return false; | ||
| 961 | + } | ||
| 962 | + out = std::mktime(&tm); | ||
| 963 | + return out != -1; | ||
| 964 | +} | ||
| 965 | + | ||
| 966 | +bool DynamicLogManager::IsGreaterThanNow(const std::string& timeStr) const | ||
| 967 | +{ | ||
| 968 | + std::time_t t {}; | ||
| 969 | + if (!ParseTime(timeStr, t)) { | ||
| 970 | + return false; | ||
| 971 | + } | ||
| 972 | + return t > std::time(nullptr); | ||
| 973 | +} | ||
| 974 | + | ||
| 975 | +void DynamicLogManager::ResetToDefaultLogSeverity() | ||
| 976 | +{ | ||
| 977 | + lastLogSeverity_.clear(); | ||
| 978 | + lastValidHours_ = defaultHours_; | ||
| 979 | + lastValidTimeStamp_.clear(); | ||
| 980 | + ApplyLogSeverity(defaultLogSeverity_); | ||
| 981 | +} | ||
| 982 | + | ||
| 983 | +void DynamicLogManager::ApplyLogSeverity(const std::string& severity) | ||
| 984 | +{ | ||
| 985 | + LogManager::GetInstance().LoadByComponentByString<LogSeverity>(severity, GetAllLogSeverity(), | ||
| 986 | + [](const std::string& s) { | ||
| 987 | + LogSeverity lvl; | ||
| 988 | + if (!String2LogSeverity(s, lvl)) { | ||
| 989 | + return LogSeverity::INFO; | ||
| 990 | + } | ||
| 991 | + return lvl; | ||
| 992 | + }, | ||
| 993 | + [](ComponentConfig& c, LogSeverity v) { | ||
| 994 | + c.minLevel = v; | ||
| 995 | + } | ||
| 996 | + ); | ||
| 997 | +} | ||
| 998 | + | ||
| 999 | +void DynamicLogManager::UpdateValidTimeStamp(DynamicLogConfig& cfg) | ||
| 1000 | +{ | ||
| 1001 | + if (cfg.logSeverity.empty() || !cfg.validTimeStamp.empty()) { | ||
| 1002 | + return; | ||
| 1003 | + } | ||
| 1004 | + std::time_t now = std::time(nullptr); | ||
| 1005 | + std::tm tm {}; | ||
| 1006 | + localtime_r(&now, &tm); | ||
| 1007 | + char buf[BUFFER_SIZE_32] = {}; | ||
| 1008 | + std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm); | ||
| 1009 | + cfg.validTimeStamp = buf; | ||
| 1010 | +} | ||
| 1011 | + | ||
| 1012 | +bool DynamicLogManager::IsWithinValidRange(const DynamicLogConfig& cfg) const | ||
| 1013 | +{ | ||
| 1014 | + std::time_t start {}; | ||
| 1015 | + if (!ParseTime(cfg.validTimeStamp, start)) { | ||
| 1016 | + return false; | ||
| 1017 | + } | ||
| 1018 | + const std::time_t end = start + cfg.validHours * 3600; | ||
| 1019 | + const std::time_t now = std::time(nullptr); | ||
| 1020 | + return now >= start && now < end; | ||
| 1021 | +} | ||
| 1022 | + | ||
| 1023 | +// ================= InitSystemLog ================= | ||
| 1024 | + | ||
| 1025 | +void InitSystemLog() | ||
| 1026 | +{ | ||
| 1027 | + LogManager::GetInstance(); | ||
| 1028 | + DynamicLogManager::GetInstance(); | ||
| 1029 | +} | ||
| 1030 | + | ||
| 569 | } // namespace mindie_llm | 1031 | } // namespace mindie_llm |