Better terminal handling & logging

This commit is contained in:
Marcus Holland-Moritz 2020-12-14 01:42:25 +01:00
parent d853e9f3bf
commit a4eafa7aa2
8 changed files with 166 additions and 15 deletions

View File

@ -169,6 +169,7 @@ list(
src/dwarfs/progress.cpp src/dwarfs/progress.cpp
src/dwarfs/scanner.cpp src/dwarfs/scanner.cpp
src/dwarfs/similarity.cpp src/dwarfs/similarity.cpp
src/dwarfs/terminal.cpp
src/dwarfs/util.cpp src/dwarfs/util.cpp
src/dwarfs/worker_group.cpp) src/dwarfs/worker_group.cpp)

View File

@ -57,5 +57,6 @@ class console_writer : public logger {
const progress_mode pg_mode_; const progress_mode pg_mode_;
const size_t width_; const size_t width_;
const display_mode mode_; const display_mode mode_;
const bool color_;
}; };
} // namespace dwarfs } // namespace dwarfs

View File

@ -80,6 +80,7 @@ class stream_logger : public logger {
std::ostream& os_; std::ostream& os_;
std::mutex mx_; std::mutex mx_;
std::atomic<level_type> threshold_; std::atomic<level_type> threshold_;
const bool color_;
}; };
class level_logger { class level_logger {

51
include/dwarfs/terminal.h Normal file
View File

@ -0,0 +1,51 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/**
* \author Marcus Holland-Moritz (github@mhxnet.de)
* \copyright Copyright (c) Marcus Holland-Moritz
*
* This file is part of dwarfs.
*
* dwarfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dwarfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <iosfwd>
namespace dwarfs {
enum class termcolor {
NORMAL,
RED,
GREEN,
YELLOW,
BLUE,
CYAN,
WHITE,
MAGENTA,
BOLD_RED,
BOLD_GREEN,
BOLD_YELLOW,
BOLD_BLUE,
BOLD_MAGENTA,
BOLD_CYAN,
BOLD_WHITE,
NUM_COLORS
};
bool stream_is_fancy_terminal(std::ostream& os);
char const* terminal_color(termcolor color);
} // namespace dwarfs

View File

@ -31,6 +31,7 @@
#include "dwarfs/entry_interface.h" #include "dwarfs/entry_interface.h"
#include "dwarfs/inode.h" #include "dwarfs/inode.h"
#include "dwarfs/progress.h" #include "dwarfs/progress.h"
#include "dwarfs/terminal.h"
#include "dwarfs/util.h" #include "dwarfs/util.h"
namespace dwarfs { namespace dwarfs {
@ -50,7 +51,8 @@ console_writer::console_writer(std::ostream& os, progress_mode pg_mode,
, frac_(0.0) , frac_(0.0)
, pg_mode_(pg_mode) , pg_mode_(pg_mode)
, width_(width) , width_(width)
, mode_(mode) { , mode_(mode)
, color_(stream_is_fancy_terminal(os)) {
os_.imbue(std::locale(os_.getloc(), os_.imbue(std::locale(os_.getloc(),
new boost::posix_time::time_facet("%H:%M:%S.%f"))); new boost::posix_time::time_facet("%H:%M:%S.%f")));
if (threshold > level_type::INFO) { if (threshold > level_type::INFO) {
@ -79,19 +81,21 @@ void console_writer::write(level_type level, const std::string& output) {
const char* prefix = ""; const char* prefix = "";
const char* suffix = ""; const char* suffix = "";
switch (level) { if (color_) {
case ERROR: switch (level) {
prefix = "\033[1;31m"; case ERROR:
suffix = "\033[0m"; prefix = terminal_color(termcolor::BOLD_RED);
break; suffix = terminal_color(termcolor::NORMAL);
break;
case WARN: case WARN:
prefix = "\033[1;33m"; prefix = terminal_color(termcolor::BOLD_YELLOW);
suffix = "\033[0m"; suffix = terminal_color(termcolor::NORMAL);
break; break;
default: default:
break; break;
}
} }
std::lock_guard<std::mutex> lock(mx_); std::lock_guard<std::mutex> lock(mx_);

View File

@ -32,6 +32,7 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "dwarfs/logger.h" #include "dwarfs/logger.h"
#include "dwarfs/terminal.h"
namespace dwarfs { namespace dwarfs {
@ -91,7 +92,8 @@ logger::level_type logger::parse_level(std::string_view level) {
} }
stream_logger::stream_logger(std::ostream& os, level_type threshold) stream_logger::stream_logger(std::ostream& os, level_type threshold)
: os_(os) { : os_(os)
, color_(stream_is_fancy_terminal(os)) {
os_.imbue(std::locale(os_.getloc(), os_.imbue(std::locale(os_.getloc(),
new boost::posix_time::time_facet("%H:%M:%S.%f"))); new boost::posix_time::time_facet("%H:%M:%S.%f")));
set_threshold(threshold); set_threshold(threshold);
@ -100,9 +102,28 @@ stream_logger::stream_logger(std::ostream& os, level_type threshold)
void stream_logger::write(level_type level, const std::string& output) { void stream_logger::write(level_type level, const std::string& output) {
if (level <= threshold_) { if (level <= threshold_) {
auto t = boost::posix_time::microsec_clock::local_time(); auto t = boost::posix_time::microsec_clock::local_time();
const char* prefix = "";
const char* suffix = "";
if (color_) {
switch (level) {
case ERROR:
prefix = terminal_color(termcolor::BOLD_RED);
suffix = terminal_color(termcolor::NORMAL);
break;
case WARN:
prefix = terminal_color(termcolor::BOLD_YELLOW);
suffix = terminal_color(termcolor::NORMAL);
break;
default:
break;
}
}
std::lock_guard<std::mutex> lock(mx_); std::lock_guard<std::mutex> lock(mx_);
os_ << t << " " << output << "\n"; os_ << prefix << t << " " << output << suffix << "\n";
if (threshold_ == TRACE) { if (threshold_ == TRACE) {
backtrace(os_); backtrace(os_);

71
src/dwarfs/terminal.cpp Normal file
View File

@ -0,0 +1,71 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/**
* \author Marcus Holland-Moritz (github@mhxnet.de)
* \copyright Copyright (c) Marcus Holland-Moritz
*
* This file is part of dwarfs.
*
* dwarfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dwarfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
*/
#include <array>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <unistd.h>
#include "dwarfs/terminal.h"
namespace dwarfs {
bool stream_is_fancy_terminal(std::ostream& os) {
if (&os == &std::cout && !::isatty(::fileno(stdout))) {
return false;
}
if (&os == &std::cerr && !::isatty(::fileno(stderr))) {
return false;
}
auto term = ::getenv("TERM");
std::cerr << "term: " << term << std::endl;
return term && term[0] && ::strcmp(term, "dumb");
}
char const* terminal_color(termcolor color) {
static constexpr std::array<char const*,
static_cast<size_t>(termcolor::NUM_COLORS)>
colors = {{
"\033[0m",
"\033[31m",
"\033[32m",
"\033[33m",
"\033[34m",
"\033[35m",
"\033[36m",
"\033[37m",
"\033[1;31m",
"\033[1;32m",
"\033[1;33m",
"\033[1;34m",
"\033[1;35m",
"\033[1;36m",
"\033[1;37m",
}};
return colors.at(static_cast<size_t>(color));
}
} // namespace dwarfs

View File

@ -68,6 +68,7 @@
#include "dwarfs/progress.h" #include "dwarfs/progress.h"
#include "dwarfs/scanner.h" #include "dwarfs/scanner.h"
#include "dwarfs/script.h" #include "dwarfs/script.h"
#include "dwarfs/terminal.h"
#include "dwarfs/util.h" #include "dwarfs/util.h"
#ifdef DWARFS_HAVE_PYTHON #ifdef DWARFS_HAVE_PYTHON
@ -599,7 +600,7 @@ int mkdwarfs(int argc, char** argv) {
if (no_progress) { if (no_progress) {
progress_mode = "none"; progress_mode = "none";
} }
if (progress_mode != "none" && !::isatty(::fileno(stderr))) { if (progress_mode != "none" && !stream_is_fancy_terminal(std::cerr)) {
progress_mode = "simple"; progress_mode = "simple";
} }
if (!progress_modes.count(progress_mode)) { if (!progress_modes.count(progress_mode)) {