C++ Log日志系统
闲得无聊,瞎写的一个东西。
好多地方能够优化甚至可能重写,也没写,就记下了个思路在这里。
主要熟练一下C++17的内容。
2023.10.31 更新 ============================================================
这次我将它更新了。
将文件操作放在逻辑线程是个非常沙雕的行为,在此我要将文件操作和log登记操作分开(分成多个线程)进行。
类似于 存在一个log客户端负责登记log,然后log服务器负责写入文件。
因为我在弄一个简单的工具,所以好多内容都需要一些公共的前置代码。
c++20.
前置需要的代码:
1. 公共定义 inc.h
1 #pragma once 2 3 #include <stdint.h> 4 #include <assert.h> 5 #include <string_view> 6 7 /// Namespace 'lunacia' define. 8 #define BEGIN_LUNA_NAMESPACE namespace lunacia { 9 #define END_NAMESPACE } 10 #define LUNA_SPACE ::lunacia:: 11 12 /// Different platform. 13 #ifdef _MSC_VER // for MSVC 14 #define FORCE_INLINE __forceinline 15 #define RESTRICT_POINTER __restrict 16 17 #elif defined __GNUC__ // for gcc on Linux/Apple OS X 18 #define FORCE_INLINE __inline__ __attribute__((always_inline)) 19 #define RESTRICT_POINTER __restrict__ 20 21 #elif (defined(__BORLANDC__) || defined(__WATCOMC__)) 22 #define FORCE_INLINE __inline 23 #define RESTRICT_POINTER 24 25 #else 26 #define FORCE_INLINE inline 27 #define RESTRICT_POINTER 28 29 #endif // _MSC_VER 30 31 #ifdef _DEBUG 32 #define RELEASE_INLINE 33 #define RELEASE_NOTHROW 34 #else 35 #define RELEASE_INLINE FORCE_INLINE 36 #define RELEASE_NOTHROW noexcept 37 #endif 38 39 #define TRY_INLINE inline 40 41 /// Code to string. 42 #define ___S(s) #s 43 #define code2string(s) ___S(s) 44 #undef ___S 45 46 namespace lunacia 47 { 48 /// Default memory allocator. 49 template<typename T_> 50 using default_allocator = typename ::std::allocator<T_>; 51 52 template<typename T_> 53 T_& get_instance() { static T_ ins; return ins; } 54 55 /// Constructor 56 template<typename T_, typename ... TArgs_> 57 FORCE_INLINE void call_construct(T_* ptr, TArgs_&& ...args) noexcept(std::is_nothrow_constructible<T_, TArgs_...>::value) 58 { 59 ::new((void*)(ptr)) T_(std::forward<TArgs_>(args) ... ); 60 } 61 62 template<typename T_> 63 FORCE_INLINE void call_destory(T_* ptr) 64 { 65 ptr->~T_(); 66 } 67 68 /// typedef 69 typedef int32_t isize_t; 70 typedef uint32_t usize_t; 71 typedef ::std::string_view constant_string; 72 };
2. 通用工具 common.h
单例基类,禁止copy基类,禁止移动基类,最大最小值计算
1 #pragma once 2 3 #include <memory> 4 #include <functional> 5 #include <cstring> //for memset 6 7 #include "inc.h" 8 9 namespace lunacia::common 10 { 11 class copy_disable 12 { 13 public: 14 copy_disable() {} 15 ~copy_disable() {} 16 17 private: 18 copy_disable(const copy_disable&) = delete; 19 copy_disable& operator=(const copy_disable&) = delete; 20 }; 21 22 class move_disable 23 { 24 public: 25 move_disable() {} 26 ~move_disable() {} 27 28 private: 29 move_disable(move_disable&&) = delete; 30 }; 31 32 template<typename T_> 33 class singleton 34 { 35 public: 36 static T_& instance() noexcept(std::is_nothrow_constructible<T_>::value) 37 { 38 static T_ ins{token()}; 39 return ins; 40 } 41 42 virtual ~singleton() = default; 43 singleton(const singleton&) = delete; 44 singleton& operator=(const singleton&) = delete; 45 46 protected: 47 struct token {}; // helper class, scarecrow it is. 48 singleton() noexcept = default; 49 }; 50 51 template<int is_, typename T1_, typename T2_> 52 struct type_if //true 53 { 54 using type = T1_; 55 }; 56 57 template<typename T1_, typename T2_> 58 struct type_if<0, T1_, T2_> //false 59 { 60 using type = T2_; 61 }; 62 63 template<usize_t i_, typename t_, typename ... ts_> 64 struct get_type 65 : public get_type<i_ - 1, ts_...> 66 { 67 }; 68 69 template<typename t_, typename ... ts_> 70 struct get_type<0, t_, ts_...> 71 { 72 using type = t_; 73 }; 74 75 #ifdef min 76 #undef min 77 #endif 78 79 template<typename T_> 80 constexpr T_ min(T_ a, T_ b) 81 { 82 return (a < b) ? a : b; 83 } 84 85 template<typename ... T_, typename T0__ = std::common_type_t<T_...> > 86 constexpr T0__ min(T0__ n0, T0__ n1, T_&& ... n) 87 { 88 return lunacia::common::min( 89 lunacia::common::min(n0, n1) 90 , std::forward<T_>(n) ... 91 ); 92 } 93 94 #ifdef max 95 #undef max 96 #endif 97 98 template<typename T_> 99 constexpr T_ max(T_ a, T_ b) 100 { 101 return (a > b) ? a : b; 102 } 103 104 template<typename ... T_, typename T0__ = std::common_type_t<T_...> > 105 constexpr T0__ max(T0__ n0, T0__ n1, T_&& ... n) 106 { 107 return lunacia::common::max( 108 lunacia::common::max( 109 std::forward<T0__>(n0) 110 , std::forward<T0__>(n1) 111 ) 112 , std::forward<T_>(n)...); 113 } 114 115 constexpr usize_t align(usize_t i, usize_t align) 116 { 117 return (i + align - 1) & ~(align - 1); 118 } 119 120 END_NAMESPACE // end namespace lunacia::common
3. 队列 (环形队列,固定容量,单生产者单消费者线程安全) queue.h
模板参数的第二参数为 true 时,启用线程安全(单生产者单消费者)。默认为 false.
1 #pragma once 2 3 #include "inc.h" 4 #include "common.h" 5 #include <atomic> 6 7 namespace lunacia 8 { 9 10 namespace __queue_detail 11 { 12 inline constexpr usize_t CHUNK_SIZE = 16; 13 14 template< 15 typename T_ 16 , typename allocator_t_ = default_allocator<T_> 17 > class __alterable_array 18 { 19 public: 20 using value_type = T_; 21 using value_pointer = T_*; 22 using self_t = __alterable_array<value_type, allocator_t_>; 23 using allocator_t = allocator_t_; 24 using allocator_traits_t = typename std::allocator_traits<allocator_t>; 25 using size_type = usize_t; 26 27 __alterable_array() 28 : _cap(0) 29 , _size(0) 30 , _data(nullptr) 31 { 32 } 33 34 __alterable_array(const self_t& r) 35 : _cap(r._cap) 36 , _size(r._size) 37 { 38 reallocate(r._cap); 39 for (usize_t i = 0; i < r._size; i++) 40 { 41 call_construct(_data + i, *(r._data + i)); 42 } 43 } 44 45 __alterable_array(size_type recap) 46 : _size(0) 47 { 48 reallocate(recap); 49 } 50 51 ~__alterable_array() 52 { 53 clear(); 54 } 55 56 FORCE_INLINE void clear() { deallocate(); } 57 58 value_type& operator[](size_type i) { return *(_data + i); } 59 const value_type& operator[](size_type i) const { return *(_data + i); } 60 61 template<typename ... args_t_> 62 void emplace_back(args_t_&& ... args) 63 { 64 if(_cap == _size) 65 reallocate(_cap + (size_type)(__queue_detail::CHUNK_SIZE)); 66 67 call_construct(_data + _size, std::forward<args_t_>(args) ...); 68 ++_size; 69 } 70 71 void reallocate(size_type recap) 72 { 73 // only extend 74 value_pointer new_cap = allocator_traits_t::allocate(get_instance<allocator_t>(), recap); 75 if(_data) 76 { 77 std::memcpy(new_cap, _data, sizeof(value_type) * _cap); 78 deallocate(); 79 } 80 _data = new_cap; 81 _cap = recap; 82 } 83 84 void deallocate() 85 { 86 allocator_traits_t::deallocate(get_instance<allocator_t>(), _data, _cap); 87 _data = nullptr; 88 } 89 90 size_type _cap; 91 size_type _size; 92 value_pointer _data; 93 }; 94 95 template<typename T_> 96 struct __block final 97 { 98 enum { size = sizeof(T_) }; 99 __block() 100 { 101 } 102 103 explicit __block(const T_& data) 104 : __block(&data) 105 { 106 } 107 108 explicit __block(const T_* pdata) 109 { 110 set(pdata); 111 } 112 113 FORCE_INLINE void set(const T_* pdata) 114 { 115 std::memcpy((void*)(_memory), (void*)(pdata), size); 116 } 117 118 FORCE_INLINE T_* cast() noexcept { return (T_*)(_memory); } 119 FORCE_INLINE const T_* cast() const noexcept { return (const T_*)(_memory); } 120 operator T_*() noexcept { return cast(); } 121 122 char _memory[size] = { 0 }; 123 }; 124 125 template< 126 typename T_ 127 , typename allocator_t_ = default_allocator<T_> 128 > struct __chunk 129 { 130 using value_type = T_; 131 using value_pointer = T_*; 132 using allocator_traits_t = typename std::allocator_traits<allocator_t_>; 133 using block_t = __block<value_type>; 134 135 // |----------------| 136 // front back 137 block_t _data[CHUNK_SIZE]; 138 }; 139 140 }; //namespace __queue_detail 141 142 template< 143 typename T_ 144 , bool IS_LOCK = false 145 , typename allocator_t_ = default_allocator<T_> 146 //, typename mutex_t_ = common::default_lock 147 > class queue 148 { 149 public: 150 using value_type = T_; 151 using allocator_type = allocator_t_; 152 using allocator_traits_t = typename std::allocator_traits<allocator_type>; 153 //using mutex_type = mutex_t_; 154 //using self = lunacia::queue<value_type, allocator_type, mutex_type>; 155 using self = lunacia::queue<value_type, IS_LOCK, allocator_type>; 156 using data_chunk_t = __queue_detail::__chunk<value_type, allocator_type>; 157 using data_chunk_allocator_t = typename allocator_traits_t::template rebind_alloc<data_chunk_t>; 158 using data_map_t = __queue_detail::__alterable_array<data_chunk_t, data_chunk_allocator_t>; 159 using size_type = lunacia::usize_t; 160 //using lock_value_t = typename common::lock_value_trait<value_type, mutex_type>::type; 161 using index_type = typename common::type_if<IS_LOCK, std::atomic<size_type>, size_type>::type; 162 163 public: 164 queue() 165 : queue(0) 166 { 167 } 168 169 queue(size_type cap) 170 : _front(0) 171 , _back(0) 172 { 173 recap(cap); 174 } 175 176 queue(const self&) requires(!IS_LOCK) = default; 177 self& operator=(const self&) requires(!IS_LOCK) = default; 178 179 ~queue() 180 { 181 clear(); 182 } 183 184 void clear() 185 { 186 _data_map.clear(); 187 _front = 0; 188 _back = 0; 189 _cap = 0; 190 } 191 192 void recap(size_type cap) 193 { 194 _cap = cap + 1; 195 _data_map.reallocate( 196 common::align(cap, __queue_detail::CHUNK_SIZE) / __queue_detail::CHUNK_SIZE 197 ); 198 } 199 200 bool empty() const 201 { 202 return _front == _back; 203 } 204 205 bool full() const 206 { 207 return (_cap <= 0) || ((_back + 1) % _cap == _front); 208 } 209 210 size_type size() const 211 { 212 return (_back + _cap - _front) % _cap; 213 } 214 215 bool pop() //pop front 216 { 217 if(empty()) 218 return false; 219 220 call_destory(get_value(_front)); 221 advance(_front); 222 return true; 223 } 224 225 template<typename ... args_t_> 226 bool emplace(args_t_&& ... args) 227 { 228 if(full()) 229 return false; 230 231 call_construct(get_value(_back), std::forward<args_t_>(args) ...); 232 advance(_back); 233 return true; 234 } 235 236 bool push(const value_type& val) //push_back 237 { 238 return emplace(val); 239 } 240 241 bool push(value_type&& val) 242 { 243 return emplace(std::move((std::remove_cvref_t<value_type>)(val))); 244 } 245 246 value_type& front() 247 { 248 assert(!empty() && "can not get front for empty."); 249 return *get_value(_front); 250 } 251 252 const value_type& front() const 253 { 254 return const_cast<self*>(this)->front(); 255 } 256 257 private: 258 void advance(index_type& index) 259 { 260 index = (index + 1) % _cap; 261 } 262 263 size_type peek(index_type index) const 264 { 265 return (index + 1) % _cap; 266 } 267 268 value_type* get_value(size_type i) 269 { 270 return _data_map[i / __queue_detail::CHUNK_SIZE] 271 ._data[i % __queue_detail::CHUNK_SIZE].cast(); 272 } 273 274 value_type* get_value(size_type i) const 275 { 276 return const_cast<self*>(this)->get_value(i); 277 } 278 279 private: 280 data_map_t _data_map; 281 index_type _front; 282 index_type _back; 283 size_type _cap; 284 }; 285 286 287 }; //End of lunacia
4. 工作线程外壳 thread_handler.h
继承 __handler 类,子类实现虚函数 __once_run,就可以开启另外的线程循环调用 __once_run,调用 stop 或 close 停止线程。
1 #pragma once 2 3 #include <atomic> 4 #include <thread> 5 6 namespace lunacia 7 { 8 9 enum state_type 10 { 11 ST_NONE = 0, 12 ST_CREATED, 13 ST_RUNNING, 14 ST_PRE_STOP, 15 ST_STOP, 16 ST_ERROR 17 }; 18 19 class __handler 20 { 21 public: 22 __handler(); 23 virtual ~__handler(); 24 25 public: 26 virtual bool start(); 27 virtual void stop(); 28 virtual void close(); 29 virtual bool __once_run() = 0; 30 inline state_type state() const noexcept { return _state; } 31 32 private: 33 void __thread_run(); 34 35 protected: 36 std::atomic<state_type> _state; 37 }; 38 39 } //End of namespace lunacia
4. 工作线程外壳 thread_handler.cpp
1 #include "thread_mgr.h" 2 #include "thread_handler.h" 3 4 namespace lunacia 5 { 6 7 __handler::__handler() 8 : _state(ST_NONE) 9 { 10 } 11 12 __handler::~__handler() 13 { 14 this->close(); 15 } 16 17 void __handler::__thread_run() 18 { 19 _state = ST_RUNNING; 20 while(_state == ST_RUNNING) [[likely]] 21 { 22 if(__once_run()) [[likely]] 23 continue; 24 25 break; 26 } 27 28 _state = ST_STOP; 29 } 30 31 bool __handler::start() 32 { 33 if(!thread_mgr::instance().exec_by_idle([this](){ this->__thread_run(); })) 34 return false; 35 36 //std::thread(&__handler::__thread_run, this).detach(); 37 while(_state != ST_RUNNING) 38 { 39 if(_state == ST_ERROR || _state == ST_STOP) [[unlikely]] 40 { 41 return false; 42 } 43 44 std::this_thread::yield(); 45 } 46 47 return true; 48 } 49 50 void __handler::stop() 51 { 52 if(_state != ST_RUNNING) 53 return; 54 55 _state = ST_PRE_STOP; 56 while(_state == ST_PRE_STOP) 57 { 58 std::this_thread::yield(); 59 } 60 } 61 62 void __handler::close() 63 { 64 this->stop(); 65 } 66 67 } //End of namespace lunacia
5. 线程池 thread_mgr.h
可通过枚举 thread_tactic 以及 set_tactic 函数决定线程在执行完工作后是否保留(我不可能让线程池在每次需要工作时都新创建一个)。默认为 EXIT
1 #pragma once 2 3 #include <thread> 4 #include <condition_variable> 5 #include "common.h" 6 #include "lock.h" 7 #include "queue.h" 8 9 namespace lunacia 10 { 11 12 constexpr usize_t __THREAD_COUNT_LIMIT = 8; 13 constexpr usize_t __TASK_COUNT_LIMIT = 128; 14 15 enum class thread_tactic 16 { 17 TH_EXIT, 18 TH_RETAIN, 19 }; 20 21 class thread_info 22 : private common::copy_disable 23 { 24 public: 25 using proc_t = std::function<void()>; 26 27 thread_info(){}; 28 thread_info(uint32_t id, thread_tactic tactic = thread_tactic::TH_EXIT); 29 ~thread_info(); 30 31 public: 32 void start(); 33 bool execute(proc_t&& task); 34 void clear(); 35 36 inline void set_tactic(thread_tactic tactic) noexcept { _tactic = tactic; } 37 inline bool is_idle() const noexcept { return _tasks.empty(); } 38 inline bool is_busy() const noexcept { return !is_idle(); } 39 inline bool is_starting() const noexcept { return _is_starting; }; 40 inline uint32_t id() const noexcept { return _id; } 41 42 private: 43 friend void __proc(std::stop_token stoken, thread_info* p_this); 44 45 uint32_t _id; 46 std::atomic_bool _is_starting; 47 thread_tactic _tactic; 48 std::jthread _thread; 49 lunacia::queue<proc_t, true> _tasks; 50 }; 51 52 class thread_mgr 53 : private common::copy_disable 54 , public common::singleton<thread_mgr> 55 { 56 public: 57 thread_mgr(token); 58 ~thread_mgr(); 59 60 public: 61 thread_info* get_idle(); 62 thread_info* get(uint32_t id); 63 bool exec_by_idle(thread_info::proc_t&& proc); 64 bool exec_by_id(uint32_t id, thread_info::proc_t&& proc); 65 66 private: 67 common::default_lock _lock; 68 std::array<thread_info, __THREAD_COUNT_LIMIT> _threads; 69 }; 70 71 } //End of namespace lunacia
5. 线程池 thread_mgr.cpp
1 #include "thread_mgr.h" 2 3 namespace lunacia 4 { 5 6 void __proc(std::stop_token stoken, thread_info* p_this) 7 { 8 p_this->_is_starting = true; 9 while(!stoken.stop_requested()) 10 { 11 if(!p_this->_tasks.empty()) 12 { 13 auto& task_f = p_this->_tasks.front(); 14 task_f(); 15 p_this->_tasks.pop(); 16 17 if( 18 p_this->_tactic == thread_tactic::TH_EXIT 19 && p_this->_tasks.empty() 20 ) break; 21 } 22 else 23 { 24 std::this_thread::yield(); 25 } 26 } 27 28 while(!p_this->_tasks.empty()) 29 { 30 auto& task_f = p_this->_tasks.front(); 31 task_f(); 32 p_this->_tasks.pop(); 33 } 34 35 p_this->_is_starting = false; 36 } 37 38 thread_info::thread_info(uint32_t id, thread_tactic tactic) 39 : _id(id) 40 , _is_starting(false) 41 , _tactic(tactic) 42 { 43 _tasks.recap( 44 (tactic == thread_tactic::TH_EXIT) 45 ? 1 46 : __TASK_COUNT_LIMIT 47 ); 48 } 49 50 thread_info::~thread_info() 51 { 52 clear(); 53 } 54 55 void thread_info::start() 56 { 57 if(is_starting()) [[unlikely]] 58 return; 59 60 _thread = std::jthread(&__proc, this); 61 _thread.detach(); 62 } 63 64 bool thread_info::execute(proc_t&& task) 65 { 66 if(_tasks.full()) 67 return false; 68 69 if(!is_starting()) 70 start(); 71 72 return _tasks.push(std::forward<proc_t>(task)); 73 } 74 75 void thread_info::clear() 76 { 77 _thread.request_stop(); 78 } 79 80 thread_mgr::thread_mgr(token) 81 { 82 for (usize_t i = 0; i < __THREAD_COUNT_LIMIT; ++i) 83 { 84 call_construct(&(_threads[i]), i, thread_tactic::TH_EXIT); 85 } 86 } 87 88 thread_mgr::~thread_mgr() 89 { 90 } 91 92 thread_info* thread_mgr::get_idle() 93 { 94 std::unique_lock<common::default_lock> lk(_lock); 95 for (thread_info& th : _threads) 96 { 97 if(th.is_idle()) 98 return &th; 99 } 100 101 return nullptr; 102 } 103 104 thread_info* thread_mgr::get(uint32_t id) 105 { 106 std::unique_lock<common::default_lock> lk(_lock); 107 if(id >= __THREAD_COUNT_LIMIT) [[unlikely]] 108 return nullptr; 109 110 return &(_threads[id]); 111 } 112 113 bool thread_mgr::exec_by_idle(thread_info::proc_t&& proc) 114 { 115 std::unique_lock<common::default_lock> lk(_lock); 116 thread_info* th = get_idle(); 117 if(!th) [[unlikely]] 118 return false; 119 120 return th->execute(std::forward<thread_info::proc_t>(proc)); 121 } 122 123 bool thread_mgr::exec_by_id(uint32_t id, thread_info::proc_t&& proc) 124 { 125 std::unique_lock<common::default_lock> lk(_lock); 126 thread_info* th = get(id); 127 if(!th) [[unlikely]] 128 return false; 129 130 return th->execute(std::forward<thread_info::proc_t>(proc)); 131 } 132 133 } //End of namespace lunacia
6. 递归锁 lock.h
1 #pragma once 2 3 #include "common.h" 4 5 #ifdef _MSC_VER 6 #include <Synchapi.h> 7 #elif defined __GNUC__ 8 #include <pthread.h> 9 #endif 10 11 #include <mutex> 12 13 BEGIN_LUNA_NAMESPACE 14 15 namespace common 16 { 17 class __lock_rule 18 : private copy_disable 19 , private move_disable 20 { 21 public: 22 virtual void lock() = 0; 23 virtual bool try_lock() = 0; 24 virtual void unlock() = 0; 25 }; 26 27 class free_lock final 28 : public __lock_rule 29 { 30 public: 31 free_lock() noexcept {} 32 ~free_lock() = default; 33 34 public: 35 FORCE_INLINE void lock() noexcept override {} 36 FORCE_INLINE bool try_lock() noexcept override { return true; } 37 FORCE_INLINE void unlock() noexcept override {} 38 }; 39 40 //@notice Recursive lock must! 41 class default_lock final 42 : public __lock_rule 43 { 44 public: 45 default_lock() noexcept; 46 ~default_lock(); 47 48 public: 49 void lock() override; 50 void unlock() override; 51 bool try_lock() override; 52 53 private: 54 #ifdef _MSC_VER 55 ::CRITICAL_SECTION _cs; 56 #else 57 ::pthread_mutex_t _mx; 58 #endif 59 }; 60 61 template< 62 typename data_t_ 63 , typename mutex_t_ 64 //, typename lock_t_ = std::unique_lock<mutex_t_> 65 > struct value_lock 66 { 67 using data_t = data_t_; 68 using mutex_t = mutex_t_; 69 using self = value_lock<data_t_, mutex_t_>; 70 71 enum : bool { is_free = std::is_same_v<mutex_t, free_lock> }; 72 73 data_t& _data; 74 mutex_t& _mutex; 75 76 explicit value_lock(data_t& data, mutex_t& mutex) 77 : _data(data) 78 , _mutex(mutex) 79 { 80 lock(); 81 } 82 83 ~value_lock() 84 { 85 unlock(); 86 } 87 88 FORCE_INLINE operator data_t&() noexcept { return _data; } 89 FORCE_INLINE operator const data_t&() const noexcept { return _data; } 90 91 FORCE_INLINE operator data_t*() noexcept { return &_data; } 92 FORCE_INLINE operator const data_t*() const noexcept { return &_data; } 93 94 FORCE_INLINE self& operator=(data_t& r) 95 { 96 //only value referance 97 _data = r; 98 return *this; 99 } 100 101 FORCE_INLINE self& operator=(const data_t& r) 102 { 103 //only value referance 104 _data = r; 105 return *this; 106 } 107 108 FORCE_INLINE data_t& get_data() noexcept { return _data; } 109 FORCE_INLINE const data_t& get_data() const noexcept { return _data; } 110 111 FORCE_INLINE void lock() 112 { 113 if constexpr (is_free) 114 return; 115 116 _mutex.lock(); 117 } 118 119 FORCE_INLINE void unlock() 120 { 121 if constexpr (is_free) 122 return; 123 124 _mutex.unlock(); 125 } 126 127 }; 128 129 template< 130 typename data_t_ 131 , typename mutex_t_ 132 > struct lock_value_trait 133 { 134 using type = common::value_lock<data_t_, mutex_t_>; 135 }; 136 137 template< 138 typename data_t_ 139 , typename mutex_t_ 140 > struct get_lock_value 141 { 142 typename common::value_lock<data_t_, mutex_t_> operator()(data_t_& data, mutex_t_& mx) 143 { 144 return common::value_lock<data_t_, mutex_t_>(data, mx); 145 } 146 }; 147 148 } 149 150 END_NAMESPACE
6. 递归锁 lock_gunc.cpp
1 #ifdef __GNUC__ 2 3 #include "lock.h" 4 5 BEGIN_LUNA_NAMESPACE 6 7 namespace common 8 { 9 default_lock::default_lock() noexcept 10 : _mx(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) 11 { 12 } 13 14 default_lock::~default_lock(){} 15 16 void default_lock::lock() 17 { 18 ::pthread_mutex_lock(&_mx); 19 } 20 21 void default_lock::unlock() 22 { 23 ::pthread_mutex_unlock(&_mx); 24 } 25 26 bool default_lock::try_lock() 27 { 28 this->lock(); 29 return true; 30 } 31 } 32 33 END_NAMESPACE 34 35 #endif // __GNUC__
================================================================================================================================================================================
前置代码输出完毕,稍微有点多,并且没经过认真测试,因为还没来得及认真测。
我希望日志的使用遵循习惯,类似于:
logObj << log_level << message_part_1 << message_part_2;
上述的输出为:
2023-1-1 12:00:00.000 [log_level] message_part_1message_part_2
我也希望 操作符 << 可以支持任何的基本类型和标准库的string.
需要记录日志输出的时间,最少精确至毫秒,最好是微秒,纳秒太大不方便查看。
......
1 lunacia::log ll("/home/123.txt"); 2 ll << lunacia::log_level::LOG_FATAL << "asd" << 123 << 456ul << 2345.4564 << " " << 'c' << lunacia::log::end; 3 ll << lunacia::log_level::LOG_ERROR << "asd22" << 1243 << 9456ul << 8245.4 << " " << 'd' << lunacia::log::end; 4 ll << "etwtasd123123" << 44123 << 64526ul << 2334124345.451213123123123164 << " " << 'e' << lunacia::log::end;
输出log内容:
2023-11-01 07:32:54.989988 [Fatal] asd1234562345.456400 c 2023-11-01 07:32:54.989990 [Error] asd22124394568245.400000 d 2023-11-01 07:32:54.989992 etwtasd12312344123645262334124345.451213 e
1 #pragma once 2 3 #include "inc.h" 4 #include "queue.h" 5 #include "thread_handler.h" 6 #include "common.h" 7 8 #include <string> 9 #include <fstream> 10 #include <map> 11 #include <condition_variable> 12 13 BEGIN_LUNA_NAMESPACE 14 15 enum log_level : uint8_t 16 { 17 LOG_NONE = 0, 18 LOG_TEST, 19 LOG_DEBUG, 20 LOG_INFO, 21 LOG_WARNNING, 22 LOG_ERROR, 23 LOG_FATAL, 24 25 __LOG_MAX_ 26 }; 27 28 class log; 29 30 struct log_format 31 { 32 public: 33 log_format(); 34 log_format(log_level lv, uint64_t ms, const std::string& log); 35 log_format(const log_format&) = default; 36 ~log_format(); 37 38 public: 39 std::string to_string() const; 40 41 log_format& operator<<(log_level lv) { _level = lv; return *this; } 42 log_format& operator<<(lunacia::constant_string str) { _info += str.data(); return *this; } 43 log_format& operator<<(const std::string& str) { _info += str; return *this; } 44 log_format& operator<<(const char* str){ _info += str; return *this; } 45 log_format& operator<<(char ch) { _info += std::string(1, ch); return *this; } 46 log_format& operator<<(int16_t i) { _info += std::to_string(i); return *this; } 47 log_format& operator<<(uint16_t i) { _info += std::to_string(i); return *this; } 48 log_format& operator<<(int32_t i) { _info += std::to_string(i); return *this; } 49 log_format& operator<<(uint32_t i) { _info += std::to_string(i); return *this; } 50 log_format& operator<<(int64_t i) { _info += std::to_string(i); return *this; } 51 log_format& operator<<(uint64_t i) { _info += std::to_string(i); return *this; } 52 log_format& operator<<(double f) { _info += std::to_string(f); return *this; } 53 log_format& operator<<(long double f) { _info += std::to_string(f); return *this; } 54 log_format& operator<<(float f) { _info += std::to_string(f); return *this; } 55 56 void clear(); 57 58 public: 59 log_level _level; 60 uint64_t _time_us; 61 std::string _info; 62 }; 63 64 //log writer 65 class __log_impl 66 { 67 public: 68 __log_impl(); 69 __log_impl(const std::string& file_path); 70 virtual ~__log_impl(); 71 72 void put(const log_format& info); 73 74 public: 75 std::fstream _file_stream; 76 }; 77 78 struct __log_event 79 { 80 enum { LE_NONE, LE_CREATE, LE_REMOVE, LE_WRITE }; 81 82 __log_event(log* client) 83 : _et(LE_REMOVE) 84 , _client(client) 85 { 86 } 87 88 __log_event(log* client, const log_format& info) 89 : _et(LE_WRITE) 90 , _client(client) 91 , _write_info(info) 92 { 93 } 94 95 __log_event(log* client, const std::string& file_path) 96 : _et(LE_CREATE) 97 , _client(client) 98 , _file_path(file_path) 99 { 100 } 101 102 int _et; 103 log* _client; 104 std::string _file_path; 105 log_format _write_info; 106 }; 107 108 //log server. 109 class __log_mgr 110 : public __handler 111 , public common::singleton<__log_mgr> 112 { 113 public: 114 __log_mgr(token); 115 ~__log_mgr(); 116 117 public: 118 template<typename ... ts_> 119 bool put_event(ts_&& ...args) 120 { 121 return _events.emplace(std::forward<ts_>(args) ...); 122 } 123 124 private: 125 void create(log* client, const std::string& file_path); 126 void remove(log* client); 127 void write(log* client, const log_format& info); 128 129 bool __once_run() override; 130 131 private: 132 std::map<log*, __log_impl*> _impls; 133 lunacia::queue<__log_event, true> _events; 134 }; 135 136 //log client 137 class log 138 { 139 public: 140 log(const std::string& file_path); 141 ~log(); 142 143 public: 144 struct __token_end {}; 145 static __token_end end; 146 147 template<typename t_> 148 log& operator<<(t_&& info) { _cur << info; return *this; } 149 void operator<<(__token_end); 150 151 private: 152 log_format _cur; 153 uint64_t _impl; 154 }; 155 156 END_NAMESPACE
7. 日志 log.cpp
1 #include <iostream> 2 #include <chrono> 3 #include <string> 4 5 #include "log.h" 6 7 BEGIN_LUNA_NAMESPACE 8 9 constexpr constant_string __level_strings[(usize_t)(log_level::__LOG_MAX_)] = 10 { 11 "" 12 , "Test" 13 , "Debug" 14 , "Info" 15 , "Warnning" 16 , "Error" 17 , "Fatal" 18 }; 19 20 void __print(const std::string& info) 21 { 22 ::puts(info.c_str()); 23 } 24 25 log_format::log_format() 26 : log_format(log_level::LOG_NONE, 0, "") 27 { 28 } 29 30 log_format::log_format(log_level lv, uint64_t ms, const std::string& log) 31 : _level(lv) 32 , _time_us(ms) 33 , _info(log) 34 { 35 } 36 37 log_format::~log_format() 38 { 39 clear(); 40 } 41 42 void log_format::clear() 43 { 44 _level = log_level::LOG_NONE; 45 _time_us = 0; 46 _info = ""; 47 } 48 49 std::string log_format::to_string() const 50 { 51 std::string time; 52 { 53 const ::time_t sec = _time_us / 1000000; 54 const ::time_t us = _time_us - sec * 1000000; 55 56 tm t; 57 ::gmtime_r(&sec, &t); 58 59 char buf[64]; 60 std::strftime(buf, sizeof buf, "%F %T", &t); 61 62 time.assign(buf); 63 time += "."; 64 time += std::to_string((uint64_t)(us)); 65 } 66 67 if(_level == log_level::LOG_NONE) 68 return time + " " + _info + "\n"; 69 70 return time + " [" + __level_strings[_level].data() + "] " + _info + "\n"; 71 } 72 73 __log_impl::__log_impl() 74 { 75 } 76 77 __log_impl::__log_impl(const std::string& file_path) 78 { 79 _file_stream.open(file_path, std::ios_base::out | std::ios_base::app); 80 if(!_file_stream.is_open()) 81 { 82 std::string err_info = 83 "*********** [ERROR] Open File " 84 + file_path 85 + " Failed!!! Error code: " 86 + std::to_string(errno) 87 + ". ***********"; 88 89 __print(err_info.c_str()); 90 return; 91 } 92 } 93 94 __log_impl::~__log_impl() 95 { 96 _file_stream.close(); 97 } 98 99 void __log_impl::put(const log_format& info) 100 { 101 std::string str = info.to_string(); 102 _file_stream.write(str.c_str(), str.size()); 103 } 104 105 __log_mgr::__log_mgr(token) 106 { 107 _events.recap(1 << 12); 108 } 109 110 __log_mgr::~__log_mgr() 111 { 112 __handler::close(); 113 } 114 115 void __log_mgr::create(log* client, const std::string& file_path) 116 { 117 _impls.try_emplace(client, new __log_impl(file_path)); 118 } 119 120 void __log_mgr::remove(log* client) 121 { 122 auto it = _impls.find(client); 123 if(it != _impls.end()) [[likely]] 124 { 125 delete it->second; 126 _impls.erase(it); 127 } 128 } 129 130 void __log_mgr::write(log* client, const log_format& info) 131 { 132 auto it = _impls.find(client); 133 if(it != _impls.end()) [[likely]] 134 { 135 it->second->put(info); 136 } 137 } 138 139 bool __log_mgr::__once_run() 140 { 141 while(!_events.empty()) 142 { 143 const __log_event& ev = _events.front(); 144 switch (ev._et) 145 { 146 case __log_event::LE_CREATE: 147 create(ev._client, ev._file_path); 148 break; 149 150 case __log_event::LE_REMOVE: 151 remove(ev._client); 152 break; 153 154 case __log_event::LE_WRITE: 155 write(ev._client, ev._write_info); 156 break; 157 158 default: 159 break; 160 } 161 162 _events.pop(); 163 } 164 165 return true; 166 } 167 168 log::__token_end log::end = log::__token_end{}; 169 170 log::log(const std::string& file_path) 171 { 172 state_type st = __log_mgr::instance().state(); 173 if(st != state_type::ST_RUNNING) 174 __log_mgr::instance().start(); 175 176 if(!__log_mgr::instance().put_event(this, file_path)) 177 { 178 __print("*********** [ERROR] Create log instance failed! ***********"); 179 } 180 } 181 182 log::~log() 183 { 184 __log_mgr::instance().put_event(this); 185 } 186 187 void log::operator<<(__token_end) 188 { 189 timespec ts; 190 std::timespec_get(&ts, TIME_UTC); 191 192 _cur._time_us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000; 193 __log_mgr::instance().put_event(this, _cur); 194 _cur.clear(); 195 } 196 197 END_NAMESPACE
我发现我越来越懒,咋办。。。