libreoffice-online/loolwsd/Log.cpp
Tor Lillqvist 8207412c02 Poco::Thread::current() is not reliable for logging
It can return null for no obvious reason, leading to misleading
logging where the same thread is identified as numer zero at one place
and non-zero at another. So use the actual Linux thread id in logging.

Sure, thread ids are somewhat less convenient, as they are larger
numbers, from the same number space as process ids.
2016-09-20 13:32:43 +03:00

192 lines
5.5 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <sys/prctl.h>
#include <atomic>
#include <cassert>
#include <iomanip>
#include <sstream>
#include <string>
#include <sys/syscall.h>
#include <unistd.h>
#include <Poco/ConsoleChannel.h>
#include <Poco/FileChannel.h>
#include <Poco/FormattingChannel.h>
#include <Poco/PatternFormatter.h>
#include <Poco/Process.h>
#include <Poco/SplitterChannel.h>
#include <Poco/Thread.h>
#include <Poco/Timestamp.h>
#include "Log.hpp"
static char LogPrefix[256] = { '\0' };
namespace Log
{
using namespace Poco;
static const Poco::Int64 epochStart = Poco::Timestamp().epochMicroseconds();
/// Helper to avoid destruction ordering issues.
struct StaticNames {
std::atomic<bool> inited;
std::string name;
std::string id;
StaticNames() :
inited(true)
{
}
~StaticNames()
{
inited = false;
}
};
static StaticNames Source;
// We need a signal safe means of writing messages
// $ man 7 signal
void signalLog(const char *message)
{
while (true) {
int length = strlen(message);
int written = write (STDERR_FILENO, message, length);
if (written < 0)
{
if (errno == EINTR)
continue; // ignore.
else
break;
}
message += written;
if (message[0] == '\0')
break;
}
}
static void getPrefix(char *buffer, const char* level)
{
Poco::Int64 usec = Poco::Timestamp().epochMicroseconds() - epochStart;
const Poco::Int64 one_s = 1000000;
const Poco::Int64 hours = usec / (one_s*60*60);
usec %= (one_s*60*60);
const Poco::Int64 minutes = usec / (one_s*60);
usec %= (one_s*60);
const Poco::Int64 seconds = usec / (one_s);
usec %= (one_s);
char procName[32]; // we really need only 16
if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(procName), 0, 0, 0) != 0)
strncpy(procName, "<noid>", sizeof(procName) - 1);
const char *appName = (Source.inited ? Source.id.c_str() : "<shutdown>");
assert(strlen(appName) + 32 + 28 < 1024 - 1);
snprintf(buffer, 4095, "%s-%.04lu %d:%.2d:%.2d.%.6d [ %s ] %s ", appName,
syscall(SYS_gettid),
(int)hours, (int)minutes, (int)seconds, (int)usec,
procName, level);
}
std::string prefix(const char* level)
{
char buffer[1024];
getPrefix(buffer, level);
return std::string(buffer);
}
void signalLogPrefix()
{
char buffer[1024];
getPrefix(buffer, "SIG");
signalLog(buffer);
}
void initialize(const std::string& name,
const std::string& logLevel,
const bool withColor,
const bool logToFile,
std::map<std::string, std::string> config)
{
Source.name = name;
std::ostringstream oss;
oss << Source.name << '-'
<< std::setw(5) << std::setfill('0') << Poco::Process::id();
Source.id = oss.str();
assert (sizeof (LogPrefix) > strlen(oss.str().c_str()) + 1);
strncpy(LogPrefix, oss.str().c_str(), sizeof(LogPrefix));
// Configure the logger.
AutoPtr<Channel> channel = (isatty(fileno(stderr)) || withColor
? static_cast<Poco::Channel*>(new Poco::ColorConsoleChannel())
: static_cast<Poco::Channel*>(new Poco::ConsoleChannel()));
if (logToFile)
{
auto splitterChannel(new SplitterChannel());
splitterChannel->addChannel(channel);
AutoPtr<FileChannel> rotatedFileChannel(new FileChannel("loolwsd.log"));
for (const auto& pair : config)
{
rotatedFileChannel->setProperty(pair.first, pair.second);
}
splitterChannel->addChannel(rotatedFileChannel);
channel = splitterChannel;
}
auto& logger = Poco::Logger::create(Source.name, channel, Poco::Message::PRIO_TRACE);
logger.setLevel(logLevel.empty() ? std::string("trace") : logLevel);
info("Initializing " + name);
info("Log level is [" + std::to_string(logger.getLevel()) + "].");
}
Poco::Logger& logger()
{
return Poco::Logger::get(Source.inited ? Source.name : std::string());
}
void trace(const std::string& msg)
{
logger().trace(prefix("TRC") + msg);
}
void debug(const std::string& msg)
{
logger().debug(prefix("DBG") + msg);
}
void info(const std::string& msg)
{
logger().information(prefix("INF") + msg);
}
void warn(const std::string& msg)
{
logger().warning(prefix("WRN") + msg);
}
void error(const std::string& msg)
{
logger().error(prefix("ERR") + msg);
}
void syserror(const std::string& msg)
{
logger().error(prefix("SYS") + msg + " (errno: " + std::string(std::strerror(errno)) + ")");
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */