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
16namespace taichi {
17
18const auto default_logging_level = "info";
19
20void 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
26int Logger::get_level() {
27 return level_;
28}
29
30bool Logger::is_level_effective(const std::string &level_name) {
31 return get_level() <= level_enum_from_string(level_name);
32}
33
34int 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
57Logger::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
70void Logger::set_level_default() {
71 set_level(default_logging_level);
72}
73
74void Logger::trace(const std::string &s) {
75 console_->trace(s);
76}
77
78void Logger::debug(const std::string &s) {
79 console_->debug(s);
80}
81
82void Logger::info(const std::string &s) {
83 console_->info(s);
84}
85
86void Logger::warn(const std::string &s) {
87 console_->warn(s);
88}
89
90void 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
105void 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
110void Logger::flush() {
111 console_->flush();
112}
113
114void Logger::set_print_stacktrace_func(std::function<void()> print_fn) {
115 print_stacktrace_fn_ = print_fn;
116}
117
118// static
119Logger &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