1 /* 2 * Copyright (c) 2016, Facebook, Inc. 3 * All rights reserved. 4 * 5 * This source code is licensed under the BSD-style license found in the 6 * LICENSE file in the root directory of this source tree. An additional grant 7 * of patent rights can be found in the PATENTS file in the same directory. 8 */ 9 10 #ifndef FATAL_INCLUDE_fatal_log_log_h 11 #define FATAL_INCLUDE_fatal_log_log_h 12 13 #include <fatal/preprocessor.h> 14 #include <fatal/time/time.h> 15 16 #include <atomic> 17 #include <chrono> 18 #include <iostream> 19 #include <memory> 20 #include <ostream> 21 #include <type_traits> 22 #include <utility> 23 24 #include <cstdlib> 25 26 // for internal use only 27 28 namespace fatal { 29 namespace log { 30 namespace detail { 31 namespace log_impl { 32 33 template <typename TOut, typename TInfo> 34 struct logger { 35 using info = TInfo; 36 37 struct writer { writerlogger::writer38 explicit writer(TOut *out) noexcept: out_(out) {} 39 40 writer(writer const &) = delete; writerlogger::writer41 writer(writer &&rhs) noexcept: out_(rhs.out_) { rhs.out_ = nullptr; } 42 43 template <typename T> 44 writer &operator <<(T &&value) & { 45 if (out_) { 46 *out_ << std::forward<T>(value); 47 } 48 49 return *this; 50 } 51 52 template <typename T> 53 writer &&operator <<(T &&value) && { 54 if (out_) { 55 *out_ << std::forward<T>(value); 56 } 57 58 return std::move(*this); 59 } 60 ~writerlogger::writer61 ~writer() { 62 if (out_) { 63 *out_ << '\n'; 64 } 65 } 66 67 private: 68 TOut *out_; 69 }; 70 loggerlogger71 logger(TOut *out, source_info source) noexcept: 72 writer_(out), 73 source_(source) 74 {} 75 76 logger(logger const &) = delete; 77 logger(logger &&rhs) = default; 78 ~loggerlogger79 ~logger() { 80 if (info::abort::value) { 81 std::abort(); 82 } 83 } 84 85 template <typename T> 86 writer operator <<(T &&value) { 87 writer_ << info::signature::value; 88 89 if (info::show_level::value) { 90 writer_ << info::value; 91 } 92 93 auto const now = std::chrono::duration_cast<std::chrono::nanoseconds>( 94 std::chrono::system_clock::now().time_since_epoch() 95 ); 96 97 // TODO: output date in an absolute format 98 time::pretty_print( 99 writer_ << " [" << source_.file() << ':' << source_.line() << "] at ", 100 now 101 ) << ": " << std::forward<T>(value); 102 103 return std::move(writer_); 104 } 105 106 private: 107 writer writer_; 108 source_info source_; 109 }; 110 111 using level_t = unsigned; 112 113 template <typename TCategory, level_t Level> 114 struct log_level { setlog_level115 static void set(level_t level) { 116 value() = level; 117 } 118 getlog_level119 static level_t get() { 120 return value(); 121 } 122 123 private: valuelog_level124 static std::atomic<level_t> &value() { 125 static std::atomic<level_t> instance(Level); 126 return instance; 127 } 128 }; 129 130 template < 131 typename TCategory, 132 level_t Level, 133 char Signature, 134 bool ShowLevel, 135 bool Abort = false 136 > 137 struct level_info: 138 std::integral_constant<level_t, Level> 139 { 140 using category = TCategory; 141 using show_level = std::integral_constant<bool, ShowLevel>; 142 using signature = std::integral_constant<char, Signature>; 143 using abort = std::integral_constant<bool, Abort>; 144 }; 145 146 struct log_tag {}; 147 struct verbose_tag {}; 148 149 using level_FATAL = level_info<log_tag, 0, 'F', false, true>; 150 using level_CRITICAL = level_info<log_tag, 1, 'C', false>; 151 using level_ERROR = level_info<log_tag, 2, 'E', false>; 152 using level_WARNING = level_info<log_tag, 3, 'W', false>; 153 using level_INFO = level_info<log_tag, 4, 'I', false>; 154 155 template <level_t Level> 156 using level_verbose = level_info<verbose_tag, Level, 'V', true>; 157 158 template <typename> struct by_category; 159 160 template <> 161 struct by_category<log_tag> { 162 using level = detail::log_impl::log_level< 163 detail::log_impl::log_tag, 164 detail::log_impl::level_INFO::value 165 >; 166 }; 167 168 template <> 169 struct by_category<verbose_tag> { 170 using level = detail::log_impl::log_level< 171 detail::log_impl::verbose_tag, 0 172 >; 173 }; 174 175 } // namespace log_impl { 176 } // namespace detail { 177 178 struct null_logger { 179 template <typename T> 180 null_logger const &operator <<(T &&) const { return *this; } 181 }; 182 183 using level = detail::log_impl::by_category< 184 detail::log_impl::log_tag 185 >::level; 186 187 using v_level = detail::log_impl::by_category< 188 detail::log_impl::verbose_tag 189 >::level; 190 191 // TODO: ADD THE ABILITY TO TURN VERBOSE LOGGING ON AND OFF 192 template <typename TInfo> 193 log::detail::log_impl::logger<std::ostream, TInfo> log(source_info source) { 194 return log::detail::log_impl::logger<std::ostream, TInfo>( 195 TInfo::value <= detail::log_impl::by_category< 196 typename TInfo::category 197 >::level::get() 198 ? std::addressof(std::cerr) 199 : nullptr, 200 source 201 ); 202 } 203 204 } // namespace log { 205 206 #define FATAL_LOG(Level) \ 207 ::fatal::log::log<::fatal::log::detail::log_impl::level_##Level>( \ 208 FATAL_SOURCE_INFO() \ 209 ) 210 211 #define FATAL_VLOG(Level) \ 212 ::fatal::log::log<::fatal::log::detail::log_impl::level_verbose<Level>>( \ 213 FATAL_SOURCE_INFO() \ 214 ) 215 216 #ifdef NDEBUG 217 # define FATAL_DLOG(Level) ::fatal::log::null_logger() 218 # define FATAL_DVLOG(Level) ::fatal::log::null_logger() 219 #else // NDEBUG 220 # define FATAL_DLOG(Level) FATAL_LOG(Level) 221 # define FATAL_DVLOG(Level) FATAL_VLOG(Level) 222 #endif // NDEBUG 223 224 } // namespace fatal { 225 226 #endif // FATAL_INCLUDE_fatal_log_log_h 227