fatal/log/log.h (169 lines of code) (raw):
/*
* Copyright (c) 2016, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#ifndef FATAL_INCLUDE_fatal_log_log_h
#define FATAL_INCLUDE_fatal_log_log_h
#include <fatal/preprocessor.h>
#include <fatal/time/time.h>
#include <atomic>
#include <chrono>
#include <iostream>
#include <memory>
#include <ostream>
#include <type_traits>
#include <utility>
#include <cstdlib>
// for internal use only
namespace fatal {
namespace log {
namespace detail {
namespace log_impl {
template <typename TOut, typename TInfo>
struct logger {
using info = TInfo;
struct writer {
explicit writer(TOut *out) noexcept: out_(out) {}
writer(writer const &) = delete;
writer(writer &&rhs) noexcept: out_(rhs.out_) { rhs.out_ = nullptr; }
template <typename T>
writer &operator <<(T &&value) & {
if (out_) {
*out_ << std::forward<T>(value);
}
return *this;
}
template <typename T>
writer &&operator <<(T &&value) && {
if (out_) {
*out_ << std::forward<T>(value);
}
return std::move(*this);
}
~writer() {
if (out_) {
*out_ << '\n';
}
}
private:
TOut *out_;
};
logger(TOut *out, source_info source) noexcept:
writer_(out),
source_(source)
{}
logger(logger const &) = delete;
logger(logger &&rhs) = default;
~logger() {
if (info::abort::value) {
std::abort();
}
}
template <typename T>
writer operator <<(T &&value) {
writer_ << info::signature::value;
if (info::show_level::value) {
writer_ << info::value;
}
auto const now = std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now().time_since_epoch()
);
// TODO: output date in an absolute format
time::pretty_print(
writer_ << " [" << source_.file() << ':' << source_.line() << "] at ",
now
) << ": " << std::forward<T>(value);
return std::move(writer_);
}
private:
writer writer_;
source_info source_;
};
using level_t = unsigned;
template <typename TCategory, level_t Level>
struct log_level {
static void set(level_t level) {
value() = level;
}
static level_t get() {
return value();
}
private:
static std::atomic<level_t> &value() {
static std::atomic<level_t> instance(Level);
return instance;
}
};
template <
typename TCategory,
level_t Level,
char Signature,
bool ShowLevel,
bool Abort = false
>
struct level_info:
std::integral_constant<level_t, Level>
{
using category = TCategory;
using show_level = std::integral_constant<bool, ShowLevel>;
using signature = std::integral_constant<char, Signature>;
using abort = std::integral_constant<bool, Abort>;
};
struct log_tag {};
struct verbose_tag {};
using level_FATAL = level_info<log_tag, 0, 'F', false, true>;
using level_CRITICAL = level_info<log_tag, 1, 'C', false>;
using level_ERROR = level_info<log_tag, 2, 'E', false>;
using level_WARNING = level_info<log_tag, 3, 'W', false>;
using level_INFO = level_info<log_tag, 4, 'I', false>;
template <level_t Level>
using level_verbose = level_info<verbose_tag, Level, 'V', true>;
template <typename> struct by_category;
template <>
struct by_category<log_tag> {
using level = detail::log_impl::log_level<
detail::log_impl::log_tag,
detail::log_impl::level_INFO::value
>;
};
template <>
struct by_category<verbose_tag> {
using level = detail::log_impl::log_level<
detail::log_impl::verbose_tag, 0
>;
};
} // namespace log_impl {
} // namespace detail {
struct null_logger {
template <typename T>
null_logger const &operator <<(T &&) const { return *this; }
};
using level = detail::log_impl::by_category<
detail::log_impl::log_tag
>::level;
using v_level = detail::log_impl::by_category<
detail::log_impl::verbose_tag
>::level;
// TODO: ADD THE ABILITY TO TURN VERBOSE LOGGING ON AND OFF
template <typename TInfo>
log::detail::log_impl::logger<std::ostream, TInfo> log(source_info source) {
return log::detail::log_impl::logger<std::ostream, TInfo>(
TInfo::value <= detail::log_impl::by_category<
typename TInfo::category
>::level::get()
? std::addressof(std::cerr)
: nullptr,
source
);
}
} // namespace log {
#define FATAL_LOG(Level) \
::fatal::log::log<::fatal::log::detail::log_impl::level_##Level>( \
FATAL_SOURCE_INFO() \
)
#define FATAL_VLOG(Level) \
::fatal::log::log<::fatal::log::detail::log_impl::level_verbose<Level>>( \
FATAL_SOURCE_INFO() \
)
#ifdef NDEBUG
# define FATAL_DLOG(Level) ::fatal::log::null_logger()
# define FATAL_DVLOG(Level) ::fatal::log::null_logger()
#else // NDEBUG
# define FATAL_DLOG(Level) FATAL_LOG(Level)
# define FATAL_DVLOG(Level) FATAL_VLOG(Level)
#endif // NDEBUG
} // namespace fatal {
#endif // FATAL_INCLUDE_fatal_log_log_h