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

thread_mgr 单例;
thread_mgr::get_idle() 获得一个空闲的线程,返回 nullptr 表示不存在空闲线程;
thread_mgr::get(uint32_t id) 根据id获取线程(此ID非std::this_thread::id,而是 thread_info::id() 。此函数存在的意义就是有些逻辑需要占有一个线程,但并不总是繁忙的。
 
 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.

需要记录日志输出的时间,最少精确至毫秒,最好是微秒,纳秒太大不方便查看。

......

 

log_format 包含了一条日志的所有信息:
  日志等级 _level;
  日志时间 _time_us,微秒;
  日志的自定义输出内容 _info;
__log_impl 负责将一条内容为 log_format  的日志输出至文件,由 __log_mgr 使用;
__log_mgr 拥有一个线程,此线程调用 __log_impl 的逻辑,将自定义的log一条一条地交给__log_impl 处理;
__log_mgr 拥有一个队列,元素类型为__log_event,每个__log_event交给线程处理;
__log_mgr 拥有一个表,此表记录了所有的 log 使用者以及使用者所控制的 __log_impl;
__log_event 是 log 使用者自定义行为产生的事件:
  如果是 CREATE,则新建一个 __log_impl,记录在__log_mgr 的表中;
  如果是 REMOVE,则销毁一个 __log_impl,并从__log_mgr 的表中移除;
  如果是 WRITE, 则将一条日志交给__log_impl,写入文件;
log 类是使用者所使用的顶层类,log::end 表示一条日志结束。
使用例子:
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

 

 
7. 日志 log.h
  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

 

 

 

 

我发现我越来越懒,咋办。。。

posted on 2020-10-29 18:22  __Even  阅读(1429)  评论(0编辑  收藏  举报

导航