Clingo
Loading...
Searching...
No Matches
logger.hh
1#pragma once
2
3#include <bitset>
4#include <cstdint>
5#include <cstdio>
6#include <functional>
7#include <limits>
8#include <sstream>
9#include <stdexcept>
10#ifndef _WIN32
11#include <unistd.h>
12#else
13#define isatty(X) false
14#endif
15
16namespace CppClingo {
17
20
22static constexpr size_t default_message_limit = 20;
23
27enum class MessageCode : uint8_t {
28 trace = 0,
29 debug = 1,
30 info = 2,
35 warn = 7,
36 error = 8,
37};
38
40enum class LogLevel : uint8_t {
41 trace = static_cast<uint8_t>(MessageCode::trace),
42 debug = static_cast<uint8_t>(MessageCode::debug),
43 info = static_cast<uint8_t>(MessageCode::info),
44 warn = static_cast<uint8_t>(MessageCode::warn),
45 error = static_cast<uint8_t>(MessageCode::error),
46};
47
49class parse_error : public std::runtime_error {
50 public:
52 parse_error() : std::runtime_error{"parsing failed"} {}
53};
54
56class rewrite_error : public std::runtime_error {
57 public:
59 rewrite_error() : std::runtime_error{"rewriting failed"} {}
60};
61
63class Logger {
64 public:
66 using Printer = std::function<void(MessageCode, std::string_view)>;
68 Logger(size_t limit = default_message_limit) : Logger{nullptr, limit} {}
70 Logger(Printer p, size_t limit = default_message_limit)
71 : p_(std::move(p)), limit_{limit}, cur_limit_(limit), color_{p_ == nullptr && isatty(fileno(stderr)) == 1} {}
72
74 [[nodiscard]] auto check(MessageCode code) -> bool;
78 void enable(MessageCode code, bool enable);
80 [[nodiscard]] auto enabled(MessageCode code) const -> bool;
82 void print(MessageCode code, std::string_view msg);
84 void set_level(LogLevel level);
86 void set_limit(size_t limit);
88 [[nodiscard]] auto message_prefix(MessageCode code) const -> std::string_view;
92 void reset();
94 void enable_color(bool color) { color_ = color; };
95
96 private:
97 Printer p_;
98 LogLevel level_ = LogLevel::info;
99 size_t limit_;
100 size_t cur_limit_;
101 std::bitset<static_cast<int>(MessageCode::error) + 1> disabled_;
102 bool color_;
103};
104
106class Report {
107 public:
109 Report(Logger &p, MessageCode code) : log_(p), code_(code) { out_ << log_.message_prefix(code) << ": "; }
111 template <class Loc> Report(Logger &p, MessageCode code, Loc const &loc) : log_(p), code_(code) {
112 out_ << loc << ": " << log_.message_prefix(code) << ": ";
113 }
115 ~Report() noexcept(false) { log_.print(code_, out_.view()); }
117 [[nodiscard]] auto out() -> std::ostringstream & { return out_; }
118
119 private:
120 std::ostringstream out_;
121 Logger &log_;
122 MessageCode code_;
123};
124
125inline auto Logger::check(MessageCode code) -> bool {
126 // ignore the message
127 if (static_cast<uint8_t>(code) < static_cast<uint8_t>(level_) || disabled_[static_cast<int>(code)]) {
128 return false;
129 }
130 // report trace and debug messages without reducing the limit
131 if (code < MessageCode::info) {
132 return true;
133 }
134 // report the message
135 if (cur_limit_ > 0) {
136 if (cur_limit_ != std::numeric_limits<size_t>::max()) {
137 --cur_limit_;
138 }
139 return true;
140 }
141 // ignore the message due to limit
142 return false;
143}
144
145inline auto Logger::enabled(MessageCode code) const -> bool {
146 if (code >= MessageCode::error) {
147 return true;
148 }
149 if (code < static_cast<MessageCode>(level_) || disabled_[static_cast<int>(code)]) {
150 return false;
151 }
152 return code < MessageCode::info || cur_limit_ > 0;
153}
154
155inline void Logger::enable(MessageCode code, bool enabled) {
156 disabled_[static_cast<int>(code)] = !enabled;
157}
158
159inline void Logger::set_level(LogLevel level) {
160 level_ = level;
161}
162
163inline void Logger::set_limit(size_t limit) {
164 cur_limit_ = limit;
165}
166
167inline void Logger::print(MessageCode code, std::string_view msg) {
168 if (p_ != nullptr) {
169 try {
170 p_(code, msg);
171 } catch (std::exception const &e) {
172 fprintf(stderr, "logging failed: %s\n", e.what());
173 fflush(stderr);
174 }
175 } else {
176 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
177 fprintf(stderr, "%.*s\n", static_cast<int>(msg.size()), msg.data());
178 fflush(stderr);
179 }
180}
181
182inline void Logger::reset() {
183 cur_limit_ = limit_;
184}
185
186inline auto Logger::message_prefix(MessageCode code) const -> std::string_view {
187 auto const *prefix = color_ ? "\033[31m"
188 "error"
189 "\033[0m"
190 : "error";
191 if (code < MessageCode::debug) {
192 prefix = color_ ? "\033[32m"
193 "trace"
194 "\033[0m"
195 : "trace";
196 } else if (code < MessageCode::info) {
197 prefix = color_ ? "\033[34m"
198 "debug"
199 "\033[0m"
200 : "debug";
201 } else if (code < MessageCode::warn) {
202 prefix = color_ ? "\033[35m"
203 "info"
204 "\033[0m"
205 : "info";
206 } else if (code < MessageCode::error) {
207 prefix = color_ ? "\033[33m"
208 "warning"
209 "\033[0m"
210 : "warning";
211 }
212 return prefix;
213}
214
215// NOLINTBEGIN(cppcoreguidelines-macro-usage)
216
218#define CLINGO_REPORT(p, id) \
219 if ((p).check(::CppClingo::MessageCode::id)) \
220 CppClingo::Report(p, ::CppClingo::MessageCode::id).out()
221
223#define CLINGO_REPORT_LOC(p, id, loc) \
224 if ((p).check(::CppClingo::MessageCode::id)) \
225 CppClingo::Report(p, ::CppClingo::MessageCode::id, loc).out()
226
228#define CLINGO_REPORT_STR(p, id, msg) \
229 if ((p).check(::CppClingo::MessageCode::id)) { \
230 (p).print(::CppClingo::MessageCode::id, msg); \
231 }
232
233// NOLINTEND(cppcoreguidelines-macro-usage)
234
236
237} // namespace CppClingo
Simple logger to report message to stderr or via a callback.
Definition logger.hh:63
Logger(Printer p, size_t limit=default_message_limit)
Construct a logger reporting messages via the given callback.
Definition logger.hh:70
void enable_color(bool color)
Explicitly enable or disable coloring.
Definition logger.hh:94
std::function< void(MessageCode, std::string_view)> Printer
Callback to report messages.
Definition logger.hh:66
Logger(size_t limit=default_message_limit)
Construct a logger reporting messages to stderr.
Definition logger.hh:68
Helper class to ease logging.
Definition logger.hh:106
auto out() -> std::ostringstream &
Get message sink.
Definition logger.hh:117
Report(Logger &p, MessageCode code, Loc const &loc)
Construct reporter with additional location information.
Definition logger.hh:111
Report(Logger &p, MessageCode code)
Construct reporter.
Definition logger.hh:109
~Report() noexcept(false)
Destroy the reporter and output message.
Definition logger.hh:115
Exception to indicate that parsing failed.
Definition logger.hh:49
parse_error()
Default constructor.
Definition logger.hh:52
Exception to indicate that parsing failed.
Definition logger.hh:56
rewrite_error()
Default constructor.
Definition logger.hh:59
void reset()
Reset the logger to the constructed state.
Definition logger.hh:182
void set_level(LogLevel level)
Set the log level.
Definition logger.hh:159
void set_limit(size_t limit)
Set the message limit.
Definition logger.hh:163
auto message_prefix(MessageCode code) const -> std::string_view
Get a string representation of the message category.
Definition logger.hh:186
auto enabled(MessageCode code) const -> bool
Check if the given message code is enabled.
Definition logger.hh:145
MessageCode
Codes of messages.
Definition logger.hh:27
void enable(MessageCode code, bool enable)
Enable or disable a message code.
Definition logger.hh:155
void print(MessageCode code, std::string_view msg)
Unconditionally output a message with a given code.
Definition logger.hh:167
LogLevel
Log levels for coarse-grained configuration of logging.
Definition logger.hh:40
auto check(MessageCode code) -> bool
Check if a message with the given code should be reported.
Definition logger.hh:125
@ info_operation_undefined
Generic info messages.
@ warn
Info message for global variables.
@ info_atom_undefined
Info message for undefined operations.
@ debug
Trace messages.
@ info
Debug messages.
@ info_global_variable
Info message for duplicate includes.
@ info_file_included
Info message for undefined atoms.
@ info
Output info messages.