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.cc 10 kB

5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /**
  2. * Copyright 2020 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 "common/log.h"
  17. #include <sys/time.h>
  18. #include "glog/logging.h"
  19. namespace mindspore {
  20. namespace serving {
  21. int g_ms_serving_log_level = LOG_WARNING;
  22. #undef Dlog
  23. #define Dlog(module_id, level, format, ...) \
  24. do { \
  25. DlogInner((module_id), (level), (format), ##__VA_ARGS__); \
  26. } while (0)
  27. static std::string GetTime() {
  28. #define BUFLEN 80
  29. static char buf[BUFLEN];
  30. #if defined(_WIN32) || defined(_WIN64)
  31. time_t time_seconds = time(0);
  32. struct tm now_time;
  33. localtime_s(&now_time, &time_seconds);
  34. sprintf_s(buf, BUFLEN, "%d-%d-%d %d:%d:%d", now_time.tm_year + 1900, now_time.tm_mon + 1, now_time.tm_mday,
  35. now_time.tm_hour, now_time.tm_min, now_time.tm_sec);
  36. #else
  37. struct timeval cur_time;
  38. (void)gettimeofday(&cur_time, nullptr);
  39. struct tm now;
  40. (void)localtime_r(&cur_time.tv_sec, &now);
  41. (void)strftime(buf, BUFLEN, "%Y-%m-%d-%H:%M:%S", &now); // format date and time
  42. // set micro-second
  43. buf[27] = '\0';
  44. int idx = 26;
  45. auto num = cur_time.tv_usec;
  46. for (int i = 5; i >= 0; i--) {
  47. buf[idx--] = static_cast<char>(num % 10 + '0');
  48. num /= 10;
  49. if (i % 3 == 0) {
  50. buf[idx--] = '.';
  51. }
  52. }
  53. #endif
  54. return std::string(buf);
  55. }
  56. static std::string GetProcName() {
  57. #if defined(__APPLE__) || defined(__FreeBSD__)
  58. const char *appname = getprogname();
  59. #elif defined(_GNU_SOURCE)
  60. const char *appname = program_invocation_name;
  61. #else
  62. const char *appname = "?";
  63. #endif
  64. // some times, the appname is an absolute path, its 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. static std::string GetLogLevel(MsLogLevel level) {
  76. switch (level) {
  77. case LOG_DEBUG:
  78. return "DEBUG";
  79. case LOG_INFO:
  80. return "INFO";
  81. case LOG_WARNING:
  82. return "WARNING";
  83. case LOG_ERROR:
  84. default:
  85. return "ERROR";
  86. }
  87. }
  88. // convert MsLogLevel to corresponding glog level
  89. static int GetGlogLevel(MsLogLevel level) {
  90. switch (level) {
  91. case LOG_DEBUG:
  92. case LOG_INFO:
  93. return google::GLOG_INFO;
  94. case LOG_WARNING:
  95. return google::GLOG_WARNING;
  96. case LOG_ERROR:
  97. default:
  98. return google::GLOG_ERROR;
  99. }
  100. }
  101. void LogWriter::OutputLog(const std::string &msg_str) const {
  102. if (log_level_ < g_ms_serving_log_level) {
  103. return;
  104. }
  105. auto submodule_name = "SERVING";
  106. google::LogMessage("", 0, GetGlogLevel(log_level_)).stream()
  107. << "[" << GetLogLevel(log_level_) << "] " << submodule_name << "(" << getpid() << "," << GetProcName()
  108. << "):" << GetTime() << " "
  109. << "[" << file_ << ":" << line_ << "] " << func_ << "] " << msg_str << std::endl;
  110. }
  111. static MsLogLevel GetGlobalLogLevel() { return static_cast<MsLogLevel>(FLAGS_v); }
  112. static std::string GetEnv(const std::string &envvar) {
  113. const char *value = ::getenv(envvar.c_str());
  114. if (value == nullptr) {
  115. return std::string();
  116. }
  117. return std::string(value);
  118. }
  119. enum LogConfigToken {
  120. INVALID, // indicate invalid token
  121. LEFT_BRACE, // '{'
  122. RIGHT_BRACE, // '}'
  123. VARIABLE, // '[A-Za-z][A-Za-z0-9_]*'
  124. NUMBER, // [0-9]+
  125. COMMA, // ','
  126. COLON, // ':'
  127. EOS, // End Of String, '\0'
  128. NUM_LOG_CFG_TOKENS
  129. };
  130. static const char *g_tok_names[NUM_LOG_CFG_TOKENS] = {
  131. "invalid", // indicate invalid token
  132. "{", // '{'
  133. "}", // '}'
  134. "variable", // '[A-Za-z][A-Za-z0-9_]*'
  135. "number", // [0-9]+
  136. ",", // ','
  137. ":", // ':'
  138. "end-of-string", // End Of String, '\0'
  139. };
  140. static inline bool IsAlpha(char ch) { return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); }
  141. static inline bool IsDigit(char ch) { return ch >= '0' && ch <= '9'; }
  142. class LogConfigLexer {
  143. public:
  144. explicit LogConfigLexer(const std::string &text) : buffer_(text) {
  145. cur_idx_ = 0;
  146. cur_token_ = LogConfigToken::INVALID;
  147. }
  148. ~LogConfigLexer() = default;
  149. // skip white space, and return the first char after white space
  150. char SkipWhiteSpace() {
  151. while (cur_idx_ < buffer_.size()) {
  152. char ch = buffer_[cur_idx_];
  153. if (ch == ' ' || ch == '\t') {
  154. ++cur_idx_;
  155. continue;
  156. }
  157. return ch;
  158. }
  159. return '\0';
  160. }
  161. LogConfigToken GetNext(std::string *const ptr) {
  162. char ch = SkipWhiteSpace();
  163. // clang-format off
  164. static const std::map<char, LogConfigToken> single_char_map = {
  165. {'{', LogConfigToken::LEFT_BRACE},
  166. {'}', LogConfigToken::RIGHT_BRACE},
  167. {',', LogConfigToken::COMMA},
  168. {':', LogConfigToken::COLON},
  169. {'\0', LogConfigToken::EOS},
  170. };
  171. // clang-format on
  172. auto iter = single_char_map.find(ch);
  173. if (iter != single_char_map.end()) {
  174. if (ptr != nullptr) {
  175. *ptr = std::string() + ch;
  176. }
  177. ++cur_idx_;
  178. return iter->second;
  179. } else if (IsAlpha(ch)) {
  180. std::ostringstream oss;
  181. do {
  182. oss << ch;
  183. ch = buffer_[++cur_idx_];
  184. } while (cur_idx_ < buffer_.size() && (IsAlpha(ch) || IsDigit(ch) || ch == '_'));
  185. if (ptr != nullptr) {
  186. *ptr = std::string(oss.str());
  187. }
  188. return LogConfigToken::VARIABLE;
  189. } else if (IsDigit(ch)) {
  190. std::ostringstream oss;
  191. do {
  192. oss << ch;
  193. ch = buffer_[++cur_idx_];
  194. } while (cur_idx_ < buffer_.size() && IsDigit(ch));
  195. if (ptr != nullptr) {
  196. *ptr = std::string(oss.str());
  197. }
  198. return LogConfigToken::NUMBER;
  199. }
  200. return LogConfigToken::INVALID;
  201. }
  202. private:
  203. std::string buffer_;
  204. size_t cur_idx_;
  205. LogConfigToken cur_token_;
  206. std::string cur_text_;
  207. };
  208. class LogConfigParser {
  209. public:
  210. explicit LogConfigParser(const std::string &cfg) : lexer(cfg) {}
  211. ~LogConfigParser() = default;
  212. bool Expect(LogConfigToken expected, LogConfigToken tok) {
  213. if (expected != tok) {
  214. MSI_LOG_WARNING << "`, but got `" << g_tok_names[tok] << "`. The whole configuration will be ignored.";
  215. return false;
  216. }
  217. return true;
  218. }
  219. // The text of config MS_SUBMODULE_LOG_v is in the form {submodule1:log_level1,submodule2:log_level2,...}.
  220. // Valid values of log levels are: 0 - debug, 1 - info, 2 - warning, 3 - error
  221. // e.g. MS_SUBMODULE_LOG_v={PARSER:0, ANALYZER:2, PIPELINE:1}
  222. std::map<std::string, std::string> Parse() {
  223. std::map<std::string, std::string> log_levels;
  224. bool flag_error = false;
  225. std::string text;
  226. auto tok = lexer.GetNext(&text);
  227. // empty string
  228. if (tok == LogConfigToken::EOS) {
  229. return log_levels;
  230. }
  231. if (!Expect(LogConfigToken::LEFT_BRACE, tok)) {
  232. return log_levels;
  233. }
  234. do {
  235. std::string key, val;
  236. tok = lexer.GetNext(&key);
  237. if (!Expect(LogConfigToken::VARIABLE, tok)) {
  238. flag_error = true;
  239. break;
  240. }
  241. tok = lexer.GetNext(&text);
  242. if (!Expect(LogConfigToken::COLON, tok)) {
  243. flag_error = true;
  244. break;
  245. }
  246. tok = lexer.GetNext(&val);
  247. if (!Expect(LogConfigToken::NUMBER, tok)) {
  248. flag_error = true;
  249. break;
  250. }
  251. log_levels[key] = val;
  252. tok = lexer.GetNext(&text);
  253. } while (tok == LogConfigToken::COMMA);
  254. if (!flag_error && !Expect(LogConfigToken::RIGHT_BRACE, tok)) {
  255. flag_error = true;
  256. }
  257. if (flag_error) {
  258. log_levels.clear();
  259. }
  260. return log_levels;
  261. }
  262. private:
  263. LogConfigLexer lexer;
  264. };
  265. bool ParseLogLevel(const std::string &str_level, MsLogLevel *ptr_level) {
  266. if (str_level.size() == 1) {
  267. int ch = str_level.c_str()[0];
  268. ch = ch - '0'; // subtract ASCII code of '0', which is 48
  269. if (ch >= LOG_DEBUG && ch <= LOG_ERROR) {
  270. if (ptr_level != nullptr) {
  271. *ptr_level = static_cast<MsLogLevel>(ch);
  272. }
  273. return true;
  274. }
  275. }
  276. return false;
  277. }
  278. void InitSubModulesLogLevel() {
  279. // initialize submodule's log level using global
  280. auto global_log_level = GetGlobalLogLevel();
  281. g_ms_serving_log_level = global_log_level;
  282. // set submodule's log level
  283. auto submodule = GetEnv("MS_SUBMODULE_LOG_v");
  284. MSI_LOG(DEBUG) << "MS_SUBMODULE_LOG_v=`" << submodule << "`";
  285. LogConfigParser parser(submodule);
  286. auto configs = parser.Parse();
  287. for (const auto &cfg : configs) {
  288. if (cfg.first == "SERVING") {
  289. MsLogLevel submodule_log_level;
  290. if (!ParseLogLevel(cfg.second, &submodule_log_level)) {
  291. MSI_LOG(WARNING) << "Illegal log level value " << cfg.second << " for " << cfg.first << ", ignore it.";
  292. continue;
  293. }
  294. g_ms_serving_log_level = submodule_log_level;
  295. }
  296. }
  297. }
  298. void common_log_init(void) {
  299. // do not use glog predefined log prefix
  300. FLAGS_log_prefix = false;
  301. // set default log level to WARNING
  302. if (mindspore::serving::GetEnv("GLOG_v").empty()) {
  303. FLAGS_v = mindspore::serving::LOG_WARNING;
  304. }
  305. // set default log file mode to 0640
  306. if (mindspore::serving::GetEnv("GLOG_logfile_mode").empty()) {
  307. FLAGS_logfile_mode = 0640;
  308. }
  309. std::string logtostderr = mindspore::serving::GetEnv("GLOG_logtostderr");
  310. // default print log to screen
  311. if (logtostderr.empty()) {
  312. FLAGS_logtostderr = true;
  313. } else if (logtostderr == "0" && mindspore::serving::GetEnv("GLOG_log_dir").empty()) {
  314. FLAGS_logtostderr = true;
  315. MSI_LOG(WARNING) << "`GLOG_log_dir` is not set, output log to screen.";
  316. }
  317. mindspore::serving::InitSubModulesLogLevel();
  318. }
  319. } // namespace serving
  320. } // namespace mindspore
  321. extern "C" {
  322. #if defined(_WIN32) || defined(_WIN64)
  323. __attribute__((constructor)) void mindspore_serving_log_init(void) {
  324. #else
  325. void mindspore_serving_log_init(void) {
  326. #endif
  327. mindspore::serving::common_log_init();
  328. }
  329. }

A lightweight and high-performance service module that helps MindSpore developers efficiently deploy online inference services in the production environment.