statusbarlog 1.0
This api is used for creating statusbars and for better logging alongside these statusbars.
Loading...
Searching...
No Matches
statusbar_log Namespace Reference

Functions

void FlushOutput ()
 
void SaveCursorPosition ()
 
void RestoreCursorPosition ()
 
void ClearToEndOfLine ()
 
void ClearFromStartOfLine ()
 
void ClearLine ()
 
void ClearCurrentLine ()
 
int Log (const LogLevel log_level, const std::string &filename, const char *fmt,...)
 
int CreateStatusbarHandle (StatusbarHandle &statusbar_handle, const std::vector< unsigned int > _positions, const std::vector< unsigned int > _bar_sizes, const std::vector< std::string > _prefixes, const std::vector< std::string > _postfixes)
 
int DestroyStatusbarHandle (StatusbarHandle &statusbar_handle)
 
int UpdateStatusbar (StatusbarHandle &statusbar_handle, const std::size_t idx, const double percent)
 

Function Documentation

◆ ClearCurrentLine()

void statusbar_log::ClearCurrentLine ( )

Definition at line 409 of file statusbarlog.cpp.

409 {
410 std::cout << "\r" // Return to line start
411 << "\033[2K"; // Clear entire line
412 _ConditionalFlush();
413}
Here is the caller graph for this function:

◆ ClearFromStartOfLine()

void statusbar_log::ClearFromStartOfLine ( )

Definition at line 399 of file statusbarlog.cpp.

399 {
400 std::cout << "\033[1K"; // ANSI escape code to clear to end of line
401 _ConditionalFlush();
402}

◆ ClearLine()

void statusbar_log::ClearLine ( )

Definition at line 404 of file statusbarlog.cpp.

404 {
405 std::cout << "\033[2K";
406 _ConditionalFlush();
407}

◆ ClearToEndOfLine()

void statusbar_log::ClearToEndOfLine ( )

Definition at line 394 of file statusbarlog.cpp.

394 {
395 std::cout << "\033[0K"; // ANSI escape code to clear to end of line
396 _ConditionalFlush();
397}

◆ CreateStatusbarHandle()

int statusbar_log::CreateStatusbarHandle ( StatusbarHandle &  statusbar_handle,
const std::vector< unsigned int >  _positions,
const std::vector< unsigned int >  _bar_sizes,
const std::vector< std::string >  _prefixes,
const std::vector< std::string >  _postfixes 
)

Definition at line 488 of file statusbarlog.cpp.

