~ubuntu-branches/ubuntu/trusty/mediascanner/trusty-proposed

« back to all changes in this revision

Viewing changes to src/mediascanner/logging.cpp

  • Committer: Package Import Robot
  • Author(s): Łukasz 'sil2100' Zemczak
  • Date: 2013-08-13 18:47:34 UTC
  • Revision ID: package-import@ubuntu.com-20130813184734-u3lyvu2u1hgybryc
Tags: upstream-0.3.93
Import upstream version 0.3.93

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This file is part of the Ubuntu TV Media Scanner
 
3
 * Copyright (C) 2012-2013 Canonical Ltd.
 
4
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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/>.
 
16
 *
 
17
 * Contact: Jim Hodapp <jim.hodapp@canonical.com>
 
18
 * Authored by: Mathias Hasselmann <mathias@openismus.com>
 
19
 */
 
20
#include "mediascanner/logging.h"
 
21
 
 
22
// GLib based libraries
 
23
#include <glib.h>
 
24
 
 
25
// C Standard Library
 
26
#include <errno.h>
 
27
#include <stdio.h>
 
28
#include <stdlib.h>
 
29
#include <unistd.h>
 
30
 
 
31
// Boost C++
 
32
#include <boost/algorithm/string.hpp>
 
33
#include <boost/assert.hpp>
 
34
#include <boost/foreach.hpp>
 
35
#include <boost/locale/format.hpp>
 
36
 
 
37
// GCC Extensions to the C++ Standard Library
 
38
#include <ext/stdio_sync_filebuf.h>
 
39
 
 
40
// C++ Standard Library
 
41
#include <map>
 
42
#include <string>
 
43
 
 
44
// Media Scanner Library
 
45
#include "mediascanner/locale.h"
 
46
 
 
47
// Boost C++
 
48
using boost::algorithm::starts_with;
 
49
using boost::algorithm::to_lower_copy;
 
50
using boost::locale::format;
 
51
 
 
52
namespace mediascanner {
 
53
namespace logging {
 
54
 
 
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;
 
59
 
 
60
    if (current_env != parsed_env) {
 
61
        std::istringstream env(safe_string(current_env));
 
62
        settings.clear();
 
63
 
 
64
        for (std::string name; std::getline(env, name, ':'); ) {
 
65
            if (name.empty())
 
66
                continue;
 
67
 
 
68
            Flags flags = Explicit | Enabled;
 
69
 
 
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);
 
75
            }
 
76
 
 
77
            SettingsMap::iterator it = settings.find(name);
 
78
 
 
79
            if (it == settings.end())
 
80
                it = settings.insert(std::make_pair(name, flags)).first;
 
81
            else
 
82
                it->second = flags;
 
83
        }
 
84
 
 
85
        parsed_env = current_env;
 
86
    }
 
87
 
 
88
    return settings;
 
89
}
 
90
 
 
91
Domain::Flags Domain::LookupFlags(const std::string &name, Flags preset) {
 
92
    std::string best_match;
 
93
    Flags flags = preset;
 
94
 
 
95
    BOOST_FOREACH (const SettingsMap::value_type &entry, ReadSettings()) {
 
96
        if (entry.first == name) {
 
97
            flags = entry.second;
 
98
            break;
 
99
        }
 
100
 
 
101
        if ((preset & (Explicit | Enabled | Disabled)) == (Explicit | Disabled))
 
102
            continue;
 
103
 
 
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;
 
109
        }
 
110
    }
 
111
 
 
112
    return flags;
 
113
}
 
114
 
 
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
 
117
// the parent()
 
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()) {
 
126
    VerifyGraph();
 
127
    VerifyPrefix();
 
128
}
 
129
 
 
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()) {
 
138
    VerifyGraph();
 
139
    VerifyPrefix();
 
140
}
 
141
 
 
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");
 
145
}
 
146
 
 
147
void Domain::VerifyPrefix() const {
 
148
    const std::string::size_type n = name().rfind('/');
 
149
 
 
150
    if (n != std::string::npos) {
 
151
        BOOST_ASSERT_MSG(parent_, "Parent needed for prefixed domain names");
 
152
 
 
153
        const std::string prefix = name().substr(0, n);
 
154
 
 
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();
 
160
 
 
161
            BOOST_ASSERT_MSG(parent_->name() == prefix,
 
162
                             error_message.c_str());
 
163
        }
 
164
    }
 
165
}
 
166
 
 
167
static MessageSinkPtr new_sink(const std::string &prefix,
 
168
                               DefaultMessageSink::Color color) {
 
169
    return MessageSinkPtr(new DefaultMessageSink(&std::clog, prefix, color));
 
170
}
 
171
 
 
172
static Domain* unknown() {
 
173
    static Domain domain("unknown", null_ptr,
 
174
                         new_sink("UNKNOWN", DefaultMessageSink::Red));
 
175
    return &domain;
 
176
}
 
177
 
 
178
Domain* error() {
 
179
    static Domain domain("error", unknown(),
 
180
                         new_sink("ERROR", DefaultMessageSink::Red));
 
181
    return &domain;
 
182
}
 
183
 
 
184
Domain* critical() {
 
185
    static Domain domain("critical", error(),
 
186
                         new_sink("CRITICAL", DefaultMessageSink::Brown));
 
187
    return &domain;
 
188
}
 
189
 
 
190
Domain* warning() {
 
191
    static Domain domain("warning", critical(),
 
192
                         new_sink("WARNING", DefaultMessageSink::Brown));
 
193
    return &domain;
 
194
}
 
