]>
Commit | Line | Data |
---|---|---|
1d09f67e TL |
1 | // Licensed to the Apache Software Foundation (ASF) under one |
2 | // or more contributor license agreements. See the NOTICE file | |
3 | // distributed with this work for additional information | |
4 | // regarding copyright ownership. The ASF licenses this file | |
5 | // to you under the Apache License, Version 2.0 (the | |
6 | // "License"); you may not use this file except in compliance | |
7 | // with the License. You may obtain a copy of the License at | |
8 | // | |
9 | // http://www.apache.org/licenses/LICENSE-2.0 | |
10 | // | |
11 | // Unless required by applicable law or agreed to in writing, | |
12 | // software distributed under the License is distributed on an | |
13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
14 | // KIND, either express or implied. See the License for the | |
15 | // specific language governing permissions and limitations | |
16 | // under the License. | |
17 | ||
18 | #include "arrow/util/logging.h" | |
19 | ||
20 | #ifdef ARROW_WITH_BACKTRACE | |
21 | #include <execinfo.h> | |
22 | #endif | |
23 | #include <cstdlib> | |
24 | #include <iostream> | |
25 | ||
26 | #ifdef ARROW_USE_GLOG | |
27 | ||
28 | #include <signal.h> | |
29 | #include <vector> | |
30 | ||
31 | #include "glog/logging.h" | |
32 | ||
33 | // Restore our versions of DCHECK and friends, as GLog defines its own | |
34 | #undef DCHECK | |
35 | #undef DCHECK_OK | |
36 | #undef DCHECK_EQ | |
37 | #undef DCHECK_NE | |
38 | #undef DCHECK_LE | |
39 | #undef DCHECK_LT | |
40 | #undef DCHECK_GE | |
41 | #undef DCHECK_GT | |
42 | ||
43 | #define DCHECK ARROW_DCHECK | |
44 | #define DCHECK_OK ARROW_DCHECK_OK | |
45 | #define DCHECK_EQ ARROW_DCHECK_EQ | |
46 | #define DCHECK_NE ARROW_DCHECK_NE | |
47 | #define DCHECK_LE ARROW_DCHECK_LE | |
48 | #define DCHECK_LT ARROW_DCHECK_LT | |
49 | #define DCHECK_GE ARROW_DCHECK_GE | |
50 | #define DCHECK_GT ARROW_DCHECK_GT | |
51 | ||
52 | #endif | |
53 | ||
54 | namespace arrow { | |
55 | namespace util { | |
56 | ||
57 | // This code is adapted from | |
58 | // https://github.com/ray-project/ray/blob/master/src/ray/util/logging.cc. | |
59 | ||
60 | // This is the default implementation of arrow log, | |
61 | // which is independent of any libs. | |
62 | class CerrLog { | |
63 | public: | |
64 | explicit CerrLog(ArrowLogLevel severity) : severity_(severity), has_logged_(false) {} | |
65 | ||
66 | virtual ~CerrLog() { | |
67 | if (has_logged_) { | |
68 | std::cerr << std::endl; | |
69 | } | |
70 | if (severity_ == ArrowLogLevel::ARROW_FATAL) { | |
71 | PrintBackTrace(); | |
72 | std::abort(); | |
73 | } | |
74 | } | |
75 | ||
76 | std::ostream& Stream() { | |
77 | has_logged_ = true; | |
78 | return std::cerr; | |
79 | } | |
80 | ||
81 | template <class T> | |
82 | CerrLog& operator<<(const T& t) { | |
83 | if (severity_ != ArrowLogLevel::ARROW_DEBUG) { | |
84 | has_logged_ = true; | |
85 | std::cerr << t; | |
86 | } | |
87 | return *this; | |
88 | } | |
89 | ||
90 | protected: | |
91 | const ArrowLogLevel severity_; | |
92 | bool has_logged_; | |
93 | ||
94 | void PrintBackTrace() { | |
95 | #ifdef ARROW_WITH_BACKTRACE | |
96 | void* buffer[255]; | |
97 | const int calls = backtrace(buffer, static_cast<int>(sizeof(buffer) / sizeof(void*))); | |
98 | backtrace_symbols_fd(buffer, calls, 1); | |
99 | #endif | |
100 | } | |
101 | }; | |
102 | ||
103 | #ifdef ARROW_USE_GLOG | |
104 | typedef google::LogMessage LoggingProvider; | |
105 | #else | |
106 | typedef CerrLog LoggingProvider; | |
107 | #endif | |
108 | ||
109 | ArrowLogLevel ArrowLog::severity_threshold_ = ArrowLogLevel::ARROW_INFO; | |
110 | // Keep the log directory. | |
111 | static std::unique_ptr<std::string> log_dir_; | |
112 | ||
113 | #ifdef ARROW_USE_GLOG | |
114 | ||
115 | // Glog's severity map. | |
116 | static int GetMappedSeverity(ArrowLogLevel severity) { | |
117 | switch (severity) { | |
118 | case ArrowLogLevel::ARROW_DEBUG: | |
119 | return google::GLOG_INFO; | |
120 | case ArrowLogLevel::ARROW_INFO: | |
121 | return google::GLOG_INFO; | |
122 | case ArrowLogLevel::ARROW_WARNING: | |
123 | return google::GLOG_WARNING; | |
124 | case ArrowLogLevel::ARROW_ERROR: | |
125 | return google::GLOG_ERROR; | |
126 | case ArrowLogLevel::ARROW_FATAL: | |
127 | return google::GLOG_FATAL; | |
128 | default: | |
129 | ARROW_LOG(FATAL) << "Unsupported logging level: " << static_cast<int>(severity); | |
130 | // This return won't be hit but compiler needs it. | |
131 | return google::GLOG_FATAL; | |
132 | } | |
133 | } | |
134 | ||
135 | #endif | |
136 | ||
137 | void ArrowLog::StartArrowLog(const std::string& app_name, | |
138 | ArrowLogLevel severity_threshold, | |
139 | const std::string& log_dir) { | |
140 | severity_threshold_ = severity_threshold; | |
141 | // In InitGoogleLogging, it simply keeps the pointer. | |
142 | // We need to make sure the app name passed to InitGoogleLogging exist. | |
143 | // We should avoid using static string is a dynamic lib. | |
144 | static std::unique_ptr<std::string> app_name_; | |
145 | app_name_.reset(new std::string(app_name)); | |
146 | log_dir_.reset(new std::string(log_dir)); | |
147 | #ifdef ARROW_USE_GLOG | |
148 | int mapped_severity_threshold = GetMappedSeverity(severity_threshold_); | |
149 | google::SetStderrLogging(mapped_severity_threshold); | |
150 | // Enble log file if log_dir is not empty. | |
151 | if (!log_dir.empty()) { | |
152 | auto dir_ends_with_slash = log_dir; | |
153 | if (log_dir[log_dir.length() - 1] != '/') { | |
154 | dir_ends_with_slash += "/"; | |
155 | } | |
156 | auto app_name_without_path = app_name; | |
157 | if (app_name.empty()) { | |
158 | app_name_without_path = "DefaultApp"; | |
159 | } else { | |
160 | // Find the app name without the path. | |
161 | size_t pos = app_name.rfind('/'); | |
162 | if (pos != app_name.npos && pos + 1 < app_name.length()) { | |
163 | app_name_without_path = app_name.substr(pos + 1); | |
164 | } | |
165 | } | |
166 | // If InitGoogleLogging is called but SetLogDestination is not called, | |
167 | // the log will be output to /tmp besides stderr. If log_dir is not | |
168 | // provided, we'd better not call InitGoogleLogging. | |
169 | google::InitGoogleLogging(app_name_->c_str()); | |
170 | google::SetLogFilenameExtension(app_name_without_path.c_str()); | |
171 | for (int i = static_cast<int>(severity_threshold_); | |
172 | i <= static_cast<int>(ArrowLogLevel::ARROW_FATAL); ++i) { | |
173 | int level = GetMappedSeverity(static_cast<ArrowLogLevel>(i)); | |
174 | google::SetLogDestination(level, dir_ends_with_slash.c_str()); | |
175 | } | |
176 | } | |
177 | #endif | |
178 | } | |
179 | ||
180 | void ArrowLog::UninstallSignalAction() { | |
181 | #ifdef ARROW_USE_GLOG | |
182 | ARROW_LOG(DEBUG) << "Uninstall signal handlers."; | |
183 | // This signal list comes from glog's signalhandler.cc. | |
184 | // https://github.com/google/glog/blob/master/src/signalhandler.cc#L58-L70 | |
185 | std::vector<int> installed_signals({SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGTERM}); | |
186 | #ifdef WIN32 | |
187 | for (int signal_num : installed_signals) { | |
188 | ARROW_CHECK(signal(signal_num, SIG_DFL) != SIG_ERR); | |
189 | } | |
190 | #else | |
191 | struct sigaction sig_action; | |
192 | memset(&sig_action, 0, sizeof(sig_action)); | |
193 | sigemptyset(&sig_action.sa_mask); | |
194 | sig_action.sa_handler = SIG_DFL; | |
195 | for (int signal_num : installed_signals) { | |
196 | ARROW_CHECK(sigaction(signal_num, &sig_action, NULL) == 0); | |
197 | } | |
198 | #endif | |
199 | #endif | |
200 | } | |
201 | ||
202 | void ArrowLog::ShutDownArrowLog() { | |
203 | #ifdef ARROW_USE_GLOG | |
204 | if (!log_dir_->empty()) { | |
205 | google::ShutdownGoogleLogging(); | |
206 | } | |
207 | #endif | |
208 | } | |
209 | ||
210 | void ArrowLog::InstallFailureSignalHandler() { | |
211 | #ifdef ARROW_USE_GLOG | |
212 | google::InstallFailureSignalHandler(); | |
213 | #endif | |
214 | } | |
215 | ||
216 | bool ArrowLog::IsLevelEnabled(ArrowLogLevel log_level) { | |
217 | return log_level >= severity_threshold_; | |
218 | } | |
219 | ||
220 | ArrowLog::ArrowLog(const char* file_name, int line_number, ArrowLogLevel severity) | |
221 | // glog does not have DEBUG level, we can handle it using is_enabled_. | |
222 | : logging_provider_(nullptr), is_enabled_(severity >= severity_threshold_) { | |
223 | #ifdef ARROW_USE_GLOG | |
224 | if (is_enabled_) { | |
225 | logging_provider_ = | |
226 | new google::LogMessage(file_name, line_number, GetMappedSeverity(severity)); | |
227 | } | |
228 | #else | |
229 | auto logging_provider = new CerrLog(severity); | |
230 | *logging_provider << file_name << ":" << line_number << ": "; | |
231 | logging_provider_ = logging_provider; | |
232 | #endif | |
233 | } | |
234 | ||
235 | std::ostream& ArrowLog::Stream() { | |
236 | auto logging_provider = reinterpret_cast<LoggingProvider*>(logging_provider_); | |
237 | #ifdef ARROW_USE_GLOG | |
238 | // Before calling this function, user should check IsEnabled. | |
239 | // When IsEnabled == false, logging_provider_ will be empty. | |
240 | return logging_provider->stream(); | |
241 | #else | |
242 | return logging_provider->Stream(); | |
243 | #endif | |
244 | } | |
245 | ||
246 | bool ArrowLog::IsEnabled() const { return is_enabled_; } | |
247 | ||
248 | ArrowLog::~ArrowLog() { | |
249 | if (logging_provider_ != nullptr) { | |
250 | delete reinterpret_cast<LoggingProvider*>(logging_provider_); | |
251 | logging_provider_ = nullptr; | |
252 | } | |
253 | } | |
254 | ||
255 | } // namespace util | |
256 | } // namespace arrow |