Modern C++ Kafka API
Log.h
1 #pragma once
2 
3 #include <kafka/Project.h>
4 
5 #include <kafka/Utility.h>
6 
7 #include <algorithm>
8 #include <array>
9 #include <cassert>
10 #include <functional>
11 #include <iostream>
12 #include <mutex>
13 
14 
15 namespace KAFKA_API {
16 
17 struct Log
18 {
19  enum Level
20  {
21  Emerg = 0,
22  Alert = 1,
23  Crit = 2,
24  Err = 3,
25  Warning = 4,
26  Notice = 5,
27  Info = 6,
28  Debug = 7
29  };
30 
31  static const std::string& levelString(std::size_t level)
32  {
33  static const std::vector<std::string> levelNames = {"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG", "INVALID"};
34  static const std::size_t maxIndex = levelNames.size() - 1;
35 
36  return levelNames[(std::min)(level, maxIndex)];
37  }
38 };
39 
40 
41 // Log Buffer
42 template <std::size_t MAX_CAPACITY>
43 class LogBuffer
44 {
45 public:
46  LogBuffer() { clear(); }
47 
48  LogBuffer& clear()
49  {
50  _buf[0] = 0;
51  _wptr = _buf.data();
52  return *this;
53  }
54 
55  template<class ...Args>
56  LogBuffer& print(const char* format, Args... args)
57  {
58  assert(!(_buf[0] != 0 && _wptr == _buf.data())); // means it has already been used as a plain buffer (with `str()`)
59 
60  auto cnt = std::snprintf(_wptr, capacity(), format, args...); // returns number of characters written if successful (not including '\0')
61  if (cnt > 0)
62  {
63  _wptr = (std::min)(_wptr + cnt, _buf.data() + MAX_CAPACITY - 1);
64  }
65  return *this;
66  }
67  LogBuffer& print(const char* format) { return print("%s", format); }
68 
69  std::size_t capacity() const { return static_cast<size_t>(_buf.data() + MAX_CAPACITY - _wptr); }
70  char* str() { return _buf.data(); }
71  const char* c_str() const { return _buf.data(); }
72 
73 private:
74  std::array<char, MAX_CAPACITY> _buf;
75  char* _wptr = nullptr;
76 };
77 
78 
79 // Default Logger
80 inline void DefaultLogger(int level, const char* /*filename*/, int /*lineno*/, const char* msg)
81 {
82  std::cout << "[" << utility::getCurrentTime() << "]" << Log::levelString(static_cast<std::size_t>(level)) << " " << msg;
83  std::cout << std::endl;
84 }
85 
86 // Null Logger
87 inline void NullLogger(int /*level*/, const char* /*filename*/, int /*lineno*/, const char* /*msg*/)
88 {
89 }
90 
91 
92 // Global Logger
93 template <typename T = void>
94 struct GlobalLogger
95 {
96  static clients::LogCallback logCb;
97  static std::once_flag initOnce;
98 
99  static const constexpr int LOG_BUFFER_SIZE = 1024;
100 
101  template<class ...Args>
102  static void doLog(int level, const char* filename, int lineno, const char* format, Args... args)
103  {
104  if (!GlobalLogger<>::logCb) return;
105 
106  LogBuffer<LOG_BUFFER_SIZE> logBuffer;
107  logBuffer.print(format, args...);
108  GlobalLogger<>::logCb(level, filename, lineno, logBuffer.c_str());
109  }
110 };
111 
112 template <typename T>
113 clients::LogCallback GlobalLogger<T>::logCb;
114 
115 template <typename T>
116 std::once_flag GlobalLogger<T>::initOnce;
117 
121 inline void setGlobalLogger(clients::LogCallback cb)
122 {
123  std::call_once(GlobalLogger<>::initOnce, [](){}); // Then no need to init within the first KAFKA_API_LOG call.
124  GlobalLogger<>::logCb = std::move(cb);
125 }
126 
133 #define KAFKA_API_LOG(level, ...) do { \
134  std::call_once(GlobalLogger<>::initOnce, [](){ GlobalLogger<>::logCb = DefaultLogger; }); \
135  GlobalLogger<>::doLog(level, __FILE__, __LINE__, ##__VA_ARGS__); \
136 } while (0)
137 
138 
139 } // end of KAFKA_API
140