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#include "glog/logging.h"
28#endif
29
30namespace arrow {
31namespace util {
32
33// This code is adapted from
34// https://github.com/ray-project/ray/blob/master/src/ray/util/logging.cc.
35
36// This is the default implementation of arrow log,
37// which is independent of any libs.
38class CerrLog {
39 public:
40 explicit CerrLog(ArrowLogLevel severity) : severity_(severity), has_logged_(false) {}
41
42 virtual ~CerrLog() {
43 if (has_logged_) {
44 std::cerr << std::endl;
45 }
46 if (severity_ == ArrowLogLevel::ARROW_FATAL) {
47 PrintBackTrace();
48 std::abort();
49 }
50 }
51
52 std::ostream& Stream() {
53 has_logged_ = true;
54 return std::cerr;
55 }
56
57 template <class T>
58 CerrLog& operator<<(const T& t) {
59 if (severity_ != ArrowLogLevel::ARROW_DEBUG) {
60 has_logged_ = true;
61 std::cerr << t;
62 }
63 return *this;
64 }
65
66 protected:
67 const ArrowLogLevel severity_;
68 bool has_logged_;
69
70 void PrintBackTrace() {
71#ifdef ARROW_WITH_BACKTRACE
72 void* buffer[255];
73 const int calls = backtrace(buffer, static_cast<int>(sizeof(buffer) / sizeof(void*)));
74 backtrace_symbols_fd(buffer, calls, 1);
75#endif
76 }
77};
78
79#ifdef ARROW_USE_GLOG
80typedef google::LogMessage LoggingProvider;
81#else
82typedef CerrLog LoggingProvider;
83#endif
84
85ArrowLogLevel ArrowLog::severity_threshold_ = ArrowLogLevel::ARROW_INFO;
86std::unique_ptr<std::string> ArrowLog::app_name_;
87
88#ifdef ARROW_USE_GLOG
89
90// Glog's severity map.
91static int GetMappedSeverity(ArrowLogLevel severity) {
92 switch (severity) {
93 case ArrowLogLevel::ARROW_DEBUG:
94 return google::GLOG_INFO;
95 case ArrowLogLevel::ARROW_INFO:
96 return google::GLOG_INFO;
97 case ArrowLogLevel::ARROW_WARNING:
98 return google::GLOG_WARNING;
99 case ArrowLogLevel::ARROW_ERROR:
100 return google::GLOG_ERROR;
101 case ArrowLogLevel::ARROW_FATAL:
102 return google::GLOG_FATAL;
103 default:
104 ARROW_LOG(FATAL) << "Unsupported logging level: " << static_cast<int>(severity);
105 // This return won't be hit but compiler needs it.
106 return google::GLOG_FATAL;
107 }
108}
109
110#endif
111
112void ArrowLog::StartArrowLog(const std::string& app_name,
113 ArrowLogLevel severity_threshold,
114 const std::string& log_dir) {
115 severity_threshold_ = severity_threshold;
116 app_name_.reset(new std::string(app_name.c_str()));
117#ifdef ARROW_USE_GLOG
118 int mapped_severity_threshold = GetMappedSeverity(severity_threshold_);
119 google::InitGoogleLogging(app_name_->c_str());
120 google::SetStderrLogging(mapped_severity_threshold);
121 // Enble log file if log_dir is not empty.
122 if (!log_dir.empty()) {
123 auto dir_ends_with_slash = log_dir;
124 if (log_dir[log_dir.length() - 1] != '/') {
125 dir_ends_with_slash += "/";
126 }
127 auto app_name_without_path = app_name;
128 if (app_name.empty()) {
129 app_name_without_path = "DefaultApp";
130 } else {
131 // Find the app name without the path.
132 size_t pos = app_name.rfind('/');
133 if (pos != app_name.npos && pos + 1 < app_name.length()) {
134 app_name_without_path = app_name.substr(pos + 1);
135 }
136 }
137 google::SetLogFilenameExtension(app_name_without_path.c_str());
138 google::SetLogDestination(mapped_severity_threshold, log_dir.c_str());
139 }
140#endif
141}
142
143void ArrowLog::ShutDownArrowLog() {
144#ifdef ARROW_USE_GLOG
145 google::ShutdownGoogleLogging();
146#endif
147}
148
149void ArrowLog::InstallFailureSignalHandler() {
150#ifdef ARROW_USE_GLOG
151 google::InstallFailureSignalHandler();
152#endif
153}
154
155ArrowLog::ArrowLog(const char* file_name, int line_number, ArrowLogLevel severity)
156 // glog does not have DEBUG level, we can handle it using is_enabled_.
157 : logging_provider_(nullptr), is_enabled_(severity >= severity_threshold_) {
158#ifdef ARROW_USE_GLOG
159 if (is_enabled_) {
160 logging_provider_ =
161 new google::LogMessage(file_name, line_number, GetMappedSeverity(severity));
162 }
163#else
164 auto logging_provider = new CerrLog(severity);
165 *logging_provider << file_name << ":" << line_number << ": ";
166 logging_provider_ = logging_provider;
167#endif
168}
169
170std::ostream& ArrowLog::Stream() {
171 auto logging_provider = reinterpret_cast<LoggingProvider*>(logging_provider_);
172#ifdef ARROW_USE_GLOG
173 // Before calling this function, user should check IsEnabled.
174 // When IsEnabled == false, logging_provider_ will be empty.
175 return logging_provider->stream();
176#else
177 return logging_provider->Stream();
178#endif
179}
180
181bool ArrowLog::IsEnabled() const { return is_enabled_; }
182
183ArrowLog::~ArrowLog() {
184 if (logging_provider_ != nullptr) {
185 delete reinterpret_cast<LoggingProvider*>(logging_provider_);
186 logging_provider_ = nullptr;
187 }
188}
189
190} // namespace util
191} // namespace arrow
192