Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
logging.cc
Go to the documentation of this file.
1 // Copyright 2012 Cloudera Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "common/logging.h"
16 
17 #include <boost/foreach.hpp>
18 #include <boost/thread/locks.hpp>
19 #include <boost/thread/mutex.hpp>
20 #include <boost/uuid/uuid.hpp>
21 #include <boost/uuid/uuid_generators.hpp>
22 #include <boost/uuid/uuid_io.hpp>
23 #include <cerrno>
24 #include <ctime>
25 #include <fstream>
26 #include <glob.h>
27 #include <gutil/strings/substitute.h>
28 #include <iostream>
29 #include <map>
30 #include <sstream>
31 #include <stdio.h>
32 #include <sys/stat.h>
33 
34 #include "common/logging.h"
35 #include "util/error-util.h"
36 #include "util/redactor.h"
37 #include "util/test-info.h"
38 
39 #include "common/names.h"
40 
41 DEFINE_string(log_filename, "",
42  "Prefix of log filename - "
43  "full path is <log_dir>/<log_filename>.[INFO|WARN|ERROR|FATAL]");
44 DEFINE_bool(redirect_stdout_stderr, true,
45  "If true, redirects stdout/stderr to INFO/ERROR log.");
46 
47 DECLARE_string(redaction_rules_file);
48 
49 using boost::uuids::random_generator;
50 
51 bool logging_initialized = false;
52 
54 
55 void impala::InitGoogleLoggingSafe(const char* arg) {
56  mutex::scoped_lock logging_lock(logging_mutex);
57  if (logging_initialized) return;
58  if (!FLAGS_log_filename.empty()) {
59  for (int severity = google::INFO; severity <= google::FATAL; ++severity) {
60  google::SetLogSymlink(severity, FLAGS_log_filename.c_str());
61  }
62  }
63 
64  // This forces our logging to use /tmp rather than looking for a
65  // temporary directory if none is specified. This is done so that we
66  // can reliably construct the log file name without duplicating the
67  // complex logic that glog uses to guess at a temporary dir.
68  if (FLAGS_log_dir.empty()) {
69  FLAGS_log_dir = "/tmp";
70  }
71 
72  // Don't double log to stderr on any threshold.
73  FLAGS_stderrthreshold = google::FATAL + 1;
74 
75  if (FLAGS_redirect_stdout_stderr && !TestInfo::is_test()) {
76  // We will be redirecting stdout/stderr to INFO/LOG so override any glog settings
77  // that log to stdout/stderr...
78  FLAGS_logtostderr = false;
79  FLAGS_alsologtostderr = false;
80  }
81 
82  if (!FLAGS_logtostderr) {
83  // Verify that a log file can be created in log_dir by creating a tmp file.
84  stringstream ss;
85  random_generator uuid_generator;
86  ss << FLAGS_log_dir << "/" << "impala_test_log." << uuid_generator();
87  const string file_name = ss.str();
88  ofstream test_file(file_name.c_str());
89  if (!test_file.is_open()) {
90  stringstream error_msg;
91  error_msg << "Could not open file in log_dir " << FLAGS_log_dir;
92  perror(error_msg.str().c_str());
93  // Unlock the mutex before exiting the program to avoid mutex d'tor assert.
94  logging_mutex.unlock();
95  exit(1);
96  }
97  remove(file_name.c_str());
98  }
99 
100  google::InitGoogleLogging(arg);
101  if (!FLAGS_redaction_rules_file.empty()) {
102  // This depends on a patched glog. The patch is at thirdparty/patches/glog.
103  google::InstallLogMessageListenerFunction(impala::Redact);
104  }
105 
106  // Needs to be done after InitGoogleLogging
107  if (FLAGS_log_filename.empty()) {
108  FLAGS_log_filename = google::ProgramInvocationShortName();
109  }
110 
111  if (FLAGS_redirect_stdout_stderr && !TestInfo::is_test()) {
112  // Needs to be done after InitGoogleLogging, to get the INFO/ERROR file paths.
113  // Redirect stdout to INFO log and stderr to ERROR log
114  string info_log_path, error_log_path;
115  GetFullLogFilename(google::INFO, &info_log_path);
116  GetFullLogFilename(google::ERROR, &error_log_path);
117 
118  // The log files are created on first use, log something to each before redirecting.
119  LOG(INFO) << "stdout will be logged to this file.";
120  LOG(ERROR) << "stderr will be logged to this file.";
121 
122  // Print to stderr/stdout before redirecting so people looking for these logs in
123  // the standard place know where to look.
124  cout << "Redirecting stdout to " << info_log_path << endl;
125  cerr << "Redirecting stderr to " << error_log_path << endl;
126 
127  // TODO: how to handle these errors? Maybe abort the process?
128  if (freopen(info_log_path.c_str(), "a", stdout) == NULL) {
129  cout << "Could not redirect stdout: " << GetStrErrMsg();
130  }
131  if (freopen(error_log_path.c_str(), "a", stderr) == NULL) {
132  cerr << "Could not redirect stderr: " << GetStrErrMsg();
133  }
134  }
135 
136  logging_initialized = true;
137 }
138 
139 void impala::GetFullLogFilename(google::LogSeverity severity, string* filename) {
140  stringstream ss;
141  ss << FLAGS_log_dir << "/" << FLAGS_log_filename << "."
142  << google::GetLogSeverityName(severity);
143  *filename = ss.str();
144 }
145 
147  // This method may only correctly be called once (which this lock does not
148  // enforce), but this lock protects against concurrent calls with
149  // InitGoogleLoggingSafe
150  mutex::scoped_lock logging_lock(logging_mutex);
151  google::ShutdownGoogleLogging();
152 }
153 
155  LOG(INFO) << "Flags (see also /varz are on debug webserver):" << endl
156  << google::CommandlineFlagsIntoString();
157 }
158 
159 void impala::CheckAndRotateLogFiles(int max_log_files) {
160  // Map capturing mtimes, oldest files first
161  typedef map<time_t, string> LogFileMap;
162  // Ignore bad input or disable log rotation
163  if (max_log_files <= 1) return;
164  // Check log files for all severities
165  for (int severity = 0; severity < google::NUM_SEVERITIES; ++severity) {
166  // Build glob pattern for input
167  // e.g. /tmp/impalad.*.INFO.*
168  string fname = strings::Substitute("$0/$1.*.$2*", FLAGS_log_dir, FLAGS_log_filename,
169  google::GetLogSeverityName(severity));
170 
171  LogFileMap log_file_mtime;
172  glob_t result;
173  glob(fname.c_str(), GLOB_TILDE, NULL, &result);
174  for (size_t i = 0; i < result.gl_pathc; ++i) {
175  // Get the mtime for each match
176  struct stat stat_val;
177  if (stat(result.gl_pathv[i], &stat_val) != 0) {
178  LOG(ERROR) << "Could not read last-modified-timestamp for log file "
179  << result.gl_pathv[i] << ", will not delete (error was: "
180  << strerror(errno) << ")";
181  continue;
182  }
183  log_file_mtime[stat_val.st_mtime] = result.gl_pathv[i];
184  }
185  globfree(&result);
186 
187  // Iterate over the map and remove oldest log files first when too many
188  // log files exist
189  if (log_file_mtime.size() <= max_log_files) return;
190  int files_to_delete = log_file_mtime.size() - max_log_files;
191  DCHECK_GT(files_to_delete, 0);
192  BOOST_FOREACH(const LogFileMap::reference val, log_file_mtime) {
193  if (unlink(val.second.c_str()) == 0) {
194  LOG(INFO) << "Old log file deleted during log rotation: " << val.second;
195  } else {
196  LOG(ERROR) << "Failed to delete old log file: "
197  << val.second << "(error was: " << strerror(errno) << ")";
198  }
199  if (--files_to_delete == 0) break;
200  }
201  }
202 }
void ShutdownLogging()
Definition: logging.cc:146
mutex logging_mutex
Definition: logging.cc:53
bool logging_initialized
Definition: logging.cc:51
void Redact(string *value, bool *changed)
Definition: redactor.cc:309
void GetFullLogFilename(google::LogSeverity severity, std::string *filename)
void InitGoogleLoggingSafe(const char *arg)
Definition: logging.cc:55
void CheckAndRotateLogFiles(int max_log_files)
Definition: logging.cc:159
string GetStrErrMsg()
Definition: error-util.cc:30
DEFINE_bool(redirect_stdout_stderr, true,"If true, redirects stdout/stderr to INFO/ERROR log.")
void LogCommandLineFlags()
Writes all command-line flags to the log at level INFO.
Definition: logging.cc:154
DECLARE_string(redaction_rules_file)
DEFINE_string(log_filename,"","Prefix of log filename - ""full path is <log_dir>/<log_filename>.[INFO|WARN|ERROR|FATAL]")