已合并
[RFC]: MindIE-LLM 日志模块整改,包括 CPP 和 Python两侧——第二部分(支持动态调整日志等级、封装CPP接口) #384
[RFC]: MindIE-LLM 日志模块整改,包括 CPP 和 Python两侧——第二部分(支持动态调整日志等级、封装CPP接口) #384
已合并
KaiMa创建于 2月9日
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 fi40 fi
41 41 
42- mkdir -p $OUTPUT_DIR $CACHE_DIR $BUILD_DIR42+ 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;then44 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_info54 fn_build_version_info
55 fn_build_third_party55 fn_build_third_party
56 fn_build_src56 fn_build_src
57+ cp $OUTPUT_DIR/lib/libfoundation.so $MINDIE_LLM_LIB_DIR/foundation.so
57 if [ "$build_type" = "release" ]; then58 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 fi60 fi
@@ -6,6 +6,8 @@ CACHE_DIR=$CODE_ROOT/.cache
6BUILD_DIR=$CODE_ROOT/build6BUILD_DIR=$CODE_ROOT/build
7OUTPUT_DIR=$CODE_ROOT/output7OUTPUT_DIR=$CODE_ROOT/output
8RELEASE_DIR=$CODE_ROOT/release8RELEASE_DIR=$CODE_ROOT/release
9+MINDIE_LLM_DIR=$CODE_ROOT/mindie_llm
10+MINDIE_LLM_LIB_DIR=$MINDIE_LLM_DIR/lib
9LOG_PATH="/var/log/mindie_log/"11LOG_PATH="/var/log/mindie_log/"
10LOG_NAME="mindie_llm_install.log"12LOG_NAME="mindie_llm_install.log"
11ARCH="aarch64"13ARCH="aarch64"
@@ -1,4 +1,5 @@
1add_subdirectory(utils)1add_subdirectory(utils)
2+add_subdirectory(py_interface)
2add_subdirectory(config_manager)3add_subdirectory(config_manager)
3add_subdirectory(block_manager)4add_subdirectory(block_manager)
4add_subdirectory(engine)5add_subdirectory(engine)
@@ -34,6 +34,7 @@ const std::string DEFAULT_MINDIE_LOG_VERBOSE = "1";
34const std::string DEFAULT_MINDIE_LOG_ROTATE = "-fs 20 -r 10"; // Rotating log files, 20 MB each, keep 10 files.34const std::string DEFAULT_MINDIE_LOG_ROTATE = "-fs 20 -r 10"; // Rotating log files, 20 MB each, keep 10 files.
35const std::string DEFAULT_CHECK_PERM = "";35const std::string DEFAULT_CHECK_PERM = "";
36 36 
37+const std::string& GetDefaultMindIELLMHomePath();
37 38 
38class EnvVar {39class EnvVar {
39public:40public:
@@ -27,23 +27,28 @@
27 27 
28#include "string_utils.h"28#include "string_utils.h"
29#include "safe_envvar.h"29#include "safe_envvar.h"
30+#include "safe_io.h"
30 31 
31namespace mindie_llm {32namespace 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- 
37static const std::string ALL_COMPONENT = "__all__";33static 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 PID46// Component enumeration object supporting independent control, with all component logs falling into same file by PID
39enum class LogComponent: uint8_t { LLM = 0, LLMMODELS, SERVER, __COUNT__ };47enum 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 consumption52// Buffer-pushing struct for async writer thread consumption
48struct MsgPkg {53struct 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 
72class LogManager {75class 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 
161private:166private:
@@ -191,24 +196,91 @@ public:
191 return *this;196 return *this;
192 }197 }
193 198 
199+private:
200+ std::string BuildStackTrace();
201+ 
194private:202private:
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 
204Logger& GetThreadLogger(LogComponent comp, LogSeverity level);213Logger& 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_llm280} // namespace mindie_llm
207 281 
208// ================= Macro =================282// ================= Macro =================
209// llm log interface283// llm log interface
210-#define LOG_AUDIT_LLM \
211- mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__)
212#define LOG_DEBUG_LLM \284#define LOG_DEBUG_LLM \
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#define LOG_INFO_LLM \286#define LOG_INFO_LLM \
@@ -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#define LOG_CRITICAL_LLM \292#define LOG_CRITICAL_LLM \
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+#define LOG_AUDIT_LLM \
295+ mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__)
222// model log interface296// model log interface
223-#define LOG_AUDIT_MODEL \
224- mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__)
225#define LOG_DEBUG_MODEL \297#define LOG_DEBUG_MODEL \
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#define LOG_INFO_MODEL \299#define LOG_INFO_MODEL \
@@ -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#define LOG_CRITICAL_MODEL \305#define LOG_CRITICAL_MODEL \
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+#define LOG_AUDIT_MODEL \
308+ mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__)
235// server log interface309// server log interface
236-#define LOG_AUDIT_SERVER \
237- mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__)
238#define LOG_DEBUG_SERVER \310#define LOG_DEBUG_SERVER \
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#define LOG_INFO_SERVER \312#define LOG_INFO_SERVER \
@@ -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#define LOG_CRITICAL_SERVER \318#define LOG_CRITICAL_SERVER \
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+#define LOG_AUDIT_SERVER \
321+ mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__)
248 322 
249#endif323#endif
@@ -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+#include <Python.h>
14+ 
15+#include "pylog.h"
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+#include "pylog.h"
14+ 
15+#include <Python.h>
16+#include <frameobject.h>
17+ 
18+#include "system_log.h"
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+#ifndef PY_LOG_H
14+#define PY_LOG_H
15+ 
16+#include <Python.h>
17+ 
18+namespace FOUNDATION {
19+ PyObject *GetLogModule();
20+} // namespace FOUNDATION
21+ 
22+#endif
@@ -32,6 +32,7 @@ target_link_libraries(${TARGET_NAME}
32 mindieservice_endpoint32 mindieservice_endpoint
33 config_manager33 config_manager
34 mindie_llm_utils34 mindie_llm_utils
35+ system_log
35 pybind11::embed36 pybind11::embed
36)37)
37 38 
@@ -30,6 +30,7 @@
30#include "config_manager.h"30#include "config_manager.h"
31#include "endpoint.h"31#include "endpoint.h"
32#include "msServiceProfiler/Tracer.h"32#include "msServiceProfiler/Tracer.h"
33+#include "system_log.h"
33 34 
34using namespace mindie_llm;35using namespace mindie_llm;
35static std::mutex g_exitMtx;36static std::mutex g_exitMtx;
@@ -315,9 +316,7 @@ void RegisterSignal(void)
315 316 
316void RunEP(std::unordered_map<std::string, std::string> commandLineArgsMap)317void 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 
391int main(int argc, char *argv[])390int 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#include "safe_envvar.h"12#include "safe_envvar.h"
13 13 
14+#include <mutex>
15+ 
16+#include <Python.h>
17+ 
14namespace mindie_llm {18namespace 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+ 
16EnvVar& EnvVar::GetInstance()57EnvVar& EnvVar::GetInstance()
17{58{
18 static EnvVar instance;59 static EnvVar instance;
@@ -12,9 +12,9 @@
12#include "safe_io.h"12#include "safe_io.h"
13 13 
14#include <fstream>14#include <fstream>
15+#include <iostream>
15 16 
16#include "safe_path.h"17#include "safe_path.h"
17-#include "log.h"
18 18 
19namespace mindie_llm {19namespace mindie_llm {
20 20 
@@ -68,23 +68,21 @@ bool CheckJsonDepthCallbackNoLogger(int depth, Json::parse_event_t ev, Json& obj
68bool CheckJsonDepthCallback(int depth, Json::parse_event_t ev, Json& obj)68bool 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 
75bool CheckJsonDepthCallbackUlog(int depth, Json::parse_event_t ev, Json& obj)75bool 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 
83bool CheckOrderedJsonDepthCallback(int depth, OrderedJson::parse_event_t ev, OrderedJson& obj)82bool 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- 
5file(GLOB SOURCE_FILES 1file(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#include <sys/uio.h>22#include <sys/uio.h>
23#include <sys/prctl.h>23#include <sys/prctl.h>
24#include <algorithm>24#include <algorithm>
25+#include <execinfo.h>
26+#include <cxxabi.h>
27+#include <dlfcn.h>
28+ 
29+#include <Python.h>
25 30 
26#include "safe_envvar.h"31#include "safe_envvar.h"
27#include "safe_path.h"32#include "safe_path.h"
@@ -30,46 +35,104 @@
30namespace mindie_llm {35namespace mindie_llm {
31 36 
32const std::string LLM = "llm";37const 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 
53static const std::unordered_map<LogType, std::string> logType2StrMap = {86static 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+ 
59enum class TimestampFormat { READABLE, TIGHT };120enum 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 
75void AppendCurTimestamp(std::string& out, TimestampFormat format)138void AppendCurTimestamp(std::string& out, TimestampFormat format)
@@ -102,7 +165,7 @@ void AppendCurTimestamp(std::string& out, TimestampFormat format)
102 165 
103std::string GetTightTimestamp()166std::string GetTightTimestamp()
104{167{
105- constexpr size_t tightTimestampLength = 17; // YYYYMMDDHHMMSSmmm168+ 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)
157inline void AppendLevel(std::string& out, LogSeverity level)228inline 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 
164inline void FilterAndAppend(std::string& out, const char* input, size_t length)235inline 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 
233LogManager& LogManager::GetInstance()387LogManager& 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 
242LogManager::~LogManager()398LogManager::~LogManager()
243{399{
@@ -270,11 +426,10 @@ void LogManager::Init()
270 426 
271void LogManager::LoadComponentConfigs()427void 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 
327bool LogManager::IsPrintLog(LogComponent comp, LogSeverity level)482bool 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 
336void LogManager::GetLogRotate()492void 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+ 
465void LogManager::RotateLogs(LogType type)640void LogManager::RotateLogs(LogType type)
466{641{
467 // RotateLogs only called in flush thread. RotateLogs <- Writer <- FlushLoop642 // 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+ 
532LogLine::LogLine(LogComponent comp, LogSeverity level, const char* file, size_t line)709LogLine::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 
539LogLine::~LogLine()719LogLine::~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 
550Logger& GetThreadLogger(LogComponent comp, LogSeverity level)748Logger& 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_llm1031} // namespace mindie_llm