492 {
493 statusbar_handle.valid = false;
494 statusbar_handle.id = 0;
495 if (_positions.size() != _bar_sizes.size() ||
496 _bar_sizes.size() != _prefixes.size() ||
497 _prefixes.size() != _postfixes.size()) {
498 LogErr(kFilename,
499 "Failed to create statusbar_handle The vecotors '_positions', "
500 "'_bar_sizes', '_prefixes' and "
501 "'_postfixes' must have the same size! Got: '_positions': %d, "
502 "'_bar_sizes': %d, '_prefixes': %d, '_postfixes': %d.",
503 _positions.size(), _bar_sizes.size(), _prefixes.size(),
504 _postfixes.size());
505 return -1;
506 }
507
508 if (_statusbar_registry.size() - _statusbar_free_handles.size() >=
509 kMaxHandles) {
510 LogErr(
511 kFilename,
512 "Failed to create statusbar handle. Maximum status bars (%zu) reached",
513 statusbar_log::kMaxHandles);
514 return -2;
515 }
516
517 std::unique_lock<std::mutex> console_lock(_console_mutex, std::defer_lock);
518 std::unique_lock<std::mutex> registry_lock(_registry_mutex, std::defer_lock);
519 std::unique_lock<std::mutex> ID_count_lock(_id_count_mutex, std::defer_lock);
520 std::lock(console_lock, registry_lock, ID_count_lock);
521
522 _handle_id_count++;
523 if (_handle_id_count == 0) {
524 console_lock.unlock();
525 registry_lock.unlock();
526 LogWrn(kFilename,
527 "Max number of possible statusbar handle.ids reached, looping back "
528 "to 1");
529 std::lock(console_lock, registry_lock);
530 _handle_id_count++;
531 }
532
533 const std::size_t num_bars = _positions.size();
534 const std::vector<double> percentages(num_bars, 0.0);
535 const std::vector<std::size_t> spin_idxs(num_bars, 0);
536
537 std::vector<std::string> sanitized_prefixes;
538 sanitized_prefixes.reserve(_prefixes.size());
539 std::vector<std::string> sanitized_postfixes;
540 sanitized_prefixes.reserve(_postfixes.size());
541 std::vector<unsigned int> sanitized_bar_sizes;
542 sanitized_bar_sizes.reserve(_bar_sizes.size());
543 for (std::size_t i = 0; i < _prefixes.size(); ++i) {
544 std::string _prefix = _prefixes[i];
545 if (_prefix.length() > kMaxPrefixLength) {
546 _prefix.resize(kMaxPrefixLength - 3);
547 _prefix += "...";
548 }
549 sanitized_prefixes.push_back(_SanitizeString(_prefix));
550
551 std::string _postfix = _postfixes[i];
552 if (_postfix.length() > kMaxPostfixLength) {
553 _postfix.resize(kMaxPostfixLength - 3);
554 _postfix += "...";
555 }
556 sanitized_postfixes.push_back(_SanitizeString(_postfix));
557
558 sanitized_bar_sizes.push_back(
559 std::min<unsigned int>(_bar_sizes[i], kMaxBarWidth));
560 }
561
562 if (!_statusbar_free_handles.empty()) {
563 StatusbarHandle free_handle = _statusbar_free_handles.back();
564 _statusbar_free_handles.pop_back();
565 statusbar_handle.idx = free_handle.idx;
566 _statusbar_registry[statusbar_handle.idx] = {
567 percentages, _positions,
568 sanitized_bar_sizes, sanitized_prefixes,
569 sanitized_postfixes, spin_idxs,
570 _handle_id_count, false};
571 } else {
572 statusbar_handle.idx = _statusbar_registry.size();
573 _statusbar_registry.emplace_back(Statusbar{
574 percentages, _positions, sanitized_bar_sizes, sanitized_prefixes,
575 sanitized_postfixes, spin_idxs, _handle_id_count, false});
576 }
577 statusbar_handle.id = _handle_id_count;
578 statusbar_handle.valid = true;
579 for (std::size_t idx = 0; idx < num_bars; idx++) {
580 _DrawStatusbarComponent(
581 0.0, _statusbar_registry[statusbar_handle.idx].bar_sizes[idx],
582 _statusbar_registry[statusbar_handle.idx].prefixes[idx],
583 _statusbar_registry[statusbar_handle.idx].postfixes[idx],
584 _statusbar_registry[statusbar_handle.idx].spin_idxs[idx],
585 _statusbar_registry[statusbar_handle.idx].positions[idx]);
586 }
587 return kStatusbarLogSuccess;
588}
const std::string kFilename
std::vector< std::size_t > spin_idxs
Spinner animation indices.
std::vector< double > percentages
Progress percentages (0-100) for each bar.

◆ DestroyStatusbarHandle()

int statusbar_log::DestroyStatusbarHandle ( StatusbarHandle &  statusbar_handle)

Definition at line 590 of file statusbarlog.cpp.

