2
* This file is part of the Ubuntu TV Media Scanner
3
* Copyright (C) 2012-2013 Canonical Ltd.
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU Lesser General Public License version 3 as
7
* published by the Free Software Foundation.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Contact: Jim Hodapp <jim.hodapp@canonical.com>
18
* Authored by: Mathias Hasselmann <mathias@openismus.com>
20
#include "mediascanner/logging.h"
22
// GLib based libraries
32
#include <boost/algorithm/string.hpp>
33
#include <boost/assert.hpp>
34
#include <boost/foreach.hpp>
35
#include <boost/locale/format.hpp>
37
// GCC Extensions to the C++ Standard Library
38
#include <ext/stdio_sync_filebuf.h>
40
// C++ Standard Library
44
// Media Scanner Library
45
#include "mediascanner/locale.h"
48
using boost::algorithm::starts_with;
49
using boost::algorithm::to_lower_copy;
50
using boost::locale::format;
52
namespace mediascanner {
55
const Domain::SettingsMap &Domain::ReadSettings() {
56
const char *const current_env = ::getenv("MEDIASCANNER_DEBUG");
57
static const char *parsed_env = null_ptr;
58
static SettingsMap settings;
60
if (current_env != parsed_env) {
61
std::istringstream env(safe_string(current_env));
64
for (std::string name; std::getline(env, name, ':'); ) {
68
Flags flags = Explicit | Enabled;
70
if (name[0] == '-' || name[0] == '!') {
71
name = name.substr(1);
72
flags = Explicit | Disabled;
73
} else if (name[0] == '+') {
74
name = name.substr(1);
77
SettingsMap::iterator it = settings.find(name);
79
if (it == settings.end())
80
it = settings.insert(std::make_pair(name, flags)).first;
85
parsed_env = current_env;
91
Domain::Flags Domain::LookupFlags(const std::string &name, Flags preset) {
92
std::string best_match;
95
BOOST_FOREACH (const SettingsMap::value_type &entry, ReadSettings()) {
96
if (entry.first == name) {
101
if ((preset & (Explicit | Enabled | Disabled)) == (Explicit | Disabled))
104
if (starts_with(name, entry.first)
105
&& name[entry.first.length()] == '/'
106
&& entry.first.length() > best_match.length()) {
107
best_match = entry.first;
108
flags = entry.second;
115
// Leave parent as const to permit static const declarations of logging
116
// domains in application code. Of course once could work arround by using
118
Domain::Domain(const std::string &name, Flags flags,
119
const Domain *parent, MessageSinkPtr sink)
120
: message_sink_(sink)
121
, parent_(const_cast<Domain *>(parent))
122
, name_(to_lower_copy(name))
123
, initial_flags_(LookupFlags(name_, flags))
124
, flags_(initial_flags_)
125
, default_message_sink_(message_sink()) {
130
Domain::Domain(const std::string &name,
131
const Domain *parent, MessageSinkPtr sink)
132
: message_sink_(sink)
133
, parent_(const_cast<Domain *>(parent))
134
, name_(to_lower_copy(name))
135
, initial_flags_(LookupFlags(name_, Enabled))
136
, flags_(initial_flags_)
137
, default_message_sink_(message_sink()) {
142
void Domain::VerifyGraph() const {
143
for (const Domain *other = parent_; other; other = other->parent_)
144
BOOST_ASSERT_MSG(this != other, "Domain graphs must be cycle-free");
147
void Domain::VerifyPrefix() const {
148
const std::string::size_type n = name().rfind('/');
150
if (n != std::string::npos) {
151
BOOST_ASSERT_MSG(parent_, "Parent needed for prefixed domain names");
153
const std::string prefix = name().substr(0, n);
155
if (parent_->name() != prefix) {
156
const std::string error_message =
157
(format("The parent domain's name (\"{1}\") must match "
158
"the prefix of this domain's name (\"{2}\").")
159
% parent_->name() % name()).str();
161
BOOST_ASSERT_MSG(parent_->name() == prefix,
162
error_message.c_str());
167
static MessageSinkPtr new_sink(const std::string &prefix,
168
DefaultMessageSink::Color color) {
169
return MessageSinkPtr(new DefaultMessageSink(&std::clog, prefix, color));
172
static Domain* unknown() {
173
static Domain domain("unknown", null_ptr,
174
new_sink("UNKNOWN", DefaultMessageSink::Red));
179
static Domain domain("error", unknown(),
180
new_sink("ERROR", DefaultMessageSink::Red));
185
static Domain domain("critical", error(),
186
new_sink("CRITICAL", DefaultMessageSink::Brown));
191
static Domain domain("warning", critical(),
192
new_sink("WARNING", DefaultMessageSink::Brown));
197
static Domain domain("message", warning(),
198
new_sink("WARNING", DefaultMessageSink::Default));
203
static Domain domain("info", message(),
204
new_sink("INFO", DefaultMessageSink::Blue));
209
static Domain domain("debug", Domain::Disabled, info(),
210
new_sink("DEBUG", DefaultMessageSink::Green));
215
static Domain domain("trace", Domain::Disabled, debug(),
216
new_sink("TRACE", DefaultMessageSink::Cyan));
220
static const MessageSinkPtr initial_default_sink(new DefaultMessageSink);
221
MessageSinkPtr MessageSink::default_instance_ = initial_default_sink;
223
MessageSink::~MessageSink() {
226
void MessageSink::Report(const std::string &domain_name,
227
const std::wstring &message) {
228
Report(domain_name, FromUnicode(message));
231
void MessageSink::set_default_instance(MessageSinkPtr instance) {
232
if (instance == initial_default_sink) {
233
for (Domain *domain = trace(); domain; domain->parent()) {
234
if (domain->message_sink() == default_instance_)
235
domain->set_message_sink(domain->default_message_sink());
238
for (Domain *domain = trace(); domain; domain->parent()) {
239
if (domain->message_sink() == domain->default_message_sink())
240
domain->set_message_sink(instance);
244
default_instance_ = instance;
247
static bool supports_color_codes(std::ostream *stream) {
248
using __gnu_cxx::stdio_sync_filebuf;
250
if (stdio_sync_filebuf<char> *const stdio =
251
dynamic_cast<stdio_sync_filebuf<char> *>(stream->rdbuf())) // NOLINT:runtime/rtti
252
return ::isatty(::fileno(stdio->file()));
257
static std::string ansi_color_code(DefaultMessageSink::Color color) {
259
case DefaultMessageSink::Default:
261
case DefaultMessageSink::Black:
263
case DefaultMessageSink::Red:
265
case DefaultMessageSink::Green:
267
case DefaultMessageSink::Brown:
269
case DefaultMessageSink::Blue:
271
case DefaultMessageSink::Magenta:
273
case DefaultMessageSink::Cyan:
275
case DefaultMessageSink::White:
277
case DefaultMessageSink::Bold:
281
return std::string();
284
DefaultMessageSink::DefaultMessageSink(std::ostream *stream,
285
const std::string &prefix,
289
if (not prefix_.empty()) {
290
if (color != Default && supports_color_codes(stream_))
291
prefix_ = ansi_color_code(color) + prefix_
292
+ ansi_color_code(Default);
298
void DefaultMessageSink::Report(const std::string &domain_name,
299
const std::string &message) {
300
std::ostringstream buffer;
302
buffer << program_invocation_short_name << "[" << getpid() << "]: "
303
<< prefix_ << domain_name << ": " << message << std::endl;
305
// Write formatted message in one go
306
// to avoid overlapping of messages from different threads.
307
const std::string result = buffer ? buffer.str() : message + "/n";
308
stream_->write(result.data(), result.size());
311
static void on_glib_message(const char *domain_name,
312
GLogLevelFlags log_level,
313
const char *text, void *) {
316
switch (log_level & G_LOG_LEVEL_MASK) {
317
case G_LOG_LEVEL_ERROR:
321
case G_LOG_LEVEL_CRITICAL:
325
case G_LOG_LEVEL_WARNING:
329
case G_LOG_LEVEL_MESSAGE:
332
case G_LOG_LEVEL_INFO:
336
case G_LOG_LEVEL_DEBUG:
345
Domain(domain->name() + "/" + domain_name, domain).print(text);
351
static bool capturing_glib_messages = false;
353
void capture_glib_messages() {
354
g_log_set_default_handler(on_glib_message, 0);
355
capturing_glib_messages = true;
358
bool is_capturing_glib_messages() {
359
return capturing_glib_messages;
362
} // namespace logging
363
} // namespace mediascanner