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:
CerrLog(ArrowLogLevel severity)64   explicit CerrLog(ArrowLogLevel severity) : severity_(severity), has_logged_(false) {}
65 
~CerrLog()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 
Stream()76   std::ostream& Stream() {
77     has_logged_ = true;
78     return std::cerr;
79   }
80 
81   template <class T>
operator <<(const T & 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 
PrintBackTrace()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.
GetMappedSeverity(ArrowLogLevel severity)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 
StartArrowLog(const std::string & app_name,ArrowLogLevel severity_threshold,const std::string & log_dir)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 
UninstallSignalAction()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 
ShutDownArrowLog()202 void ArrowLog::ShutDownArrowLog() {
203 #ifdef ARROW_USE_GLOG
204   if (!log_dir_->empty()) {
205     google::ShutdownGoogleLogging();
206   }
207 #endif
208 }
209 
InstallFailureSignalHandler()210 void ArrowLog::InstallFailureSignalHandler() {
211 #ifdef ARROW_USE_GLOG
212   google::InstallFailureSignalHandler();
213 #endif
214 }
215 
IsLevelEnabled(ArrowLogLevel log_level)216 bool ArrowLog::IsLevelEnabled(ArrowLogLevel log_level) {
217   return log_level >= severity_threshold_;
218 }
219 
ArrowLog(const char * file_name,int line_number,ArrowLogLevel severity)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 
Stream()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 
IsEnabled() const246 bool ArrowLog::IsEnabled() const { return is_enabled_; }
247 
~ArrowLog()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
257