• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MikkelSchubert / adapterremoval / #45

20 Sep 2024 06:49PM UTC coverage: 26.244% (-49.2%) from 75.443%
#45

push

travis-ci

web-flow
attempt to fix coveralls run

2458 of 9366 relevant lines covered (26.24%)

4362.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.21
/src/logging.cpp
1
/*************************************************************************\
2
 * AdapterRemoval - cleaning next-generation sequencing reads            *
3
 *                                                                       *
4
 * Copyright (C) 2022 by Mikkel Schubert - mikkelsch@gmail.com           *
5
 *                                                                       *
6
 * This program is free software: you can redistribute it and/or modify  *
7
 * it under the terms of the GNU General Public License as published by  *
8
 * the Free Software Foundation, either version 3 of the License, or     *
9
 * (at your option) any later version.                                   *
10
 *                                                                       *
11
 * This program is distributed in the hope that it will be useful,       *
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 * GNU General Public License for more details.                          *
15
 *                                                                       *
16
 * You should have received a copy of the GNU General Public License     *
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
18
\*************************************************************************/
19
#include "logging.hpp"
20
#include "debug.hpp"    // for AR_REQUIRE, AR_FAIL
21
#include "main.hpp"     // for NAME, VERSION
22
#include "strutils.hpp" // for split_lines, cli_formatter, string_vec
23
#include <algorithm>    // for max, min
24
#include <iomanip>      // for operator<<, put_time
25
#include <iostream>     // for cerr
26
#include <limits>       // for numeric_limits
27
#include <mutex>        // for mutex, unique_lock
28
#include <sys/ioctl.h>  // for ioctl, winsize, TIOCGWINSZ
29
#include <unistd.h>     // for size_t, STDERR_FILENO
30
#include <vector>       // for vector
31

