PageRenderTime 53ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/internal/Logger.cpp

https://github.com/amcjen/ncore
C++ | 345 lines | 241 code | 74 blank | 30 comment | 42 complexity | e8cfc8a3616225891abb1a456daead13 MD5 | raw file
  1. // STL includes
  2. #include <sstream>
  3. #include <iostream>
  4. #include <stdexcept>
  5. #include <iomanip>
  6. #include <algorithm>
  7. #include <iterator>
  8. #include <sstream>
  9. #include <numeric>
  10. // C includes
  11. #include <stdio.h>
  12. #include <stdarg.h>
  13. #include <regex.h>
  14. // Library includes
  15. #include <pthread.h>
  16. // Project includes
  17. #include <Dispatcher.h>
  18. #include <Clock.h>
  19. #include <Parser.h>
  20. #include <Logger.h>
  21. using namespace std;
  22. /****************************************************************************/
  23. extern "C" unsigned long millis(void);
  24. /****************************************************************************/
  25. //
  26. // Rate throttler
  27. //
  28. static const int lines_per_check = 10;
  29. void Logger::throttle_output_rate(void)
  30. {
  31. if ( clock && ! --lines_remaining )
  32. {
  33. lines_remaining += lines_per_check;
  34. unsigned long now = clock->millis();
  35. if ( now - last_check < rate_limit )
  36. {
  37. unsigned long wait = rate_limit - (now - last_check);
  38. ostringstream warning;
  39. warning << "delay " << wait << " (rate limit)";
  40. add_message(string("IN LOG "),warning.str());
  41. clock->delay(wait);
  42. }
  43. last_check = clock->millis();
  44. }
  45. }
  46. /****************************************************************************/
  47. struct count_contains
  48. {
  49. string key;
  50. count_contains(const string& _key): key(_key) {}
  51. int operator()(int sum,const string& whole)
  52. {
  53. return sum + ( (whole.find(key) != std::string::npos )?1:0 );
  54. }
  55. };
  56. /****************************************************************************/
  57. //
  58. // Public interface
  59. //
  60. Logger::Logger(void): clock(NULL), last_check(0), rate_limit(default_rate_limit), lines_remaining(lines_per_check), verbose(false)
  61. {
  62. pthread_mutex_init(&mutex,NULL);
  63. }
  64. /****************************************************************************/
  65. Logger::Logger(Clock& _clock): clock(&_clock), last_check(0), rate_limit(default_rate_limit), lines_remaining(lines_per_check), verbose(false)
  66. {
  67. pthread_mutex_init(&mutex,NULL);
  68. }
  69. /****************************************************************************/
  70. Logger::~Logger()
  71. {
  72. pthread_mutex_destroy(&mutex);
  73. }
  74. /****************************************************************************/
  75. void Logger::clear(void)
  76. {
  77. vector<string>::clear();
  78. last_check = 0;
  79. lines_remaining = lines_per_check;
  80. pthread_mutex_destroy(&mutex);
  81. pthread_mutex_init(&mutex,NULL);
  82. rate_limit = default_rate_limit;
  83. verbose = false;
  84. }
  85. /****************************************************************************/
  86. int Logger::lines_contain(const std::string& value) const
  87. {
  88. return accumulate(begin(),end(),0,count_contains(value));
  89. }
  90. /****************************************************************************/
  91. void Logger::internal(const std::string& module, const std::string& format,...)
  92. {
  93. pthread_mutex_lock( &mutex );
  94. va_list ap;
  95. va_start(ap,format);
  96. vsnprintf(buffer,sizeof(buffer),format.c_str(),ap);
  97. va_end(ap);
  98. string preamble = string("IN ") + module + " ";
  99. preamble.resize(7);
  100. add_buffer(preamble);
  101. pthread_mutex_unlock( &mutex );
  102. }
  103. /****************************************************************************/
  104. void Logger::sketch(const std::string& module, const std::string& format,...)
  105. {
  106. pthread_mutex_lock( &mutex );
  107. va_list ap;
  108. va_start(ap,format);
  109. vsnprintf(buffer,sizeof(buffer),format.c_str(),ap);
  110. va_end(ap);
  111. string preamble = string("SK ") + module + " ";
  112. preamble.resize(7);
  113. add_buffer(preamble);
  114. pthread_mutex_unlock( &mutex );
  115. throttle_output_rate();
  116. }
  117. /****************************************************************************/
  118. void Logger::sketch_v(const std::string& module, const std::string& format, va_list ap)
  119. {
  120. pthread_mutex_lock( &mutex );
  121. vsnprintf(buffer,sizeof(buffer),format.c_str(),ap);
  122. string preamble = string("SK ") + module + " ";
  123. preamble.resize(7);
  124. add_buffer(preamble);
  125. pthread_mutex_unlock( &mutex );
  126. throttle_output_rate();
  127. }
  128. /****************************************************************************/
  129. void Logger::add_message(const std::string& preamble,const std::string& message)
  130. {
  131. ostringstream ss;
  132. ss << "NCORE: ";
  133. if (clock)
  134. ss << setfill('0') << setw(6) << clock->millis() << " ";
  135. if (preamble.size())
  136. ss << preamble << " ";
  137. ss << message << endl;
  138. if ( verbose )
  139. cout << ">" << ss.str() ;
  140. push_back(ss.str());
  141. }
  142. /****************************************************************************/
  143. void Logger::add_buffer(const std::string& preamble)
  144. {
  145. add_message(preamble,string(buffer));
  146. }
  147. /****************************************************************************/
  148. void Logger::add(const std::string& format,...)
  149. {
  150. pthread_mutex_lock( &mutex );
  151. va_list ap;
  152. va_start(ap,format);
  153. vsnprintf(buffer,sizeof(buffer),format.c_str(),ap);
  154. va_end(ap);
  155. add_buffer(string());
  156. pthread_mutex_unlock( &mutex );
  157. throttle_output_rate();
  158. }
  159. /****************************************************************************/
  160. bool Logger::runCommand( const Parser& parser )
  161. {
  162. bool result = false;
  163. const string& command = parser.at(0);
  164. if ( command == "list" )
  165. {
  166. result = command_list(parser);
  167. }
  168. else if ( command == "log" )
  169. {
  170. result = command_log(parser);
  171. }
  172. else if ( command == "help" )
  173. {
  174. const string& command = parser.at(1);
  175. if ( command == "list" )
  176. {
  177. cout << "list -- list all log entries from beginning" << endl;
  178. cout << "list /regexp/ -- list all log entries that match the regexp" << endl;
  179. }
  180. else if ( command == "log" )
  181. {
  182. cout << "log rate <xxx> -- set minimim time elapsed between 10 log entries" << endl;
  183. }
  184. result = true;
  185. }
  186. return result;
  187. }
  188. /****************************************************************************/
  189. struct not_matches
  190. {
  191. regex_t *regex;
  192. not_matches(const string& _key)
  193. {
  194. regex = (regex_t*) malloc(sizeof(regex_t));
  195. if ( regcomp(regex, _key.c_str(), REG_EXTENDED) )
  196. throw new runtime_error( string("Error in regular expression ") + _key );
  197. }
  198. ~not_matches()
  199. {
  200. regfree(regex);
  201. }
  202. bool operator()(const string& test) const
  203. {
  204. regmatch_t match;
  205. return regexec(regex, test.c_str(), 1, &match, 0);
  206. }
  207. };
  208. /****************************************************************************/
  209. bool Logger::command_list(const vector<string>& _commands) const
  210. {
  211. bool result = false;
  212. if ( _commands.size() == 1 )
  213. {
  214. pthread_mutex_lock( const_cast<pthread_mutex_t*>(&mutex) );
  215. copy(begin(),end(),ostream_iterator<string>(cout,""));
  216. pthread_mutex_unlock( const_cast<pthread_mutex_t*>(&mutex) );
  217. result = true;
  218. }
  219. else if ( _commands.size() == 2 )
  220. {
  221. string operand = _commands.at(1);
  222. if (operand.at(0) == '/' && operand.at(operand.size()-1) == '/')
  223. {
  224. string pattern = operand.substr(1,operand.size()-2);
  225. pthread_mutex_lock( const_cast<pthread_mutex_t*>(&mutex) );
  226. remove_copy_if(begin(),end(),ostream_iterator<string>(cout,""),not_matches(pattern));
  227. pthread_mutex_unlock( const_cast<pthread_mutex_t*>(&mutex) );
  228. result = true;
  229. }
  230. else
  231. throw new runtime_error("Expected regex as operand");
  232. }
  233. else
  234. throw new runtime_error("No parameters expected");
  235. return result;
  236. }
  237. /****************************************************************************/
  238. bool Logger::command_log(const vector<string>& _commands)
  239. {
  240. vector<string>::const_iterator current = _commands.begin() + 1;
  241. if ( current == _commands.end() )
  242. throw new runtime_error("Expecting operand");
  243. const string& operand = *current++;
  244. if ( operand == "rate" )
  245. {
  246. if ( current == _commands.end() )
  247. throw new runtime_error("Expecting rate value");
  248. istringstream ss(*current++);
  249. unsigned long rate;
  250. ss >> rate;
  251. internal("LOG","set rate limit to %ims",rate);
  252. pthread_mutex_lock( const_cast<pthread_mutex_t*>(&mutex) );
  253. rate_limit = rate;
  254. pthread_mutex_unlock( const_cast<pthread_mutex_t*>(&mutex) );
  255. }
  256. else if ( operand == "verbose" )
  257. {
  258. setVerbose(true);
  259. internal("LOG","set to verbose");
  260. }
  261. else if ( operand == "quiet" )
  262. {
  263. setVerbose(false);
  264. internal("LOG","set to quiet");
  265. }
  266. else
  267. throw new runtime_error("Unknown operand");
  268. return true;
  269. }
  270. /****************************************************************************/
  271. // vim:cin:ai:sts=2 sw=2 ft=cpp