-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Expand file tree
/
Copy pathlogging.cpp
More file actions
284 lines (247 loc) · 9.1 KB
/
logging.cpp
File metadata and controls
284 lines (247 loc) · 9.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/**
* @file src/logging.cpp
* @brief Definitions for logging related functions.
*/
// standard includes
#include <fstream>
#include <iomanip>
#include <iostream>
// lib includes
#include <boost/core/null_deleter.hpp>
#include <boost/format.hpp>
#include <boost/log/attributes/clock.hpp>
#include <boost/log/common.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/utility/exception_handler.hpp>
// local includes
#include "logging.h"
// conditional includes
#ifdef __ANDROID__
#include <android/log.h>
#else
#include <display_device/logging.h>
#endif
extern "C" {
#include <libavutil/log.h>
}
using namespace std::literals;
namespace bl = boost::log;
boost::shared_ptr<boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend>> sink;
bl::sources::severity_logger<int> verbose(0); // Dominating output
bl::sources::severity_logger<int> debug(1); // Follow what is happening
bl::sources::severity_logger<int> info(2); // Should be informed about
bl::sources::severity_logger<int> warning(3); // Strange events
bl::sources::severity_logger<int> error(4); // Recoverable errors
bl::sources::severity_logger<int> fatal(5); // Unrecoverable errors
#ifdef SUNSHINE_TESTS
bl::sources::severity_logger<int> tests(10); // Automatic tests output
#endif
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
namespace logging {
deinit_t::~deinit_t() {
deinit();
}
void deinit() {
log_flush();
bl::core::get()->remove_sink(sink);
sink.reset();
}
void formatter(const boost::log::record_view &view, boost::log::formatting_ostream &os) {
constexpr const char *message = "Message";
constexpr const char *severity = "Severity";
auto log_level = view.attribute_values()[severity].extract<int>().get();
std::string_view log_type;
switch (log_level) {
case 0:
log_type = "Verbose: "sv;
break;
case 1:
log_type = "Debug: "sv;
break;
case 2:
log_type = "Info: "sv;
break;
case 3:
log_type = "Warning: "sv;
break;
case 4:
log_type = "Error: "sv;
break;
case 5:
log_type = "Fatal: "sv;
break;
#ifdef SUNSHINE_TESTS
case 10:
log_type = "Tests: "sv;
break;
#endif
};
auto now = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now - std::chrono::time_point_cast<std::chrono::seconds>(now)
);
auto t = std::chrono::system_clock::to_time_t(now);
auto lt = *std::localtime(&t);
os << "["sv << std::put_time(<, "%Y-%m-%d %H:%M:%S.") << boost::format("%03u") % ms.count() << "]: "sv
<< log_type << view.attribute_values()[message].extract<std::string>();
}
#ifdef __ANDROID__
namespace sinks = boost::log::sinks;
namespace expr = boost::log::expressions;
void android_log(const std::string &message, int severity) {
android_LogPriority android_priority;
switch (severity) {
case 0:
android_priority = ANDROID_LOG_VERBOSE;
break;
case 1:
android_priority = ANDROID_LOG_DEBUG;
break;
case 2:
android_priority = ANDROID_LOG_INFO;
break;
case 3:
android_priority = ANDROID_LOG_WARN;
break;
case 4:
android_priority = ANDROID_LOG_ERROR;
break;
case 5:
android_priority = ANDROID_LOG_FATAL;
break;
default:
android_priority = ANDROID_LOG_UNKNOWN;
break;
}
__android_log_print(android_priority, "Sunshine", "%s", message.c_str());
}
// custom sink backend for android
struct android_sink_backend: public sinks::basic_sink_backend<sinks::concurrent_feeding> {
void consume(const bl::record_view &rec) {
int log_sev = rec[severity].get();
const std::string log_msg = rec[expr::smessage].get();
// log to android
android_log(log_msg, log_sev);
}
};
#endif
[[nodiscard]] std::unique_ptr<deinit_t> init(int min_log_level, const std::string &log_file) {
if (sink) {
// Deinitialize the logging system before reinitializing it. This can probably only ever be hit in tests.
deinit();
}
#ifndef __ANDROID__
setup_av_logging(min_log_level);
setup_libdisplaydevice_logging(min_log_level);
#endif
sink = boost::make_shared<text_sink>();
#ifndef SUNSHINE_TESTS
boost::shared_ptr<std::ostream> stream {&std::cout, boost::null_deleter()};
sink->locked_backend()->add_stream(stream);
#endif
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(log_file));
sink->set_filter(severity >= min_log_level);
sink->set_formatter(&formatter);
// Prevent the async sink's background thread from dying on backend exceptions.
// Without this, a single I/O error (disk full, file locked, broken stdout, etc.)
// kills the thread and all subsequent log records are silently lost.
sink->set_exception_handler(bl::make_exception_suppressor());
// Flush after each log record to ensure log file contents on disk isn't stale.
// This is particularly important when running from a Windows service.
sink->locked_backend()->auto_flush(true);
bl::core::get()->add_sink(sink);
#ifdef __ANDROID__
auto android_sink = boost::make_shared<sinks::synchronous_sink<android_sink_backend>>();
bl::core::get()->add_sink(android_sink);
#endif
return std::make_unique<deinit_t>();
}
#ifndef __ANDROID__
void setup_av_logging(int min_log_level) {
if (min_log_level >= 1) {
av_log_set_level(AV_LOG_QUIET);
} else {
av_log_set_level(AV_LOG_DEBUG);
}
av_log_set_callback([](void *ptr, int level, const char *fmt, va_list vl) {
static int print_prefix = 1;
char buffer[1024];
av_log_format_line(ptr, level, fmt, vl, buffer, sizeof(buffer), &print_prefix);
if (level <= AV_LOG_ERROR) {
// We print AV_LOG_FATAL at the error level. FFmpeg prints things as fatal that
// are expected in some cases, such as lack of codec support or similar things.
BOOST_LOG(error) << buffer;
} else if (level <= AV_LOG_WARNING) {
BOOST_LOG(warning) << buffer;
} else if (level <= AV_LOG_INFO) {
BOOST_LOG(info) << buffer;
} else if (level <= AV_LOG_VERBOSE) {
// AV_LOG_VERBOSE is less verbose than AV_LOG_DEBUG
BOOST_LOG(debug) << buffer;
} else {
BOOST_LOG(verbose) << buffer;
}
});
}
void setup_libdisplaydevice_logging(int min_log_level) {
constexpr int min_level {static_cast<int>(display_device::Logger::LogLevel::verbose)};
constexpr int max_level {static_cast<int>(display_device::Logger::LogLevel::fatal)};
const auto log_level {static_cast<display_device::Logger::LogLevel>(std::min(std::max(min_level, min_log_level), max_level))};
display_device::Logger::get().setLogLevel(log_level);
display_device::Logger::get().setCustomCallback([](const display_device::Logger::LogLevel level, const std::string &message) {
switch (level) {
case display_device::Logger::LogLevel::verbose:
BOOST_LOG(verbose) << message;
break;
case display_device::Logger::LogLevel::debug:
BOOST_LOG(debug) << message;
break;
case display_device::Logger::LogLevel::info:
BOOST_LOG(info) << message;
break;
case display_device::Logger::LogLevel::warning:
BOOST_LOG(warning) << message;
break;
case display_device::Logger::LogLevel::error:
BOOST_LOG(error) << message;
break;
case display_device::Logger::LogLevel::fatal:
BOOST_LOG(fatal) << message;
break;
}
});
}
#endif
void log_flush() {
if (sink) {
sink->flush();
}
}
void print_help(const char *name) {
std::cout
<< "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
<< std::endl
<< " Note: The configuration will be created if it doesn't exist."sv << std::endl
<< std::endl
<< " --help | print help"sv << std::endl
<< " --creds username password | set user credentials for the Web manager"sv << std::endl
<< " --version | print the version of sunshine"sv << std::endl
<< std::endl
<< " flags"sv << std::endl
<< " -0 | Read PIN from stdin"sv << std::endl
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv << std::endl
<< " -2 | Force replacement of headers in video stream"sv << std::endl
<< " -p | Enable/Disable UPnP"sv << std::endl
<< std::endl;
}
std::string bracket(const std::string &input) {
return "["s + input + "]"s;
}
std::wstring bracket(const std::wstring &input) {
return L"["s + input + L"]"s;
}
} // namespace logging