2016-04-14 19:12:43 -05:00
|
|
|
/* -*- 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>
|
|
|
|
|
2016-08-07 22:32:39 -05:00
|
|
|
#include <atomic>
|
2016-04-14 19:12:43 -05:00
|
|
|
#include <cassert>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
|
|
|
|
2016-09-20 04:58:50 -05:00
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2016-04-14 19:12:43 -05:00
|
|
|
#include <Poco/ConsoleChannel.h>
|
2016-08-07 22:32:39 -05:00
|
|
|
#include <Poco/FileChannel.h>
|
|
|
|
#include <Poco/FormattingChannel.h>
|
|
|
|
#include <Poco/PatternFormatter.h>
|
2016-04-14 19:12:43 -05:00
|
|
|
#include <Poco/Process.h>
|
2016-08-07 22:32:39 -05:00
|
|
|
#include <Poco/SplitterChannel.h>
|
2016-04-14 19:12:43 -05:00
|
|
|
#include <Poco/Thread.h>
|
2016-07-30 22:06:12 -05:00
|
|
|
#include <Poco/Timestamp.h>
|
2016-04-14 19:12:43 -05:00
|
|
|
|
|
|
|
#include "Log.hpp"
|
|
|
|
|
|
|
|
static char LogPrefix[256] = { '\0' };
|
|
|
|
|
|
|
|
namespace Log
|
|
|
|
{
|
2016-08-07 22:32:39 -05:00
|
|
|
using namespace Poco;
|
|
|
|
|
2016-04-14 19:12:43 -05:00
|
|
|
static const Poco::Int64 epochStart = Poco::Timestamp().epochMicroseconds();
|
2016-08-13 23:01:13 -05:00
|
|
|
/// Helper to avoid destruction ordering issues.
|
2016-04-14 19:12:43 -05:00
|
|
|
struct StaticNames {
|
2016-08-07 22:32:39 -05:00
|
|
|
std::atomic<bool> inited;
|
2016-04-14 19:12:43 -05:00
|
|
|
std::string name;
|
|
|
|
std::string id;
|
|
|
|
StaticNames() :
|
|
|
|
inited(true)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
~StaticNames()
|
|
|
|
{
|
|
|
|
inited = false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
static StaticNames Source;
|
|
|
|
|
2016-04-15 14:46:44 -05:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-30 13:42:57 -05:00
|
|
|
static void getPrefix(char *buffer, const char* level)
|
2016-04-14 19:12:43 -05:00
|
|
|
{
|
2016-09-27 07:40:51 -05:00
|
|
|
// FIXME: If running under systemd it is redundant to output timestamps, as those will be
|
|
|
|
// attached to messages that the systemd journalling mechanism picks up anyway, won't they?
|
|
|
|
|
2016-04-14 19:12:43 -05:00
|
|
|
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);
|
|
|
|
|
2016-04-15 14:46:44 -05:00
|
|
|
char procName[32]; // we really need only 16
|
2016-04-18 01:39:54 -05:00
|
|
|
if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(procName), 0, 0, 0) != 0)
|
2016-04-20 02:10:26 -05:00
|
|
|
strncpy(procName, "<noid>", sizeof(procName) - 1);
|
2016-04-15 14:46:44 -05:00
|
|
|
|
|
|
|
const char *appName = (Source.inited ? Source.id.c_str() : "<shutdown>");
|
|
|
|
assert(strlen(appName) + 32 + 28 < 1024 - 1);
|
2016-04-14 19:12:43 -05:00
|
|
|
|
2016-09-20 04:58:50 -05:00
|
|
|
snprintf(buffer, 4095, "%s-%.04lu %d:%.2d:%.2d.%.6d [ %s ] %s ", appName,
|
|
|
|
syscall(SYS_gettid),
|
2016-04-15 14:46:44 -05:00
|
|
|
(int)hours, (int)minutes, (int)seconds, (int)usec,
|
2016-08-30 13:42:57 -05:00
|
|
|
procName, level);
|
2016-04-15 14:46:44 -05:00
|
|
|
}
|
|
|
|
|
2016-08-30 13:42:57 -05:00
|
|
|
std::string prefix(const char* level)
|
2016-04-15 14:46:44 -05:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
2016-08-30 13:42:57 -05:00
|
|
|
getPrefix(buffer, level);
|
2016-04-15 14:46:44 -05:00
|
|
|
return std::string(buffer);
|
|
|
|
}
|
2016-04-14 19:12:43 -05:00
|
|
|
|
2016-04-15 14:46:44 -05:00
|
|
|
void signalLogPrefix()
|
|
|
|
{
|
|
|
|
char buffer[1024];
|
2016-08-30 13:42:57 -05:00
|
|
|
getPrefix(buffer, "SIG");
|
2016-04-15 14:46:44 -05:00
|
|
|
signalLog(buffer);
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
|
|
|
|
2016-08-07 22:32:39 -05:00
|
|
|
void initialize(const std::string& name,
|
|
|
|
const std::string& logLevel,
|
|
|
|
const bool withColor,
|
|
|
|
const bool logToFile,
|
|
|
|
std::map<std::string, std::string> config)
|
2016-04-14 19:12:43 -05:00
|
|
|
{
|
|
|
|
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));
|
|
|
|
|
2016-07-30 10:52:10 -05:00
|
|
|
// Configure the logger.
|
2016-09-27 07:30:45 -05:00
|
|
|
AutoPtr<Channel> channel;
|
2016-08-07 22:32:39 -05:00
|
|
|
|
|
|
|
if (logToFile)
|
|
|
|
{
|
2016-09-27 07:30:45 -05:00
|
|
|
channel = static_cast<Poco::Channel*>(new FileChannel("loolwsd.log"));
|
2016-08-07 22:32:39 -05:00
|
|
|
for (const auto& pair : config)
|
|
|
|
{
|
2016-09-27 07:30:45 -05:00
|
|
|
channel->setProperty(pair.first, pair.second);
|
2016-08-07 22:32:39 -05:00
|
|
|
}
|
|
|
|
}
|
2016-09-27 07:30:45 -05:00
|
|
|
else if (withColor)
|
|
|
|
channel = static_cast<Poco::Channel*>(new Poco::ColorConsoleChannel());
|
|
|
|
else
|
|
|
|
channel = static_cast<Poco::Channel*>(new Poco::ConsoleChannel());
|
2016-08-07 22:32:39 -05:00
|
|
|
|
2016-04-14 19:12:43 -05:00
|
|
|
auto& logger = Poco::Logger::create(Source.name, channel, Poco::Message::PRIO_TRACE);
|
|
|
|
|
2016-07-30 10:52:10 -05:00
|
|
|
logger.setLevel(logLevel.empty() ? std::string("trace") : logLevel);
|
2016-04-14 19:12:43 -05:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2016-08-30 13:42:57 -05:00
|
|
|
logger().trace(prefix("TRC") + msg);
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void debug(const std::string& msg)
|
|
|
|
{
|
2016-08-30 13:42:57 -05:00
|
|
|
logger().debug(prefix("DBG") + msg);
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void info(const std::string& msg)
|
|
|
|
{
|
2016-08-30 13:42:57 -05:00
|
|
|
logger().information(prefix("INF") + msg);
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void warn(const std::string& msg)
|
|
|
|
{
|
2016-08-30 13:42:57 -05:00
|
|
|
logger().warning(prefix("WRN") + msg);
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void error(const std::string& msg)
|
|
|
|
{
|
2016-08-30 13:42:57 -05:00
|
|
|
logger().error(prefix("ERR") + msg);
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void syserror(const std::string& msg)
|
|
|
|
{
|
2016-08-30 13:42:57 -05:00
|
|
|
logger().error(prefix("SYS") + msg + " (errno: " + std::string(std::strerror(errno)) + ")");
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|