00001
00002
00010
00011 #include "LogOutputStream.h"
00012
00013 #include <assert.h>
00014 #include <cctype>
00015 #include <cstring>
00016 #include <iostream>
00017
00018 #include <icl_core/internal_raw_debug.h>
00019 #include <icl_core/os.h>
00020 #include <icl_core/os_lxrt.h>
00021 #include <icl_core/os_string.h>
00022 #include <icl_core_config/Config.h>
00023
00024 #include "LoggingManager.h"
00025 #include "ThreadStream.h"
00026
00027 namespace icl_core {
00028 namespace logging {
00029
00030 const icl_core::String LogOutputStream::m_default_log_format = "<~T.~3M> ~S(~L)~ C~(O~::D: ~E";
00031 const icl_core::ThreadPriority LogOutputStream::m_default_worker_thread_priority = 5;
00032
00033 LogOutputStream::LogOutputStream(const icl_core::String& name,
00034 const icl_core::String& config_prefix,
00035 icl_core::logging::LogLevel log_level,
00036 bool use_worker_thread)
00037 : m_name(name),
00038 m_log_level(log_level),
00039 m_time_format("%Y-%m-%d %H:%M:%S"),
00040 m_use_worker_thread(use_worker_thread),
00041 m_no_worker_thread_push_mutex(1),
00042 m_format_mutex(1)
00043 {
00044 LoggingManager::instance().assertInitialized();
00045
00046 icl_core::String log_format = m_default_log_format;
00047 icl_core::config::get<icl_core::String>(config_prefix + "/Format", log_format);
00048 changeLogFormat(log_format.c_str());
00049
00050 if (m_use_worker_thread)
00051 {
00052 icl_core::ThreadPriority priority = m_default_worker_thread_priority;
00053 icl_core::config::get<icl_core::ThreadPriority>(config_prefix + "/ThreadPriority", priority);
00054
00055 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
00056 size_t message_queue_size = cDEFAULT_FIXED_OUTPUT_STREAM_QUEUE_SIZE;
00057 icl_core::config::get<size_t>(config_prefix + "/MesageQueueSize", message_queue_size);
00058
00059 m_worker_thread = new WorkerThread(this, message_queue_size, priority);
00060 #else
00061 m_worker_thread = new WorkerThread(this, priority);
00062 #endif
00063 }
00064 else
00065 {
00066 m_worker_thread = NULL;
00067 }
00068 }
00069
00070 LogOutputStream::LogOutputStream(const icl_core::String& name,
00071 icl_core::logging::LogLevel log_level,
00072 bool use_worker_thread)
00073 : m_name(name),
00074 m_log_level(log_level),
00075 m_time_format("%Y-%m-%d %H:%M:%S"),
00076 m_use_worker_thread(use_worker_thread),
00077 m_no_worker_thread_push_mutex(1),
00078 m_format_mutex(1)
00079 {
00080 LoggingManager::instance().assertInitialized();
00081 changeLogFormat(m_default_log_format.c_str());
00082 if (m_use_worker_thread)
00083 {
00084 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
00085 m_worker_thread = new WorkerThread(this, cDEFAULT_FIXED_OUTPUT_STREAM_QUEUE_SIZE,
00086 m_default_worker_thread_priority);
00087 #else
00088 m_worker_thread = new WorkerThread(this, m_default_worker_thread_priority);
00089 #endif
00090 }
00091 else
00092 {
00093 m_worker_thread = NULL;
00094 }
00095 }
00096
00097 LogOutputStream::~LogOutputStream()
00098 {
00099 if (m_use_worker_thread)
00100 {
00101 if (m_worker_thread->running())
00102 {
00103 std::cerr << "WARNING: Destroyed LogOutputStream while thread is still alive. "
00104 << "Please call Shutdown() before destruction." << std::endl;
00105 }
00106
00107 delete m_worker_thread;
00108 m_worker_thread = NULL;
00109 }
00110 }
00111
00112 void LogOutputStream::changeLogFormat(const char *format)
00113 {
00114
00115 if (format[0] != 0)
00116 {
00117 parseLogFormat(format);
00118
00119 if (m_format_mutex.wait())
00120 {
00121 m_log_format.clear();
00122 m_log_format = m_new_log_format;
00123 m_new_log_format.clear();
00124
00125 m_format_mutex.post();
00126 }
00127 }
00128 }
00129
00130 void LogOutputStream::push(icl_core::logging::LogLevel log_level,
00131 const char* log_stream_description, const char *filename,
00132 int line, const char *classname, const char *objectname,
00133 const char *function, const char *text)
00134 {
00135 if (log_level >= getLogLevel())
00136 {
00137 LogMessage new_entry(icl_core::TimeStamp::now(), log_level, log_stream_description,
00138 filename, line, classname, objectname, function, text);
00139
00140 if (m_use_worker_thread)
00141 {
00142
00143 if (m_worker_thread->m_mutex->wait())
00144 {
00145 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
00146 if (!m_worker_thread->isMessageQueueFull())
00147 {
00148 m_worker_thread->m_message_queue[m_worker_thread->m_message_queue_write_index] = new_entry;
00149 m_worker_thread->incrementIndex(m_worker_thread->m_message_queue_write_index);
00150 }
00151 #else
00152 m_worker_thread->m_message_queue.push(new_entry);
00153 #endif
00154 m_worker_thread->m_mutex->post();
00155 m_worker_thread->m_fill_count->post();
00156 }
00157 }
00158 else
00159 {
00160 if (m_no_worker_thread_push_mutex.wait())
00161 {
00162 pushImpl(new_entry);
00163 m_no_worker_thread_push_mutex.post();
00164 }
00165 }
00166 }
00167 }
00168
00169 void LogOutputStream::pushImpl(const LogMessage& log_message)
00170 {
00171 if (m_format_mutex.wait())
00172 {
00173 std::stringstream msg;
00174 for (icl_core::List<LogFormatEntry>::const_iterator it = m_log_format.begin();
00175 it != m_log_format.end(); ++it)
00176 {
00177 switch (it->type)
00178 {
00179 case LogFormatEntry::eT_TEXT:
00180 {
00181 msg << it->text;
00182 break;
00183 }
00184 case LogFormatEntry::eT_CLASSNAME:
00185 {
00186 if (std::strcmp(log_message.class_name, "") != 0)
00187 {
00188 msg << it->text << log_message.class_name;
00189 }
00190 break;
00191 }
00192 case LogFormatEntry::eT_OBJECTNAME:
00193 {
00194 if (std::strcmp(log_message.object_name, "") != 0)
00195 {
00196 msg << it->text << log_message.object_name << it->suffix;
00197 }
00198 break;
00199 }
00200 case LogFormatEntry::eT_FUNCTION:
00201 {
00202 if (std::strcmp(log_message.function_name, "") != 0)
00203 {
00204 msg << it->text << log_message.function_name;
00205 }
00206 break;
00207 }
00208 case LogFormatEntry::eT_MESSAGE:
00209 {
00210 msg << log_message.message_text;
00211 break;
00212 }
00213 case LogFormatEntry::eT_FILENAME:
00214 {
00215 msg << log_message.filename;
00216 break;
00217 }
00218 case LogFormatEntry::eT_LINE:
00219 {
00220 msg << log_message.line;
00221 break;
00222 }
00223 case LogFormatEntry::eT_LEVEL:
00224 {
00225 msg << logLevelDescription(log_message.log_level);
00226 break;
00227 }
00228 case LogFormatEntry::eT_STREAM:
00229 {
00230 msg << log_message.log_stream;
00231 break;
00232 }
00233 case LogFormatEntry::eT_TIMESTAMP:
00234 {
00235 char time_buffer[100];
00236 memset(time_buffer, 0, 100);
00237
00238 #ifdef _SYSTEM_LXRT_
00239
00240
00241 if (icl_core::os::isThisLxrtTask() && icl_core::os::isThisHRT())
00242 {
00243 icl_core::os::snprintf(time_buffer, 99, "%d %02d:%02d:%02d(HRT)",
00244 int(log_message.timestamp.days()),
00245 int(log_message.timestamp.hours()),
00246 int(log_message.timestamp.minutes()),
00247 int(log_message.timestamp.seconds()));
00248 }
00249 else
00250 #endif
00251 {
00252 log_message.timestamp.strfLocaltime(time_buffer, 100, m_time_format);
00253 }
00254
00255 msg << time_buffer;
00256 break;
00257 }
00258 case LogFormatEntry::eT_TIMESTAMP_MS:
00259 {
00260 int32_t msec = log_message.timestamp.tsNSec() / 1000000;
00261 size_t msec_len = 1;
00262 if (msec >= 10)
00263 {
00264 msec_len = 2;
00265 }
00266 if (msec >= 100)
00267 {
00268 msec_len = 3;
00269 }
00270 for (size_t i = it->width; i > msec_len; --i)
00271 {
00272 msg << "0";
00273 }
00274 msg << msec;
00275 break;
00276 }
00277 }
00278 }
00279 m_format_mutex.post();
00280
00281 pushImpl(msg.str());
00282 }
00283 }
00284
00285 void LogOutputStream::pushImpl(const icl_core::String&)
00286 {
00287 std::cerr << "LOG OUTPUT STREAM ERROR: pushImpl() is not implemented!!!" << std::endl;
00288 }
00289
00290 void LogOutputStream::printConfiguration() const
00291 {
00292 std::cerr << " " << name() << " : " << logLevelDescription(m_log_level) << std::endl;
00293 }
00294
00295
00297 #ifdef _IC_BUILDER_DEPRECATED_STYLE_
00298
00302 void LogOutputStream::ChangeTimeFormat(const char* format)
00303 {
00304 changeTimeFormat(format);
00305 }
00306
00310 void LogOutputStream::ChangeLogFormat(const char *format)
00311 {
00312 changeLogFormat(format);
00313 }
00314
00318 void LogOutputStream::Push(icl_core::logging::LogLevel log_level,
00319 const char *log_stream_description, const char *filename,
00320 int line, const char *classname, const char *objectname,
00321 const char *function, const char *text)
00322 {
00323 push(log_level, log_stream_description, filename, line, classname, objectname, function, text);
00324 }
00325
00329 void LogOutputStream::Start()
00330 {
00331 start();
00332 }
00333
00338 void LogOutputStream::Shutdown()
00339 {
00340 shutdown();
00341 }
00342
00346 ICL_CORE_VC_DEPRECATE_STYLE_USE(LogOutputStream::getLogLevel)
00347 icl_core::logging::LogLevel LogOutputStream::LogLevel() const
00348 {
00349 return getLogLevel();
00350 }
00351
00355 void LogOutputStream::SetLogLevel(icl_core::logging::LogLevel log_level)
00356 {
00357 setLogLevel(log_level);
00358 }
00359
00363 icl_core::String LogOutputStream::Name() const
00364 {
00365 return name();
00366 }
00367
00372 void LogOutputStream::PrintConfiguration() const
00373 {
00374 printConfiguration();
00375 }
00376
00377 #endif
00378
00379
00380 void LogOutputStream::parseLogFormat(const char *format)
00381 {
00382 LogFormatEntry new_entry;
00383
00384
00385 if (format[0] == '~')
00386 {
00387 ++format;
00388
00389
00390 while (format[0] != 0 && std::isdigit(format[0]))
00391 {
00392 new_entry.width = 10 * new_entry.width + (format[0] - '0');
00393 ++format;
00394 }
00395
00396
00397 char *prefix_ptr = new_entry.text;
00398 while (format[0] != 0 && format[0] != 'C' && format[0] != 'O' && format[0] != 'D'
00399 && format[0] != 'E' && format[0] != 'F' && format[0] != 'G' && format[0] != 'L'
00400 && format[0] != 'S' && format[0] != 'T' && format[0] != 'M')
00401 {
00402 *prefix_ptr = format[0];
00403 ++prefix_ptr;
00404 ++format;
00405 }
00406
00407
00408 if (format[0] == 'C')
00409 {
00410 new_entry.type = LogFormatEntry::eT_CLASSNAME;
00411 }
00412 else if (format[0] == 'O')
00413 {
00414 new_entry.type = LogFormatEntry::eT_OBJECTNAME;
00415 if (new_entry.text[0] == '(')
00416 {
00417 std::strncpy(new_entry.suffix, ")", 100);
00418 }
00419 else if (new_entry.text[0] == '[')
00420 {
00421 std::strncpy(new_entry.suffix, "]", 100);
00422 }
00423 else if (new_entry.text[0] == '{')
00424 {
00425 std::strncpy(new_entry.suffix, "}", 100);
00426 }
00427 }
00428 else if (format[0] == 'D')
00429 {
00430 new_entry.type = LogFormatEntry::eT_FUNCTION;
00431 }
00432 else if (format[0] == 'E')
00433 {
00434 new_entry.type = LogFormatEntry::eT_MESSAGE;
00435 }
00436 else if (format[0] == 'F')
00437 {
00438 new_entry.type = LogFormatEntry::eT_FILENAME;
00439 }
00440 else if (format[0] == 'G')
00441 {
00442 new_entry.type = LogFormatEntry::eT_LINE;
00443 }
00444 else if (format[0] == 'L')
00445 {
00446 new_entry.type = LogFormatEntry::eT_LEVEL;
00447 }
00448 else if (format[0] == 'S')
00449 {
00450 new_entry.type = LogFormatEntry::eT_STREAM;
00451 }
00452 else if (format[0] == 'T')
00453 {
00454 new_entry.type = LogFormatEntry::eT_TIMESTAMP;
00455 }
00456 else if (format[0] == 'M')
00457 {
00458 new_entry.type = LogFormatEntry::eT_TIMESTAMP_MS;
00459 }
00460
00461 if (format[0] != 0)
00462 {
00463 m_new_log_format.push_back(new_entry);
00464 }
00465
00466 ++format;
00467 }
00468 else
00469 {
00470 char *text_ptr = new_entry.text;
00471 while (format[0] != '~' && format[0] != 0)
00472 {
00473 *text_ptr = format[0];
00474 ++text_ptr;
00475 ++format;
00476 }
00477
00478 if (new_entry.text[0] != 0)
00479 {
00480 m_new_log_format.push_back(new_entry);
00481 }
00482 }
00483
00484
00485 if (format[0] == 0)
00486 {
00487 return;
00488 }
00489 else
00490 {
00491 parseLogFormat(format);
00492 }
00493 }
00494
00495 void LogOutputStream::start()
00496 {
00497 if (m_use_worker_thread)
00498 {
00499 m_worker_thread->start();
00500 }
00501 }
00502
00503 void LogOutputStream::shutdown()
00504 {
00505 if (m_use_worker_thread)
00506 {
00507 if (m_worker_thread->running())
00508 {
00509 m_worker_thread->stop();
00510 m_worker_thread->m_fill_count->post();
00511 m_worker_thread->join();
00512 }
00513 }
00514 }
00515
00516 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
00517 LogOutputStream::WorkerThread::WorkerThread(LogOutputStream *output_stream, size_t message_queue_size,
00518 icl_core::ThreadPriority priority)
00519 : Thread(priority),
00520 m_output_stream(output_stream),
00521 m_message_queue_size(message_queue_size),
00522 m_message_queue_write_index(0),
00523 m_message_queue_read_index(0)
00524 {
00525 m_message_queue = new LogMessage[message_queue_size+1];
00526
00527 m_mutex = new Semaphore(1);
00528 m_fill_count = new Semaphore(0);
00529 }
00530 #else
00531 LogOutputStream::WorkerThread::WorkerThread(LogOutputStream *output_stream, icl_core::ThreadPriority priority)
00532 : Thread(priority),
00533 m_output_stream(output_stream)
00534 {
00535 m_mutex = new Semaphore(1);
00536 m_fill_count = new Semaphore(0);
00537 }
00538 #endif
00539
00540 LogOutputStream::WorkerThread::~WorkerThread()
00541 {
00542 delete m_mutex;
00543 delete m_fill_count;
00544 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
00545 delete[] m_message_queue;
00546 #endif
00547 }
00548
00549 void LogOutputStream::WorkerThread::run()
00550 {
00551 m_output_stream->onStart();
00552
00553
00554 while (execute())
00555 {
00556 if (m_fill_count->wait())
00557 {
00558 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
00559 if (!isMessageQueueEmpty())
00560 #else
00561 if (!m_message_queue.empty())
00562 #endif
00563 {
00564 if (m_mutex->wait())
00565 {
00566 LogMessage log_message;
00567 bool push = false;
00568 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
00569 if (!isMessageQueueEmpty())
00570 {
00571 log_message = m_message_queue[m_message_queue_read_index];
00572 incrementIndex(m_message_queue_read_index);
00573 push = true;
00574 }
00575 #else
00576 if (!m_message_queue.empty())
00577 {
00578 log_message = m_message_queue.front();
00579 m_message_queue.pop();
00580 push = true;
00581 }
00582 #endif
00583
00584 m_mutex->post();
00585
00586 if (push)
00587 {
00588 m_output_stream->pushImpl(log_message);
00589 }
00590 }
00591 }
00592 }
00593 else if (execute())
00594 {
00595 PRINTF("LogOutputStream(%s)::run: semaphore wait failed\n", m_output_stream->m_name.c_str());
00596 icl_core::os::usleep(10000);
00597 }
00598 }
00599
00600
00601 if (m_mutex->wait())
00602 {
00603 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
00604 while (!isMessageQueueEmpty())
00605 {
00606 LogMessage log_message = m_message_queue[m_message_queue_read_index];
00607 incrementIndex(m_message_queue_read_index);
00608 m_output_stream->pushImpl(log_message);
00609 }
00610 #else
00611 while (!m_message_queue.empty())
00612 {
00613 LogMessage log_message = m_message_queue.front();
00614 m_message_queue.pop();
00615 m_output_stream->pushImpl(log_message);
00616 }
00617 #endif
00618
00619 m_mutex->post();
00620 }
00621
00622 m_output_stream->onShutdown();
00623 }
00624
00625
00626 #ifdef ICL_CORE_LOG_OUTPUT_STREAM_USE_FIXED_QUEUE
00627 void LogOutputStream::WorkerThread::incrementIndex(size_t& index)
00628 {
00629 ++index;
00630 if (index >= m_message_queue_size)
00631 {
00632 index = 0;
00633 }
00634 }
00635
00636 bool LogOutputStream::WorkerThread::isMessageQueueEmpty()
00637 {
00638 return m_message_queue_read_index == m_message_queue_write_index;
00639 }
00640
00641 bool LogOutputStream::WorkerThread::isMessageQueueFull()
00642 {
00643 return ((m_message_queue_write_index == m_message_queue_read_index - 1)
00644 || (m_message_queue_write_index == m_message_queue_size - 1
00645 && m_message_queue_read_index == 0));
00646 }
00647 #endif
00648
00649 LogOutputStream::LogMessage::LogMessage(const icl_core::TimeStamp& timestamp,
00650 icl_core::logging::LogLevel log_level,
00651 const char *log_stream, const char *filename,
00652 size_t line, const char *class_name,
00653 const char *object_name, const char *function_name,
00654 const char *message_text)
00655 : timestamp(timestamp),
00656 log_level(log_level),
00657 line(line)
00658 {
00659 std::strncpy(LogMessage::log_stream, log_stream, cMAX_IDENTIFIER_LENGTH+1);
00660 std::strncpy(LogMessage::filename, filename, cMAX_DESCRIPTION_LENGTH+1);
00661 std::strncpy(LogMessage::class_name, class_name, cMAX_IDENTIFIER_LENGTH+1);
00662 std::strncpy(LogMessage::object_name, object_name, cMAX_DESCRIPTION_LENGTH+1);
00663 std::strncpy(LogMessage::function_name, function_name, cMAX_IDENTIFIER_LENGTH+1);
00664 std::strncpy(LogMessage::message_text, message_text, cDEFAULT_LOG_SIZE+1);
00665 }
00666
00667 }
00668 }