LogSender.cpp
Go to the documentation of this file.
1/*
2* This file is part of ArmarX.
3*
4* Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5*
6* ArmarX is free software; you can redistribute it and/or modify
7* it under the terms of the GNU General Public License version 2 as
8* published by the Free Software Foundation.
9*
10* ArmarX is distributed in the hope that it will be useful, but
11* WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17*
18* @package ArmarXCore::core
19* @author Nils Adermann (naderman at naderman dot de)
20* @date 2010
21* @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22* GNU General Public License
23*/
24
25#include "LogSender.h"
26
27#include <memory>
28#include <optional>
29
30#include <dlfcn.h>
31#include <sys/syscall.h>
32
33#include <boost/logic/tribool.hpp>
34
35#include <IceUtil/Time.h>
36
37#include <SimoxUtility/algorithm/string/string_tools.h>
38
40#include <ArmarXCore/interface/core/Log.h>
41
42#include "SpamFilterData.h"
43#include <cxxabi.h>
44#include <execinfo.h>
45
46namespace armarx
47{
49 {
50 //! Flag to specify a minimum logging level for this LogSender<br/>
51 //! usually is set by Logging-Class
53
57
58 std::string currentFile;
60 std::string currentFunction;
62 boost::logic::tribool printBackTrace;
63
64 std::optional<Ice::Int> threadId;
65
66 static std::mutex mutex;
67 };
68
72 std::vector<LogMessage> LogSender_buffer;
73 std::mutex LogSender::Impl::mutex;
78 std::ostream LogSender_outbuf(std::cout.rdbuf());
79 std::ostream LogSender_errbuf(std::cerr.rdbuf());
80
83 {
84 try
85 {
86 return std::make_shared<LogSender>();
87 }
88 catch (...)
89 {
90 return nullptr;
91 }
92 }
93
95 {
96 impl->minimumLoggingLevel = MessageTypeT::UNDEFINED;
97 impl->cancelNextMessage = false;
98 impl->currentSeverity = MessageTypeT::INFO;
99 impl->printBackTrace = boost::logic::indeterminate;
100
101 initConsole();
102 resetLocation();
103 }
104
105 LogSender::LogSender(const std::string& componentName, LogPrx logProxy) : impl(new Impl)
106 {
107 impl->minimumLoggingLevel = MessageTypeT::UNDEFINED;
108 impl->cancelNextMessage = false;
109 impl->currentSeverity = MessageTypeT::INFO;
110 impl->printBackTrace = true;
111
112 initConsole();
113 setProxy(componentName, logProxy);
114 resetLocation();
115 }
116
118 {
119 if (!currentMessage.str().empty())
120 {
121 flush();
122 }
123 }
124
125 void
126 LogSender::setProxy(const std::string& componentName, LogPrx logProxy)
127 {
128 std::unique_lock lock(Impl::mutex);
129 LogSender_logProxy = logProxy;
130 LogSender_componentName = componentName;
131
132 if (!logProxy)
133 {
134 return;
135 }
136
137 for (unsigned int i = 0; i < LogSender_buffer.size(); ++i)
138 {
140 }
141
142 LogSender_buffer.clear();
143 }
144
145 void
146 LogSender::SetComponentName(const std::string& componentName)
147 {
148 std::unique_lock lock(Impl::mutex);
149 LogSender_componentName = componentName;
150 }
151
152 void
153 LogSender::SetLoggingGroup(const std::string& loggingGroup)
154 {
155 std::unique_lock lock(Impl::mutex);
156 LogSender_loggingGroup = loggingGroup;
157 }
158
160 LogSender::operator<<(const std::chrono::nanoseconds& timestamp)
161 {
162 if (timestamp.count() > 1000)
163 {
164 return *this << std::chrono::duration_cast<std::chrono::microseconds>(timestamp);
165 }
166
167 currentMessage << timestamp.count() << " ns";
168 return *this;
169 }
170
172 LogSender::operator<<(const std::chrono::microseconds& timestamp)
173 {
174 if (timestamp.count() > 1000)
175 {
176 return *this << std::chrono::duration_cast<std::chrono::milliseconds>(timestamp);
177 }
178
179 if (timestamp.count() <= 1. / 1000)
180 {
181 return *this << std::chrono::duration_cast<std::chrono::nanoseconds>(timestamp);
182 }
183
184 currentMessage << timestamp.count() << " µs";
185 return *this;
186 }
187
189 LogSender::operator<<(const std::chrono::milliseconds& timestamp)
190 {
191 if (timestamp.count() > 1000)
192 {
193 return *this << std::chrono::duration_cast<std::chrono::seconds>(timestamp);
194 }
195
196 if (timestamp.count() <= 1. / 1000)
197 {
198 return *this << std::chrono::duration_cast<std::chrono::microseconds>(timestamp);
199 }
200
201 currentMessage << timestamp.count() << " ms";
202 return *this;
203 }
204
206 LogSender::operator<<(const std::chrono::seconds& timestamp)
207 {
208 if (timestamp.count() > 60)
209 {
210 return *this << std::chrono::duration_cast<std::chrono::minutes>(timestamp);
211 }
212
213 if (timestamp.count() <= 1. / 1000)
214 {
215 return *this << std::chrono::duration_cast<std::chrono::milliseconds>(timestamp);
216 }
217
218 currentMessage << timestamp.count() << " s";
219 return *this;
220 }
221
223 LogSender::operator<<(const std::chrono::minutes& timestamp)
224 {
225 if (timestamp.count() < 1. / 60)
226 {
227 return *this << std::chrono::duration_cast<std::chrono::seconds>(timestamp);
228 }
229
230 currentMessage << timestamp.count() << " min";
231 return *this;
232 }
233
235 LogSender::operator<<(const IceUtil::Time& timestamp)
236 {
237 double time_count = timestamp.toMicroSecondsDouble();
238 std::string unit = "µs";
239
240 if (time_count >= 1000)
241 {
242 time_count /= 1000;
243 unit = "ms";
244
245 if (time_count >= 1000)
246 {
247 time_count /= 1000;
248 unit = "s";
249
250 if (time_count >= 60)
251 {
252 time_count /= 60;
253 unit = "min";
254
255 if (time_count >= 60)
256 {
257 time_count /= 60;
258 unit = "h";
259
260 if (time_count >= 24)
261 {
262 return *this << timestamp.toDateTime();
263 }
264 }
265 }
266 }
267 }
268
269 return *this << std::to_string(time_count) + " " + unit;
270 }
271
273 GetColorCode(MessageType type)
274 {
275 switch (type)
276 {
277 case eVERBOSE:
278 return LogSender::eBlue;
279 break;
280
281 case eIMPORTANT:
282 return LogSender::eGreen;
283
284 case eWARN:
285 return LogSender::eYellow;
286 break;
287
288 case eERROR:
289 case eFATAL:
290 return LogSender::eRed;
291 break;
292
293 default:
294 return LogSender::eReset;
295 break;
296 }
297 }
298
299 std::string
301 {
302
303 ConsoleColor colorCode = GetColorCode((MessageType)verbosityLevel);
304 return GetColorCodeString(colorCode);
305 }
306
307 std::string
309 {
310 std::stringstream stream;
311
312 if (colorCode == LogSender::eReset)
313 {
314 stream << "\033[0"
315 << "m";
316 }
317 else
318 {
319 stream << "\033[3" << colorCode << "m";
320 }
321
322 return stream.str();
323 }
324
325 void
326 LogSender::resetLocation()
327 {
328 impl->currentFile = "";
329 impl->currentLine = -1;
330 impl->currentFunction = "";
331 }
332
333 void
334 LogSender::log(MessageTypeT severity, std::string message)
335 {
336 simox::alg::trim(message);
337 if (impl->cancelNextMessage)
338 {
339 impl->cancelNextMessage = false;
340 return;
341 }
342
344 {
345 return;
346 }
347
348 if (severity < impl->minimumLoggingLevel)
349 {
350 return;
351 }
352
353 if (impl->minimumLoggingLevel == MessageTypeT::UNDEFINED &&
355 {
356 return;
357 }
358
359 IceUtil::Time time = IceUtil::Time::now();
360
361 std::string catStr;
362
363 if (!impl->currentTag.tagName.empty())
364 {
365 catStr = "[" + impl->currentTag.tagName + "]: ";
366 }
367 else
368 {
369 catStr = "[" + CropFunctionName(impl->currentFunction) + "]: ";
370 }
371
372 std::string outputStr;
373
375 {
376 outputStr += GetColorCodeString(severity);
377
378 if (severity >= MessageTypeT::FATAL)
379 {
380 outputStr += "\033[1m"; // Bold text
381 }
382 }
383
384 outputStr += "[" + time.toDateTime().substr(time.toDateTime().find(' ') + 1) + "]" + "[" +
385 LogSender_componentName + "]" + catStr;
386
388 {
389 outputStr += GetColorCodeString(eReset);
390 }
391
392 outputStr += message;
393
395 {
397 }
398
399 LogMessage msg;
400 msg.who = LogSender_componentName;
401 msg.group = LogSender_loggingGroup;
402 msg.time = time.toMicroSeconds();
403 msg.tag = impl->currentTag.tagName;
404 msg.type = (MessageType)severity;
405 msg.what = message;
406 msg.file = impl->currentFile;
407 msg.line = impl->currentLine;
408 msg.function = impl->currentFunction;
409 msg.threadId = impl->threadId ? *impl->threadId : getThreadId();
410
411 if ((severity >= MessageTypeT::WARN && boost::logic::indeterminate(impl->printBackTrace)) ||
412 impl->printBackTrace)
413 {
414 msg.backtrace = CreateBackTrace();
415 }
416
418 {
419 LogSender_logProxy->begin_writeLog(
420 msg); // use async function, so that it returns faster
421 }
422
423
424 try
425 {
426 std::unique_lock lock(
427 Impl::mutex); // std::cout is not threadsafe and also lock for inserting into buffer
428
429 if (severity >= MessageTypeT::ERROR)
430 {
431 LogSender_errbuf << "[" << msg.threadId << "]" << outputStr << std::endl;
432 }
433 else
434 {
435 LogSender_outbuf << "[" << msg.threadId << "]" << outputStr << std::endl;
436 }
437
439 {
440 LogSender_buffer.push_back(msg);
441 }
442
443 resetLocation();
444 }
445 catch (...)
446 {
447 std::cout << outputStr << std::endl;
448 return; // mutex may be deleted already if the system shuts down at this moment
449 }
450 }
451
452 long
454 {
455 return syscall(SYS_gettid);
456 }
457
458 long
460 {
461#ifdef WIN32
462 return 0;
463#else
464 return getpid();
465#endif
466 }
467
468 void
470 {
471
472 log(impl->currentSeverity, currentMessage.str());
473 currentMessage.str(""); // reset
474 }
475
476 std::string
477 LogSender::CropFunctionName(std::string const& originalFunctionName)
478 {
479 unsigned int maxLength = 40;
480 std::string result;
481
482 if (originalFunctionName.length() <= maxLength)
483 {
484 return originalFunctionName;
485 }
486
487 std::string beforeFunctionName =
488 originalFunctionName.substr(0, originalFunctionName.rfind("::"));
489 std::string::size_type namespaceDotsPos =
490 beforeFunctionName.find("::", beforeFunctionName.find(' '));
491
492 if (namespaceDotsPos != std::string::npos)
493 {
494 std::string afterClassName =
495 originalFunctionName.substr(originalFunctionName.rfind("::"));
496 result = beforeFunctionName.substr(namespaceDotsPos + 2) + afterClassName;
497 }
498 else
499 {
500 result = originalFunctionName;
501 }
502
503 if (result.length() <= maxLength)
504 {
505 return result;
506 }
507
508
509 result = result.substr(0, result.find("("));
510 result += "(...)";
511
512 return result;
513 }
514
515 std::string
517 {
518 void* callstack[128];
519 const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
520 char buf[1024];
521
522 int nFrames = ::backtrace(callstack, nMaxFrames);
523 char** symbols = backtrace_symbols(callstack, nFrames);
524
525 std::ostringstream trace_buf;
526
527 for (int i = linesToSkip; i < nFrames; i++)
528 {
529 Dl_info info;
530
531 if (dladdr(callstack[i], &info) && info.dli_sname)
532 {
533 char* demangled = nullptr;
534 int status = -1;
535
536 if (info.dli_sname[0] == '_')
537 {
538 demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
539 }
540
541 snprintf(buf,
542 sizeof(buf),
543 "%-3d %*p %s + %zd\n",
544 i - linesToSkip + 1,
545 int(2 + sizeof(void*) * 2),
546 callstack[i],
547 status == 0 ? demangled
548 : info.dli_sname == nullptr ? symbols[i]
549 : info.dli_sname,
550 (char*)callstack[i] - (char*)info.dli_saddr);
551 free(demangled);
552 }
553 else
554 {
555 snprintf(buf,
556 sizeof(buf),
557 "%-3d %*p %s\n",
558 i - linesToSkip + 1,
559 int(2 + sizeof(void*) * 2),
560 callstack[i],
561 symbols[i]);
562 }
563
564 trace_buf << buf;
565 }
566
567 free(symbols);
568
569 if (nFrames == nMaxFrames)
570 {
571 trace_buf << "[truncated]\n";
572 }
573
574 return trace_buf.str();
575 }
576
579 {
580 return impl->currentSeverity;
581 }
582
584 LogSender::setFile(const std::string& file)
585 {
586 impl->currentFile = file;
587 return shared_from_this();
588 }
589
592 {
593 impl->currentLine = line;
594 return shared_from_this();
595 }
596
598 LogSender::setFunction(const std::string& function)
599 {
600 impl->currentFunction = function;
601 return shared_from_this();
602 }
603
606 {
607 impl->minimumLoggingLevel = level;
608 return shared_from_this();
609 }
610
612 LogSender::setBacktrace(bool printBackTrace)
613 {
614 impl->printBackTrace = printBackTrace;
615 return shared_from_this();
616 }
617
620 {
621 impl->threadId = tid;
622 return shared_from_this();
623 }
624
627 {
628 impl->currentTag = tag;
629 return shared_from_this();
630 }
631
632 //LogSenderPtr LogSender::setSpamFilter(SpamFilterMapPtr spamFilter)
633 //{
634 // this->spamFilter = spamFilter;
635 // return shared_from_this();
636 //}
637
638 std::string
640 {
641 switch (type)
642 {
644 return "Debug";
645
647 return "Verbose";
648
650 return "Info";
651
653 return "Important";
654
656 return "Warning";
657
659 return "Error";
660
662 return "Fatal";
663
664 default:
665 return "Undefined";
666 }
667 }
668
670 LogSender::StringToLevel(const std::string& typeStr)
671 {
672 if (simox::alg::to_lower(typeStr) == "debug")
673 {
674 return MessageTypeT::DEBUG;
675 }
676
677 if (simox::alg::to_lower(typeStr) == "verbose")
678 {
680 }
681
682 if (simox::alg::to_lower(typeStr) == "important")
683 {
685 }
686
687 if (simox::alg::to_lower(typeStr) == "warning")
688 {
689 return MessageTypeT::WARN;
690 }
691
692 if (simox::alg::to_lower(typeStr) == "error")
693 {
694 return MessageTypeT::ERROR;
695 }
696
697 if (simox::alg::to_lower(typeStr) == "fatal")
698 {
699 return MessageTypeT::FATAL;
700 }
701
703 }
704
705 void
707 {
709 {
710 return;
711 }
712
714 std::cout << "setting global logging level to " << LogSender::levelToString(level)
715 << std::endl;
716 }
717
723
724 void
725 LogSender::SetLoggingActivated(bool activated, bool showMessage)
726 {
727 if (LogSender_LoggingActivated && showMessage && !activated)
728 {
729 std::cout << "logging is now DEACTIVATED" << std::endl;
730 }
731
732 if (activated && LogSender_LoggingActivated != activated && showMessage)
733 {
734 std::cout << "logging is now ACTIVATED" << std::endl;
735 }
736 LogSender_LoggingActivated = activated;
737 }
738
739 void
741 {
742 LogSender_SendLogging = activated;
743 }
744
745 void
747 {
748 LogSender_ColoringActivated = activated;
749 }
750
751 void
752 LogSender::initConsole()
753 {
754 static bool initialized = false;
755
756 if (!initialized)
757 {
758 initialized = true;
759 char* termType = getenv("TERM");
760
761 if (termType != nullptr)
762 {
763 std::string termTypeStr(termType);
764
765 if (termTypeStr != "xterm" && termTypeStr != "emacs" &&
766 termTypeStr != "xterm-256color")
767 {
769 }
770 }
771 else
772 {
774 }
775 }
776 }
777
778 template <>
780 LogSender::operator<< <LogSender::ConsoleColor>(const LogSender::ConsoleColor& colorCode)
781 {
782 currentMessage << LogSender::GetColorCodeString(colorCode);
783
784 return *this;
785 }
786
787 template <>
788 LogSender& LogSender::operator<< <MessageTypeT>(const MessageTypeT& severity)
789 {
790 impl->currentSeverity = severity;
791
792 return *this;
793 }
794
795 template <>
796 LogSender& LogSender::operator<< <LogTag>(const LogTag& tag)
797 {
798 impl->currentTag = tag;
799
800 return *this;
801 }
802
803 template <>
804 LogSender& LogSender::operator<< <LogSender::manipulator>(const manipulator& manipulator)
805 {
806 (this->*manipulator)();
807
808 return *this;
809 }
810
813 {
814 flush();
815
816 return *this;
817 }
818
819 template <>
820 LogSender& LogSender::operator<< <bool>(const bool& duality)
821 {
822 if (duality)
823 {
824 currentMessage << "true";
825 }
826 else
827 {
828 currentMessage << "false";
829 }
830
831 return *this;
832 }
833
834 template <>
835 LogSender& LogSender::operator<< <SpamFilterDataPtr>(const SpamFilterDataPtr& spamFilterData)
836 {
837 if (!spamFilterData)
838 {
839 return *this;
840 }
841
842 std::unique_lock lock(*spamFilterData->mutex);
843 float deactivationDurationSec = spamFilterData->durationSec;
844 SpamFilterMapPtr spamFilter = spamFilterData->filterMap;
845 std::string id = impl->currentFile + ":" + to_string(impl->currentLine);
846 auto it = spamFilter->find(spamFilterData->identifier);
847
848 if (it != spamFilter->end())
849 {
850 auto itSub = it->second.find(id);
851 IceUtil::Time durationEnd = IceUtil::Time::now() + IceUtil::Time::milliSecondsDouble(
852 deactivationDurationSec * 1000);
853
854 if (itSub == it->second.end())
855 {
856 itSub = it->second.insert(std::make_pair(id, durationEnd)).first;
857 impl->cancelNextMessage = false;
858 }
859 else if (IceUtil::Time::now() < itSub->second)
860 {
861 impl->cancelNextMessage = true;
862 }
863 else
864 {
865 impl->cancelNextMessage = false;
866 itSub->second = durationEnd;
867 }
868 }
869
870 return *this;
871 }
872} // namespace armarx
std::string timestamp()
#define ARMARXCORE_IMPORT_EXPORT
Wrapper for the Log IceStorm topic with convenience methods for logging.
Definition LogSender.h:66
static std::string CropFunctionName(const std::string &originalFunctionName)
LogSenderPtr setFile(const std::string &file)
Set the source code filename associated with this message.
static std::string GetColorCodeString(MessageTypeT verbosityLevel)
static void SetGlobalMinimumLoggingLevel(MessageTypeT level)
With setGlobalMinimumLoggingLevel the minimum verbosity-level of log-messages can be set for the whol...
static std::string CreateBackTrace(int linesToSkip=1)
LogSenderPtr setTag(const LogTag &tag)
LogSenderPtr setFunction(const std::string &function)
Set the function name associated with this message.
LogSenderPtr setLocalMinimumLoggingLevel(MessageTypeT level)
static void SetLoggingGroup(const std::string &loggingGroup)
static LogSenderPtr createLogSender()
Definition LogSender.cpp:82
MessageTypeT getSeverity()
Retrieves the current message severity.
void(LogSender::* manipulator)()
Definition LogSender.h:78
LogSenderPtr setBacktrace(bool printBackTrace)
static long getProcessId()
LogSender & operator<<(const T &message)
Appends a variable to the current message stringstream.
Definition LogSender.h:114
void log(MessageTypeT severity, std::string message)
Sends a message to the logging component.
LogSenderPtr setLine(int line)
Set the source code line associated with this message.
static long getThreadId()
static void SetColoredConsoleActivated(bool activated=true)
LogSenderPtr setThreadId(Ice::Int tid)
CoutType &(* StandardEndLine)(CoutType &)
Definition LogSender.h:84
static void SetComponentName(const std::string &componentName)
static void SetSendLoggingActivated(bool activated=true)
void flush()
Sends the current message to the log and resets the content.
static std::string levelToString(MessageTypeT type)
static MessageTypeT GetGlobalMinimumLoggingLevel()
static MessageTypeT StringToLevel(const std::string &typeStr)
stringToLevel converts a string into a LoggingLevel, if possible.
static void setProxy(const std::string &componentName, LogPrx logProxy)
static void SetLoggingActivated(bool activated=true, bool showMessage=true)
setLoggingActivated() is used to activate or disable the logging facilities in the whole application
This file offers overloads of toIce() and fromIce() functions for STL container types.
::IceInternal::ProxyHandle<::IceProxy::armarx::Log > LogPrx
Definition LogSender.h:58
bool LogSender_ColoringActivated
Definition LogSender.cpp:76
std::ostream LogSender_outbuf(std::cout.rdbuf())
LogSender::ConsoleColor GetColorCode(MessageType type)
std::shared_ptr< SpamFilterData > SpamFilterDataPtr
Definition Logging.h:227
LogPrx LogSender_logProxy
Definition LogSender.cpp:69
bool LogSender_LoggingActivated
Definition LogSender.cpp:74
std::vector< LogMessage > LogSender_buffer
Definition LogSender.cpp:72
bool LogSender_SendLogging
Definition LogSender.cpp:77
std::ostream LogSender_errbuf(std::cerr.rdbuf())
std::shared_ptr< LogSender > LogSenderPtr
Typedef of std::shared_ptr for convenience.
Definition Logging.h:224
std::shared_ptr< SpamFilterMap > SpamFilterMapPtr
MessageTypeT
Definition LogSender.h:46
const std::string & to_string(const std::string &s)
MessageTypeT LogSender_GlobalMinimumLoggingLevel
Definition LogSender.cpp:75
std::string LogSender_componentName
Definition LogSender.cpp:70
std::string LogSender_loggingGroup
Definition LogSender.cpp:71
static std::mutex mutex
Definition LogSender.cpp:66
boost::logic::tribool printBackTrace
Definition LogSender.cpp:62
std::string currentFunction
Definition LogSender.cpp:60
std::optional< Ice::Int > threadId
Definition LogSender.cpp:64
MessageTypeT minimumLoggingLevel
Flag to specify a minimum logging level for this LogSender usually is set by Logging-Class.
Definition LogSender.cpp:52
MessageTypeT currentSeverity
Definition LogSender.cpp:56
SpamFilterMapPtr spamFilter
Definition LogSender.cpp:54