590 {
591 std::unique_lock<std::mutex> console_lock(_console_mutex, std::defer_lock);
592 std::unique_lock<std::mutex> registry_lock(_registry_mutex, std::defer_lock);
593 std::lock(console_lock, registry_lock);
594
595 const int err = _IsValidHandle(statusbar_handle);
596 if (err != kStatusbarLogSuccess) {
597 console_lock.unlock();
598 registry_lock.unlock();
599 _IsValidHandleVerbose(statusbar_handle);
600 LogErr(kFilename, "Failed to destory statusbar_handle!");
601 return err;
602 }
603 Statusbar& target = _statusbar_registry[statusbar_handle.idx];
604
605 for (std::size_t i = 0; i < target.positions.size(); i++) {
606 _MoveCursorUp(target.positions[i]);
607 ClearCurrentLine();
608 _MoveCursorUp(-target.positions[i]);
609 }
610 std::cout.flush();
611
612 target.percentages.clear();
613 target.positions.clear();
614 target.bar_sizes.clear();
615
616 for (std::string& str : target.prefixes) {
617 std::fill(str.begin(), str.end(), '\0');
618 }
619 for (std::string& str : target.postfixes) {
620 std::fill(str.begin(), str.end(), '\0');
621 }
622 target.prefixes.clear();
623 target.postfixes.clear();
624
625 target.id = 0;
626 target.spin_idxs.clear();
627
628 _statusbar_free_handles.push_back(statusbar_handle);
629
630 statusbar_handle.valid = false;
631 statusbar_handle.id = 0;
632
633 return kStatusbarLogSuccess;
634}
std::vector< std::string > prefixes
Text displayed before each bar.
std::vector< std::string > postfixes
Text displayed after each bar.
Here is the call graph for this function:

◆ FlushOutput()

void statusbar_log::FlushOutput ( )

Definition at line 382 of file statusbarlog.cpp.

382{ std::cout << std::flush; }

◆ Log()

int statusbar_log::Log ( const LogLevel  log_level,
const std::string &  filename,
const char *  fmt,
  ... 
)

Definition at line 415 of file statusbarlog.cpp.

416 {
417 if (log_level > kLogLevel) return kStatusbarLogSuccess;
418 std::unique_lock<std::mutex> console_lock(_console_mutex, std::defer_lock);
419 std::unique_lock<std::mutex> registry_lock(_registry_mutex, std::defer_lock);
420 std::lock(console_lock, registry_lock);
421 // std::lock_guard<std::mutex> ID_count_lock(_id_count_mutex);
422
423 const bool statusbars_active = !_statusbar_registry.empty();
424
425 const char* prefix = "";
426 // clang-format off
427 switch(log_level){
428 case kLogLevelErr: prefix = "ERROR"; break;
429 case kLogLevelWrn: prefix = "WARNING"; break;
430 case kLogLevelInf: prefix = "INFO"; break;
431 case kLogLevelDbg: prefix = "DEBUG"; break;
432 default: break;
433 }
434 // clang-format on
435
436 int move = 0;
437 if (statusbars_active) {
438 for (std::size_t i = 0; i < _statusbar_registry.size(); ++i) {
439 for (std::size_t j = 0; j < _statusbar_registry[i].positions.size();
440 ++j) {
441 int current_pos = _statusbar_registry[i].positions[j];
442 if (current_pos > move) {
443 move = current_pos;
444 }
445 }
446 }
447 }
448
449 va_list args;
450 va_list args_copy;
451 _MoveCursorUp(move);
452 if (statusbars_active) printf("\r\033[2K\r");
453 va_start(args, fmt);
454 va_copy(args_copy, args);
455 unsigned int size = std::vsnprintf(nullptr, 0, fmt, args_copy);
456 size = std::min(size, kMaxLogLength);
457 std::vector<char> buffer(size + 1);
458 std::vsnprintf(buffer.data(), buffer.size(), fmt, args);
459 std::string message = _SanitizeStringWithNewline(buffer.data());
460 va_end(args);
461
462 std::string sanitized_filename = _SanitizeStringWithNewline(filename);
463 if (sanitized_filename.length() > kMaxFilenameLength) {
464 sanitized_filename.resize(kMaxFilenameLength - 3);
465 sanitized_filename += "...";
466 }
467
468 printf("%s [%s]: %s\n", prefix, sanitized_filename.c_str(), message.c_str());
469 _ConditionalFlush();
470 _MoveCursorUp(-move);
471
472 if (statusbars_active) {
473 for (std::size_t i = 0; i < _statusbar_registry.size(); ++i) {
474 for (std::size_t j = 0; j < _statusbar_registry[i].positions.size();
475 ++j) {
476 _DrawStatusbarComponent(_statusbar_registry[i].percentages[j],
477 _statusbar_registry[i].bar_sizes[j],
478 _statusbar_registry[i].prefixes[j],
479 _statusbar_registry[i].postfixes[j],
480 _statusbar_registry[i].spin_idxs[j],
481 _statusbar_registry[i].positions[j]);
482 }
483 }
484 }
485 return kStatusbarLogSuccess;
486}
std::vector< unsigned int > positions
Vertical positions (1=topmost).
std::vector< unsigned int > bar_sizes
Total width (characters) of each bar.

