• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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