LogOutputStream.cpp
Go to the documentation of this file.
1 // this is for emacs file handling -*- mode: c++; indent-tabs-mode: nil -*-
2 
3 // -- BEGIN LICENSE BLOCK ----------------------------------------------
4 // This file is part of FZIs ic_workspace.
5 //
6 // This program is free software licensed under the LGPL
7 // (GNU LESSER GENERAL PUBLIC LICENSE Version 3).
8 // You can find a copy of this license in LICENSE folder in the top
9 // directory of the source code.
10 //
11 // © Copyright 2016 FZI Forschungszentrum Informatik, Karlsruhe, Germany
12 //
13 // -- END LICENSE BLOCK ------------------------------------------------
14 
15 //----------------------------------------------------------------------
23 //----------------------------------------------------------------------
24 #include "LogOutputStream.h"
25 
26 #include <assert.h>
27 #include <cctype>
28 #include <cstring>
29 #include <iostream>
30 
32 #include <icl_core/os.h>
33 #include <icl_core/os_lxrt.h>
34 #include <icl_core/os_string.h>
35 #include <icl_core_config/Config.h>
36 
37 #include "LoggingManager.h"
38 #include "ThreadStream.h"
39 
40 namespace icl_core {
41 namespace logging {
42 
43 const icl_core::String LogOutputStream::m_default_log_format = "<~T.~3M> ~S(~L)~ C~(O~::D: ~E";
45 
47  const icl_core::String& config_prefix,
49  bool use_worker_thread)
50  : m_name(name),
51  m_log_level(log_level),
52  m_time_format("%Y-%m-%d %H:%M:%S"),
53  m_use_worker_thread(use_worker_thread),
54  m_no_worker_thread_push_mutex(1),
55  m_format_mutex(1)
56 {
58 
60  icl_core::config::get<icl_core::String>(config_prefix + "/Format", log_format);
61  changeLogFormat(log_format.c_str());
62 
64  {
66  icl_core::config::get<icl_core::ThreadPriority>(config_prefix + "/ThreadPriority", priority);
67 
68 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
69  size_t message_queue_size = cDEFAULT_FIXED_OUTPUT_STREAM_QUEUE_SIZE;
70  icl_core::config::get<size_t>(config_prefix + "/MesageQueueSize", message_queue_size);
71 
72  m_worker_thread = new WorkerThread(this, message_queue_size, priority);
73 #else
74  m_worker_thread = new WorkerThread(this, priority);
75 #endif
76  }
77  else
78  {
79  m_worker_thread = NULL;
80  }
81 }
82 
85  bool use_worker_thread)
86  : m_name(name),
87  m_log_level(log_level),
88  m_time_format("%Y-%m-%d %H:%M:%S"),
89  m_use_worker_thread(use_worker_thread),
92 {
96  {
97 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
100 #else
102 #endif
103  }
104  else
105  {
106  m_worker_thread = NULL;
107  }
108 }
109 
111 {
113  {
114  if (m_worker_thread->running())
115  {
116  std::cerr << "WARNING: Destroyed LogOutputStream while thread is still alive. "
117  << "Please call Shutdown() before destruction." << std::endl;
118  }
119 
120  delete m_worker_thread;
121  m_worker_thread = NULL;
122  }
123 }
124 
125 void LogOutputStream::changeLogFormat(const char *format)
126 {
127  // Stop processing at the end of the format string.
128  if (format[0] != 0)
129  {
130  parseLogFormat(format);
131 
132  if (m_format_mutex.wait())
133  {
134  m_log_format.clear();
136  m_new_log_format.clear();
137 
139  }
140  }
141 }
142 
144  const char* log_stream_description, const char *filename,
145  int line, const char *classname, const char *objectname,
146  const char *function, const char *text)
147 {
148  if (log_level >= getLogLevel())
149  {
150  LogMessage new_entry(icl_core::TimeStamp::now(), log_level, log_stream_description,
151  filename, line, classname, objectname, function, text);
152 
154  {
155  // Hand the log text over to the output implementation.
156  if (m_worker_thread->m_mutex->wait())
157  {
158 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
159  if (!m_worker_thread->isMessageQueueFull())
160  {
161  m_worker_thread->m_message_queue[m_worker_thread->m_message_queue_write_index] = new_entry;
162  m_worker_thread->incrementIndex(m_worker_thread->m_message_queue_write_index);
163  }
164 #else
165  m_worker_thread->m_message_queue.push(new_entry);
166 #endif
169  }
170  }
171  else
172  {
174  {
175  pushImpl(new_entry);
177  }
178  }
179  }
180 }
181 
182 void LogOutputStream::pushImpl(const LogMessage& log_message)
183 {
184  if (m_format_mutex.wait())
185  {
186  std::stringstream msg;
188  it != m_log_format.end(); ++it)
189  {
190  switch (it->type)
191  {
193  {
194  msg << it->text;
195  break;
196  }
198  {
199  if (std::strcmp(log_message.class_name, "") != 0)
200  {
201  msg << it->text << log_message.class_name;
202  }
203  break;
204  }
206  {
207  if (std::strcmp(log_message.object_name, "") != 0)
208  {
209  msg << it->text << log_message.object_name << it->suffix;
210  }
211  break;
212  }
214  {
215  if (std::strcmp(log_message.function_name, "") != 0)
216  {
217  msg << it->text << log_message.function_name;
218  }
219  break;
220  }
222  {
223  msg << log_message.message_text;
224  break;
225  }
227  {
228  msg << log_message.filename;
229  break;
230  }
232  {
233  msg << log_message.line;
234  break;
235  }
237  {
238  msg << logLevelDescription(log_message.log_level);
239  break;
240  }
242  {
243  msg << log_message.log_stream;
244  break;
245  }
247  {
248  char time_buffer[100];
249  memset(time_buffer, 0, 100);
250 
251 #ifdef _SYSTEM_LXRT_
252  // Don't use strfLocaltime() in a hard realtime LXRT task, because
253  // it might use a POSIX mutex!
255  {
256  icl_core::os::snprintf(time_buffer, 99, "%d %02d:%02d:%02d(HRT)",
257  int(log_message.timestamp.days()),
258  int(log_message.timestamp.hours()),
259  int(log_message.timestamp.minutes()),
260  int(log_message.timestamp.seconds()));
261  }
262  else
263 #endif
264  {
265  log_message.timestamp.strfLocaltime(time_buffer, 100, m_time_format);
266  }
267 
268  msg << time_buffer;
269  break;
270  }
272  {
273  int32_t msec = log_message.timestamp.tsNSec() / 1000000;
274  size_t msec_len = 1;
275  if (msec >= 10)
276  {
277  msec_len = 2;
278  }
279  if (msec >= 100)
280  {
281  msec_len = 3;
282  }
283  for (size_t i = it->width; i > msec_len; --i)
284  {
285  msg << "0";
286  }
287  msg << msec;
288  break;
289  }
290  }
291  }
293 
294  pushImpl(msg.str());
295  }
296 }
297 
299 {
300  std::cerr << "LOG OUTPUT STREAM ERROR: pushImpl() is not implemented!!!" << std::endl;
301 }
302 
304 {
305  std::cerr << " " << name() << " : " << logLevelDescription(m_log_level) << std::endl;
306 }
307 
308 
310 #ifdef _IC_BUILDER_DEPRECATED_STYLE_
311 
315 void LogOutputStream::ChangeTimeFormat(const char* format)
316 {
317  changeTimeFormat(format);
318 }
319 
323 void LogOutputStream::ChangeLogFormat(const char *format)
324 {
325  changeLogFormat(format);
326 }
327 
331 void LogOutputStream::Push(icl_core::logging::LogLevel log_level,
332  const char *log_stream_description, const char *filename,
333  int line, const char *classname, const char *objectname,
334  const char *function, const char *text)
335 {
336  push(log_level, log_stream_description, filename, line, classname, objectname, function, text);
337 }
338 
342 void LogOutputStream::Start()
343 {
344  start();
345 }
346 
351 void LogOutputStream::Shutdown()
352 {
353  shutdown();
354 }
355 
361 {
362  return getLogLevel();
363 }
364 
368 void LogOutputStream::SetLogLevel(icl_core::logging::LogLevel log_level)
369 {
370  setLogLevel(log_level);
371 }
372 
376 icl_core::String LogOutputStream::Name() const
377 {
378  return name();
379 }
380 
385 void LogOutputStream::PrintConfiguration() const
386 {
388 }
389 
390 #endif
391 
393 void LogOutputStream::parseLogFormat(const char *format)
394 {
395  LogFormatEntry new_entry;
396 
397  // The format string starts with a field specifier.
398  if (format[0] == '~')
399  {
400  ++format;
401 
402  // Read the field width.
403  while (format[0] != 0 && std::isdigit(format[0]))
404  {
405  new_entry.width = 10 * new_entry.width + (format[0] - '0');
406  ++format;
407  }
408 
409  // Read optional prefix text.
410  char *prefix_ptr = new_entry.text;
411  while (format[0] != 0 && format[0] != 'C' && format[0] != 'O' && format[0] != 'D'
412  && format[0] != 'E' && format[0] != 'F' && format[0] != 'G' && format[0] != 'L'
413  && format[0] != 'S' && format[0] != 'T' && format[0] != 'M')
414  {
415  *prefix_ptr = format[0];
416  ++prefix_ptr;
417  ++format;
418  }
419 
420  // Read the field type.
421  if (format[0] == 'C')
422  {
424  }
425  else if (format[0] == 'O')
426  {
428  if (new_entry.text[0] == '(')
429  {
430  std::strncpy(new_entry.suffix, ")", 100);
431  }
432  else if (new_entry.text[0] == '[')
433  {
434  std::strncpy(new_entry.suffix, "]", 100);
435  }
436  else if (new_entry.text[0] == '{')
437  {
438  std::strncpy(new_entry.suffix, "}", 100);
439  }
440  }
441  else if (format[0] == 'D')
442  {
443  new_entry.type = LogFormatEntry::eT_FUNCTION;
444  }
445  else if (format[0] == 'E')
446  {
447  new_entry.type = LogFormatEntry::eT_MESSAGE;
448  }
449  else if (format[0] == 'F')
450  {
451  new_entry.type = LogFormatEntry::eT_FILENAME;
452  }
453  else if (format[0] == 'G')
454  {
455  new_entry.type = LogFormatEntry::eT_LINE;
456  }
457  else if (format[0] == 'L')
458  {
459  new_entry.type = LogFormatEntry::eT_LEVEL;
460  }
461  else if (format[0] == 'S')
462  {
463  new_entry.type = LogFormatEntry::eT_STREAM;
464  }
465  else if (format[0] == 'T')
466  {
468  }
469  else if (format[0] == 'M')
470  {
472  }
473 
474  if (format[0] != 0)
475  {
476  m_new_log_format.push_back(new_entry);
477  }
478 
479  ++format;
480  }
481  else
482  {
483  char *text_ptr = new_entry.text;
484  while (format[0] != '~' && format[0] != 0)
485  {
486  *text_ptr = format[0];
487  ++text_ptr;
488  ++format;
489  }
490 
491  if (new_entry.text[0] != 0)
492  {
493  m_new_log_format.push_back(new_entry);
494  }
495  }
496 
497  // Stop processing at the end of the format string.
498  if (format[0] == 0)
499  {
500  return;
501  }
502  else
503  {
504  parseLogFormat(format);
505  }
506 }
507 
509 {
511  {
513  }
514 }
515 
517 {
519  {
520  if (m_worker_thread->running())
521  {
525  }
526  }
527 }
528 
529 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
530 LogOutputStream::WorkerThread::WorkerThread(LogOutputStream *output_stream, size_t message_queue_size,
531  icl_core::ThreadPriority priority)
532  : Thread(priority),
533  m_output_stream(output_stream),
534  m_message_queue_size(message_queue_size),
535  m_message_queue_write_index(0),
536  m_message_queue_read_index(0)
537 {
538  m_message_queue = new LogMessage[message_queue_size+1];
539 
540  m_mutex = new Semaphore(1);
541  m_fill_count = new Semaphore(0);
542 }
543 #else
545  : Thread(priority),
546  m_output_stream(output_stream)
547 {
548  m_mutex = new Semaphore(1);
549  m_fill_count = new Semaphore(0);
550 }
551 #endif
552 
554 {
555  delete m_mutex;
556  delete m_fill_count;
557 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
558  delete[] m_message_queue;
559 #endif
560 }
561 
563 {
565 
566  // Wait for new messages to arrive.
567  while (execute())
568  {
569  if (m_fill_count->wait())
570  {
571 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
572  if (!isMessageQueueEmpty())
573 #else
574  if (!m_message_queue.empty())
575 #endif
576  {
577  if (m_mutex->wait())
578  {
579  LogMessage log_message;
580  bool push = false;
581 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
582  if (!isMessageQueueEmpty())
583  {
584  log_message = m_message_queue[m_message_queue_read_index];
585  incrementIndex(m_message_queue_read_index);
586  push = true;
587  }
588 #else
589  if (!m_message_queue.empty())
590  {
591  log_message = m_message_queue.front();
592  m_message_queue.pop();
593  push = true;
594  }
595 #endif
596 
597  m_mutex->post();
598 
599  if (push)
600  {
601  m_output_stream->pushImpl(log_message);
602  }
603  }
604  }
605  }
606  else if (execute())
607  {
608  PRINTF("LogOutputStream(%s)::run: semaphore wait failed\n", m_output_stream->m_name.c_str());
609  icl_core::os::usleep(10000);
610  }
611  }
612 
613  // Write out all remaining log messages.
614  if (m_mutex->wait())
615  {
616 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
617  while (!isMessageQueueEmpty())
618  {
619  LogMessage log_message = m_message_queue[m_message_queue_read_index];
620  incrementIndex(m_message_queue_read_index);
621  m_output_stream->pushImpl(log_message);
622  }
623 #else
624  while (!m_message_queue.empty())
625  {
626  LogMessage log_message = m_message_queue.front();
627  m_message_queue.pop();
628  m_output_stream->pushImpl(log_message);
629  }
630 #endif
631 
632  m_mutex->post();
633  }
634 
636 }
637 
638 
639 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
640 void LogOutputStream::WorkerThread::incrementIndex(size_t& index)
641 {
642  ++index;
643  if (index >= m_message_queue_size)
644  {
645  index = 0;
646  }
647 }
648 
649 bool LogOutputStream::WorkerThread::isMessageQueueEmpty()
650 {
651  return m_message_queue_read_index == m_message_queue_write_index;
652 }
653 
654 bool LogOutputStream::WorkerThread::isMessageQueueFull()
655 {
656  return ((m_message_queue_write_index == m_message_queue_read_index - 1)
657  || (m_message_queue_write_index == m_message_queue_size - 1
658  && m_message_queue_read_index == 0));
659 }
660 #endif
661 
663  icl_core::logging::LogLevel log_level,
664  const char *log_stream, const char *filename,
665  size_t line, const char *class_name,
666  const char *object_name, const char *function_name,
667  const char *message_text)
668  : timestamp(timestamp),
669  log_level(log_level),
670  line(line)
671 {
672  std::strncpy(LogMessage::log_stream, log_stream, cMAX_IDENTIFIER_LENGTH+1);
673  std::strncpy(LogMessage::filename, filename, cMAX_DESCRIPTION_LENGTH+1);
674  std::strncpy(LogMessage::class_name, class_name, cMAX_IDENTIFIER_LENGTH+1);
675  std::strncpy(LogMessage::object_name, object_name, cMAX_DESCRIPTION_LENGTH+1);
676  std::strncpy(LogMessage::function_name, function_name, cMAX_IDENTIFIER_LENGTH+1);
677  std::strncpy(LogMessage::message_text, message_text, cDEFAULT_LOG_SIZE+1);
678 }
679 
680 }
681 }
void post()
Increments the semaphore.
Definition: Semaphore.cpp:80
signed int int32_t
Definition: msvc_stdint.h:90
Represents absolute times.
Definition: TimeStamp.h:61
const size_t cMAX_IDENTIFIER_LENGTH
Definition: Constants.h:42
const size_t cMAX_DESCRIPTION_LENGTH
Definition: Constants.h:47
static TimeStamp now()
Definition: TimeStamp.cpp:111
Contains icl_logging::ThreadStream.
Contains icl_logging::LoggingManager.
int64_t minutes() const
Definition: TimeBase.h:123
Contains global functions for string manipulation, encapsulated into the icl_core::os namespace...
int64_t days() const
Use this function if you want to express the time in days.
Definition: TimeBase.h:107
char filename[cMAX_DESCRIPTION_LENGTH+1]
void changeTimeFormat(const char *format)
void setLogLevel(icl_core::logging::LogLevel log_level)
void strfLocaltime(char *dest, size_t max_len, const char *format) const
Definition: TimeStamp.cpp:211
static LoggingManager & instance()
void start()
Starts the worker thread of the log output stream.
#define cDEFAULT_FIXED_OUTPUT_STREAM_QUEUE_SIZE
Definition: Constants.h:60
Base header file for the configuration framework.
uint32_t tsNSec() const
Definition: TimeStamp.h:283
icl_core::logging::LogLevel getLogLevel() const
void push(icl_core::logging::LogLevel log_level, const char *log_stream_description, const char *filename, int line, const char *classname, const char *objectname, const char *function, const char *text)
#define ICL_CORE_VC_DEPRECATE_STYLE_USE(arg)
Definition: Deprecate.h:66
icl_core::List< LogFormatEntry > m_new_log_format
Contains a system independet PRINTF macro.
int usleep(unsigned long useconds)
Definition: os_time.h:56
icl_core::List< LogFormatEntry > m_log_format
icl_core::logging::LogLevel m_log_level
LogMessage(const icl_core::TimeStamp &timestamp=icl_core::TimeStamp(), icl_core::logging::LogLevel log_level=eLL_MUTE, const char *log_stream="", const char *filename="", size_t line=0, const char *class_name="", const char *object_name="", const char *function_name="", const char *message_text="")
ThreadStream & endl(ThreadStream &stream)
Definition: ThreadStream.h:249
Contains global functions, encapsulated into the icl_core::os namespace.
char object_name[cMAX_DESCRIPTION_LENGTH+1]
LogOutputStream(const icl_core::String &name, const icl_core::String &config_prefix, icl_core::logging::LogLevel log_level, bool use_worker_thread=true)
bool isThisHRT()
Definition: os_lxrt.cpp:159
Implements a platform independent mutex.
Definition: Semaphore.h:44
Contains global LXRT functions.
This is an output stream class for log messages.
std::string String
Definition: BaseTypes.h:43
virtual void pushImpl(const LogMessage &log_message)
bool isThisLxrtTask()
Definition: os_lxrt.cpp:150
char class_name[cMAX_IDENTIFIER_LENGTH+1]
WorkerThread(LogOutputStream *output_stream, icl_core::ThreadPriority priority)
void changeLogFormat(const char *format)
void * memset(void *dest, int c, size_t count)
Definition: os_mem.h:46
#define PRINTF
static const icl_core::ThreadPriority m_default_worker_thread_priority
char function_name[cMAX_IDENTIFIER_LENGTH+1]
char log_stream[cMAX_IDENTIFIER_LENGTH+1]
int64_t hours() const
Definition: TimeBase.h:115
const char * logLevelDescription(LogLevel log_level)
Definition: LogLevel.cpp:41
Defines an entry for the message queue.
int32_t ThreadPriority
Definition: os_thread.h:50
#define cDEFAULT_LOG_SIZE
Definition: Constants.h:36
void parseLogFormat(const char *format)
static const icl_core::String m_default_log_format
Contains icl_logging::LogOutputStream.
int64_t seconds() const
Definition: TimeBase.h:131
int snprintf(char *buffer, size_t maxlen, const char *format,...)
Definition: os_string.cpp:28
icl_core::String name() const


fzi_icl_core
Author(s):
autogenerated on Mon Jun 10 2019 13:17:58