blob: ddf04448f7c6825e25a945b8705e6d19d3a148bb [file] [log] [blame]
/*
* Copyright (C) 2004-2016 Savoir-faire Linux Inc.
*
* Author: Julien Bonjean <julien.bonjean@savoirfairelinux.com>
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <string>
#include <sstream>
#include <iomanip>
#include <ios>
#include <mutex>
#include <thread>
#include "logger.h"
#ifdef __linux__
#include <syslog.h>
#include <unistd.h>
#include <sys/syscall.h>
#endif // __linux__
#ifdef WIN32
#include "winsyslog.h"
#endif
#define BLACK "\033[22;30m"
#define GREEN "\033[22;32m"
#define BROWN "\033[22;33m"
#define BLUE "\033[22;34m"
#define MAGENTA "\033[22;35m"
#define GREY "\033[22;37m"
#define DARK_GREY "\033[01;30m"
#define LIGHT_RED "\033[01;31m"
#define LIGHT_SCREEN "\033[01;32m"
#define LIGHT_BLUE "\033[01;34m"
#define LIGHT_MAGENTA "\033[01;35m"
#define LIGHT_CYAN "\033[01;36m"
#define WHITE "\033[01;37m"
#define END_COLOR "\033[0m"
#ifndef _WIN32
#define RED "\033[22;31m"
#define YELLOW "\033[01;33m"
#define CYAN "\033[22;36m"
#else
#define RED FOREGROUND_RED
#define YELLOW FOREGROUND_RED + FOREGROUND_GREEN
#define CYAN FOREGROUND_BLUE + FOREGROUND_GREEN
#endif
static int consoleLog;
static int debugMode;
static std::mutex logMutex;
static std::string
getHeader(const char* ctx)
{
#ifdef __linux__
auto tid = syscall(__NR_gettid) & 0xffff;
#else
auto tid = std::this_thread::get_id();
#endif // __linux__
// Timestamp
unsigned int secs, milli;
struct timeval tv;
if (!gettimeofday(&tv, NULL)) {
secs = tv.tv_sec;
milli = tv.tv_usec / 1000; // suppose that milli < 1000
} else {
secs = time(NULL);
milli = 0;
}
std::ostringstream out;
const auto prev_fill = out.fill();
out << '[' << secs
<< '.' << std::right << std::setw(3) << std::setfill('0') << milli << std::left
<< '|' << std::right << std::setw(5) << std::setfill(' ') << tid << std::left;
out.fill(prev_fill);
// Context
if (ctx)
out << "|" << std::setw(24) << ctx;
out << "] ";
return out.str();
}
void
logger(const int level, const char* format, ...)
{
if (!debugMode && level == LOG_DEBUG)
return;
va_list ap;
va_start(ap, format);
vlogger(level, format, ap);
va_end(ap);
}
void
vlogger(const int level, const char *format, va_list ap)
{
if (!debugMode && level == LOG_DEBUG)
return;
// syslog is supposed to thread-safe, but not all implementations (Android?)
// follow strictly POSIX rules... so we lock our mutex in any cases.
std::lock_guard<std::mutex> lk {logMutex};
if (consoleLog) {
#ifndef _WIN32
const char* color_header = CYAN;
const char* color_prefix = "";
#else
WORD color_header = CYAN;
WORD color_prefix = FOREGROUND_GREEN;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
WORD saved_attributes;
#endif
switch (level) {
case LOG_ERR:
color_prefix = RED;
break;
case LOG_WARNING:
color_prefix = YELLOW;
break;
}
#ifndef _WIN32
fputs(color_header, stderr);
#else
GetConsoleScreenBufferInfo(hConsole, &consoleInfo);
saved_attributes = consoleInfo.wAttributes;
SetConsoleTextAttribute(hConsole, color_header);
#endif
auto sep = strchr(format, '|'); // must exist, check LOG_FORMAT
std::string ctx(format, sep - format);
format = sep + 2;
fputs(getHeader(ctx.c_str()).c_str(), stderr);
#ifndef _WIN32
fputs(END_COLOR, stderr);
fputs(color_prefix, stderr);
#else
SetConsoleTextAttribute(hConsole, saved_attributes);
SetConsoleTextAttribute(hConsole, color_prefix);
#endif
vfprintf(stderr, format, ap);
#ifndef _WIN32
fputs(END_COLOR"\n", stderr);
#else
fputs("\n", stderr);
SetConsoleTextAttribute(hConsole, saved_attributes);
#endif
} else {
vsyslog(level, format, ap);
}
}
void
setConsoleLog(int c)
{
if (c)
closelog();
else {
#ifdef _WIN32
openlog(LOGFILE, WINLOG_PID, WINLOG_MAIL);
#else
openlog(LOGFILE, LOG_NDELAY, LOG_USER);
#endif /* _WIN32 */
}
consoleLog = c;
}
void
setDebugMode(int d)
{
debugMode = d;
}
int
getDebugMode(void)
{
return debugMode;
}
void
strErr(void)
{
#ifdef __GLIBC__
RING_ERR("%m");
#else
char buf[1000];
const char *errstr;
switch (strerror_r(errno, buf, sizeof(buf))) {
case 0:
errstr = buf;
break;
case ERANGE: /* should never happen */
errstr = "unknown (too big to display)";
break;
default:
errstr = "unknown (invalid error number)";
break;
}
RING_ERR("%s", errstr);
#endif
}