1 | /******************************************************************************* |
2 | Copyright (c) The Taichi Authors (2016- ). All Rights Reserved. |
3 | The use of this software is governed by the LICENSE file. |
4 | *******************************************************************************/ |
5 | |
6 | #include "taichi/common/logging.h" |
7 | |
8 | #include "spdlog/common.h" |
9 | #include "spdlog/sinks/stdout_color_sinks.h" |
10 | #include "spdlog/spdlog.h" |
11 | #ifdef ANDROID |
12 | #include "spdlog/sinks/android_sink.h" |
13 | #endif |
14 | #include "taichi/common/core.h" |
15 | |
16 | namespace taichi { |
17 | |
18 | const auto default_logging_level = "info" ; |
19 | |
20 | void Logger::set_level(const std::string &level_name) { |
21 | auto new_level = level_enum_from_string(level_name); |
22 | level_ = new_level; |
23 | spdlog::set_level((spdlog::level::level_enum)level_); |
24 | } |
25 | |
26 | int Logger::get_level() { |
27 | return level_; |
28 | } |
29 | |
30 | bool Logger::is_level_effective(const std::string &level_name) { |
31 | return get_level() <= level_enum_from_string(level_name); |
32 | } |
33 | |
34 | int Logger::level_enum_from_string(const std::string &level_name) { |
35 | if (level_name == "trace" ) { |
36 | return spdlog::level::trace; |
37 | } else if (level_name == "debug" ) { |
38 | return spdlog::level::debug; |
39 | } else if (level_name == "info" ) { |
40 | return spdlog::level::info; |
41 | } else if (level_name == "warn" ) { |
42 | return spdlog::level::warn; |
43 | } else if (level_name == "error" ) { |
44 | return spdlog::level::err; |
45 | } else if (level_name == "critical" ) { |
46 | return spdlog::level::critical; |
47 | } else if (level_name == "off" ) { |
48 | return spdlog::level::off; |
49 | } else { |
50 | TI_ERROR( |
51 | "Unknown logging level [{}]. Levels = trace, debug, info, warn, error, " |
52 | "critical, off" , |
53 | level_name); |
54 | } |
55 | } |
56 | |
57 | Logger::Logger() { |
58 | #ifdef ANDROID |
59 | console_ = spdlog::android_logger_mt("android" , "taichi" ); |
60 | console_->flush_on(spdlog::level::trace); |
61 | #else |
62 | console_ = spdlog::stderr_color_mt("stderr" ); |
63 | console_->flush_on(spdlog::level::trace); |
64 | #endif |
65 | TI_LOG_SET_PATTERN("%^[%L %D %X.%e %t] %v%$" ); |
66 | |
67 | set_level_default(); |
68 | } |
69 | |
70 | void Logger::set_level_default() { |
71 | set_level(default_logging_level); |
72 | } |
73 | |
74 | void Logger::trace(const std::string &s) { |
75 | console_->trace(s); |
76 | } |
77 | |
78 | void Logger::debug(const std::string &s) { |
79 | console_->debug(s); |
80 | } |
81 | |
82 | void Logger::info(const std::string &s) { |
83 | console_->info(s); |
84 | } |
85 | |
86 | void Logger::warn(const std::string &s) { |
87 | console_->warn(s); |
88 | } |
89 | |
90 | void Logger::error(const std::string &s, bool raise_exception) { |
91 | console_->error(s); |
92 | fmt::print("\n\n" ); |
93 | if (print_stacktrace_fn_) { |
94 | print_stacktrace_fn_(); |
95 | } |
96 | if (taichi::CoreState::get_instance().trigger_gdb_when_crash) { |
97 | #if defined(TI_PLATFORM_LINUX) |
98 | trash(system(fmt::format("sudo gdb -p {}" , PID::get_pid()).c_str())); |
99 | #endif |
100 | } |
101 | if (raise_exception) |
102 | throw s; |
103 | } |
104 | |
105 | void Logger::critical(const std::string &s) { |
106 | Logger::error(s); // simply forward to Logger::error since we actually never |
107 | // use TI_CRITICAL |
108 | } |
109 | |
110 | void Logger::flush() { |
111 | console_->flush(); |
112 | } |
113 | |
114 | void Logger::set_print_stacktrace_func(std::function<void()> print_fn) { |
115 | print_stacktrace_fn_ = print_fn; |
116 | } |
117 | |
118 | // static |
119 | Logger &Logger::get_instance() { |
120 | // Use the singleton pattern, instead of defining a global variable. This is |
121 | // because I've moved the signal handler registration + pybind11's |
122 | // py::register_exception_translator to |
123 | // taichi/system/hacked_signal_handler.cpp. We instantiate a global |
124 | // HackedSIgnalHandler (in the anonymous namespace), whose constructor |
125 | // registers the signal handlers. |
126 | |
127 | // This decouples Logger from pybind11. However, it has introduced a problem |
128 | // if we continue to keep a global Logger instance: the construction order |
129 | // between Logger and HackedSIgnalHandler is unspecified, and it actually |
130 | // crashes on my system. So we use the singleton pattern instead. |
131 | static Logger *l = new Logger(); |
132 | return *l; |
133 | } |
134 | |
135 | } // namespace taichi |
136 | |