2 * Copyright (C) 2009 Patrick Ohly <patrick.ohly@gmx.de>
3 * Copyright (C) 2009 Intel Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) version 3.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 #include <syncevo/Logging.h>
22 #include <syncevo/LogStdout.h>
23 #include <syncevo/LogRedirect.h>
28 #include <syncevo/declarations.h>
31 static RecMutex logMutex;
33 * POD to have it initialized without relying on a constructor to run.
35 static std::string *logProcessName;
37 void Logger::setProcessName(const std::string &name)
39 RecMutex::Guard guard = logMutex.lock();
40 if (!logProcessName) {
41 logProcessName = new std::string(name);
43 *logProcessName = name;
47 std::string Logger::getProcessName()
49 RecMutex::Guard guard = logMutex.lock();
50 return logProcessName ? *logProcessName : "";
53 RecMutex::Guard Logger::lock()
55 return logMutex.lock();
68 * Create (if necessary) and return the logger stack.
69 * It has at least one entry.
71 * logMutex must be locked when calling this.
73 static std::vector<Logger::Handle> &LoggersSingleton()
75 // allocate array once and never free it because it might be needed till
76 // the very end of the application life cycle
77 static std::vector<Logger::Handle> *loggers;
79 loggers = new std::vector<Logger::Handle>;
80 // Ensure that the array is never empty.
81 boost::shared_ptr<Logger> logger(new LoggerStdout);
82 loggers->push_back(logger);
87 Logger::Handle Logger::instance()
89 RecMutex::Guard guard = logMutex.lock();
90 std::vector<Handle> &loggers = LoggersSingleton();
91 return loggers.back();
94 void Logger::addLogger(const Handle &logger)
96 RecMutex::Guard guard = logMutex.lock();
97 std::vector<Handle> &loggers = LoggersSingleton();
99 loggers.push_back(logger);
102 void Logger::removeLogger(Logger *logger)
104 RecMutex::Guard guard = logMutex.lock();
105 std::vector<Handle> &loggers = LoggersSingleton();
107 for (ssize_t i = loggers.size() - 1;
110 if (loggers[i] == logger) {
112 loggers.erase(loggers.begin() + i);
118 void Logger::formatLines(Level msglevel,
120 const std::string *processName,
121 const std::string *prefix,
124 boost::function<void (std::string &buffer, size_t expectedTotal)> print)
128 // in case of 'SHOW' level, don't print level and prefix information
129 if (msglevel != SHOW) {
131 std::string procname;
132 std::string firstLine;
134 // Run non-blocking operations on shared data while
135 // holding the mutex.
137 RecMutex::Guard guard = logMutex.lock();
138 const std::string *realProcname;
141 realProcname = processName;
143 if (!logProcessName) {
144 logProcessName = new std::string;
146 realProcname = logProcessName;
148 if (!realProcname->empty()) {
149 procname.reserve(realProcname->size() + 1);
151 procname += *realProcname;
154 if (outputlevel >= DEBUG) {
155 // add relative time stamp
156 Timespec now = Timespec::monotonic();
158 // first message, start counting time
160 time_t nowt = time(NULL);
161 struct tm tm_gm, tm_local;
163 gmtime_r(&nowt, &tm_gm);
164 localtime_r(&nowt, &tm_local);
165 reltime = " 00:00:00";
166 strftime(buffer[0], sizeof(buffer[0]),
167 "%a %Y-%m-%d %H:%M:%S",
169 strftime(buffer[1], sizeof(buffer[1]),
173 StringPrintf("[DEBUG%s%s] %s UTC = %s\n",
179 if (now >= m_startTime) {
180 Timespec delta = now - m_startTime;
181 reltime = StringPrintf(" %02ld:%02ld:%02ld",
182 delta.tv_sec / (60 * 60),
183 (delta.tv_sec % (60 * 60)) / 60,
186 reltime = " ??:??:??";
192 if (!firstLine.empty()) {
195 tag = StringPrintf("[%s%s%s] %s%s",
196 levelToStr(msglevel),
199 prefix ? prefix->c_str() : "",
203 std::string output = StringPrintfV(format, args);
206 // Print individual lines.
208 // Total size is guessed by assuming an average line length of
209 // around 40 characters to predict number of lines.
210 size_t expectedTotal = (output.size() / 40 + 1) * tag.size() + output.size();
213 size_t next = output.find('\n', pos);
214 if (next != output.npos) {
216 line.reserve(tag.size() + next + 1 - pos);
218 line.append(output, pos, next + 1 - pos);
219 print(line, expectedTotal);
225 if (pos < output.size() || output.empty()) {
226 // handle dangling last line or empty chunk (don't
227 // want empty line for that, print at least the tag)
229 line.reserve(tag.size() + output.size() - pos + 1);
231 line.append(output, pos, output.size() - pos);
233 print(line, expectedTotal);
236 if (!boost::ends_with(output, "\n")) {
237 // append newline if necessary
244 Logger::MessageOptions::MessageOptions(Level level) :
254 Logger::MessageOptions::MessageOptions(Level level,
255 const std::string *prefix,
258 const char *function,
264 m_function(function),
270 Logger::Handle::Handle() throw ()
274 Logger::Handle::Handle(Logger *logger) throw ()
276 m_logger.reset(logger);
279 Logger::Handle::Handle(const Handle &other) throw ()
281 m_logger = other.m_logger;
284 Logger::Handle & Logger::Handle::operator = (const Handle &other) throw ()
286 if (this != &other) {
287 m_logger = other.m_logger;
292 Logger::Handle::~Handle() throw ()
297 void Logger::Handle::message(Level level,
298 const std::string *prefix,
301 const char *function,
306 va_start(args, format);
307 m_logger->messagev(MessageOptions(level, prefix, file, line, function), format, args);
311 void Logger::Handle::message(Level level,
312 const std::string &prefix,
315 const char *function,
320 va_start(args, format);
321 m_logger->messagev(MessageOptions(level, &prefix, file, line, function), format, args);
325 void Logger::Handle::messageWithOptions(const MessageOptions &options,
330 va_start(args, format);
331 m_logger->messagev(options, format, args);
335 const char *Logger::levelToStr(Level level)
338 case SHOW: return "SHOW";
339 case ERROR: return "ERROR";
340 case WARNING: return "WARNING";
341 case INFO: return "INFO";
342 case DEV: return "DEVELOPER";
343 case DEBUG: return "DEBUG";
344 default: return "???";
348 Logger::Level Logger::strToLevel(const char *str)
350 // order is based on a rough estimate of message frequency of the
351 // corresponding type
352 if (!str || !strcmp(str, "DEBUG")) {
354 } else if (!strcmp(str, "INFO")) {
356 } else if (!strcmp(str, "SHOW")) {
358 } else if (!strcmp(str, "ERROR")) {
360 } else if (!strcmp(str, "WARNING")) {
362 } else if (!strcmp(str, "DEV")) {
370 void Logger::glogFunc(const gchar *logDomain,
371 GLogLevelFlags logLevel,
372 const gchar *message,
376 (logLevel & (G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL)) ? ERROR :
377 (logLevel & G_LOG_LEVEL_WARNING) ? WARNING :
378 (logLevel & (G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO)) ? SHOW :
381 // Downgrade some know error messages as registered with
382 // the LogRedirect helper class. That messages are registered
383 // there is a historic artifact.
384 if (level != DEBUG &&
385 LogRedirect::ignoreError(message)) {
389 Logger::instance().message(level,
395 logDomain ? logDomain : "",
396 logDomain ? ": " : "",
402 int Logger::sysyncPrintf(FILE *stream,
407 va_start(args, format);
408 static const std::string prefix("SYSYNC");
409 if (boost::starts_with(format, prefix) &&
410 format[prefix.size()] == ' ') {
411 // Skip initial "SYSYNC " prefix, because it will get re-added
412 // in a better way (= to each line) via the prefix parameter.
413 format += prefix.size() + 1;
415 Logger::instance().messagev(MessageOptions(DEBUG, &prefix, NULL, 0, NULL), format, args);