◆ RestoreCursorPosition()

void statusbar_log::RestoreCursorPosition ( )

Definition at line 389 of file statusbarlog.cpp.

389 {
390 std::cout << "\033[u"; // ANSI escape code to restore cursor position
391 _ConditionalFlush();
392}

◆ SaveCursorPosition()

void statusbar_log::SaveCursorPosition ( )

Definition at line 384 of file statusbarlog.cpp.

384 {
385 std::cout << "\033[s"; // ANSI escape code to save cursor position
386 _ConditionalFlush();
387}

◆ UpdateStatusbar()

int statusbar_log::UpdateStatusbar ( StatusbarHandle &  statusbar_handle,
const std::size_t  idx,
const double  percent 
)

Definition at line 636 of file statusbarlog.cpp.

637 {
638 std::unique_lock<std::mutex> console_lock(_console_mutex, std::defer_lock);
639 std::unique_lock<std::mutex> registry_lock(_registry_mutex, std::defer_lock);
640 std::lock(console_lock, registry_lock);
641
642 // std::lock_guard<std::mutex> ID_count_lock(_id_count_mutex);
643
644 const int err = _IsValidHandle(statusbar_handle);
645 if (err != kStatusbarLogSuccess) {
646 console_lock.unlock();
647 registry_lock.unlock();
648 _IsValidHandleVerbose(statusbar_handle);
649 LogErr(kFilename, "Failed to update statusbar: Invalid handle.");
650 return err;
651 }
652
653 if (percent > 100.0 || percent < 0.0) {
654 console_lock.unlock();
655 registry_lock.unlock();
656 LogErr(kFilename, "Failed to update statusbar: Invalid percentage.");
657 return -5;
658 }
659
660 Statusbar& statusbar = _statusbar_registry[statusbar_handle.idx];
661
662 if (idx >= statusbar.percentages.size()) {
663 console_lock.unlock();
664 registry_lock.unlock();
665 LogErr(kFilename, "Failed to update statusbar: Invalid bar index.");
666 return -6;
667 }
668
669 statusbar.percentages[idx] = percent;
670 statusbar.spin_idxs[idx] = statusbar.spin_idxs[idx] + 1;
671 int bar_error_code = _DrawStatusbarComponent(
672 percent, statusbar.bar_sizes[idx], statusbar.prefixes[idx],
673 statusbar.postfixes[idx], statusbar.spin_idxs[idx],
674 statusbar.positions[idx]);
675
676 if (bar_error_code != kStatusbarLogSuccess && !statusbar.error_reported) {
677 statusbar.error_reported = true;
678 const char* why;
679 switch (bar_error_code) {
680 case -1:
681 why = "Terminal width detection failed (Windows)";
682 break;
683 case -2:
684 why = "Terminal width detection failed (Linux)";
685 break;
686 case -3:
687 why = "Truncating was needed";
688 break;
689 case -4:
690 why =
691 "Terminal width detection failed (Windows) and truncation was "
692 "needed";
693 break;
694 case -5:
695 why =
696 "Terminal width detection failed (Linux) and truncation was "
697 "needed";
698 break;
699 }
700 LogErr(kFilename, "%s on statusbar with ID %u at bar idx %zu!", why,
701 statusbar.id, idx);
702 }
703
704 return kStatusbarLogSuccess;
705}