00a44d6e81
This is important for when we abort with some explanation. Often said explanation doesn't show up anywhere to be useful. Also, issue fatal logs for abnormal exist and use SFL to log errno. Reviewed-on: https://gerrit.libreoffice.org/57540 Reviewed-by: Jan Holesovsky <kendy@collabora.com> Tested-by: Jan Holesovsky <kendy@collabora.com> (cherry picked from commit ad7964393eadb68873b820e0a620fb40f1e1b06a) Change-Id: Ic67064ef40ef6e93d26e5847ecd32bdd49c3cc8b
199 lines
5.5 KiB
C++
199 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 <config.h>
|
|
|
|
#ifdef __linux
|
|
#include <sys/prctl.h>
|
|
#include <sys/syscall.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
|
|
#include <atomic>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#include <Poco/ConsoleChannel.h>
|
|
#include <Poco/DateTimeFormatter.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"
|
|
#include "Util.hpp"
|
|
|
|
namespace Log
|
|
{
|
|
using namespace Poco;
|
|
|
|
/// Helper to avoid destruction ordering issues.
|
|
struct StaticNameHelper
|
|
{
|
|
std::atomic<bool> inited;
|
|
std::string name;
|
|
std::string id;
|
|
StaticNameHelper() :
|
|
inited(true)
|
|
{
|
|
}
|
|
~StaticNameHelper()
|
|
{
|
|
inited = false;
|
|
}
|
|
};
|
|
static StaticNameHelper Source;
|
|
|
|
// We need a signal safe means of writing messages
|
|
// $ man 7 signal
|
|
void signalLog(const char *message)
|
|
{
|
|
while (true)
|
|
{
|
|
const int length = std::strlen(message);
|
|
const int written = write (STDERR_FILENO, message, length);
|
|
if (written < 0)
|
|
{
|
|
if (errno == EINTR)
|
|
continue; // ignore.
|
|
else
|
|
break;
|
|
}
|
|
|
|
message += written;
|
|
if (message[0] == '\0')
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We need a signal safe means of writing messages
|
|
// $ man 7 signal
|
|
void signalLogNumber(std::size_t num)
|
|
{
|
|
int i;
|
|
char buf[22];
|
|
buf[21] = '\0';
|
|
for (i = 20; i > 0 && num > 0; --i)
|
|
{
|
|
buf[i] = '0' + num % 10;
|
|
num /= 10;
|
|
}
|
|
signalLog(buf + i + 1);
|
|
}
|
|
|
|
char* prefix(char* buffer, const std::size_t len, const char* level)
|
|
{
|
|
const char *threadName = Util::getThreadName();
|
|
#ifdef __linux
|
|
const long osTid = Util::getThreadId();
|
|
#elif defined IOS
|
|
const auto osTid = pthread_mach_thread_np(pthread_self());
|
|
#endif
|
|
Poco::DateTime time;
|
|
snprintf(buffer, len, "%s-%.05lu %.4u-%.2u-%.2u %.2u:%.2u:%.2u.%.6u [ %s ] %s ",
|
|
(Source.inited ? Source.id.c_str() : "<shutdown>"),
|
|
osTid,
|
|
time.year(), time.month(), time.day(),
|
|
time.hour(), time.minute(), time.second(),
|
|
time.millisecond() * 1000 + time.microsecond(),
|
|
threadName, level);
|
|
return buffer;
|
|
}
|
|
|
|
void signalLogPrefix()
|
|
{
|
|
char buffer[1024];
|
|
prefix(buffer, sizeof(buffer) - 1, "SIG");
|
|
signalLog(buffer);
|
|
}
|
|
|
|
void initialize(const std::string& name,
|
|
const std::string& logLevel,
|
|
const bool withColor,
|
|
const bool logToFile,
|
|
const std::map<std::string, std::string>& config)
|
|
{
|
|
Source.name = name;
|
|
std::ostringstream oss;
|
|
oss << Source.name;
|
|
#ifndef MOBILEAPP // Just one process in a mobile app, the pid is uninteresting.
|
|
oss << '-'
|
|
<< std::setw(5) << std::setfill('0') << Poco::Process::id();
|
|
#endif
|
|
Source.id = oss.str();
|
|
|
|
// Configure the logger.
|
|
AutoPtr<Channel> channel;
|
|
|
|
if (logToFile)
|
|
{
|
|
channel = static_cast<Poco::Channel*>(new FileChannel("loolwsd.log"));
|
|
for (const auto& pair : config)
|
|
{
|
|
channel->setProperty(pair.first, pair.second);
|
|
}
|
|
}
|
|
else if (withColor)
|
|
channel = static_cast<Poco::Channel*>(new Poco::ColorConsoleChannel());
|
|
else
|
|
channel = static_cast<Poco::Channel*>(new Poco::ConsoleChannel());
|
|
|
|
/**
|
|
* Open the channel explicitly, instead of waiting for first log message
|
|
* This is important especially for the kit process where opening the channel
|
|
* after chroot can cause file creation inside the jail instead of outside
|
|
* */
|
|
channel->open();
|
|
auto& logger = Poco::Logger::create(Source.name, channel, Poco::Message::PRIO_TRACE);
|
|
|
|
logger.setLevel(logLevel.empty() ? std::string("trace") : logLevel);
|
|
|
|
const std::time_t t = std::time(nullptr);
|
|
oss.str("");
|
|
oss.clear();
|
|
|
|
oss << "Initializing " << name << ".";
|
|
|
|
// TODO: replace with std::put_time when we move to gcc 5+.
|
|
char buf[32];
|
|
if (strftime(buf, sizeof(buf), "%a %F %T%z", std::localtime(&t)) > 0)
|
|
{
|
|
oss << " Local time: " << buf << ".";
|
|
}
|
|
|
|
oss << " Log level is [" << logger.getLevel() << "].";
|
|
LOG_INF(oss.str());
|
|
}
|
|
|
|
Poco::Logger& logger()
|
|
{
|
|
return Poco::Logger::get(Source.inited ? Source.name : std::string());
|
|
}
|
|
|
|
void shutdown()
|
|
{
|
|
logger().shutdown();
|
|
|
|
// Flush
|
|
std::flush(std::cout);
|
|
fflush(stdout);
|
|
std::flush(std::cerr);
|
|
fflush(stderr);
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|