XTL  0.1
eXtended Template Library
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
log.hpp
Go to the documentation of this file.
1 
7 #pragma once
8 
9 
10 #include <xtd/xtd.hpp>
11 
12 #include <chrono>
13 #include <thread>
14 #include <condition_variable>
15 #include <mutex>
16 #include <future>
17 #include <deque>
18 
19 #if (XTD_LOG_TARGET_CSV | XTD_LOG_TARGET_XML)
20  #include <fstream>
21 #endif
22 
23 #if XTD_LOG_TARGET_SYSLOG
24  #include <syslog.h>
25 #endif
26 
27 #if XTD_LOG_TARGET_COUT
28  #include <iostream>
29 #endif
30 
31 
32 #include <xtd/string.hpp>
33 #include <xtd/source_location.hpp>
34 #include <xtd/filesystem.hpp>
35 #include <xtd/process.hpp>
36 #include <xtd/executable.hpp>
37 #include <xtd/meta.hpp>
38 
39 #define FATAL(...) xtd::log::get().write(xtd::log::type::fatal, here(), __VA_ARGS__)
40 #define ERR(...) xtd::log::get().write(xtd::log::type::error, here(), __VA_ARGS__)
41 #define WARNING(...) xtd::log::get().write(xtd::log::type::warning, here(), __VA_ARGS__)
42 #define INFO(...) xtd::log::get().write(xtd::log::type::info, here(), __VA_ARGS__)
43 #define DBG(...) xtd::log::get().write(xtd::log::type::debug, here(), __VA_ARGS__)
44 
45 namespace xtd{
46 
47  class log{
48  public:
49  enum class type{
50  fatal,
51  error,
52  warning,
53  info,
54  debug,
55  enter,
56  leave,
57  };
58 
59  static const char * type_string(type oType){
60  switch (oType){
61  case xtd::log::type::fatal:
62  return "fatal";
63  case xtd::log::type::error:
64  return "error";
65  case xtd::log::type::warning:
66  return "warning";
67  case xtd::log::type::info:
68  return "info";
69  case xtd::log::type::debug:
70  return "debug";
71  case xtd::log::type::enter:
72  return "enter";
73  case xtd::log::type::leave:
74  return "leave";
75  default:
76  break;
77  }
78  }
79 
80  private:
81 
82  class message{
83  public:
84  using pointer_type = std::shared_ptr<message>;
85  using deque_type = std::deque<pointer_type>;
86  using time_type = std::chrono::time_point<std::chrono::system_clock>;
87 
88  message(type msg_type, const xtd::source_location& location, xtd::string&& text)
89  : _tid(std::this_thread::get_id()), _type(msg_type), _location(location), _text(std::move(text)), _time(std::chrono::system_clock::now()){}
90  message(const message& src)
91  :_tid(src._tid), _type(src._type), _location(src._location), _text(src._text), _time(src._time){}
92  message& operator=(const message& src){
93  if (this == &src){
94  return *this;
95  }
96  _tid=src._tid;
97  _type = src._type;
98  _location = src._location;
99  _text = src._text;
100  _time= src._time;
101  return *this;
102  }
103 
104  std::thread::id _tid;
105  type _type;
106  source_location _location;
107  xtd::string _text;
108  time_type _time;
109  };
110 
111  class log_target{
112  public:
113  virtual ~log_target() = default;
114  using pointer_type = std::shared_ptr<log_target>;
115  using vector_type = std::vector<pointer_type>;
116  virtual void operator()(const message::pointer_type&) = 0;
117  };
118 
119 #if (XTD_LOG_TARGET_SYSLOG)
120  class syslog_target : public log_target{
121  public:
122 
123  syslog_target(){ openlog(nullptr, LOG_PID | LOG_NDELAY, 0); }
124  ~syslog_target() override { closelog(); }
125  void operator()(const message::pointer_type& oMessage) override {
126  int iFacility = LOG_MAKEPRI(LOG_USER, LOG_DEBUG);
127  switch (oMessage->_type){
128  case type::fatal:
129  {
130  iFacility = LOG_MAKEPRI(LOG_USER, LOG_CRIT);
131  break;
132  }
133  case type::error:
134  {
135  iFacility = LOG_MAKEPRI(LOG_USER, LOG_ERR);
136  break;
137  }
138  case type::warning:
139  {
140  iFacility = LOG_MAKEPRI(LOG_USER, LOG_WARNING);
141  break;
142  }
143  case type::info:
144  {
145  iFacility = LOG_MAKEPRI(LOG_USER, LOG_INFO);
146  break;
147  }
148  case type::debug:
149  case type::enter:
150  case type::leave:
151  {
152  iFacility = LOG_MAKEPRI(LOG_USER, LOG_DEBUG);
153  break;
154  }
155  }
156  syslog(iFacility, "%s", oMessage->_text.c_str());
157  }
158  };
159 #endif
160 
161 #if (XTD_LOG_TARGET_WINDBG)
162  class win_dbg_target : public log_target{
163  public:
164  ~win_dbg_target() override = default;
165  void operator()(const message::pointer_type& oMessage) override {
166  OutputDebugStringA(oMessage->_text.c_str());
167  }
168  };
169 #endif
170 
171 
172 #if (XTD_LOG_TARGET_COUT)
173  class std_cout_target : public log_target{
174  public:
175  ~std_cout_target() override = default;
176  void operator()(const message::pointer_type& oMessage) override{
177  std::cout << oMessage->_text << std::endl;
178  }
179  };
180 #endif
181 
182 #if (XTD_LOG_TARGET_CSV)
183  class csv_target : public log_target{
184  std::ofstream _LogFile;
185  std::mutex _FileLock;
186  public:
187 
188 
189 
190  void operator()(const message::pointer_type& oMsg) override{
191  static thread_local size_t _StackDepth = 1;
192  if (type::leave == oMsg->_type) --_StackDepth;
193  std::string sMsgPrefix(_StackDepth, ',');
194  std::hash<std::thread::id> oHash;
195  auto sMsg = xtd::string::format(oHash(oMsg->_tid), ",", oMsg->_time.time_since_epoch().count(), ",", type_string(oMsg->_type), ",", oMsg->_location.file(), ",", oMsg->_location.line(), sMsgPrefix, oMsg->_text);
196  if (type::enter == oMsg->_type) ++_StackDepth;
197  std::unique_lock<std::mutex> oLock(_FileLock);
198  _LogFile << sMsg << std::endl;
199  }
200 
201  csv_target() : _LogFile(), _FileLock(){
202  auto oLogPath = xtd::filesystem::home_directory_path();
203  oLogPath /= xtd::executable::this_executable().path().filename();
204  oLogPath /= xtd::string::format(intrinsic_cast(xtd::process::this_process().id()));
205  oLogPath.append(".csv");
206  _LogFile.open(oLogPath.string(), std::ios::out);
207  }
208  };
209 #endif
210 
211  void callback_thread(){
212  _CallbackThreadStarted.set_value();
213  while ( !_CallbackThreadExit ){
214  callback_type oCallback;
215  {
216  std::unique_lock<std::mutex> oLock(_CallbackLock);
217  _CallbackCheck.wait(oLock, [this]{
218  return _Callbacks.size();
219  });
220  oCallback = _Callbacks.front();
221  _Callbacks.pop_front();
222  oLock.unlock();
223  _CallbackCheck.notify_one();
224  }
225  if (oCallback) {
226  oCallback();
227  }
228  }
229  _CallbackThreadFinished.set_value();
230  }
231 
232  log() : _Messages(), _Callbacks(), _CallbackThread(), _CallbackLock(), _CallbackCheck(), _LogTargets(){
233 
234 #if (XTD_LOG_TARGET_SYSLOG)
235  _LogTargets.emplace_back(new syslog_target);
236 #endif
237 
238 #if (XTD_LOG_TARGET_WINDBG)
239  _LogTargets.emplace_back(new win_dbg_target);
240 #endif
241 
242 #if (XTD_LOG_TARGET_COUT)
243  _LogTargets.emplace_back(new std_cout_target);
244 #endif
245 
246 #if (XTD_LOG_TARGET_CSV)
247  _LogTargets.emplace_back(new csv_target);
248 #endif
249 
250  _CallbackThread = std::thread(&log::callback_thread, this);
251  _CallbackThreadStarted.get_future().get();
252  }
253 
254  ~log(){
255  {
256  std::lock_guard<std::mutex> oLock(_CallbackLock);
257  _Callbacks.push_back([this](){
258  _CallbackThreadExit = true;
259  });
260  _CallbackCheck.notify_one();
261  }
262  _CallbackThread.join();
263  _CallbackThreadFinished.get_future().get();
264  }
265 
266  using callback_type = std::function<void()>;
267  using callback_deque = std::deque<callback_type>;
268 
269  message::deque_type _Messages;
270  callback_deque _Callbacks;
271  std::thread _CallbackThread;
272  std::mutex _CallbackLock;
273  std::condition_variable _CallbackCheck;
274  log_target::vector_type _LogTargets;
275  std::promise<void> _CallbackThreadStarted;
276  std::promise<void> _CallbackThreadFinished;
277  bool _CallbackThreadExit = false;
278 
279  public:
280 
281  static log& get(){
282  static log _log;
283  return _log;
284  }
285 
286  template <typename ... _ArgTs>
287  inline void write(type mesageType, const source_location& location, _ArgTs&&...oArgs){
288  auto sMessage = xtd::string::format(std::forward<_ArgTs>(oArgs)...);
289  if ('\n' != sMessage.back()){
290  sMessage += '\n';
291  }
292  auto oMessage = std::make_shared<message>(mesageType, location, std::move(sMessage));
293  {
294  std::lock_guard<std::mutex> oLock(_CallbackLock);
295  _Callbacks.push_back([oMessage, this](){
296  for (auto oTarget : _LogTargets){
297  (*oTarget)(oMessage);
298  }
299  });
300  }
301  _CallbackCheck.notify_one();
302  }
303 
304  };
305 
306 
307 }
represents an executable binary on disk or memory
template meta-programming utilities
constexpr processor_intrinsic< _Ty >::type intrinsic_cast(_Ty src)
casts a pointer to the processor intrinsic storage type
Definition: meta.hpp:70
specializations of std::basic_string for advanced and common string handling
host, target and build configurations and settings Various components are purpose built for specific ...
maintains info about locations within source code
handle necessary filesystem and path functionality until C++17 is finalized
Definition: log.hpp:47
represents an in-memory process
static xstring format()
Type safe formatting Appends each item in the parameter list together performing type-safe verificati...
Definition: string.hpp:62
Contains information about the location of source code Used in error reporting and logging...