195
 
 
196
Domain* message() {
 
197
    static Domain domain("message", warning(),
 
198
                         new_sink("WARNING", DefaultMessageSink::Default));
 
199
    return &domain;
 
200
}
 
201
 
 
202
Domain* info() {
 
203
    static Domain domain("info", message(),
 
204
                         new_sink("INFO", DefaultMessageSink::Blue));
 
205
    return &domain;
 
206
}
 
207
 
 
208
Domain* debug() {
 
209
    static Domain domain("debug", Domain::Disabled, info(),
 
210
                         new_sink("DEBUG", DefaultMessageSink::Green));
 
211
    return &domain;
 
212
}
 
213
 
 
214
Domain* trace() {
 
215
    static Domain domain("trace", Domain::Disabled, debug(),
 
216
                         new_sink("TRACE", DefaultMessageSink::Cyan));
 
217
    return &domain;
 
218
}
 
219
 
 
220
static const MessageSinkPtr initial_default_sink(new DefaultMessageSink);
 
221
MessageSinkPtr MessageSink::default_instance_ = initial_default_sink;
 
222
 
 
223
MessageSink::~MessageSink() {
 
224
}
 
225
 
 
226
void MessageSink::Report(const std::string &domain_name,
 
227
                         const std::wstring &message) {
 
228
    Report(domain_name, FromUnicode(message));
 
229
}
 
230
 
 
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());
 
236
        }
 
237
    } else {
 
238
        for (Domain *domain = trace(); domain; domain->parent()) {
 
239
            if (domain->message_sink() == domain->default_message_sink())
 
240
                domain->set_message_sink(instance);
 
241
        }
 
242
    }
 
243
 
 
244
    default_instance_ = instance;
 
245
}
 
246
 
 
247
static bool supports_color_codes(std::ostream *stream) {
 
248
    using __gnu_cxx::stdio_sync_filebuf;
 
249
 
 
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()));
 
253
 
 
254
    return false;
 
255
}
 
256
 
 
257
static std::string ansi_color_code(DefaultMessageSink::Color color) {
 
258
    switch (color) {
 
259
    case DefaultMessageSink::Default:
 
260
        return "\033[0m";
 
261
    case DefaultMessageSink::Black:
 
262
        return "\033[1;30m";
 
263
    case DefaultMessageSink::Red:
 
264
        return "\033[1;31m";
 
265
    case DefaultMessageSink::Green:
 
266
        return "\033[1;32m";
 
267
    case DefaultMessageSink::Brown:
 
268
        return "\033[0;33m";
 
269
    case DefaultMessageSink::Blue:
 
270
        return "\033[1;34m";
 
271
    case DefaultMessageSink::Magenta:
 
272
        return "\033[1;35m";
 
273
    case DefaultMessageSink::Cyan:
 
274
        return "\033[1;36m";
 
275
    case DefaultMessageSink::White:
 
276
        return "\033[1;37m";
 
277
    case DefaultMessageSink::Bold:
 
278
        return "\033[1m";
 
279
    }
 
280
 
 
281
    return std::string();
 
282
}
 
283
 
 
284
DefaultMessageSink::DefaultMessageSink(std::ostream *stream,
 
285
                                       const std::string &prefix,
 
286
                                       Color color)
 
287
    : stream_(stream)
 
288
    , prefix_(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);
 
293
 
 
294
        prefix_ += " ";
 
295
    }
 
296
}
 
297
 
 
298
void DefaultMessageSink::Report(const std::string &domain_name,
 
299
                                const std::string &message) {
 
300
    std::ostringstream buffer;
 
301
 
 
302
    buffer << program_invocation_short_name << "[" << getpid() << "]: "
 
303
           << prefix_ << domain_name << ": " << message << std::endl;
 
304
 
 
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());
 
309
}
 
310
 
 
311
static void on_glib_message(const char *domain_name,
 
312
                            GLogLevelFlags log_level,
 
313
                            const char *text, void *) {
 
314
    Domain *domain;
 
315
 
 
316
    switch (log_level & G_LOG_LEVEL_MASK) {
 
317
    case G_LOG_LEVEL_ERROR:
 
318
        domain = error();
 
319
        break;
 
320
 
 
321
    case G_LOG_LEVEL_CRITICAL:
 
322
        domain = critical();
 
323
        break;
 
324
 
 
325
    case G_LOG_LEVEL_WARNING:
 
326
        domain = warning();
 
327
        break;
 
328
 
 
329
    case G_LOG_LEVEL_MESSAGE:
 
330
        domain = message();
 
331
 
 
332
    case G_LOG_LEVEL_INFO:
 
333
        domain = info();
 
334
        break;
 
335
 
 
336
    case G_LOG_LEVEL_DEBUG:
 
337
        domain = debug();
 
338
        break;
 
339
 
 
340
    default:
 
341
        domain = unknown();
 
342
    }
 
343
 
 
344
    if (domain_name) {
 
345
        Domain(domain->name() + "/" + domain_name, domain).print(text);
 
346
    } else {
 
347
        domain->print(text);
 
348
    }
 
349
}
 
350
 
 
351
static bool capturing_glib_messages = false;
 
352
 
 
353
void capture_glib_messages() {
 
354
    g_log_set_default_handler(on_glib_message, 0);
 
355
    capturing_glib_messages = true;
 
356
}
 
357
 
 
358
bool is_capturing_glib_messages() {
 
359
    return capturing_glib_messages;
 
360
}
 
361
 
 
362
} // namespace logging
 
363
} // namespace mediascanner