1 /**
2 * Copyright 2019-2023 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "utils/log_adapter.h"
18 #include <cstddef>
19 #include <sstream>
20 #include <string>
21
22 #ifndef _MSC_VER
23 #include <unistd.h>
24 #include <sys/time.h>
25 #include <sys/stat.h>
26 #else
27 #include <direct.h>
28 #endif
29 #include <map>
30 #include <iomanip>
31 #include <regex>
32 #include <thread>
33 #include <vector>
34 #include <limits>
35 #include "utils/convert_utils_base.h"
36
37 // namespace to support utils module definition
38 namespace mindspore {
39 constexpr int kNameMaxLength = 18;
40 constexpr size_t kStep = 2;
41 constexpr auto kSplitLine = "\n----------------------------------------------------\n";
42 constexpr auto kFrameworkErrorTitle = "Framework Error Message:";
43 // set default log level to WARNING for all sub modules
44 int g_ms_submodule_log_levels[NUM_SUBMODUES] = {MsLogLevel::kWarning};
45 // set default vlog level to 0
46 int g_ms_vlog_level_from = 0;
47 int g_ms_vlog_level_to = 0;
48 #if defined(_WIN32) || defined(_WIN64)
49 enum MsLogLevel this_thread_max_log_level = MsLogLevel::kException;
50 #else
51 thread_local enum MsLogLevel this_thread_max_log_level = MsLogLevel::kException;
52 #endif
53
54 #ifdef USE_GLOG
55 #define google mindspore_private
GetProcName()56 static std::string GetProcName() {
57 #if defined(__APPLE__) || defined(__FreeBSD__)
58 const std::string appname = getprogname();
59 #elif defined(_GNU_SOURCE) && !defined(ANDROID)
60 const std::string appname = program_invocation_name;
61 #else
62 const std::string appname = "?";
63 #endif
64 // sometimes, the app name is an absolute path, it is too long
65 std::string app_name(appname);
66 std::size_t pos = app_name.rfind("/");
67 if (pos == std::string::npos) {
68 return app_name;
69 }
70 if (pos + 1 >= app_name.size()) {
71 return app_name;
72 }
73 return app_name.substr(pos + 1);
74 }
75
GetLogLevel(MsLogLevel level)76 static std::string GetLogLevel(MsLogLevel level) {
77 static const char *const level_names[] = {
78 "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL",
79 };
80 if (level > this_thread_max_log_level) {
81 level = this_thread_max_log_level;
82 }
83 return std::string(level_names[level]);
84 }
85
86 // convert MsLogLevel to corresponding glog level
GetGlogLevel(MsLogLevel level)87 static int GetGlogLevel(MsLogLevel level) {
88 switch (level >= this_thread_max_log_level ? this_thread_max_log_level : level) {
89 case MsLogLevel::kDebug:
90 case MsLogLevel::kInfo:
91 return google::GLOG_INFO;
92 case MsLogLevel::kWarning:
93 return google::GLOG_WARNING;
94 case MsLogLevel::kError:
95 case MsLogLevel::kException:
96 default:
97 return google::GLOG_ERROR;
98 }
99 }
100
101 // get threshold level
GetThresholdLevel(const std::string & threshold)102 static int GetThresholdLevel(const std::string &threshold) {
103 if (threshold.empty()) {
104 return google::GLOG_WARNING;
105 } else if (threshold == "DEBUG" || threshold == "INFO") {
106 return google::GLOG_INFO;
107 } else if (threshold == "WARNING") {
108 return google::GLOG_WARNING;
109 } else if (threshold == "ERROR" || threshold == "CRITICAL") {
110 return google::GLOG_ERROR;
111 } else {
112 return google::GLOG_WARNING;
113 }
114 }
115 #undef google
116 #else
117
118 #undef Dlog
119 #define Dlog(module_id, level, format, ...) \
120 do { \
121 DlogInner((module_id), (level), (format), ##__VA_ARGS__); \
122 } while (0)
123
124 // convert MsLogLevel to corresponding slog level
GetSlogLevel(MsLogLevel level)125 static int GetSlogLevel(MsLogLevel level) {
126 switch (level) {
127 case MsLogLevel::kDebug:
128 return DLOG_DEBUG;
129 case MsLogLevel::kInfo:
130 return DLOG_INFO;
131 case MsLogLevel::kWarning:
132 return DLOG_WARN;
133 case MsLogLevel::kError:
134 case MsLogLevel::kException:
135 default:
136 return DLOG_ERROR;
137 }
138 }
139 #endif
140
exception_handler()141 LogWriter::ExceptionHandler &LogWriter::exception_handler() {
142 static LogWriter::ExceptionHandler g_exception_handler = nullptr;
143 return g_exception_handler;
144 }
145
message_handler()146 LogWriter::MessageHandler &LogWriter::message_handler() {
147 static LogWriter::MessageHandler g_message_handler = nullptr;
148 return g_message_handler;
149 }
150
trace_provider()151 LogWriter::TraceProvider &LogWriter::trace_provider() {
152 static LogWriter::TraceProvider g_trace_provider = nullptr;
153 return g_trace_provider;
154 }
155
get_trace_str_provider()156 LogWriter::GetTraceStrProvider &LogWriter::get_trace_str_provider() {
157 static LogWriter::GetTraceStrProvider g_get_trace_str_provider = nullptr;
158 return g_get_trace_str_provider;
159 }
160
GetExceptionHandler()161 const LogWriter::ExceptionHandler &LogWriter::GetExceptionHandler() {
162 const auto &exception_handler_tmp = exception_handler();
163 return exception_handler_tmp;
164 }
165
SetExceptionHandler(const LogWriter::ExceptionHandler & new_exception_handler)166 void LogWriter::SetExceptionHandler(const LogWriter::ExceptionHandler &new_exception_handler) {
167 auto &exception_handler_tmp = exception_handler();
168 exception_handler_tmp = new_exception_handler;
169 }
170
GetMessageHandler()171 const LogWriter::MessageHandler &LogWriter::GetMessageHandler() { return message_handler(); }
172
SetMessageHandler(const LogWriter::MessageHandler & new_message_handler)173 void LogWriter::SetMessageHandler(const LogWriter::MessageHandler &new_message_handler) {
174 message_handler() = new_message_handler;
175 }
176
GetTraceProvider()177 const LogWriter::TraceProvider &LogWriter::GetTraceProvider() {
178 const auto &trace_provider_tmp = trace_provider();
179 return trace_provider_tmp;
180 }
181
SetTraceProvider(const LogWriter::TraceProvider & new_trace_provider)182 void LogWriter::SetTraceProvider(const LogWriter::TraceProvider &new_trace_provider) {
183 auto &trace_provider_tmp = trace_provider();
184 if (trace_provider_tmp != nullptr) {
185 MS_LOG(INFO) << "trace provider has been set, skip.";
186 return;
187 }
188 trace_provider_tmp = new_trace_provider;
189 }
190
SetGetTraceStrProvider(const LogWriter::GetTraceStrProvider & provider)191 void LogWriter::SetGetTraceStrProvider(const LogWriter::GetTraceStrProvider &provider) {
192 auto &provider_tmp = get_trace_str_provider();
193 if (provider_tmp != nullptr) {
194 MS_LOG(INFO) << "trace provider has been set, skip.";
195 return;
196 }
197 provider_tmp = provider;
198 }
199
GetEnv(const std::string & envvar)200 static inline std::string GetEnv(const std::string &envvar) {
201 const char *value = std::getenv(envvar.c_str());
202
203 if (value == nullptr) {
204 return std::string();
205 }
206
207 return std::string(value);
208 }
209
210 // When GLOG_logtostderr is set to 0, logs are output to a file, then will print duplicate message
211 // in exception log and stack. Otherwise when GLOG_logtostderr is set to 1, logs are output to the screen,
212 // then will only print message in exception stack.
213 static MsLogLevel GetGlobalLogLevel();
RemoveLabelBeforeOutputLog(const std::ostringstream & msg) const214 void LogWriter::RemoveLabelBeforeOutputLog(const std::ostringstream &msg) const {
215 auto log_level = GetGlobalLogLevel();
216 if (log_level <= MsLogLevel::kInfo || GetEnv("GLOG_logtostderr") == "0") {
217 std::string str = msg.str();
218 // replace any titles enclosed in "#dmsg#" or "#umsg#", as well as its formatted couterparts with "\n"
219 std::regex title_re{R"(\#[d|u]msg\#.+?\#[d|u]msg\#|)" + std::string(kSplitLine) + R"(- .+?)" +
220 std::string(kSplitLine)};
221 auto replaced = std::regex_replace(str, title_re, "\n");
222 if (!replaced.empty() && replaced[0] == '\n') {
223 (void)replaced.erase(0, 1);
224 }
225 std::ostringstream replaced_msg;
226 replaced_msg << replaced;
227 OutputLog(replaced_msg);
228 }
229 }
230
231 // Function to split string based on character delimiter
SplitString(const std::string & message,const std::string & delimiter,std::vector<std::string> * output)232 void SplitString(const std::string &message, const std::string &delimiter, std::vector<std::string> *output) {
233 size_t pos1 = 0;
234 size_t pos2 = message.find(delimiter);
235 MS_EXCEPTION_IF_NULL(output);
236
237 while (pos2 != std::string::npos) {
238 (void)output->emplace_back(message.substr(pos1, pos2 - pos1));
239 pos1 = pos2 + delimiter.size();
240 pos2 = message.find(delimiter, pos1);
241 }
242
243 if (pos1 != message.length()) {
244 (void)output->emplace_back(message.substr(pos1));
245 }
246 }
247
248 // Parse exception message format like: Error Description#dmsg#Developer Message Title#dmsg#Developer Message Content
249 // #umsg#User Message Title#umsg#User Message Content
ParseExceptionMessage(const std::string & message,std::ostringstream & oss,std::vector<std::string> * dmsg,std::vector<std::string> * umsg)250 void ParseExceptionMessage(const std::string &message, std::ostringstream &oss, std::vector<std::string> *dmsg,
251 std::vector<std::string> *umsg) {
252 std::vector<std::string> vec;
253 SplitString(message, "#dmsg#", &vec); // first:split message by label #dmsg#
254
255 if (!vec.empty() && (vec[0].find("#umsg#") == std::string::npos)) {
256 oss << vec[0];
257 }
258
259 MS_EXCEPTION_IF_NULL(dmsg);
260 MS_EXCEPTION_IF_NULL(umsg);
261
262 for (size_t i = 0; i < vec.size(); i++) {
263 if (vec[i].find("#umsg#") != std::string::npos) {
264 std::vector<std::string> temp;
265 SplitString(vec[i], "#umsg#", &temp); // second:split message by label #umsg#
266 if (!temp.empty()) {
267 if (i == 0) {
268 oss << temp[0];
269 } else {
270 (void)dmsg->emplace_back(temp[0]);
271 }
272 (void)umsg->insert(umsg->cend(), temp.cbegin() + 1, temp.cend());
273 }
274 } else {
275 if (i != 0) {
276 (void)dmsg->emplace_back(vec[i]);
277 }
278 }
279 }
280 }
281
PrintMessage(std::ostringstream & oss,const std::string & title,const std::string & content)282 void PrintMessage(std::ostringstream &oss, const std::string &title, const std::string &content) {
283 const std::string &message = oss.str();
284 size_t length = message.length();
285 if ((length != 0) && (message[length - 1] != '\n')) {
286 oss << "\n";
287 }
288
289 oss << kSplitLine << title << kSplitLine << content;
290 }
291
CombineExceptionMessageWithSameTitle(std::ostringstream & oss,const std::string & title,const std::string & content)292 void CombineExceptionMessageWithSameTitle(std::ostringstream &oss, const std::string &title,
293 const std::string &content) {
294 if (title.empty() || content.empty()) {
295 return;
296 }
297 // ignore the passed framework title if content has a formatted title itself
298 if (title.find(kFrameworkErrorTitle) != std::string::npos && content.find(kSplitLine) == 0) {
299 oss << '\n' << content;
300 return;
301 }
302 std::string message = oss.str();
303 size_t position = message.find(title + kSplitLine);
304 if (position != std::string::npos) {
305 position += title.length() + strlen(kSplitLine);
306 if (content[content.length() - 1] != '\n') {
307 (void)message.insert(position, content + "\n");
308 } else {
309 (void)message.insert(position, content);
310 }
311 oss.str("");
312 oss << message;
313 } else {
314 PrintMessage(oss, title, content);
315 }
316 }
317
DisplayDevExceptionMessage(std::ostringstream & oss,const std::vector<std::string> & dmsg,const LocationInfo & location)318 void DisplayDevExceptionMessage(std::ostringstream &oss, const std::vector<std::string> &dmsg,
319 const LocationInfo &location) {
320 bool display = true;
321 if (GetEnv("MS_EXCEPTION_DISPLAY_LEVEL") == "1") {
322 display = false;
323 }
324
325 if (display) {
326 size_t size = dmsg.size();
327 if ((size != 0) && (size % kStep == 0)) {
328 for (size_t i = 0; i < size; i += kStep) {
329 std::ostringstream dmsg_title;
330 dmsg_title << "- " << dmsg[i] << " (For framework developers)";
331 CombineExceptionMessageWithSameTitle(oss, dmsg_title.str(), dmsg[i + 1]);
332 }
333 }
334
335 const std::string CPP_CALL_STACK_TITLE = "- C++ Call Stack: (For framework developers)";
336 std::ostringstream cpp_call_stack_content;
337 cpp_call_stack_content << location.file_ << ":" << location.line_ << " " << location.func_ << "\n";
338 CombineExceptionMessageWithSameTitle(oss, CPP_CALL_STACK_TITLE, cpp_call_stack_content.str());
339 }
340 }
341
DisplayUserExceptionMessage(std::ostringstream & oss,const std::vector<std::string> & umsg)342 void DisplayUserExceptionMessage(std::ostringstream &oss, const std::vector<std::string> &umsg) {
343 size_t size = umsg.size();
344 if ((size != 0) && (size % kStep == 0)) {
345 for (size_t i = 0; i < size; i += kStep) {
346 std::ostringstream umsg_title;
347 umsg_title << "- " << umsg[i];
348 CombineExceptionMessageWithSameTitle(oss, umsg_title.str(), umsg[i + 1]);
349 }
350 }
351 }
352
GetNodeDebugInfoStr() const353 const std::string LogWriter::GetNodeDebugInfoStr() const {
354 if (node_ == nullptr) {
355 return std::string();
356 }
357 const auto &provider = get_trace_str_provider();
358 if (provider != nullptr) {
359 return provider(node_, true);
360 }
361 return std::string();
362 }
363
OutputLog(const std::ostringstream & msg) const364 void LogWriter::OutputLog(const std::ostringstream &msg) const {
365 #ifdef USE_GLOG
366 #define google mindspore_private
367 std::string submodule_name = GetSubModuleName(submodule_);
368 google::LogMessage("", 0, GetGlogLevel(log_level_)).stream()
369 #ifdef _MSC_VER
370 << "[" << GetLogLevel(log_level_) << "] " << submodule_name << "("
371 << "," << std::hex
372 #else
373 << "[" << (vlog_level_ > 0 ? "V" + std::to_string(vlog_level_) : GetLogLevel(log_level_)) << "] " << submodule_name
374 << "(" << getpid() << "," << std::hex
375 #endif
376 << std::this_thread::get_id() << std::dec << "," << GetProcName() << "):" << GetTimeString() << " "
377 << "[" << location_.file_ << ":" << location_.line_ << "] " << location_.func_ << "] " << msg.str() << std::endl;
378 #undef google
379 #else
380 auto str_msg = msg.str();
381 auto slog_module_id = (submodule_ == SM_MD ? MD : ME);
382 Dlog(static_cast<int>(slog_module_id), GetSlogLevel(log_level_), "[%s:%d] %s] %s", location_.file_, location_.line_,
383 location_.func_, str_msg.c_str());
384 #endif
385 }
386
operator <(const LogStream & stream) const387 void LogWriter::operator<(const LogStream &stream) const noexcept {
388 std::ostringstream msg;
389 msg << stream.sstream_->rdbuf();
390 msg << GetNodeDebugInfoStr();
391 OutputLog(msg);
392 }
393
operator ^(const LogStream & stream) const394 void LogWriter::operator^(const LogStream &stream) const {
395 std::ostringstream msg;
396 msg << stream.sstream_->rdbuf();
397 msg << GetNodeDebugInfoStr();
398
399 const auto &message_handler = GetMessageHandler();
400 if (message_handler != nullptr) {
401 message_handler(&msg);
402 }
403
404 if (is_internal_exception_) {
405 msg << "#umsg#Framework Unexpected Exception Raised:#umsg#This exception is caused by framework's unexpected "
406 "error. Please create an issue at https://gitee.com/mindspore/mindspore/issues to get help.";
407 }
408
409 std::ostringstream oss;
410 std::vector<std::string> dmsg;
411 std::vector<std::string> umsg;
412
413 ParseExceptionMessage(msg.str(), oss, &dmsg, &umsg);
414 DisplayUserExceptionMessage(oss, umsg);
415 thread_local bool running = false;
416 if (!running) {
417 running = true;
418 if (this_thread_max_log_level >= MsLogLevel::kException) {
419 RemoveLabelBeforeOutputLog(msg);
420 }
421 DisplayDevExceptionMessage(oss, dmsg, location_);
422 const auto &trace_provider = GetTraceProvider();
423 if (trace_provider != nullptr) {
424 trace_provider(oss, true);
425 }
426 running = false;
427 }
428
429 const auto &exception_handler = GetExceptionHandler();
430 if (exception_handler != nullptr) {
431 exception_handler(exception_type_, oss.str());
432 } else {
433 MS_LOG(ERROR) << "Runtime error for null exception handler.";
434 throw std::runtime_error(oss.str());
435 }
436 MS_LOG(ERROR) << "Runtime error for unknown exception type:" << exception_type_ << ".";
437 throw std::runtime_error(oss.str());
438 }
439
440 enum class LogConfigToken : size_t {
441 INVALID, // indicate invalid token
442 LEFT_BRACE, // '{'
443 RIGHT_BRACE, // '}'
444 VARIABLE, // '[A-Za-z][A-Za-z0-9_]*'
445 NUMBER, // [0-9]+
446 COMMA, // ','
447 COLON, // ':'
448 EOS, // End Of String, '\0'
449 NUM_LOG_CFG_TOKENS
450 };
451
452 static const char *g_tok_names[static_cast<size_t>(LogConfigToken::NUM_LOG_CFG_TOKENS)] = {
453 "invalid", // indicate invalid token
454 "{", // '{'
455 "}", // '}'
456 "variable", // '[A-Za-z][A-Za-z0-9_]*'
457 "number", // [0-9]+
458 ",", // ','
459 ":", // ':'
460 "end-of-string", // End Of String, '\0'
461 };
462
IsAlpha(char ch)463 static inline bool IsAlpha(char ch) { return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); }
464
IsDigit(char ch)465 static inline bool IsDigit(char ch) { return ch >= '0' && ch <= '9'; }
466
467 class LogConfigLexer {
468 public:
LogConfigLexer(const std::string & text)469 explicit LogConfigLexer(const std::string &text) : buffer_(text), cur_idx_(0) {}
470 ~LogConfigLexer() = default;
471
472 // skip white space, and return the first char after white space
SkipWhiteSpace()473 char SkipWhiteSpace() {
474 while (cur_idx_ < buffer_.size()) {
475 char ch = buffer_[cur_idx_];
476 if (ch == ' ' || ch == '\t') {
477 ++cur_idx_;
478 continue;
479 }
480 return ch;
481 }
482 return '\0';
483 }
484
GetNext(std::string * const ptr)485 LogConfigToken GetNext(std::string *const ptr) {
486 #ifdef DEBUG
487 std::string text;
488 auto tok = GetNextInner(&text);
489 MS_LOG(DEBUG) << "Got token " << tok << " with value [" << text << "]";
490 if (ptr != nullptr) {
491 *ptr = text;
492 }
493 return tok;
494 }
495
GetNextInner(std::string * ptr)496 LogConfigToken GetNextInner(std::string *ptr) {
497 #endif
498 char ch = SkipWhiteSpace();
499 // clang-format off
500 static const std::map<char, LogConfigToken> single_char_map = {
501 {'{', LogConfigToken::LEFT_BRACE},
502 {'}', LogConfigToken::RIGHT_BRACE},
503 {',', LogConfigToken::COMMA},
504 {':', LogConfigToken::COLON},
505 {'\0', LogConfigToken::EOS},
506 };
507 // clang-format on
508
509 auto iter = single_char_map.find(ch);
510 if (iter != single_char_map.end()) {
511 if (ptr != nullptr) {
512 *ptr = std::string(1, ch);
513 }
514 ++cur_idx_;
515 return iter->second;
516 } else if (IsAlpha(ch)) {
517 std::ostringstream oss;
518 do {
519 oss << ch;
520 ch = buffer_[++cur_idx_];
521 } while (cur_idx_ < buffer_.size() && (IsAlpha(ch) || IsDigit(ch) || ch == '_'));
522 if (ptr != nullptr) {
523 *ptr = std::string(oss.str());
524 }
525 return LogConfigToken::VARIABLE;
526 } else if (IsDigit(ch)) {
527 std::ostringstream oss;
528 do {
529 oss << ch;
530 ch = buffer_[++cur_idx_];
531 } while (cur_idx_ < buffer_.size() && IsDigit(ch));
532 if (ptr != nullptr) {
533 *ptr = std::string(oss.str());
534 }
535 return LogConfigToken::NUMBER;
536 }
537 return LogConfigToken::INVALID;
538 }
539
540 private:
541 std::string buffer_;
542 size_t cur_idx_;
543 };
544
545 class LogConfigParser {
546 public:
LogConfigParser(const std::string & cfg)547 explicit LogConfigParser(const std::string &cfg) : lexer(cfg) {}
548 ~LogConfigParser() = default;
549
Expect(LogConfigToken expected,LogConfigToken tok) const550 bool Expect(LogConfigToken expected, LogConfigToken tok) const {
551 if (expected != tok) {
552 MS_LOG(WARNING) << "Parse submodule log configuration text error, expect `"
553 << g_tok_names[static_cast<size_t>(expected)] << "`, but got `"
554 << g_tok_names[static_cast<size_t>(tok)] << "`. The whole configuration will be ignored.";
555 return false;
556 }
557 return true;
558 }
559
560 // The text of config MS_SUBMODULE_LOG_v is in the form {submodule1:log_level1,submodule2:log_level2,...}.
561 // Valid values of log levels are: 0 - debug, 1 - info, 2 - warning, 3 - error, 4 - critical
562 // e.g. MS_SUBMODULE_LOG_v={PARSER:0, ANALYZER:2, PIPELINE:1}
Parse()563 std::map<std::string, std::string> Parse() {
564 std::map<std::string, std::string> log_levels;
565
566 bool flag_error = false;
567 std::string text;
568 auto tok = lexer.GetNext(&text);
569 // empty string
570 if (tok == LogConfigToken::EOS) {
571 return log_levels;
572 }
573
574 if (!Expect(LogConfigToken::LEFT_BRACE, tok)) {
575 return log_levels;
576 }
577
578 do {
579 std::string key, val;
580 tok = lexer.GetNext(&key);
581 if (!Expect(LogConfigToken::VARIABLE, tok)) {
582 flag_error = true;
583 break;
584 }
585
586 tok = lexer.GetNext(&text);
587 if (!Expect(LogConfigToken::COLON, tok)) {
588 flag_error = true;
589 break;
590 }
591
592 tok = lexer.GetNext(&val);
593 if (!Expect(LogConfigToken::NUMBER, tok)) {
594 flag_error = true;
595 break;
596 }
597
598 log_levels[key] = val;
599 tok = lexer.GetNext(&text);
600 } while (tok == LogConfigToken::COMMA);
601
602 if (!flag_error && !Expect(LogConfigToken::RIGHT_BRACE, tok)) {
603 flag_error = true;
604 }
605
606 if (flag_error) {
607 log_levels.clear();
608 }
609 return log_levels;
610 }
611
612 private:
613 LogConfigLexer lexer;
614 };
615
ParseLogLevel(const std::string & str_level,MsLogLevel * ptr_level)616 bool ParseLogLevel(const std::string &str_level, MsLogLevel *ptr_level) {
617 if (str_level.size() == 1) {
618 int ch = str_level.c_str()[0];
619 constexpr char number_start = '0';
620 ch = ch - number_start; // subtract ASCII code of '0', which is 48
621 if (ch >= static_cast<int>(MsLogLevel::kDebug) && ch <= static_cast<int>(MsLogLevel::kException)) {
622 if (ptr_level != nullptr) {
623 *ptr_level = static_cast<MsLogLevel>(ch);
624 }
625 return true;
626 }
627 }
628 return false;
629 }
630
GetGlobalLogLevel()631 static MsLogLevel GetGlobalLogLevel() {
632 #ifdef USE_GLOG
633 return static_cast<MsLogLevel>(FLAGS_v);
634 #else
635 int log_level = MsLogLevel::kWarning; // set default log level to WARNING
636 auto str_level = GetEnv("GLOG_v");
637 if (str_level.size() == 1) {
638 int ch = str_level.c_str()[0];
639 constexpr char number_start = '0';
640 ch = ch - number_start; // subtract ASCII code of '0', which is 48
641 if (ch >= MsLogLevel::kDebug && ch <= MsLogLevel::kException) {
642 log_level = ch;
643 }
644 }
645 return static_cast<MsLogLevel>(log_level);
646 #endif
647 }
648
InitSubModulesLogLevel()649 void InitSubModulesLogLevel() {
650 // initialize submodule's log level using global
651 auto global_log_level = GetGlobalLogLevel();
652 for (int i = 0; i < static_cast<int>(NUM_SUBMODUES); ++i) {
653 g_ms_submodule_log_levels[i] = global_log_level;
654 }
655
656 // set submodule's log level
657 auto submodule = GetEnv("MS_SUBMODULE_LOG_v");
658 MS_LOG(DEBUG) << "MS_SUBMODULE_LOG_v=`" << submodule << "`";
659 LogConfigParser parser(submodule);
660 auto configs = parser.Parse();
661 for (const auto &cfg : configs) {
662 int mod_idx = -1;
663 for (int i = 0; i < static_cast<int>(NUM_SUBMODUES); ++i) {
664 if (cfg.first == GetSubModuleName(static_cast<SubModuleId>(i))) {
665 mod_idx = i;
666 break;
667 }
668 }
669 if (mod_idx < 0) {
670 MS_LOG(WARNING) << "Undefined module name " << cfg.first << ", ignore it";
671 continue;
672 }
673 MsLogLevel submodule_log_level;
674 if (!ParseLogLevel(cfg.second, &submodule_log_level)) {
675 MS_LOG(WARNING) << "Illegal log level value " << cfg.second << " for " << cfg.first << ", ignore it.";
676 continue;
677 }
678 g_ms_submodule_log_levels[mod_idx] = static_cast<int>(submodule_log_level);
679 }
680 }
681
ParsePositiveInt(const std::string & str,const size_t beg,const size_t end,int * ptr_val)682 bool ParsePositiveInt(const std::string &str, const size_t beg, const size_t end, int *ptr_val) {
683 int64_t val = 0;
684 size_t idx = beg;
685 constexpr int64_t number_10 = 10;
686 while (idx < end && IsDigit(str[idx])) {
687 val = val * number_10 + (str[idx] - '0');
688 if (val > std::numeric_limits<int>::max()) {
689 return false;
690 }
691 ++idx;
692 }
693 if (val == 0) {
694 return false;
695 }
696 if (ptr_val != nullptr) {
697 *ptr_val = static_cast<int>(val);
698 }
699 return idx == end;
700 }
701
GetKeyworkIndices(const std::string & str,size_t * ptr_idx_lparen,size_t * ptr_idx_rparen,size_t * ptr_idx_comma)702 bool GetKeyworkIndices(const std::string &str, size_t *ptr_idx_lparen, size_t *ptr_idx_rparen, size_t *ptr_idx_comma) {
703 size_t idx_lparen = std::string::npos; // index of '('
704 size_t idx_rparen = std::string::npos; // index of ')'
705 size_t idx_comma = std::string::npos; // index of ','
706
707 for (size_t i = 0; i < str.size(); ++i) {
708 char ch = str[i];
709 if (ch == '(') {
710 if (idx_lparen != std::string::npos) {
711 return false;
712 }
713 idx_lparen = i;
714 } else if (ch == ')') {
715 if (idx_rparen != std::string::npos) {
716 return false;
717 }
718 idx_rparen = i;
719 } else if (ch == ',') {
720 if (idx_comma != std::string::npos) {
721 return false;
722 }
723 idx_comma = i;
724 } else if (!IsDigit(ch)) {
725 return false;
726 }
727 }
728
729 *ptr_idx_lparen = idx_lparen;
730 *ptr_idx_rparen = idx_rparen;
731 *ptr_idx_comma = idx_comma;
732
733 return true;
734 }
735
736 // the valid values of VLOG_v can be in format: num, (num1,num2), (num1,) or (,num2)
ParseVerboseLogLevel(const std::string & str,int * from,int * to)737 bool ParseVerboseLogLevel(const std::string &str, int *from, int *to) {
738 if (str.empty()) {
739 return true;
740 }
741
742 size_t idx_lparen = std::string::npos; // index of '('
743 size_t idx_rparen = std::string::npos; // index of ')'
744 size_t idx_comma = std::string::npos; // index of ','
745 constexpr size_t number_3 = 3;
746 if (!GetKeyworkIndices(str, &idx_lparen, &idx_rparen, &idx_comma)) {
747 return false;
748 }
749
750 if (idx_lparen == std::string::npos && idx_rparen == std::string::npos && idx_comma == std::string::npos) {
751 int val = 0;
752 if (!ParsePositiveInt(str, 0, str.size(), &val)) {
753 return false;
754 }
755 *from = *to = val;
756 return true;
757 } else if (idx_lparen == 0 && idx_rparen == str.size() - 1 && idx_comma > idx_lparen && idx_comma < idx_rparen &&
758 str.size() > number_3) {
759 // VLOG_v in format (,number)
760 if (idx_comma == 1) {
761 *from = 1;
762 } else if (!ParsePositiveInt(str, 1, idx_comma, from)) {
763 return false;
764 }
765 // VLOG_v in format (number,)
766 if (idx_comma + 1 == idx_rparen) {
767 *to = std::numeric_limits<int>::max();
768 } else if (!ParsePositiveInt(str, idx_comma + 1, idx_rparen, to)) {
769 return false;
770 }
771 return true;
772 }
773
774 return false;
775 }
776
InitVerboseLogLevel()777 void InitVerboseLogLevel() {
778 auto var_vlog = mindspore::GetEnv("VLOG_v");
779 if (var_vlog.empty()) {
780 return;
781 }
782
783 int level_from = 0;
784 int level_to = 0;
785 if (ParseVerboseLogLevel(var_vlog, &level_from, &level_to)) {
786 g_ms_vlog_level_from = level_from;
787 g_ms_vlog_level_to = level_to;
788 MS_LOG(INFO) << "vlog_level_from=" << g_ms_vlog_level_from << ", vlog_level_to=" << g_ms_vlog_level_to;
789 return;
790 }
791
792 MS_LOG(WARNING) << "Value of environment var VLOG_v is invalid: " << var_vlog;
793 }
794
DispVerboseLogTags()795 void DispVerboseLogTags() {
796 MS_VLOG(VL_DISP_VLOG_TAGS) << VL_FLOW << ": start of end to end flow related log level";
797 MS_VLOG(VL_DISP_VLOG_TAGS) << VL_CORE << ": start of submodule log level";
798 MS_VLOG(VL_DISP_VLOG_TAGS) << VL_USER_CUSTOM << ": start of user defined log level";
799
800 MS_VLOG(VL_DISP_VLOG_TAGS) << VL_ASCEND_KERNEL_SELECT << ": ascend kernel select tag";
801 MS_VLOG(VL_DISP_VLOG_TAGS) << VL_DISP_VLOG_TAGS << ": log level for printing vlog tags already been used";
802 }
803
GetSubModuleName(SubModuleId module_id)804 const std::string GetSubModuleName(SubModuleId module_id) {
805 static const char sub_module_names[NUM_SUBMODUES][kNameMaxLength] = {
806 "UNKNOWN", // SM_UNKNOWN
807 "CORE", // SM_CORE
808 "ANALYZER", // SM_ANALYZER
809 "COMMON", // SM_COMMON
810 "DEBUG", // SM_DEBUG
811 "OFFLINE_DEBUG", // SM_OFFLINE_DEBUG
812 "DEVICE", // SM_DEVICE
813 "GE_ADPT", // SM_GE_ADPT
814 "IR", // SM_IR
815 "KERNEL", // SM_KERNEL
816 "MD", // SM_MD
817 "ME", // SM_ME
818 "EXPRESS", // SM_EXPRESS
819 "OPTIMIZER", // SM_OPTIMIZER
820 "PARALLEL", // SM_PARALLEL
821 "PARSER", // SM_PARSER
822 "PIPELINE", // SM_PIPELINE
823 "PRE_ACT", // SM_PRE_ACT
824 "PYNATIVE", // SM_PYNATIVE
825 "SESSION", // SM_SESSION
826 "UTILS", // SM_UTILS
827 "VM", // SM_VM
828 "PROFILER", // SM_PROFILER
829 "PS", // SM_PS
830 "PI", // SM_PI
831 "FL", // SM_FL
832 "DISTRIBUTED", // SM_DISTRIBUTED
833 "LITE", // SM_LITE
834 "ARMOUR", // SM_ARMOUR
835 "HCCL_ADPT", // SM_HCCL_ADPT
836 "RUNTIME_FRAMEWORK", // SM_RUNTIME_FRAMEWORK
837 "GE", // SM_GE
838 "API", // SM_API
839 "SYMBOLIC_SHAPE", // SM_SYMBOLIC_SHAPE
840 "GRAPH_KERNEL", // SM_GRAPH_KERNEL
841 "MINDIO", // SM MINDIO
842 };
843 return sub_module_names[IntToSize(module_id % NUM_SUBMODUES)];
844 }
845
GetTimeString()846 std::string GetTimeString() {
847 #if defined(_WIN32) || defined(_WIN64)
848 time_t time_seconds = time(0);
849 struct tm now_time;
850 localtime_s(&now_time, &time_seconds);
851 constexpr int base_year = 1900;
852 std::stringstream ss;
853 ss << now_time.tm_year + base_year << "-" << now_time.tm_mon + 1 << "-" << now_time.tm_mday << " " << now_time.tm_hour
854 << ":" << now_time.tm_min << ":" << now_time.tm_sec;
855 return ss.str();
856 #else
857 constexpr auto BUFLEN = 80;
858 char buf[BUFLEN] = {'\0'};
859 struct timeval cur_time;
860 (void)gettimeofday(&cur_time, nullptr);
861
862 struct tm now;
863 constexpr int width = 3;
864 constexpr int64_t time_convert_unit = 1000;
865 (void)localtime_r(&cur_time.tv_sec, &now);
866 (void)strftime(buf, BUFLEN, "%Y-%m-%d-%H:%M:%S", &now); // format date and time
867 std::stringstream ss;
868 ss << "." << std::setfill('0') << std::setw(width) << cur_time.tv_usec / time_convert_unit << "." << std::setfill('0')
869 << std::setw(width) << cur_time.tv_usec % time_convert_unit;
870 return std::string(buf) + ss.str();
871 #endif
872 }
873
MakeDirectory(const char * path)874 bool MakeDirectory(const char *path) {
875 #if defined(_WIN32) || defined(_WIN64)
876 if (mkdir(path) == -1) {
877 #else
878 constexpr int DEFAULT_MKDIR_MODE = 0700;
879 if (mkdir(path, DEFAULT_MKDIR_MODE) == -1) {
880 #endif
881 if (errno != EEXIST) {
882 return false;
883 }
884 }
885 return true;
886 }
887
888 bool MakePath(char *file_path) {
889 if (file_path == nullptr || file_path[0] == '\0') {
890 return true;
891 }
892
893 char *p = file_path + 1;
894 while (*p) {
895 if (*p == '\\' || *p == '/') {
896 char ch = *p;
897 *p = '\0';
898
899 if (!MakeDirectory(file_path)) {
900 *p = ch;
901 return false;
902 }
903 *p = ch;
904 }
905 p++;
906 }
907 return MakeDirectory(file_path);
908 }
909
910 // get log dir from GLOG_log_dir with RANK_ID or OMPI_COMM_WORLD_RANK.
911 std::string GetRealLogPath(const std::string &log_dir) {
912 const std::string rank_id = mindspore::GetEnv("RANK_ID");
913 const std::string gpu_rank_id = mindspore::GetEnv("OMPI_COMM_WORLD_RANK");
914 const std::string ms_node_id = mindspore::GetEnv("MS_NODE_ID");
915 std::string rank = "0";
916 if (!rank_id.empty()) {
917 rank = rank_id;
918 } else if (!gpu_rank_id.empty()) {
919 rank = gpu_rank_id;
920 } else if (!ms_node_id.empty()) {
921 rank = ms_node_id;
922 }
923 return log_dir + "/rank_" + rank + "/logs";
924 }
925 } // namespace mindspore
926
927 extern "C" {
928 #if defined(_WIN32) || defined(_WIN64) || defined(__APPLE__)
929 #ifdef _MSC_VER
common_log_init(void)930 MS_CORE_API void common_log_init(void) {
931 #else
932 __attribute__((constructor)) MS_CORE_API void common_log_init(void) {
933 #endif
934 #else
935 MS_CORE_API void common_log_init(void) {
936 #endif
937 #ifdef USE_GLOG
938 // Do not use glog predefined log prefix
939 FLAGS_log_prefix = false;
940 // Write log to files real-time
941 FLAGS_logbufsecs = 0;
942 // Set default log level to WARNING
943 if (mindspore::GetEnv("GLOG_v").empty()) {
944 FLAGS_v = static_cast<int>(mindspore::MsLogLevel::kWarning);
945 }
946
947 // Set default log file mode to 0640
948 if (mindspore::GetEnv("GLOG_logfile_mode").empty()) {
949 FLAGS_logfile_mode = 0640;
950 }
951 // Set default log file max size to 50 MB
952 FLAGS_max_log_size = 50;
953 std::string max_log_size = mindspore::GetEnv("GLOG_max_log_size");
954 if (!max_log_size.empty()) {
955 FLAGS_max_log_size = std::stoi(max_log_size);
956 }
957 std::string logtostderr = mindspore::GetEnv("GLOG_logtostderr");
958 // Default print log to screen
959 FLAGS_logtostderr = true;
960 if (logtostderr == "0") {
961 auto log_dir = mindspore::GetEnv("GLOG_log_dir");
962 if (log_dir.empty()) {
963 MS_LOG(ERROR) << "`GLOG_log_dir` is empty, it must be set while 'logtostderr' equals to 0.";
964 // Here can not throw exception and use python to catch, because the PYBIND11_MODULE is not yet been initialed.
965 exit(EXIT_FAILURE);
966 } else {
967 FLAGS_logtostderr = false;
968 // Set log dir from GLOG_log_dir with RANK_ID or OMPI_COMM_WORLD_RANK.
969 FLAGS_log_dir = mindspore::GetRealLogPath(log_dir);
970 }
971 }
972
973 // Default GLOG_stderrthreshold level to WARNING
974 auto threshold = mindspore::GetEnv("GLOG_stderrthreshold");
975 FLAGS_stderrthreshold = mindspore::GetThresholdLevel(threshold);
976
977 #endif
978 mindspore::InitSubModulesLogLevel();
979 mindspore::InitVerboseLogLevel();
980 mindspore::DispVerboseLogTags();
981 }
982
983 // shared lib init hook
984 #if defined(_WIN32) || defined(_WIN64) || defined(__APPLE__)
985 #if defined(_MSC_VER)
986 MS_CORE_API void mindspore_log_init(void) {
987 #else
988 __attribute__((constructor)) MS_CORE_API void mindspore_log_init(void) {
989 #endif
990 #else
991 MS_CORE_API void mindspore_log_init(void) {
992 #endif
993 #ifdef USE_GLOG
994 #define google mindspore_private
995 static bool is_glog_initialzed = false;
996 if (!is_glog_initialzed) {
997 std::string logtostderr = mindspore::GetEnv("GLOG_logtostderr");
998 std::string log_dir = mindspore::GetEnv("GLOG_log_dir");
999 if (logtostderr == "0" && !log_dir.empty()) {
1000 auto path = mindspore::GetRealLogPath(log_dir);
1001 if (!mindspore::MakePath(path.data())) {
1002 fprintf(stderr, "Failed to create log path %s!\n", path.c_str());
1003 }
1004 }
1005
1006 google::InitGoogleLogging("mindspore");
1007 is_glog_initialzed = true;
1008 }
1009 #undef google
1010 #endif
1011 common_log_init();
1012 }
1013
1014 #ifdef _MSC_VER
1015 typedef void(__cdecl *PF)(void);
1016 #pragma section(".CRT$XCG", read)
1017 __declspec(allocate(".CRT$XCG")) PF f[] = {mindspore_log_init};
1018 #endif
1019 }
1020