You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

log_adapter.cc 15 kB

5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /**
  2. * Copyright 2019 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. #include "utils/log_adapter.h"
  17. #include <unistd.h>
  18. #include <map>
  19. #include "pybind11/pybind11.h"
  20. #include "debug/trace.h"
  21. // namespace to support utils module definition
  22. namespace mindspore {
  23. #ifdef USE_GLOG
  24. static std::string GetTime() {
  25. #define BUFLEN 80
  26. static char buf[BUFLEN];
  27. #if defined(_WIN32) || defined(_WIN64)
  28. time_t time_seconds = time(0);
  29. struct tm now_time;
  30. localtime_s(&now_time, &time_seconds);
  31. sprintf_s(buf, BUFLEN, "%d-%d-%d %d:%d:%d", now_time.tm_year + 1900, now_time.tm_mon + 1, now_time.tm_mday,
  32. now_time.tm_hour, now_time.tm_min, now_time.tm_sec);
  33. #else
  34. struct timeval cur_time;
  35. (void)gettimeofday(&cur_time, nullptr);
  36. struct tm now;
  37. (void)localtime_r(&cur_time.tv_sec, &now);
  38. (void)strftime(buf, BUFLEN, "%Y-%m-%d-%H:%M:%S", &now); // format date and time
  39. // set micro-second
  40. buf[27] = '\0';
  41. int idx = 26;
  42. auto num = cur_time.tv_usec;
  43. for (int i = 5; i >= 0; i--) {
  44. buf[idx--] = static_cast<char>(num % 10 + '0');
  45. num /= 10;
  46. if (i % 3 == 0) {
  47. buf[idx--] = '.';
  48. }
  49. }
  50. #endif
  51. return std::string(buf);
  52. }
  53. static std::string GetProcName() {
  54. #if defined(__APPLE__) || defined(__FreeBSD__)
  55. const char *appname = getprogname();
  56. #elif defined(_GNU_SOURCE)
  57. const char *appname = program_invocation_name;
  58. #else
  59. const char *appname = "?";
  60. #endif
  61. // some times, the appname is an absolute path, its too long
  62. std::string app_name(appname);
  63. std::size_t pos = app_name.rfind("/");
  64. if (pos == std::string::npos) {
  65. return app_name;
  66. }
  67. if (pos + 1 >= app_name.size()) {
  68. return app_name;
  69. }
  70. return app_name.substr(pos + 1);
  71. }
  72. static std::string GetLogLevel(MsLogLevel level) {
  73. #define _TO_STRING(x) #x
  74. static const char *const level_names[] = {
  75. _TO_STRING(DEBUG),
  76. _TO_STRING(INFO),
  77. _TO_STRING(WARNING),
  78. _TO_STRING(ERROR),
  79. };
  80. #undef _TO_STRING
  81. if (level > ERROR) {
  82. level = ERROR;
  83. }
  84. return std::string(level_names[level]);
  85. }
  86. // convert MsLogLevel to corresponding glog level
  87. static int GetGlogLevel(MsLogLevel level) {
  88. switch (level) {
  89. case DEBUG:
  90. case INFO:
  91. return google::GLOG_INFO;
  92. case WARNING:
  93. return google::GLOG_WARNING;
  94. case ERROR:
  95. default:
  96. return google::GLOG_ERROR;
  97. }
  98. }
  99. #else
  100. #undef Dlog
  101. #define Dlog(module_id, level, format, ...) \
  102. do { \
  103. DlogInner((module_id), (level), (format), ##__VA_ARGS__); \
  104. } while (0)
  105. // convert MsLogLevel to corresponding slog level
  106. static int GetSlogLevel(MsLogLevel level) {
  107. switch (level) {
  108. case DEBUG:
  109. return DLOG_DEBUG;
  110. case INFO:
  111. return DLOG_INFO;
  112. case WARNING:
  113. return DLOG_WARN;
  114. case ERROR:
  115. default:
  116. return DLOG_ERROR;
  117. }
  118. }
  119. #endif
  120. static std::string ExceptionTypeToString(ExceptionType type) {
  121. #define _TO_STRING(x) #x
  122. // clang-format off
  123. static const char *const type_names[] = {
  124. _TO_STRING(NoExceptionType),
  125. _TO_STRING(UnknownError),
  126. _TO_STRING(ArgumentError),
  127. _TO_STRING(NotSupportError),
  128. _TO_STRING(NotExistsError),
  129. _TO_STRING(AlreadyExistsError),
  130. _TO_STRING(UnavailableError),
  131. _TO_STRING(DeviceProcessError),
  132. _TO_STRING(AbortedError),
  133. _TO_STRING(TimeOutError),
  134. _TO_STRING(ResourceUnavailable),
  135. _TO_STRING(NoPermissionError),
  136. _TO_STRING(IndexError),
  137. _TO_STRING(ValueError),
  138. _TO_STRING(TypeError),
  139. };
  140. // clang-format on
  141. #undef _TO_STRING
  142. if (type < UnknownError || type > TypeError) {
  143. type = UnknownError;
  144. }
  145. return std::string(type_names[type]);
  146. }
  147. static const char *GetSubModuleName(SubModuleId module_id) {
  148. static const char *sub_module_names[NUM_SUBMODUES] = {
  149. "UNKNOWN", // SM_UNKNOWN
  150. "ANALYZER", // SM_ANALYZER
  151. "COMMON", // SM_COMMON
  152. "DEBUG", // SM_DEBUG
  153. "DEVICE", // SM_DEVICE
  154. "GE_ADPT", // SM_GE_ADPT
  155. "IR", // SM_IR
  156. "KERNEL", // SM_KERNEL
  157. "MD", // SM_MD
  158. "ME", // SM_ME
  159. "ONNX", // SM_ONNX
  160. "OPTIMIZER", // SM_OPTIMIZER
  161. "PARALLEL", // SM_PARALLEL
  162. "PARSER", // SM_PARSER
  163. "PIPELINE", // SM_PIPELINE
  164. "PRE_ACT", // SM_PRE_ACT
  165. "PYNATIVE", // SM_PYNATIVE
  166. "SESSION", // SM_SESSION
  167. "UTILS", // SM_UTILS
  168. "VM" // SM_VM
  169. };
  170. return sub_module_names[module_id % NUM_SUBMODUES];
  171. }
  172. void LogWriter::OutputLog(const std::ostringstream &msg) const {
  173. #ifdef USE_GLOG
  174. auto submodule_name = GetSubModuleName(submodule_);
  175. google::LogMessage("", 0, GetGlogLevel(log_level_)).stream()
  176. << "[" << GetLogLevel(log_level_) << "] " << submodule_name << "(" << getpid() << "," << GetProcName()
  177. << "):" << GetTime() << " "
  178. << "[" << location_.file_ << ":" << location_.line_ << "] " << location_.func_ << "] " << msg.str() << std::endl;
  179. #else
  180. auto str_msg = msg.str();
  181. auto slog_module_id = (submodule_ == SM_MD ? MD : ME);
  182. Dlog(static_cast<int>(slog_module_id), GetSlogLevel(log_level_), "[%s:%d] %s] %s", location_.file_, location_.line_,
  183. location_.func_, str_msg.c_str());
  184. #endif
  185. }
  186. void LogWriter::operator<(const LogStream &stream) const noexcept {
  187. std::ostringstream msg;
  188. msg << stream.sstream_->rdbuf();
  189. OutputLog(msg);
  190. }
  191. void LogWriter::operator^(const LogStream &stream) const {
  192. std::ostringstream msg;
  193. msg << stream.sstream_->rdbuf();
  194. OutputLog(msg);
  195. std::ostringstream oss;
  196. oss << location_.file_ << ":" << location_.line_ << " " << location_.func_ << "] ";
  197. if (exception_type_ != NoExceptionType && exception_type_ != IndexError && exception_type_ != TypeError &&
  198. exception_type_ != ValueError) {
  199. oss << ExceptionTypeToString(exception_type_) << " ";
  200. }
  201. oss << msg.str();
  202. trace::TraceGraphEval();
  203. trace::GetEvalStackInfo(oss);
  204. if (exception_type_ == IndexError) {
  205. throw pybind11::index_error(oss.str());
  206. }
  207. if (exception_type_ == ValueError) {
  208. throw pybind11::value_error(oss.str());
  209. }
  210. if (exception_type_ == TypeError) {
  211. throw pybind11::type_error(oss.str());
  212. }
  213. pybind11::pybind11_fail(oss.str());
  214. }
  215. static std::string GetEnv(const std::string &envvar) {
  216. const char *value = ::getenv(envvar.c_str());
  217. if (value == nullptr) {
  218. return std::string();
  219. }
  220. return std::string(value);
  221. }
  222. enum LogConfigToken {
  223. INVALID, // indicate invalid token
  224. LEFT_BRACE, // '{'
  225. RIGHT_BRACE, // '}'
  226. VARIABLE, // '[A-Za-z][A-Za-z0-9_]*'
  227. NUMBER, // [0-9]+
  228. COMMA, // ','
  229. COLON, // ':'
  230. EOS, // End Of String, '\0'
  231. NUM_LOG_CFG_TOKENS
  232. };
  233. static const char *g_tok_names[NUM_LOG_CFG_TOKENS] = {
  234. "invalid", // indicate invalid token
  235. "{", // '{'
  236. "}", // '}'
  237. "variable", // '[A-Za-z][A-Za-z0-9_]*'
  238. "number", // [0-9]+
  239. ",", // ','
  240. ":", // ':'
  241. "end-of-string", // End Of String, '\0'
  242. };
  243. static inline bool IsAlpha(char ch) { return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); }
  244. static inline bool IsDigit(char ch) { return ch >= '0' && ch <= '9'; }
  245. class LogConfigLexer {
  246. public:
  247. explicit LogConfigLexer(const std::string &text) : buffer_(text) {
  248. cur_idx_ = 0;
  249. cur_token_ = LogConfigToken::INVALID;
  250. }
  251. ~LogConfigLexer() = default;
  252. // skip white space, and return the first char after white space
  253. char SkipWhiteSpace() {
  254. while (cur_idx_ < buffer_.size()) {
  255. char ch = buffer_[cur_idx_];
  256. if (ch == ' ' || ch == '\t') {
  257. ++cur_idx_;
  258. continue;
  259. }
  260. return ch;
  261. }
  262. return '\0';
  263. }
  264. LogConfigToken GetNext(std::string *const ptr) {
  265. #ifdef DEBUG
  266. std::string text;
  267. auto tok = GetNextInner(&text);
  268. MS_LOG(DEBUG) << "Got token " << tok << " with value [" << text << "]";
  269. if (ptr != nullptr) {
  270. *ptr = text;
  271. }
  272. return tok;
  273. }
  274. LogConfigToken GetNextInner(std::string *ptr) {
  275. #endif
  276. char ch = SkipWhiteSpace();
  277. // clang-format off
  278. static const std::map<char, LogConfigToken> single_char_map = {
  279. {'{', LogConfigToken::LEFT_BRACE},
  280. {'}', LogConfigToken::RIGHT_BRACE},
  281. {',', LogConfigToken::COMMA},
  282. {':', LogConfigToken::COLON},
  283. {'\0', LogConfigToken::EOS},
  284. };
  285. // clang-format on
  286. auto iter = single_char_map.find(ch);
  287. if (iter != single_char_map.end()) {
  288. if (ptr != nullptr) {
  289. *ptr = std::string() + ch;
  290. }
  291. ++cur_idx_;
  292. return iter->second;
  293. } else if (IsAlpha(ch)) {
  294. std::ostringstream oss;
  295. do {
  296. oss << ch;
  297. ch = buffer_[++cur_idx_];
  298. } while (cur_idx_ < buffer_.size() && (IsAlpha(ch) || IsDigit(ch) || ch == '_'));
  299. if (ptr != nullptr) {
  300. *ptr = std::string(oss.str());
  301. }
  302. return LogConfigToken::VARIABLE;
  303. } else if (IsDigit(ch)) {
  304. std::ostringstream oss;
  305. do {
  306. oss << ch;
  307. ch = buffer_[++cur_idx_];
  308. } while (cur_idx_ < buffer_.size() && IsDigit(ch));
  309. if (ptr != nullptr) {
  310. *ptr = std::string(oss.str());
  311. }
  312. return LogConfigToken::NUMBER;
  313. }
  314. return LogConfigToken::INVALID;
  315. }
  316. private:
  317. std::string buffer_;
  318. size_t cur_idx_;
  319. LogConfigToken cur_token_;
  320. std::string cur_text_;
  321. };
  322. class LogConfigParser {
  323. public:
  324. explicit LogConfigParser(const std::string &cfg) : lexer(cfg) {}
  325. ~LogConfigParser() = default;
  326. bool Expect(LogConfigToken expected, LogConfigToken tok) {
  327. if (expected != tok) {
  328. MS_LOG(WARNING) << "Parse submodule log configuration text error, expect `" << g_tok_names[expected]
  329. << "`, but got `" << g_tok_names[tok] << "`. The whole configuration will be ignored.";
  330. return false;
  331. }
  332. return true;
  333. }
  334. // The text of config MS_SUBMODULE_LOG_v is in the form {submodule1:log_level1,submodule2:log_level2,...}.
  335. // Valid values of log levels are: 0 - debug, 1 - info, 2 - warning, 3 - error
  336. // e.g. MS_SUBMODULE_LOG_v={PARSER:0, ANALYZER:2, PIPELINE:1}
  337. std::map<std::string, std::string> Parse() {
  338. std::map<std::string, std::string> log_levels;
  339. bool flag_error = false;
  340. std::string text;
  341. auto tok = lexer.GetNext(&text);
  342. // empty string
  343. if (tok == LogConfigToken::EOS) {
  344. return log_levels;
  345. }
  346. if (!Expect(LogConfigToken::LEFT_BRACE, tok)) {
  347. return log_levels;
  348. }
  349. do {
  350. std::string key, val;
  351. tok = lexer.GetNext(&key);
  352. if (!Expect(LogConfigToken::VARIABLE, tok)) {
  353. flag_error = true;
  354. break;
  355. }
  356. tok = lexer.GetNext(&text);
  357. if (!Expect(LogConfigToken::COLON, tok)) {
  358. flag_error = true;
  359. break;
  360. }
  361. tok = lexer.GetNext(&val);
  362. if (!Expect(LogConfigToken::NUMBER, tok)) {
  363. flag_error = true;
  364. break;
  365. }
  366. log_levels[key] = val;
  367. tok = lexer.GetNext(&text);
  368. } while (tok == LogConfigToken::COMMA);
  369. if (!flag_error && !Expect(LogConfigToken::RIGHT_BRACE, tok)) {
  370. flag_error = true;
  371. }
  372. if (flag_error) {
  373. log_levels.clear();
  374. }
  375. return log_levels;
  376. }
  377. private:
  378. LogConfigLexer lexer;
  379. };
  380. bool ParseLogLevel(const std::string &str_level, MsLogLevel *ptr_level) {
  381. if (str_level.size() == 1) {
  382. int ch = str_level.c_str()[0];
  383. ch = ch - '0'; // substract ASCII code of '0', which is 48
  384. if (ch >= DEBUG && ch <= ERROR) {
  385. if (ptr_level != nullptr) {
  386. *ptr_level = static_cast<MsLogLevel>(ch);
  387. }
  388. return true;
  389. }
  390. }
  391. return false;
  392. }
  393. static MsLogLevel GetGlobalLogLevel() {
  394. #ifdef USE_GLOG
  395. return static_cast<MsLogLevel>(FLAGS_v);
  396. #else
  397. int log_level = WARNING; // set default log level to WARNING
  398. auto str_level = GetEnv("GLOG_v");
  399. if (str_level.size() == 1) {
  400. int ch = str_level.c_str()[0];
  401. ch = ch - '0'; // substract ASCII code of '0', which is 48
  402. if (ch >= DEBUG && ch <= ERROR) {
  403. log_level = ch;
  404. }
  405. }
  406. return static_cast<MsLogLevel>(log_level);
  407. #endif
  408. }
  409. void InitSubModulesLogLevel() {
  410. // initialize submodule's log level using global
  411. auto global_log_level = GetGlobalLogLevel();
  412. for (int i = 0; i < NUM_SUBMODUES; ++i) {
  413. g_ms_submodule_log_levels[i] = global_log_level;
  414. }
  415. // set submodule's log level
  416. auto submodule = GetEnv("MS_SUBMODULE_LOG_v");
  417. MS_LOG(DEBUG) << "MS_SUBMODULE_LOG_v=`" << submodule << "`";
  418. LogConfigParser parser(submodule);
  419. auto configs = parser.Parse();
  420. for (const auto &cfg : configs) {
  421. int mod_idx = -1;
  422. for (int i = 0; i < NUM_SUBMODUES; ++i) {
  423. if (cfg.first == GetSubModuleName(static_cast<SubModuleId>(i))) {
  424. mod_idx = i;
  425. break;
  426. }
  427. }
  428. if (mod_idx < 0) {
  429. MS_LOG(WARNING) << "Undefined module name " << cfg.first << ", ignore it";
  430. continue;
  431. }
  432. MsLogLevel submodule_log_level;
  433. if (!ParseLogLevel(cfg.second, &submodule_log_level)) {
  434. MS_LOG(WARNING) << "Illegal log level value " << cfg.second << " for " << cfg.first << ", ignore it.";
  435. continue;
  436. }
  437. g_ms_submodule_log_levels[mod_idx] = submodule_log_level;
  438. }
  439. }
  440. } // namespace mindspore
  441. extern "C" {
  442. #if defined(_WIN32) || defined(_WIN64)
  443. __attribute__((constructor)) void common_log_init(void) {
  444. #else
  445. void common_log_init(void) {
  446. #endif
  447. #ifdef USE_GLOG
  448. // do not use glog predefined log prefix
  449. FLAGS_log_prefix = false;
  450. // set default log level to WARNING
  451. if (mindspore::GetEnv("GLOG_v").empty()) {
  452. FLAGS_v = mindspore::WARNING;
  453. }
  454. // set default log file mode to 0640
  455. if (mindspore::GetEnv("GLOG_logfile_mode").empty()) {
  456. FLAGS_logfile_mode = 0640;
  457. }
  458. std::string logtostderr = mindspore::GetEnv("GLOG_logtostderr");
  459. // default print log to screen
  460. if (logtostderr.empty()) {
  461. FLAGS_logtostderr = true;
  462. } else if (logtostderr == "0" && mindspore::GetEnv("GLOG_log_dir").empty()) {
  463. FLAGS_logtostderr = true;
  464. MS_LOG(WARNING) << "`GLOG_log_dir` is not set, output log to screen.";
  465. }
  466. #endif
  467. mindspore::InitSubModulesLogLevel();
  468. }
  469. // shared lib init hook
  470. #if defined(_WIN32) || defined(_WIN64)
  471. __attribute__((constructor)) void mindspore_log_init(void) {
  472. #else
  473. void mindspore_log_init(void) {
  474. #endif
  475. #ifdef USE_GLOG
  476. static bool is_glog_initialzed = false;
  477. if (!is_glog_initialzed) {
  478. #if !defined(_WIN32) && !defined(_WIN64)
  479. google::InitGoogleLogging("mindspore");
  480. #endif
  481. is_glog_initialzed = true;
  482. }
  483. #endif
  484. common_log_init();
  485. }
  486. }