comparison Core/Logging.cpp @ 4014:27628b0f6ada

merging logging code for plugins and files
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 08 Jun 2020 17:03:25 +0200
parents f0ee3f1db775
children c783f4f29390
comparison
equal deleted inserted replaced
4013:1e9f6d706237 4014:27628b0f6ada
94 94
95 namespace Orthanc 95 namespace Orthanc
96 { 96 {
97 namespace Logging 97 namespace Logging
98 { 98 {
99 void InitializePluginContext(void* pluginContext)
100 {
101 }
102
99 void Initialize() 103 void Initialize()
100 { 104 {
101 } 105 }
102 106
103 void Finalize() 107 void Finalize()
148 * output to the emscripten html5 api (depending on the 152 * output to the emscripten html5 api (depending on the
149 * definition of __EMSCRIPTEN__) 153 * definition of __EMSCRIPTEN__)
150 *********************************************************/ 154 *********************************************************/
151 155
152 #include <stdio.h> 156 #include <stdio.h>
153 #include <boost/lexical_cast.hpp>
154 157
155 #ifdef __EMSCRIPTEN__ 158 #ifdef __EMSCRIPTEN__
156 # include <emscripten/html5.h> 159 # include <emscripten/html5.h>
157 #endif 160 #endif
158 161
181 184
182 static void TraceLogFunc(const char* msg) 185 static void TraceLogFunc(const char* msg)
183 { 186 {
184 emscripten_console_log(msg); 187 emscripten_console_log(msg);
185 } 188 }
186 #else 189 #else /* __EMSCRIPTEN__ not #defined */
187 // __EMSCRIPTEN__ not #defined
188 static void ErrorLogFunc(const char* msg) 190 static void ErrorLogFunc(const char* msg)
189 { 191 {
190 fprintf(stderr, "E: %s\n", msg); 192 fprintf(stderr, "E: %s\n", msg);
191 } 193 }
192 194
202 204
203 static void TraceLogFunc(const char*) 205 static void TraceLogFunc(const char*)
204 { 206 {
205 fprintf(stdout, "T: %s\n", msg); 207 fprintf(stdout, "T: %s\n", msg);
206 } 208 }
207 #endif 209 #endif /* __EMSCRIPTEN__ */
208 // __EMSCRIPTEN__
209 210
210 InternalLogger::InternalLogger(LogLevel level, 211 InternalLogger::InternalLogger(LogLevel level,
211 const char* file /* ignored */, 212 const char* file /* ignored */,
212 int line /* ignored */) : 213 int line /* ignored */) :
213 level_(level) 214 level_(level)
251 globalErrorLogFunc(s.c_str()); 252 globalErrorLogFunc(s.c_str());
252 } 253 }
253 } 254 }
254 } 255 }
255 256
257 void InitializePluginContext(void* pluginContext)
258 {
259 }
260
256 void Initialize() 261 void Initialize()
257 { 262 {
258 } 263 }
259 264
260 void Finalize() 265 void Finalize()
298 } 303 }
299 } 304 }
300 } 305 }
301 306
302 307
303 #elif ORTHANC_ENABLE_LOGGING_PLUGIN == 1 308 #else
304 309
305 /********************************************************* 310 /*********************************************************
306 * Logger compatible with the Orthanc plugin SDK 311 * Logger compatible with the Orthanc plugin SDK, or that
312 * mimics behavior from Google Log.
307 *********************************************************/ 313 *********************************************************/
308 314
309 #include <cassert> 315 #include <cassert>
310 316
311 namespace 317 namespace
333 const void* params); 339 const void* params);
334 } OrthancPluginContext; 340 } OrthancPluginContext;
335 } 341 }
336 342
337 343
338 #include <boost/lexical_cast.hpp>
339 #include <sstream>
340
341 namespace Orthanc
342 {
343 namespace Logging
344 {
345 static OrthancPluginContext* context_ = NULL;
346
347 void Initialize(void* context)
348 {
349 assert(sizeof(_OrthancPluginService) == sizeof(int32_t));
350 context_ = reinterpret_cast<OrthancPluginContext*>(context);
351 }
352
353 InternalLogger::InternalLogger(LogLevel level,
354 const char* file /* ignored */,
355 int line /* ignored */) :
356 level_(level)
357 {
358 }
359
360 InternalLogger::~InternalLogger()
361 {
362 std::string message = messageStream_.str();
363 if (context_ != NULL)
364 {
365 switch (level_)
366 {
367 case LogLevel_ERROR:
368 context_->InvokeService(context_, _OrthancPluginService_LogError, message.c_str());
369 break;
370
371 case LogLevel_WARNING:
372 context_->InvokeService(context_, _OrthancPluginService_LogWarning, message.c_str());
373 break;
374
375 case LogLevel_INFO:
376 context_->InvokeService(context_, _OrthancPluginService_LogInfo, message.c_str());
377 break;
378
379 case LogLevel_TRACE:
380 // Not used by plugins
381 break;
382
383 default:
384 {
385 std::string s = ("Unknown log level (" + boost::lexical_cast<std::string>(level_) +
386 ") for message: " + message);
387 context_->InvokeService(context_, _OrthancPluginService_LogInfo, s.c_str());
388 break;
389 }
390 }
391 }
392 }
393 }
394 }
395
396
397 #else /* ORTHANC_ENABLE_LOGGING_PLUGIN == 0 &&
398 ORTHANC_ENABLE_LOGGING_STDIO == 0 &&
399 ORTHANC_ENABLE_LOGGING == 1 */
400
401 /*********************************************************
402 * Internal logger of Orthanc, that mimics some
403 * behavior from Google Log.
404 *********************************************************/
405
406 #include "Compatibility.h"
407 #include "Enumerations.h" 344 #include "Enumerations.h"
408 #include "Toolbox.h" 345 #include "SystemToolbox.h"
409
410 #if ORTHANC_SANDBOXED == 1
411 # include <stdio.h>
412 #else
413 # include "SystemToolbox.h"
414 #endif
415 346
416 #include <fstream> 347 #include <fstream>
417 #include <boost/filesystem.hpp> 348 #include <boost/filesystem.hpp>
418 #include <boost/thread.hpp> 349 #include <boost/thread.hpp>
419 #include <boost/date_time/posix_time/posix_time.hpp> 350 #include <boost/date_time/posix_time/posix_time.hpp>
420 351
421 352
422 namespace 353 namespace
423 { 354 {
424 struct LoggingContext 355 struct LoggingStreamsContext
425 { 356 {
426 bool infoEnabled_;
427 bool traceEnabled_;
428 std::string targetFile_; 357 std::string targetFile_;
429 std::string targetFolder_; 358 std::string targetFolder_;
430 359
431 std::ostream* error_; 360 std::ostream* error_;
432 std::ostream* warning_; 361 std::ostream* warning_;
433 std::ostream* info_; 362 std::ostream* info_;
434 363
435 std::unique_ptr<std::ofstream> file_; 364 std::unique_ptr<std::ofstream> file_;
436 365
437 LoggingContext() : 366 LoggingStreamsContext() :
438 infoEnabled_(false),
439 traceEnabled_(false),
440 error_(&std::cerr), 367 error_(&std::cerr),
441 warning_(&std::cerr), 368 warning_(&std::cerr),
442 info_(&std::cerr) 369 info_(&std::cerr)
443 { 370 {
444 } 371 }
445 }; 372 };
446 } 373 }
447 374
448 375
449 376
450 static std::unique_ptr<LoggingContext> loggingContext_; 377 static std::unique_ptr<LoggingStreamsContext> loggingStreamsContext_;
451 static boost::mutex loggingMutex_; 378 static boost::mutex loggingStreamsMutex_;
452 379 static Orthanc::Logging::NullStream nullStream_;
380 static OrthancPluginContext* pluginContext_ = NULL;
381 static bool infoEnabled_ = false;
382 static bool traceEnabled_ = false;
453 383
454 384
455 namespace Orthanc 385 namespace Orthanc
456 { 386 {
457 namespace Logging 387 namespace Logging
513 443
514 file.reset(new std::ofstream(log.string().c_str())); 444 file.reset(new std::ofstream(log.string().c_str()));
515 } 445 }
516 446
517 447
448 // "loggingStreamsMutex_" must be locked
518 static void CheckFile(std::unique_ptr<std::ofstream>& f) 449 static void CheckFile(std::unique_ptr<std::ofstream>& f)
519 { 450 {
520 if (loggingContext_->file_.get() == NULL || 451 if (loggingStreamsContext_->file_.get() == NULL ||
521 !loggingContext_->file_->is_open()) 452 !loggingStreamsContext_->file_->is_open())
522 { 453 {
523 throw OrthancException(ErrorCode_CannotWriteFile); 454 throw OrthancException(ErrorCode_CannotWriteFile);
524 } 455 }
525 } 456 }
526 457
591 prefix = (std::string(date) + path.filename().string() + ":" + 522 prefix = (std::string(date) + path.filename().string() + ":" +
592 boost::lexical_cast<std::string>(line) + "] "); 523 boost::lexical_cast<std::string>(line) + "] ");
593 } 524 }
594 525
595 526
527 void InitializePluginContext(void* pluginContext)
528 {
529 assert(sizeof(_OrthancPluginService) == sizeof(int32_t));
530
531 boost::mutex::scoped_lock lock(loggingStreamsMutex_);
532 loggingStreamsContext_.reset(NULL);
533 pluginContext_ = reinterpret_cast<OrthancPluginContext*>(pluginContext);
534 }
535
536
596 void Initialize() 537 void Initialize()
597 { 538 {
598 boost::mutex::scoped_lock lock(loggingMutex_); 539 boost::mutex::scoped_lock lock(loggingStreamsMutex_);
599 loggingContext_.reset(new LoggingContext); 540 loggingStreamsContext_.reset(new LoggingStreamsContext);
600 } 541 }
601 542
602 void Finalize() 543 void Finalize()
603 { 544 {
604 boost::mutex::scoped_lock lock(loggingMutex_); 545 boost::mutex::scoped_lock lock(loggingStreamsMutex_);
605 loggingContext_.reset(NULL); 546 loggingStreamsContext_.reset(NULL);
606 } 547 }
607 548
608 void Reset() 549 void Reset()
609 { 550 {
610 // Recover the old logging context 551 // Recover the old logging context
611 std::unique_ptr<LoggingContext> old; 552 std::unique_ptr<LoggingStreamsContext> old;
612 553
613 { 554 {
614 boost::mutex::scoped_lock lock(loggingMutex_); 555 boost::mutex::scoped_lock lock(loggingStreamsMutex_);
615 if (loggingContext_.get() == NULL) 556 if (loggingStreamsContext_.get() == NULL)
616 { 557 {
617 return; 558 return;
618 } 559 }
619 else 560 else
620 { 561 {
621 #if __cplusplus < 201103L 562 #if __cplusplus < 201103L
622 old.reset(loggingContext_.release()); 563 old.reset(loggingStreamsContext_.release());
623 #else 564 #else
624 old = std::move(loggingContext_); 565 old = std::move(loggingStreamsContext_);
625 #endif 566 #endif
626 567
627 // Create a new logging context, 568 // Create a new logging context,
628 loggingContext_.reset(new LoggingContext); 569 loggingStreamsContext_.reset(new LoggingStreamsContext);
629 } 570 }
630 } 571 }
631 572
632 EnableInfoLevel(old->infoEnabled_);
633 EnableTraceLevel(old->traceEnabled_);
634
635 if (!old->targetFolder_.empty()) 573 if (!old->targetFolder_.empty())
636 { 574 {
637 SetTargetFolder(old->targetFolder_); 575 SetTargetFolder(old->targetFolder_);
638 } 576 }
639 else if (!old->targetFile_.empty()) 577 else if (!old->targetFile_.empty())
643 } 581 }
644 582
645 583
646 void EnableInfoLevel(bool enabled) 584 void EnableInfoLevel(bool enabled)
647 { 585 {
648 boost::mutex::scoped_lock lock(loggingMutex_); 586 infoEnabled_ = enabled;
649 assert(loggingContext_.get() != NULL);
650
651 loggingContext_->infoEnabled_ = enabled;
652 587
653 if (!enabled) 588 if (!enabled)
654 { 589 {
655 // Also disable the "TRACE" level when info-level debugging is disabled 590 // Also disable the "TRACE" level when info-level debugging is disabled
656 loggingContext_->traceEnabled_ = false; 591 traceEnabled_ = false;
657 } 592 }
658 } 593 }
659 594
660 bool IsInfoLevelEnabled() 595 bool IsInfoLevelEnabled()
661 { 596 {
662 boost::mutex::scoped_lock lock(loggingMutex_); 597 return infoEnabled_;
663 assert(loggingContext_.get() != NULL);
664
665 return loggingContext_->infoEnabled_;
666 } 598 }
667 599
668 void EnableTraceLevel(bool enabled) 600 void EnableTraceLevel(bool enabled)
669 { 601 {
670 boost::mutex::scoped_lock lock(loggingMutex_); 602 traceEnabled_ = enabled;
671 assert(loggingContext_.get() != NULL);
672
673 loggingContext_->traceEnabled_ = enabled;
674 603
675 if (enabled) 604 if (enabled)
676 { 605 {
677 // Also enable the "INFO" level when trace-level debugging is enabled 606 // Also enable the "INFO" level when trace-level debugging is enabled
678 loggingContext_->infoEnabled_ = true; 607 infoEnabled_ = true;
679 } 608 }
680 } 609 }
681 610
682 bool IsTraceLevelEnabled() 611 bool IsTraceLevelEnabled()
683 { 612 {
684 boost::mutex::scoped_lock lock(loggingMutex_); 613 return traceEnabled_;
685 assert(loggingContext_.get() != NULL);
686
687 return loggingContext_->traceEnabled_;
688 } 614 }
689 615
690 616
691 void SetTargetFolder(const std::string& path) 617 void SetTargetFolder(const std::string& path)
692 { 618 {
693 boost::mutex::scoped_lock lock(loggingMutex_); 619 boost::mutex::scoped_lock lock(loggingStreamsMutex_);
694 assert(loggingContext_.get() != NULL); 620 if (loggingStreamsContext_.get() != NULL)
695 621 {
696 PrepareLogFolder(loggingContext_->file_, "" /* no suffix */, path); 622 PrepareLogFolder(loggingStreamsContext_->file_, "" /* no suffix */, path);
697 CheckFile(loggingContext_->file_); 623 CheckFile(loggingStreamsContext_->file_);
698 624
699 loggingContext_->targetFile_.clear(); 625 loggingStreamsContext_->targetFile_.clear();
700 loggingContext_->targetFolder_ = path; 626 loggingStreamsContext_->targetFolder_ = path;
701 loggingContext_->warning_ = loggingContext_->file_.get(); 627 loggingStreamsContext_->warning_ = loggingStreamsContext_->file_.get();
702 loggingContext_->error_ = loggingContext_->file_.get(); 628 loggingStreamsContext_->error_ = loggingStreamsContext_->file_.get();
703 loggingContext_->info_ = loggingContext_->file_.get(); 629 loggingStreamsContext_->info_ = loggingStreamsContext_->file_.get();
630 }
704 } 631 }
705 632
706 633
707 void SetTargetFile(const std::string& path) 634 void SetTargetFile(const std::string& path)
708 { 635 {
709 boost::mutex::scoped_lock lock(loggingMutex_); 636 boost::mutex::scoped_lock lock(loggingStreamsMutex_);
710 assert(loggingContext_.get() != NULL); 637
711 638 if (loggingStreamsContext_.get() != NULL)
712 loggingContext_->file_.reset(new std::ofstream(path.c_str(), std::fstream::app)); 639 {
713 CheckFile(loggingContext_->file_); 640 loggingStreamsContext_->file_.reset(new std::ofstream(path.c_str(), std::fstream::app));
714 641 CheckFile(loggingStreamsContext_->file_);
715 loggingContext_->targetFile_ = path; 642
716 loggingContext_->targetFolder_.clear(); 643 loggingStreamsContext_->targetFile_ = path;
717 loggingContext_->warning_ = loggingContext_->file_.get(); 644 loggingStreamsContext_->targetFolder_.clear();
718 loggingContext_->error_ = loggingContext_->file_.get(); 645 loggingStreamsContext_->warning_ = loggingStreamsContext_->file_.get();
719 loggingContext_->info_ = loggingContext_->file_.get(); 646 loggingStreamsContext_->error_ = loggingStreamsContext_->file_.get();
647 loggingStreamsContext_->info_ = loggingStreamsContext_->file_.get();
648 }
720 } 649 }
721 650
722 651
723 InternalLogger::InternalLogger(LogLevel level, 652 InternalLogger::InternalLogger(LogLevel level,
724 const char* file, 653 const char* file,
725 int line) : 654 int line) :
726 lock_(loggingMutex_, boost::defer_lock_t()), 655 lock_(loggingStreamsMutex_, boost::defer_lock_t()),
727 stream_(&null_) // By default, logging to "/dev/null" is simulated 656 level_(level),
728 { 657 stream_(&nullStream_) // By default, logging to "/dev/null" is simulated
729 lock_.lock(); 658 {
730 659 if (pluginContext_ != NULL)
731 if (loggingContext_.get() == NULL) 660 {
732 { 661 // We are logging using the Orthanc plugin SDK
733 fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n"); 662
734 return; 663 if (level == LogLevel_TRACE)
735 }
736
737 try
738 {
739 if ((level == LogLevel_INFO && !loggingContext_->infoEnabled_) ||
740 (level == LogLevel_TRACE && !loggingContext_->traceEnabled_))
741 { 664 {
742 // This logging level is disabled, directly exit and unlock 665 // No trace level in plugins, directly exit as the stream is
743 // the mutex to speed-up things. The stream is set to "/dev/null" 666 // set to "/dev/null"
744 lock_.unlock();
745 return; 667 return;
746 } 668 }
747 669 else
748 // Compute the prefix of the line, temporary release the lock as 670 {
749 // this is a time-consuming operation 671 pluginStream_.reset(new std::stringstream);
750 lock_.unlock(); 672 stream_ = pluginStream_.get();
673 }
674 }
675 else
676 {
677 // We are logging in a standalone application, not inside an Orthanc plugin
678
679 if ((level == LogLevel_INFO && !infoEnabled_) ||
680 (level == LogLevel_TRACE && !traceEnabled_))
681 {
682 // This logging level is disabled, directly exit as the
683 // stream is set to "/dev/null"
684 return;
685 }
686
751 std::string prefix; 687 std::string prefix;
752 GetLinePrefix(prefix, level, file, line); 688 GetLinePrefix(prefix, level, file, line);
753 689
754 // The prefix is computed, we now re-lock the mutex to access
755 // the stream objects. Pay attention that "loggingContext_",
756 // "infoEnabled_" or "traceEnabled_" might have changed while
757 // the mutex was unlocked.
758 lock_.lock();
759
760 if (loggingContext_.get() == NULL)
761 { 690 {
762 fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n"); 691 // We lock the global mutex. The mutex is locked until the
763 return; 692 // destructor is called: No change in the output can be done.
693 lock_.lock();
694
695 if (loggingStreamsContext_.get() == NULL)
696 {
697 fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n");
698 lock_.unlock();
699 return;
700 }
701
702 switch (level)
703 {
704 case LogLevel_ERROR:
705 stream_ = loggingStreamsContext_->error_;
706 break;
707
708 case LogLevel_WARNING:
709 stream_ = loggingStreamsContext_->warning_;
710 break;
711
712 case LogLevel_INFO:
713 case LogLevel_TRACE:
714 stream_ = loggingStreamsContext_->info_;
715 break;
716
717 default:
718 throw OrthancException(ErrorCode_InternalError);
719 }
720
721 if (stream_ == &nullStream_)
722 {
723 // The logging is disabled for this level, we can release
724 // the global mutex.
725 lock_.unlock();
726 }
727 else
728 {
729 try
730 {
731 (*stream_) << prefix;
732 }
733 catch (...)
734 {
735 // Something is going really wrong, probably running out of
736 // memory. Fallback to a degraded mode.
737 stream_ = loggingStreamsContext_->error_;
738 (*stream_) << "E???? ??:??:??.?????? ] ";
739 }
740 }
764 } 741 }
765 742 }
766 switch (level) 743 }
744
745
746 InternalLogger::~InternalLogger()
747 {
748 if (pluginStream_.get() != NULL)
749 {
750 // We are logging through the Orthanc SDK
751
752 std::string message = pluginStream_->str();
753
754 if (pluginContext_ != NULL)
767 { 755 {
768 case LogLevel_ERROR: 756 switch (level_)
769 stream_ = loggingContext_->error_; 757 {
770 break; 758 case LogLevel_ERROR:
771 759 pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogError, message.c_str());
772 case LogLevel_WARNING: 760 break;
773 stream_ = loggingContext_->warning_; 761
774 break; 762 case LogLevel_WARNING:
775 763 printf("[%s]\n", message.c_str());
776 case LogLevel_INFO: 764
777 if (loggingContext_->infoEnabled_) 765 pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogWarning, message.c_str());
778 { 766 break;
779 stream_ = loggingContext_->info_; 767
780 } 768 case LogLevel_INFO:
781 769 pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogInfo, message.c_str());
782 break; 770 break;
783 771
784 case LogLevel_TRACE: 772 default:
785 if (loggingContext_->traceEnabled_) 773 break;
786 { 774 }
787 stream_ = loggingContext_->info_;
788 }
789
790 break;
791
792 default:
793 throw OrthancException(ErrorCode_InternalError);
794 } 775 }
795 776 }
796 if (stream_ == &null_) 777 else if (stream_ != &nullStream_)
797 {
798 // The logging is disabled for this level. The stream is the
799 // "null_" member of this object, so we can release the global
800 // mutex.
801 lock_.unlock();
802 }
803
804 (*stream_) << prefix;
805 }
806 catch (...)
807 {
808 // Something is going really wrong, probably running out of
809 // memory. Fallback to a degraded mode.
810 stream_ = loggingContext_->error_;
811 (*stream_) << "E???? ??:??:??.?????? ] ";
812 }
813 }
814
815
816 InternalLogger::~InternalLogger()
817 {
818 if (stream_ != &null_)
819 { 778 {
820 *stream_ << "\n"; 779 *stream_ << "\n";
821 stream_->flush(); 780 stream_->flush();
822 } 781 }
823 } 782 }
824 783
825 784
826 void Flush() 785 void Flush()
827 { 786 {
828 boost::mutex::scoped_lock lock(loggingMutex_); 787 if (pluginContext_ != NULL)
829 788 {
830 if (loggingContext_.get() != NULL && 789 boost::mutex::scoped_lock lock(loggingStreamsMutex_);
831 loggingContext_->file_.get() != NULL) 790
832 { 791 if (loggingStreamsContext_.get() != NULL &&
833 loggingContext_->file_->flush(); 792 loggingStreamsContext_->file_.get() != NULL)
834 } 793 {
835 } 794 loggingStreamsContext_->file_->flush();
795 }
796 }
797 }
798
836 799
837 void SetErrorWarnInfoLoggingStreams(std::ostream& errorStream, 800 void SetErrorWarnInfoLoggingStreams(std::ostream& errorStream,
838 std::ostream& warningStream, 801 std::ostream& warningStream,
839 std::ostream& infoStream) 802 std::ostream& infoStream)
840 { 803 {
841 std::unique_ptr<LoggingContext> old; 804 boost::mutex::scoped_lock lock(loggingStreamsMutex_);
842 805
843 { 806 loggingStreamsContext_.reset(new LoggingStreamsContext);
844 boost::mutex::scoped_lock lock(loggingMutex_); 807 loggingStreamsContext_->error_ = &errorStream;
845 808 loggingStreamsContext_->warning_ = &warningStream;
846 #if __cplusplus < 201103L 809 loggingStreamsContext_->info_ = &infoStream;
847 old.reset(loggingContext_.release());
848 #else
849 old = std::move(loggingContext_);
850 #endif
851
852 loggingContext_.reset(new LoggingContext);
853 loggingContext_->error_ = &errorStream;
854 loggingContext_->warning_ = &warningStream;
855 loggingContext_->info_ = &infoStream;
856 }
857
858 EnableInfoLevel(old->infoEnabled_);
859 EnableTraceLevel(old->traceEnabled_);
860 } 810 }
861 } 811 }
862 } 812 }
863 813
864 814