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/.
|
|
|
|
*/
|
|
|
|
|
2017-12-20 07:06:26 -06:00
|
|
|
#include <config.h>
|
2017-03-08 10:38:22 -06:00
|
|
|
|
2018-08-29 10:47:05 -05:00
|
|
|
#ifdef __linux
|
2016-04-14 19:12:43 -05:00
|
|
|
#include <sys/prctl.h>
|
2017-01-19 18:26:57 -06:00
|
|
|
#include <sys/syscall.h>
|
2018-08-29 10:47:05 -05:00
|
|
|
#endif
|
2017-01-19 18:26:57 -06:00
|
|
|
#include <unistd.h>
|
2016-04-14 19:12:43 -05:00
|
|
|
|
2016-08-07 22:32:39 -05:00
|
|
|
#include <atomic>
|
2016-04-14 19:12:43 -05:00
|
|
|
#include <cassert>
|
2018-06-04 15:47:39 -05:00
|
|
|
#include <cstring>
|
2017-01-08 11:21:14 -06:00
|
|
|
#include <ctime>
|
2016-04-14 19:12:43 -05:00
|
|
|
#include <iomanip>
|
2018-07-16 20:42:17 -05:00
|
|
|
#include <iostream>
|
2016-04-14 19:12:43 -05:00
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
|
|
|
|
2019-11-07 04:42:24 -06:00
|
|
|
#include <Poco/AutoPtr.h>
|
2016-04-14 19:12:43 -05:00
|
|
|
#include <Poco/ConsoleChannel.h>
|
2017-01-13 09:09:47 -06:00
|
|
|
#include <Poco/DateTimeFormatter.h>
|
2016-08-07 22:32:39 -05:00
|
|
|
#include <Poco/FileChannel.h>
|
|
|
|
#include <Poco/FormattingChannel.h>
|
|
|
|
#include <Poco/PatternFormatter.h>
|
|
|
|
#include <Poco/SplitterChannel.h>
|
2016-07-30 22:06:12 -05:00
|
|
|
#include <Poco/Timestamp.h>
|
2016-04-14 19:12:43 -05:00
|
|
|
|
|
|
|
#include "Log.hpp"
|
2017-03-30 12:14:40 -05:00
|
|
|
#include "Util.hpp"
|
2016-04-14 19:12:43 -05:00
|
|
|
|
|
|
|
namespace Log
|
|
|
|
{
|
2016-08-07 22:32:39 -05:00
|
|
|
using namespace Poco;
|
|
|
|
|
2016-08-13 23:01:13 -05:00
|
|
|
/// Helper to avoid destruction ordering issues.
|
2018-09-13 03:52:08 -05:00
|
|
|
struct StaticNameHelper
|
2016-10-30 18:07:41 -05:00
|
|
|
{
|
2018-12-04 02:05:43 -06:00
|
|
|
private:
|
2019-03-24 22:16:29 -05:00
|
|
|
Poco::Logger* _logger;
|
2018-12-04 02:05:43 -06:00
|
|
|
std::string _name;
|
|
|
|
std::string _id;
|
2019-03-24 22:16:29 -05:00
|
|
|
std::atomic<bool> _inited;
|
2018-12-04 02:05:43 -06:00
|
|
|
public:
|
2018-09-13 03:52:08 -05:00
|
|
|
StaticNameHelper() :
|
2019-03-24 22:16:29 -05:00
|
|
|
_logger(nullptr),
|
2018-12-04 02:05:43 -06:00
|
|
|
_inited(true)
|
2016-04-14 19:12:43 -05:00
|
|
|
{
|
|
|
|
}
|
2018-09-13 03:52:08 -05:00
|
|
|
~StaticNameHelper()
|
2016-04-14 19:12:43 -05:00
|
|
|
{
|
2018-12-04 02:05:43 -06:00
|
|
|
_inited = false;
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
2018-12-04 02:05:43 -06:00
|
|
|
|
|
|
|
bool getInited() const { return _inited; }
|
|
|
|
|
|
|
|
void setId(const std::string& id) { _id = id; }
|
|
|
|
|
|
|
|
const std::string& getId() const { return _id; }
|
|
|
|
|
|
|
|
void setName(const std::string& name) { _name = name; }
|
|
|
|
|
|
|
|
const std::string& getName() const { return _name; }
|
2019-03-24 22:16:29 -05:00
|
|
|
|
|
|
|
void setLogger(Poco::Logger* logger) { _logger = logger; };
|
2020-04-25 12:03:32 -05:00
|
|
|
Poco::Logger* getLogger() const { return _logger; }
|
2016-04-14 19:12:43 -05:00
|
|
|
};
|
2018-09-13 03:52:08 -05:00
|
|
|
static StaticNameHelper Source;
|
2019-09-09 08:41:09 -05:00
|
|
|
bool IsShutdown = false;
|
2016-04-14 19:12:43 -05:00
|
|
|
|
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)
|
|
|
|
{
|
2016-10-30 18:07:41 -05:00
|
|
|
while (true)
|
|
|
|
{
|
2018-06-04 15:47:39 -05:00
|
|
|
const int length = std::strlen(message);
|
2019-11-18 21:13:00 -06:00
|
|
|
const int written = write(STDERR_FILENO, message, length);
|
2016-04-15 14:46:44 -05:00
|
|
|
if (written < 0)
|
|
|
|
{
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue; // ignore.
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
2016-10-30 18:07:41 -05:00
|
|
|
|
2016-04-15 14:46:44 -05:00
|
|
|
message += written;
|
|
|
|
if (message[0] == '\0')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-28 13:05:45 -05:00
|
|
|
// We need a signal safe means of writing messages
|
|
|
|
// $ man 7 signal
|
2018-06-04 15:47:39 -05:00
|
|
|
void signalLogNumber(std::size_t num)
|
2017-04-28 13:05:45 -05:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-04-25 12:03:32 -05:00
|
|
|
/// Convert an unsigned number to ascii with 0 padding.
|
|
|
|
template <int Width> void to_ascii_fixed(char* buf, size_t num)
|
2016-04-14 19:12:43 -05:00
|
|
|
{
|
2020-04-25 12:03:32 -05:00
|
|
|
buf[Width - 1] = '0' + num % 10; // Units.
|
|
|
|
|
|
|
|
if (Width > 1)
|
|
|
|
{
|
|
|
|
num /= 10;
|
|
|
|
buf[Width - 2] = '0' + num % 10; // Tens.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Width > 2)
|
|
|
|
{
|
|
|
|
num /= 10;
|
|
|
|
buf[Width - 3] = '0' + num % 10; // Hundreds.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Width > 3)
|
|
|
|
{
|
|
|
|
num /= 10;
|
|
|
|
buf[Width - 4] = '0' + num % 10; // Thousands.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Width > 4)
|
|
|
|
{
|
|
|
|
num /= 10;
|
|
|
|
buf[Width - 5] = '0' + num % 10; // Ten-Thousands.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Width > 5)
|
|
|
|
{
|
|
|
|
num /= 10;
|
|
|
|
buf[Width - 6] = '0' + num % 10; // Hundred-Thousands.
|
|
|
|
}
|
|
|
|
|
|
|
|
static_assert(Width >= 1 && Width <= 6, "Width is invalid.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Copy a null-terminated string into another.
|
|
|
|
/// Expects the destination to be large enough.
|
|
|
|
/// Note: unlike strcpy, this returns the *new* out
|
|
|
|
/// (destination) pointer, which saves a strlen call.
|
|
|
|
char* strcopy(const char* in, char* out)
|
|
|
|
{
|
|
|
|
while (*in)
|
|
|
|
*out++ = *in++;
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert unsigned long num to base-10 ascii in place.
|
|
|
|
/// Returns the *end* position.
|
|
|
|
char* to_ascii(char* buf, size_t num)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
buf[i++] = '0' + num % 10;
|
|
|
|
num /= 10;
|
|
|
|
} while (num > 0);
|
|
|
|
|
|
|
|
// Reverse.
|
|
|
|
for (char *front = buf, *back = buf + i - 1; back > front; ++front, --back)
|
|
|
|
{
|
|
|
|
const char t = *front;
|
|
|
|
*front = *back;
|
|
|
|
*back = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf + i;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* prefix(const Poco::DateTime& time, char* buffer, const char* level)
|
|
|
|
{
|
2020-07-02 14:15:08 -05:00
|
|
|
#ifdef IOS
|
|
|
|
// Don't bother with the "Source" which would be just "Mobile" always and non-informative as
|
|
|
|
// there is just one process in the app anyway.
|
|
|
|
char *pos = buffer;
|
2020-07-10 03:33:14 -05:00
|
|
|
|
|
|
|
// Don't bother with the thread identifier either. We output the thread name which is much
|
|
|
|
// more useful anyway.
|
2020-07-02 14:15:08 -05:00
|
|
|
#else
|
2020-04-25 12:03:32 -05:00
|
|
|
// Note that snprintf is deemed signal-safe in most common implementations.
|
|
|
|
char* pos = strcopy((Source.getInited() ? Source.getId().c_str() : "<shutdown>"), buffer);
|
|
|
|
*pos++ = '-';
|
|
|
|
|
|
|
|
// Thread ID.
|
2019-05-27 02:09:49 -05:00
|
|
|
const long osTid = Util::getThreadId();
|
2020-04-25 12:03:32 -05:00
|
|
|
if (osTid > 99999)
|
|
|
|
{
|
|
|
|
if (osTid > 999999)
|
|
|
|
pos = to_ascii(pos, osTid);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
to_ascii_fixed<6>(pos, osTid);
|
|
|
|
pos += 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
to_ascii_fixed<5>(pos, osTid);
|
|
|
|
pos += 5;
|
|
|
|
}
|
2020-07-10 03:33:14 -05:00
|
|
|
*pos++ = ' ';
|
2019-05-14 05:48:58 -05:00
|
|
|
#endif
|
2020-04-25 12:03:32 -05:00
|
|
|
|
|
|
|
// YYYY-MM-DD.
|
|
|
|
to_ascii_fixed<4>(pos, time.year());
|
|
|
|
pos[4] = '-';
|
|
|
|
pos += 5;
|
|
|
|
to_ascii_fixed<2>(pos, time.month());
|
|
|
|
pos[2] = '-';
|
|
|
|
pos += 3;
|
|
|
|
to_ascii_fixed<2>(pos, time.day());
|
|
|
|
pos[2] = ' ';
|
|
|
|
pos += 3;
|
|
|
|
|
|
|
|
// HH:MM:SS.uS
|
|
|
|
to_ascii_fixed<2>(pos, time.hour());
|
|
|
|
pos[2] = ':';
|
|
|
|
pos += 3;
|
|
|
|
to_ascii_fixed<2>(pos, time.minute());
|
|
|
|
pos[2] = ':';
|
|
|
|
pos += 3;
|
|
|
|
to_ascii_fixed<2>(pos, time.second());
|
|
|
|
pos[2] = '.';
|
|
|
|
pos += 3;
|
|
|
|
to_ascii_fixed<6>(pos, time.millisecond() * 1000 + time.microsecond());
|
|
|
|
pos[6] = ' ';
|
|
|
|
pos[7] = '[';
|
|
|
|
pos[8] = ' ';
|
|
|
|
pos += 9;
|
|
|
|
|
|
|
|
pos = strcopy(Util::getThreadName(), pos);
|
|
|
|
pos[0] = ' ';
|
|
|
|
pos[1] = ']';
|
|
|
|
pos[2] = ' ';
|
|
|
|
pos += 3;
|
|
|
|
pos = strcopy(level, pos);
|
|
|
|
pos[0] = ' ';
|
|
|
|
pos[1] = ' ';
|
|
|
|
pos[2] = '\0';
|
|
|
|
|
2017-01-19 18:26:57 -06:00
|
|
|
return buffer;
|
2016-04-15 14:46:44 -05:00
|
|
|
}
|
2016-04-14 19:12:43 -05:00
|
|
|
|
2016-04-15 14:46:44 -05:00
|
|
|
void signalLogPrefix()
|
|
|
|
{
|
|
|
|
char buffer[1024];
|
2020-04-25 12:03:32 -05:00
|
|
|
prefix<sizeof(buffer) - 1>(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,
|
2018-08-13 02:26:09 -05:00
|
|
|
const std::map<std::string, std::string>& config)
|
2016-04-14 19:12:43 -05:00
|
|
|
{
|
2018-12-04 02:05:43 -06:00
|
|
|
Source.setName(name);
|
2016-04-14 19:12:43 -05:00
|
|
|
std::ostringstream oss;
|
2018-12-04 02:05:43 -06:00
|
|
|
oss << Source.getName();
|
2019-02-12 05:16:40 -06:00
|
|
|
#if !MOBILEAPP // Just one process in a mobile app, the pid is uninteresting.
|
2018-09-13 11:16:00 -05:00
|
|
|
oss << '-'
|
2019-11-07 04:53:37 -06:00
|
|
|
<< std::setw(5) << std::setfill('0') << getpid();
|
2018-09-13 11:16:00 -05:00
|
|
|
#endif
|
2018-12-04 02:05:43 -06:00
|
|
|
Source.setId(oss.str());
|
2016-04-14 19:12:43 -05:00
|
|
|
|
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)
|
2019-09-29 16:00:56 -05:00
|
|
|
{
|
2016-09-27 07:30:45 -05:00
|
|
|
channel = static_cast<Poco::Channel*>(new Poco::ColorConsoleChannel());
|
2019-09-29 16:00:56 -05:00
|
|
|
channel->setProperty("traceColor", "green");
|
2019-10-22 09:30:48 -05:00
|
|
|
channel->setProperty("warningColor", "magenta");
|
2019-09-29 16:00:56 -05:00
|
|
|
}
|
2016-09-27 07:30:45 -05:00
|
|
|
else
|
|
|
|
channel = static_cast<Poco::Channel*>(new Poco::ConsoleChannel());
|
2016-08-07 22:32:39 -05:00
|
|
|
|
2017-07-12 04:38:59 -05:00
|
|
|
/**
|
|
|
|
* 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();
|
2018-12-04 02:05:43 -06:00
|
|
|
auto& logger = Poco::Logger::create(Source.getName(), channel, Poco::Message::PRIO_TRACE);
|
2019-03-24 22:16:29 -05:00
|
|
|
Source.setLogger(&logger);
|
2016-04-14 19:12:43 -05:00
|
|
|
|
2016-07-30 10:52:10 -05:00
|
|
|
logger.setLevel(logLevel.empty() ? std::string("trace") : logLevel);
|
2016-04-14 19:12:43 -05:00
|
|
|
|
2017-01-08 11:21:14 -06:00
|
|
|
const std::time_t t = std::time(nullptr);
|
|
|
|
oss.str("");
|
|
|
|
oss.clear();
|
2017-01-09 11:33:01 -06:00
|
|
|
|
2020-05-24 08:10:18 -05:00
|
|
|
oss << "Initializing " << name << '.';
|
2017-01-09 11:33:01 -06:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
{
|
2020-05-24 08:10:18 -05:00
|
|
|
oss << " Local time: " << buf << '.';
|
2017-01-09 11:33:01 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
oss << " Log level is [" << logger.getLevel() << "].";
|
2018-01-14 19:39:06 -06:00
|
|
|
LOG_INF(oss.str());
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Poco::Logger& logger()
|
|
|
|
{
|
2019-03-24 22:16:29 -05:00
|
|
|
Poco::Logger* pLogger = Source.getLogger();
|
|
|
|
return pLogger ? *pLogger
|
|
|
|
: Poco::Logger::get(Source.getInited() ? Source.getName() : std::string());
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
2018-07-16 20:42:17 -05:00
|
|
|
|
|
|
|
void shutdown()
|
|
|
|
{
|
2020-03-12 10:06:40 -05:00
|
|
|
#if !MOBILEAPP
|
2019-09-09 08:41:09 -05:00
|
|
|
IsShutdown = true;
|
2018-07-16 20:42:17 -05:00
|
|
|
|
2020-03-12 10:06:40 -05:00
|
|
|
Poco::Logger::shutdown();
|
|
|
|
|
2018-07-16 20:42:17 -05:00
|
|
|
// Flush
|
|
|
|
std::flush(std::cout);
|
|
|
|
fflush(stdout);
|
|
|
|
std::flush(std::cerr);
|
|
|
|
fflush(stderr);
|
2019-09-16 04:29:22 -05:00
|
|
|
#endif
|
2020-03-12 10:06:40 -05:00
|
|
|
}
|
2016-04-14 19:12:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|