sql_utils/base/logging.h (174 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.
*/
#ifndef THIRD_PARTY_PY_BIGQUERY_ML_UTILS_SQL_UTILS_BASE_LOGGING_H_
#define THIRD_PARTY_PY_BIGQUERY_ML_UTILS_SQL_UTILS_BASE_LOGGING_H_
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include "absl/base/attributes.h"
#include "absl/base/log_severity.h"
#define SQL_INTERNAL_LOGGING_INFO \
::bigquery_ml_utils_base::logging_internal::LogMessage(__FILE__, __LINE__)
#define SQL_INTERNAL_LOGGING_WARNING \
::bigquery_ml_utils_base::logging_internal::LogMessage( \
__FILE__, __LINE__, absl::LogSeverity::kWarning)
#define SQL_INTERNAL_LOGGING_ERROR \
::bigquery_ml_utils_base::logging_internal::LogMessage( \
__FILE__, __LINE__, absl::LogSeverity::kError)
#define SQL_INTERNAL_LOGGING_FATAL \
::bigquery_ml_utils_base::logging_internal::LogMessageFatal(__FILE__, __LINE__)
#define SQL_INTERNAL_LOGGING_QFATAL SQL_INTERNAL_LOGGING_FATAL
#ifdef NDEBUG
#define SQL_INTERNAL_LOGGING_DFATAL SQL_INTERNAL_LOGGING_ERROR
#else
#define SQL_INTERNAL_LOGGING_DFATAL SQL_INTERNAL_LOGGING_FATAL
#endif
#ifdef NDEBUG
#define SQL_DEBUG_MODE false
#else
#define SQL_DEBUG_MODE true
#endif
// Creates a message and logs it to file.
//
// SQL_LOG(severity) returns a stream object that can be written to with the <<
// operator. Log messages are emitted with terminating newlines.
// Example:
// SQL_LOG(INFO) << "Found" << num_cookies << " cookies";
//
// severity: the severity of the log message, one of LogSeverity. The
// FATAL severity will terminate the program after the log is emitted.
// Must be exactly one of INFO WARNING ERROR FATAL QFATAL DFATAL
#define SQL_LOG(severity) SQL_INTERNAL_LOGGING_##severity.stream()
// A command to SQL_LOG only if a condition is true. If the condition is false,
// nothing is logged.
// Example:
//
// SQL_LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
//
// severity: the severity of the log message, one of LogSeverity. The
// FATAL severity will terminate the program after the log is emitted.
// condition: the condition that determines whether to log the message.
#define SQL_LOG_IF(severity, condition) \
!(condition) ? (void)0 \
: ::bigquery_ml_utils_base::logging_internal::LogMessageVoidify() & \
SQL_INTERNAL_LOGGING_##severity.stream()
// A SQL_LOG command with an associated verbosity level. The verbosity threshold
// may be configured at runtime with set_vlog_level and InitLogging.
//
// SQL_VLOG statements are logged at INFO severity if they are logged at all.
// The numeric levels are on a different scale than the severity levels.
// Example:
//
// SQL_VLOG(1) << "Print when SQL_VLOG level is set to be 1 or higher";
//
// level: the numeric level that determines whether to log the message.
#define SQL_VLOG(level) \
SQL_LOG_IF(INFO, (level) <= ::bigquery_ml_utils_base::get_vlog_level())
// Terminates the program with a fatal error if the specified condition is
// false.
//
// Example:
// SQL_CHECK(!cheese.empty()) << "Out of Cheese";
//
//
// Might produce a message like:
// "Check_failed: !cheese.empty() Out of Cheese"
#define SQL_CHECK(condition) \
SQL_LOG_IF(FATAL, !(condition)) << ("Check failed: " #condition " ")
namespace bigquery_ml_utils_base {
// This formats a value for a failing CHECK_XX statement. Ordinarily,
// it uses the definition for operator<<, with a few special cases below.
template <typename T>
inline void MakeCheckOpValueString(std::ostream *os, const T &v) {
(*os) << v;
}
// Overrides for char types provide readable values for unprintable
// characters.
template <>
void MakeCheckOpValueString(std::ostream *os, const char &v);
template <>
void MakeCheckOpValueString(std::ostream *os, const signed char &v);
template <>
void MakeCheckOpValueString(std::ostream *os, const unsigned char &v);
// We need an explicit specialization for std::nullptr_t.
template <>
void MakeCheckOpValueString(std::ostream *os, const std::nullptr_t &v);
// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX
// statement. See MakeCheckOpString for sample usage.
class CheckOpMessageBuilder {
public:
// Constructs an object to format a CheckOp message. This constructor
// initializes the message first with exprtext followed by " (".
//
// exprtext A string representation of the code in file at line.
explicit CheckOpMessageBuilder(const char *exprtext);
// Deletes "stream_".
~CheckOpMessageBuilder();
// Gets the output stream for the first argument of the message.
std::ostream *ForVar1() { return stream_; }
// Gets the output stream for writing the argument of the message. This
// writes " vs. " to the stream first.
std::ostream *ForVar2();
// Gets the built string contents. The stream is finished with an added ")".
std::string *NewString();
private:
std::ostringstream *stream_;
};
template <typename T1, typename T2>
std::string *MakeCheckOpString(const T1 &v1, const T2 &v2,
const char *exprtext) {
CheckOpMessageBuilder comb(exprtext);
MakeCheckOpValueString(comb.ForVar1(), v1);
MakeCheckOpValueString(comb.ForVar2(), v2);
return comb.NewString();
}
// Helper functions for CHECK_OP macro.
// The (int, int) specialization works around the issue that the compiler
// will not instantiate the template version of the function on values of
// unnamed enum type - see comment below.
//
// name: an identifier that is the name of the comparison, such as
// Check_EQ or Check_NE.
// op: the comparison operator, such as == or !=.
#define DEFINE_CHECK_OP_IMPL(name, op) \
template <typename T1, typename T2> \
inline std::string *name##Impl(const T1 &v1, const T2 &v2, \
const char *exprtext) { \
if (v1 op v2) return nullptr; \
return MakeCheckOpString(v1, v2, exprtext); \
} \
inline std::string *name##Impl(int v1, int v2, const char *exprtext) { \
return name##Impl<int, int>(v1, v2, exprtext); \
}
// We use the full name Check_EQ, Check_NE, etc.
//
// This is to prevent conflicts when the file including logging.h provides its
// own #defines for the simpler names EQ, NE, etc. This happens if, for
// example, those are used as token names in a yacc grammar.
DEFINE_CHECK_OP_IMPL(Check_EQ, ==)
DEFINE_CHECK_OP_IMPL(Check_NE, !=)
DEFINE_CHECK_OP_IMPL(Check_LE, <=)
DEFINE_CHECK_OP_IMPL(Check_LT, <)
DEFINE_CHECK_OP_IMPL(Check_GE, >=)
DEFINE_CHECK_OP_IMPL(Check_GT, >)
#undef DEFINE_CHECK_OP_IMPL
// Function is overloaded for integral types to allow static const
// integrals declared in classes and not defined to be used as arguments to
// SQL_CHECK* macros. It's not encouraged though.
template <typename T>
inline const T &GetReferenceableValue(const T &t) {
return t;
}
inline char GetReferenceableValue(char t) { return t; }
inline unsigned char GetReferenceableValue(unsigned char t) { return t; }
inline signed char GetReferenceableValue(signed char t) { return t; }
// NOLINTNEXTLINE(runtime/int)
inline short GetReferenceableValue(short t) { return t; }
// NOLINTNEXTLINE(runtime/int)
inline unsigned short GetReferenceableValue(unsigned short t) { return t; }
inline int GetReferenceableValue(int t) { return t; }
inline unsigned int GetReferenceableValue(unsigned int t) { return t; }
// NOLINTNEXTLINE(runtime/int)
inline long GetReferenceableValue(long t) { return t; }
// NOLINTNEXTLINE(runtime/int)
inline unsigned long GetReferenceableValue(unsigned long t) { return t; }
// NOLINTNEXTLINE(runtime/int)
inline long long GetReferenceableValue(long long t) { return t; }
// NOLINTNEXTLINE(runtime/int)
inline unsigned long long GetReferenceableValue(unsigned long long t) {
return t;
}
// Compares val1 and val2 with op, and produces a SQL_LOG(FATAL) if false.
//
// name An identifier that is the name of the comparison, such as
// Check_EQ or Check_NE.
// op: comparison operator, such as == or !=.
// val1: first variable to be compared.
// val2: second variable to be compared.
#define SQL_INTERNAL_CHECK_OP(name, op, val1, val2) \
while (std::unique_ptr<std::string> _result = \
std::unique_ptr<std::string>(::bigquery_ml_utils_base::name##Impl( \
::bigquery_ml_utils_base::GetReferenceableValue(val1), \
::bigquery_ml_utils_base::GetReferenceableValue(val2), \
#val1 " " #op " " #val2))) \
::bigquery_ml_utils_base::logging_internal::LogMessageFatal(__FILE__, __LINE__, \
*_result) \
.stream()
// Produces a SQL_LOG(FATAL) unless val1 equals val2.
#define SQL_CHECK_EQ(val1, val2) \
SQL_INTERNAL_CHECK_OP(Check_EQ, ==, val1, val2)
// Produces a SQL_LOG(FATAL) unless val1 does not equal to val2.
#define SQL_CHECK_NE(val1, val2) \
SQL_INTERNAL_CHECK_OP(Check_NE, !=, val1, val2)
// Produces a SQL_LOG(FATAL) unless val1 is less than or equal to val2.
#define SQL_CHECK_LE(val1, val2) \
SQL_INTERNAL_CHECK_OP(Check_LE, <=, val1, val2)
// Produces a SQL_LOG(FATAL) unless val1 is less than val2.
#define SQL_CHECK_LT(val1, val2) \
SQL_INTERNAL_CHECK_OP(Check_LT, <, val1, val2)
// Produces a SQL_LOG(FATAL) unless val1 is greater than or equal to val2.
#define SQL_CHECK_GE(val1, val2) \
SQL_INTERNAL_CHECK_OP(Check_GE, >=, val1, val2)
// Produces a SQL_LOG(FATAL) unless val1 is greater than val2.
#define SQL_CHECK_GT(val1, val2) \
SQL_INTERNAL_CHECK_OP(Check_GT, >, val1, val2)
#define SQL_DCHECK(c) SQL_CHECK(c)
// Another alias for SQL_CHECK that in the future may include more posix/errno
// related data.
#define SQL_PCHECK(c) SQL_CHECK(c)
// Another alias for SQL_CHECK that in the future may log less verbosely.
#define SQL_SQL_CHECK(c) SQL_CHECK(c)
#define SQL_DCHECK_EQ(a, b) SQL_CHECK_EQ(a, b)
#define SQL_DCHECK_NE(a, b) SQL_CHECK_NE(a, b)
#define SQL_DCHECK_LE(a, b) SQL_CHECK_LE(a, b)
#define SQL_DCHECK_LT(a, b) SQL_CHECK_LT(a, b)
#define SQL_DCHECK_GE(a, b) SQL_CHECK_GE(a, b)
#define SQL_DCHECK_GT(a, b) SQL_CHECK_GT(a, b)
#define SQL_DLOG(c) SQL_LOG(c)
#define SQL_VLOG_IS_ON(level) ::bigquery_ml_utils_base::get_vlog_level() <= (level)
// Gets the verbosity threshold for SQL_VLOG. A SQL_VLOG command with a level greater
// than this will be ignored.
int get_vlog_level();
// Gets the log directory that was specified when initialized.
std::string get_log_directory();
// Initializes minimal logging library.
//
// This should be called in main().
//
// directory: log file directory.
// file_name: name of the log file (recommend this be initialized with argv[0]).
// level: verbosity threshold for SQL_VLOG commands. A SQL_VLOG command with
// a level equal to or lower than it will be logged.
// Returns true if initialized successfully. Behavior is undefined false.
bool InitLogging(const char *directory, const char *file_name, int level);
namespace logging_internal {
// Class representing a log message created by a log macro.
class LogMessage {
public:
// Constructs a new message with INFO severity.
//
// file: source file that produced the log.
// line: source code line that produced the log.
LogMessage(const char *file, int line);
// Constructs a new message with the specified severity.
//
// file: source file that produced the log.
// line: source code line that produced the log.
// severity: severity level of the log.
LogMessage(const char *file, int line, absl::LogSeverity severity);
// Constructs a log message with additional text that is provided by CHECK
// macros. Severity is implicitly FATAL.
//
// file: source file that produced the log.
// line: source code line that produced the log.
// result: result message of the failed check.
LogMessage(const char *file, int line, const std::string &result);
// The destructor flushes the message.
~LogMessage();
LogMessage(const LogMessage &) = delete;
void operator=(const LogMessage &) = delete;
// Gets a reference to the underlying string stream.
std::ostream &stream() { return stream_; }
protected:
void Flush();
private:
void Init(const char *file, int line, absl::LogSeverity severity);
// Sends the message to print.
void SendToLog(const std::string &message_text);
// stream_ reads all the input messages into a stringstream, then it's
// converted into a string in the destructor for printing.
std::ostringstream stream_;
const absl::LogSeverity severity_;
};
// This class is used just to take an ostream type and make it a void type to
// satisfy the ternary operator in SQL_LOG_IF.
// operator& is used because it has precedence lower than << but higher than :?
class LogMessageVoidify {
public:
void operator&(const std::ostream &) {}
};
// Default LogSeverity FATAL version of LogMessage.
// Identical to LogMessage(..., FATAL), but see comments on destructor.
class LogMessageFatal : public LogMessage {
public:
// Constructs a new message with FATAL severity.
//
// file: source file that produced the log.
// line: source code line that produced the log.
LogMessageFatal(const char *file, int line)
: LogMessage(file, line, absl::LogSeverity::kFatal) {}
// Constructs a message with FATAL severity for use by SQL_CHECK macros.
//
// file: source file that produced the log.
// line: source code line that produced the log.
// result: result message when check fails.
LogMessageFatal(const char *file, int line, const std::string &result)
: LogMessage(file, line, result) {}
// Suppresses warnings in some cases, example:
// if (impossible)
// SQL_LOG(FATAL)
// else
// return 0;
// which would otherwise yield the following compiler warning.
// "warning: control reaches end of non-void function [-Wreturn-type]"
ABSL_ATTRIBUTE_NORETURN ~LogMessageFatal();
};
} // namespace logging_internal
} // namespace bigquery_ml_utils_base
#endif // THIRD_PARTY_PY_BIGQUERY_ML_UTILS_SQL_UTILS_BASE_LOGGING_H_