32
namespace adapterremoval {
33

34
namespace log {
35

36
namespace {
37

38
//! Shared mutex for STDOUT / STDERR
39
std::recursive_mutex g_log_mutex;
40
//! Pointer to logging stream; normally this will be std::cerr;
41
std::ostream* g_log_out = &std::cerr;
42

43
//! Minimum log-level to print
44
level g_log_level = level::debug;
45
//! Include timestamps when writing log lines
46
bool g_log_timestamps = true;
47
//! Use ANSI colors when writing log lines
48
bool g_log_colors = false;
49
//! Indicates if the last message was transient
50
bool g_log_transient = false;
51
//! Indicates that the preamble has been logged
52
bool g_preamble_logged = false;
53

54
//! ANSI color codes: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
55
enum class color
56
{
57
  black = 30,
58
  red,
59
  green,
60
  yellow,
61
  blue,
62
  magenta,
63
  cyan,
64
  white,
65
  reset
66
};
67

68
/** Returns the color associated with a given log level */
69
color
70
level_color(level l)
1✔
71
{
72
  switch (l) {
1✔
73
    case level::debug:
74
      return color::cyan;
75
    case level::info:
1✔
76
      return color::green;
1✔
77
    case level::warning:
×
78
      return color::yellow;
×
79
    case level::error:
×
80
      return color::red;
×
81
    case level::cerr:
×
82
    case level::none:
×
83
      AR_FAIL("unsupported logging level");
×
84
    default:
×
85
      AR_FAIL("invalid logging level");
×
86
  }
87
}
88

89
/** Writes the name of a given log level */
90
std::ostream&
91
operator<<(std::ostream& out, level l)
36✔
92
{
93
  switch (l) {
36✔
94
    case level::debug:
2✔
95
      return out << "DEBUG";
2✔
96
    case level::info:
12✔
97
      return out << "INFO";
12✔
98
    case level::warning:
8✔
99
      return out << "WARNING";
8✔
100
    case level::error:
14✔
101
      return out << "ERROR";
14✔
102
    case level::cerr:
×
103
    case level::none:
×
104
      AR_FAIL("unsupported logging level");
×
105
    default:
×
106
      AR_FAIL("invalid logging level");
×
107
  }
108
}
109

110
/** Writes a color control code */
111
std::ostream&
112
operator<<(std::ostream& out, color c)
2✔
113
{
114
  if (c == color::reset) {
1✔
115
    out << "\033[0m";
1✔
116
  } else {
117
    out << "\033[0;" << static_cast<int>(c) << "m";
1✔
118
  }
119

120
  return out;
2✔
121
}
122

123
/** Create timestamp+level prefix for logs */
124
std::string
125
log_header(level l, bool colors = false)
36✔
126
{
127
  std::ostringstream header;
36✔
128
  if (g_log_timestamps) {
36✔
129
    header << timestamp("%Y-%m-%d %X", true) << " ";
9✔
130
  }
131

132
  if (colors) {
36✔
133
    header << "[" << level_color(l) << l << color::reset << "] ";
2✔
134
  } else {
135
    header << "[" << l << "] ";
35✔
136
  }
137

138
  return header.str();
72✔
139
}
36✔
140

141
size_t
142
log_linewidth(const std::ostream& out)
36✔
143
{
144
  // Piped logs are not pretty-printed, to make analyses easier
145
  if (&out == &std::cerr) {
36✔
146
    return get_terminal_width();
×
147
  }
148

149
  return std::numeric_limits<size_t>::max();
150
}
151

152
void
153
log_print_lines(std::ostream& out,
36✔
154
                const std::string& head,
155
                const std::string& msg)
156
{
157
  // Account for unprinted color codes:
158
  // Size of color ("\033[0;XXm" = 7) + reset ("\033[0m" = 4)
159
  const int color_width = g_log_colors ? 11 : 0;
36✔
160
  const int head_width = head.size() - color_width;
36✔
161
  const int line_width = log_linewidth(out);
36✔
162

163
  for (const auto& line : split_lines(msg)) {
360✔
164
    const auto indent = line.find_first_not_of(' ');
36✔
165
    if (indent == std::string::npos) {
36✔
166
      out << head << "\n";
8✔
167
      continue;
4✔
168
    }
169

170
    cli_formatter fmt;
32✔
171
    fmt.set_ljust(2);
32✔
172
    fmt.set_indent(indent);
32✔
173
    fmt.set_column_width(line_width - indent - head_width);
32✔
174

175
    bool first_line = true;
32✔
176
    for (const auto& fragment : split_lines(fmt.format(line))) {
384✔
177
      if (first_line) {
32✔
178
        out << head + fragment << "\n";
64✔
179
        first_line = false;
32✔
180
      } else {
181
        out << std::string(head_width, ' ') << fragment << "\n";
×
182
      }
183
    }
32✔
184
  }
36✔
185
}
36✔
186

187
} // namespace
188

189
////////////////////////////////////////////////////////////////////////////////
190

191
void
192
log_preamble()
46✔
193
{
194
  std::unique_lock<std::recursive_mutex> lock(g_log_mutex);
46✔
195
  if (!g_preamble_logged) {
46✔
196
    g_preamble_logged = true;
1✔
197
    log::info() << NAME << " " << VERSION;
5✔
198
  }
199
}
92✔
200

201
void
202
set_level(level l)
4✔
203
{
204
  std::unique_lock<std::recursive_mutex> lock(g_log_mutex);
4✔
205

206
  g_log_level = l;
4✔
207
}
8✔
208

209
void
210
set_colors(bool enabled)
3✔
211
{
212
  std::unique_lock<std::recursive_mutex> lock(g_log_mutex);
3✔
213

214
  g_log_colors = enabled;
3✔
215
}
6✔
216

217
void
218
set_timestamps(bool enabled)
4✔
219
{
220
  std::unique_lock<std::recursive_mutex> lock(g_log_mutex);
4✔
221

222
  g_log_timestamps = enabled;
4✔
223
}
8✔
224

225
size_t
226
get_terminal_width()
×
227
{
228
  struct winsize params = {};
×
229
  // Attempt to retrieve the number of columns in the terminal
230
  if (ioctl(STDERR_FILENO, TIOCGWINSZ, &params) == 0) {
×
231
    return std::min<size_t>(120, std::max<size_t>(60, params.ws_col));
×
232
  }
233

234
  return std::numeric_limits<size_t>::max();
235
}
236

237
////////////////////////////////////////////////////////////////////////////////
238

239
log_stream::log_stream(level lvl)
71✔
240
  : m_level(lvl)
71✔
241
{
242
  AR_REQUIRE(lvl < level::none);
71✔
243
}
71✔
244

245
log_stream::~log_stream()
71✔
246
{
247
  std::unique_lock<std::recursive_mutex> lock(g_log_mutex);
71✔
248
  if (m_level != level::cerr) {
71✔
249
    log_preamble();
38✔
250
  }
251

252
  if (g_log_transient) {
71✔
253
    *g_log_out << "\r\033[K";
3✔
254
    g_log_transient = false;
3✔
255
  }
256

257
  if (m_level == level::cerr) {
71✔
258
    *g_log_out << m_stream.str();
132✔
259
    g_log_out->flush();
33✔
260
    g_log_transient = m_transient;
33✔
261
  } else if (m_level >= g_log_level) {
38✔
262
    const auto head = log_header(m_level, g_log_colors);
36✔
263

264
    // Trailing newlines are permitted for ease of use, e.g. when writing
265
    // complex, multi-line log messages
266
    auto msg = m_stream.str();
36✔
267
    while (!msg.empty() && msg.back() == '\n') {
72✔
268
      msg.pop_back();
×
269
    }
270

271
    log_print_lines(*g_log_out, head, msg);
36✔
272

273
    g_log_out->flush();
36✔
274
  }
72✔
275
}
142✔
276

277
log_stream&
278
log_stream::transient()
7✔
279
{
280
  AR_REQUIRE(m_level == level::cerr);
35✔
281
  m_transient = true;
3✔
282

283
  return *this;
3✔
284
}
285

286
////////////////////////////////////////////////////////////////////////////////
287

288
log_capture::log_capture()
44✔
289
{
290
  std::unique_lock<std::recursive_mutex> lock(g_log_mutex);
44✔
291

292
  AR_REQUIRE(g_log_out == &std::cerr);
44✔
293
  g_log_out = &m_stream;
44✔
294

295
  m_level = g_log_level;
44✔
296
  m_timestamps = g_log_timestamps;
44✔
297
  m_colors = g_log_colors;
44✔
298
}
88✔
299

300
log_capture::~log_capture()
44✔
301
{
302
  std::unique_lock<std::recursive_mutex> lock(g_log_mutex);
44✔
303

304
  AR_REQUIRE(g_log_out == &m_stream);
44✔
305
  g_log_out = &std::cerr;
44✔
306
  g_log_level = m_level;
44✔
307
  g_log_timestamps = m_colors;
44✔
308
  g_log_colors = m_colors;
44✔
309
}
88✔
310

311
} // namespace log
312

313
} // namespace adapterremoval
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc