sql_utils/base/logging.cc (197 lines of code) (raw):

/* * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "sql_utils/base/logging.h" #include <errno.h> #include <fcntl.h> #include <libgen.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <cctype> #include <cstdio> #include <cstdlib> #include <ctime> #include <sstream> #include <string> #include "absl/base/attributes.h" namespace bigquery_ml_utils_base { constexpr char kDefaultDirectory[] = "/tmp/"; namespace { // The logging directory. ABSL_CONST_INIT std::string *log_file_directory = nullptr; // The log filename. ABSL_CONST_INIT std::string *log_basename = nullptr; // The SQL_VLOG level, only SQL_VLOG with level equal to or below this // level is logged. ABSL_CONST_INIT int vlog_level = 0; const char *GetBasename(const char *file_path) { const char *slash = strrchr(file_path, '/'); return slash ? slash + 1 : file_path; } bool set_log_basename(const std::string &filename) { if (log_basename || filename.empty()) { return false; } log_basename = new std::string(filename); return true; } std::string get_log_basename() { if (!log_basename || log_basename->empty()) { return "sql"; } return *log_basename; } bool EnsureDirectoryExists(const char *path) { struct stat dirStat; if (stat(path, &dirStat)) { if (errno != ENOENT) { return false; } if (mkdir(path, 0766)) { return false; } } else if (!S_ISDIR(dirStat.st_mode)) { return false; } return true; } // Sets the log directory, as specified when initialized. This // is only set once. Any request to reset it will return false. // // log_directory: log file directory. // // Returns true if and only if the log directory is set successfully. bool set_log_directory(const std::string &log_directory) { std::string tmp_directory = log_directory; if (tmp_directory.empty()) { tmp_directory = kDefaultDirectory; } if (log_file_directory || !EnsureDirectoryExists(tmp_directory.c_str())) { return false; } if (tmp_directory.back() == '/') { log_file_directory = new std::string(tmp_directory); } else { log_file_directory = new std::string(tmp_directory + "/"); } return true; } // Sets the verbosity threshold for SQL_VLOG. A SQL_VLOG command with a // level greater // than this will be ignored. // // level: verbosity threshold for SQL_VLOG to be set. A SQL_VLOG command with // level less than or equal to this will be logged. void set_vlog_level(int level) { vlog_level = level; } } // namespace std::string get_log_directory() { if (!log_file_directory) { return kDefaultDirectory; } return *log_file_directory; } int get_vlog_level() { return vlog_level; } bool InitLogging(const char *directory, const char *file_name, int level) { set_vlog_level(level); std::string log_directory = directory ? std::string(directory) : ""; if (!set_log_directory(log_directory)) { return false; } const char *binary_name = GetBasename(file_name); if (!set_log_basename(binary_name)) { return false; } std::string log_path = get_log_directory() + get_log_basename(); if (access(log_path.c_str(), F_OK) == 0 && access(log_path.c_str(), W_OK) != 0) { return false; } return true; } CheckOpMessageBuilder::CheckOpMessageBuilder(const char *exprtext) : stream_(new std::ostringstream) { *stream_ << exprtext << " ("; } CheckOpMessageBuilder::~CheckOpMessageBuilder() { delete stream_; } std::ostream *CheckOpMessageBuilder::ForVar2() { *stream_ << " vs. "; return stream_; } std::string *CheckOpMessageBuilder::NewString() { // NOLINT *stream_ << ")"; return new std::string(stream_->str()); } template <> void MakeCheckOpValueString(std::ostream *os, const char &v) { if (v >= 32 && v <= 126) { (*os) << "'" << v << "'"; } else { (*os) << "char value " << static_cast<int16_t>(v); } } template <> void MakeCheckOpValueString(std::ostream *os, const signed char &v) { if (v >= 32 && v <= 126) { (*os) << "'" << v << "'"; } else { (*os) << "signed char value " << static_cast<int16_t>(v); } } template <> void MakeCheckOpValueString(std::ostream *os, const unsigned char &v) { if (v >= 32 && v <= 126) { (*os) << "'" << v << "'"; } else { (*os) << "unsigned char value " << static_cast<uint16_t>(v); } } template <> void MakeCheckOpValueString(std::ostream *os, const std::nullptr_t &v) { (*os) << "nullptr"; } namespace logging_internal { LogMessage::LogMessage(const char *file, int line) : LogMessage(file, line, absl::LogSeverity::kInfo) {} LogMessage::LogMessage(const char *file, int line, const std::string &result) : LogMessage(file, line, absl::LogSeverity::kFatal) { stream() << "Check failed: " << result << " "; } static constexpr const char *LogSeverityNames[4] = {"INFO", "WARNING", "ERROR", "FATAL"}; LogMessage::LogMessage(const char *file, int line, absl::LogSeverity severity) : severity_(severity) { const char *filename = GetBasename(file); // Write a prefix into the log message, including local date/time, severity // level, filename, and line number. struct timespec time_stamp; clock_gettime(CLOCK_REALTIME, &time_stamp); constexpr int kTimeMessageSize = 22; char buffer[kTimeMessageSize]; strftime(buffer, kTimeMessageSize, "%Y-%m-%d %H:%M:%S ", localtime(&time_stamp.tv_sec)); stream() << buffer; stream() << LogSeverityNames[static_cast<int>(severity)] << " " << filename << " : " << line << " : "; } LogMessage::~LogMessage() { Flush(); // if FATAL occurs, abort. if (severity_ == absl::LogSeverity::kFatal) { abort(); } } void LogMessage::SendToLog(const std::string &message_text) { std::string log_path = get_log_directory() + get_log_basename(); FILE *file = fopen(log_path.c_str(), "ab"); if (file) { if (fprintf(file, "%s", message_text.c_str()) > 0) { if (message_text.back() != '\n') { fprintf(file, "\n"); } } else { fprintf(stderr, "Failed to write to log file : %s! [%s]\n", log_path.c_str(), strerror(errno)); } fclose(file); } else { fprintf(stderr, "Failed to open log file : %s! [%s]\n", log_path.c_str(), strerror(errno)); } if (severity_ >= absl::LogSeverity::kError) { fprintf(stderr, "%s\n", message_text.c_str()); fflush(stderr); } printf("%s\n", message_text.c_str()); fflush(stdout); } void LogMessage::Flush() { std::string message_text = stream_.str(); SendToLog(message_text); stream_.clear(); } LogMessageFatal::~LogMessageFatal() { Flush(); abort(); } } // namespace logging_internal } // namespace bigquery_ml_utils_base