| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIB_DRIVER2_LOGGER_INTERNAL_H_ |
| #define LIB_DRIVER2_LOGGER_INTERNAL_H_ |
| |
| #include <lib/driver2/logger.h> |
| |
| namespace driver_internal { |
| |
| static inline cpp17::optional<cpp17::string_view> FromCString(const char* value) { |
| if (value) { |
| return cpp17::optional(value); |
| } |
| return cpp17::nullopt; |
| } |
| |
| template <typename... Args> |
| constexpr size_t ArgsSize(Args... args) { |
| return sizeof...(args); |
| } |
| |
| template <typename... Args> |
| struct Tuplet { |
| std::tuple<Args...> tuple; |
| size_t size; |
| constexpr Tuplet(std::tuple<Args...> tuple, size_t size) : tuple(tuple), size(size) {} |
| }; |
| |
| template <typename Key, typename Value> |
| struct KeyValue { |
| Key key; |
| Value value; |
| constexpr KeyValue(Key key, Value value) : key(key), value(value) {} |
| }; |
| |
| template <size_t i, size_t size> |
| constexpr bool ILessThanSize() { |
| return i < size; |
| } |
| |
| template <bool expr> |
| constexpr bool Not() { |
| return !expr; |
| } |
| |
| template <typename... LogArgs> |
| constexpr driver_internal::Tuplet<LogArgs...> Args(LogArgs... values) { |
| return driver_internal::Tuplet<LogArgs...>(std::make_tuple(values...), sizeof...(values)); |
| } |
| |
| template <typename Key, typename Value> |
| constexpr driver_internal::KeyValue<Key, Value> KeyValueInternal(Key key, Value value) { |
| return driver_internal::KeyValue<Key, Value>(key, value); |
| } |
| |
| struct EncoderState { |
| fuchsia_syslog::LogBuffer buffer; |
| // Does nothing (enabled when we hit the last parameter that the user passed into us) |
| template <size_t i, size_t size, typename... T, |
| typename std::enable_if<Not<ILessThanSize<i, size>()>(), int>::type = 0> |
| void Encode(Tuplet<T...> value) {} |
| |
| // Encodes an int64 |
| void Encode(KeyValue<const char*, int64_t> value) { |
| buffer.WriteKeyValue(value.key, value.value); |
| } |
| |
| // Encodes an int |
| void Encode(KeyValue<const char*, int> value) { |
| Encode(KeyValue<const char*, int64_t>(value.key, value.value)); |
| } |
| |
| // Encodes a uint64 |
| void Encode(KeyValue<const char*, uint64_t> value) { |
| buffer.WriteKeyValue(value.key, value.value); |
| } |
| |
| // Encodes an unsigned int |
| void Encode(KeyValue<const char*, unsigned int> value) { |
| Encode(KeyValue<const char*, uint64_t>(value.key, value.value)); |
| } |
| |
| // Encodes a NULL-terminated C-string. |
| void Encode(KeyValue<const char*, const char*> value) { |
| buffer.WriteKeyValue(value.key, value.value); |
| } |
| |
| // Encodes a double floating point value |
| void Encode(KeyValue<const char*, double> value) { buffer.WriteKeyValue(value.key, value.value); } |
| |
| // Encodes a floating point value |
| void Encode(KeyValue<const char*, float> value) { buffer.WriteKeyValue(value.key, value.value); } |
| |
| // Encodes a boolean value |
| void Encode(KeyValue<const char*, bool> value) { buffer.WriteKeyValue(value.key, value.value); } |
| |
| // Encodes an arbitrary list of values recursively. |
| template <size_t i, size_t size, typename... T, |
| typename std::enable_if<ILessThanSize<i, size>(), int>::type = 0> |
| void Encode(Tuplet<T...> value) { |
| auto val = std::get<i>(value.tuple); |
| Encode(val); |
| Encode<i + 1, size>(value); |
| } |
| }; |
| |
| template <typename Msg, typename... KeyValuePairs> |
| struct LogValue { |
| constexpr LogValue(Msg msg, Tuplet<KeyValuePairs...> kvps) : msg(msg), kvps(kvps) {} |
| void LogNew(driver::Logger& logger, FuchsiaLogSeverity severity, const char* file, |
| unsigned int line, const char* condition) const { |
| EncoderState state; |
| uint32_t dropped = logger.GetAndResetDropped(); |
| logger.BeginRecord(state.buffer, severity, FromCString(file), line, FromCString(msg), |
| FromCString(condition), false, dropped); |
| // https://bugs.llvm.org/show_bug.cgi?id=41093 -- Clang loses constexpr |
| // even though this should be constexpr here. |
| state.Encode<0, sizeof...(KeyValuePairs)>(kvps); |
| logger.FlushRecord(state.buffer, dropped); |
| } |
| |
| Msg msg; |
| driver_internal::Tuplet<KeyValuePairs...> kvps; |
| }; |
| |
| template <typename Msg, typename... Args> |
| static auto MakeValue(Msg msg, driver_internal::Tuplet<Args...> args) { |
| return LogValue<Msg, Args...>(msg, args); |
| } |
| |
| template <size_t i, size_t size, typename... Values, typename... Tuple, |
| typename std::enable_if<driver_internal::Not<driver_internal::ILessThanSize<i, size>()>(), |
| int>::type = 0> |
| static auto MakeKV(std::tuple<Values...> value, std::tuple<Tuple...> tuple) { |
| return driver_internal::Tuplet<Tuple...>(tuple, size); |
| } |
| |
| template <size_t i, size_t size, typename... Values, typename... Tuple, |
| typename std::enable_if<driver_internal::ILessThanSize<i, size>(), int>::type = 0> |
| static auto MakeKV(std::tuple<Values...> value, std::tuple<Tuple...> tuple) { |
| // Key at index i, value at index i+1 |
| auto k = std::get<i>(value); |
| auto v = std::get<i + 1>(value); |
| auto new_tuple = std::tuple_cat(tuple, std::make_tuple(KeyValueInternal(k, v))); |
| return MakeKV<i + 2, size, Values...>(value, new_tuple); |
| } |
| |
| template <typename... Args, typename... EmptyTuple> |
| static auto MakeKV(std::tuple<Args...> args, std::tuple<EmptyTuple...> start_tuple) { |
| return MakeKV<0, sizeof...(Args), Args..., EmptyTuple...>(args, start_tuple); |
| } |
| |
| template <typename... Args> |
| static auto MakeKV(std::tuple<Args...> args) { |
| return MakeKV(args, std::make_tuple()); |
| } |
| |
| template <typename Msg, typename... Args> |
| static void fx_slog(driver::Logger& logger, FuchsiaLogSeverity severity, const char* file, int line, |
| Msg msg, Args... args) { |
| if (severity < logger.GetSeverity()) { |
| return; |
| } |
| MakeValue(msg, MakeKV<Args...>(std::make_tuple(args...))) |
| .LogNew(logger, severity, file, line, nullptr); |
| } |
| |
| } // namespace driver_internal |
| |
| #endif // LIB_DRIVER2_LOGGER_INTERNAL_H_ |