/*
Userspace Virtual Filesystem
Copyright (C) 2015 Sebastian Herbord. All rights reserved.
This file is part of usvfs.
usvfs 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.
usvfs 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 usvfs. If not, see .
*/
#pragma warning(disable : 4714)
#pragma warning(disable : 4503)
#pragma warning(push, 3)
#include "shmlogger.h"
#pragma warning(pop)
#pragma warning(disable : 4996)
using namespace boost::interprocess;
using namespace boost::posix_time;
SHMLogger* SHMLogger::s_Instance = nullptr;
SHMLogger::owner_t SHMLogger::owner;
SHMLogger::client_t SHMLogger::client;
SHMLogger::SHMLogger(owner_t, const std::string& queueName)
: m_QueueName(queueName),
m_LogQueue(create_only, queueName.c_str(), MESSAGE_COUNT, MESSAGE_SIZE),
m_DroppedMessages(0)
{
if (s_Instance != nullptr) {
throw std::runtime_error("duplicate shm logger instantiation");
} else {
s_Instance = this;
}
}
SHMLogger::SHMLogger(client_t, const std::string& queueName)
: m_QueueName(queueName), m_LogQueue(open_only, queueName.c_str()),
m_DroppedMessages(0)
{
if (s_Instance != nullptr) {
throw std::runtime_error("duplicate shm logger instantiation");
} else {
s_Instance = this;
}
}
SHMLogger::~SHMLogger()
{
s_Instance = nullptr;
message_queue_interop::remove(m_QueueName.c_str());
}
SHMLogger& SHMLogger::create(const char* instanceName)
{
if (s_Instance != nullptr) {
throw std::runtime_error("duplicate shm logger instantiation");
} else {
std::string queueName = std::string("__shm_sink_") + instanceName;
message_queue::remove(queueName.c_str());
new SHMLogger(owner, queueName);
atexit([]() {
delete s_Instance;
});
}
return *s_Instance;
}
SHMLogger& SHMLogger::open(const char* instanceName)
{
if (s_Instance != nullptr) {
throw std::runtime_error("duplicate shm logger instantiation");
} else {
new SHMLogger(client, std::string("__shm_sink_") + instanceName);
}
return *s_Instance;
}
void SHMLogger::free()
{
if (s_Instance != nullptr) {
SHMLogger* temp = s_Instance;
s_Instance = nullptr;
delete temp;
}
}
bool SHMLogger::tryGet(char* buffer, size_t bufferSize)
{
message_queue_interop::size_type receivedSize;
unsigned int prio;
bool res = m_LogQueue.try_receive(buffer, static_cast(bufferSize),
receivedSize, prio);
if (res) {
buffer[std::min(bufferSize - 1, static_cast(receivedSize))] = '\0';
}
return res;
}
void SHMLogger::get(char* buffer, size_t bufferSize)
{
message_queue_interop::size_type receivedSize;
unsigned int prio;
m_LogQueue.receive(buffer, static_cast(bufferSize), receivedSize, prio);
buffer[std::min(bufferSize - 1, static_cast(receivedSize))] = '\0';
}
usvfs::sinks::shm_sink::shm_sink(const char* queueName)
: m_LogQueue(open_only, (std::string("__shm_sink_") + queueName).c_str()),
m_DroppedMessages(0)
{}
void usvfs::sinks::shm_sink::flush_() {}
void usvfs::sinks::shm_sink::sink_it_(const spdlog::details::log_msg& msg)
{
int droppedMessages = m_DroppedMessages.load(std::memory_order_relaxed);
if (droppedMessages > 0) {
auto dropMessage = std::format("{} debug messages dropped", droppedMessages);
if (m_LogQueue.try_send(dropMessage.c_str(),
static_cast(dropMessage.size()), 0)) {
m_DroppedMessages.store(0, std::memory_order_relaxed);
}
}
std::string message{msg.payload};
// blacklist %USERNAME% because PII
static const std::string username = std::string(getenv("USERNAME"));
boost::algorithm::ireplace_all(message, std::string("\\") + username, "\\USERNAME");
boost::algorithm::ireplace_all(message, std::string("/") + username, "/USERNAME");
if (message.length() > SHMLogger::MESSAGE_SIZE) {
std::vector splitVec;
boost::split(splitVec, message, boost::is_any_of("\r\n"), boost::token_compress_on);
for (const std::string& line : splitVec) {
output(msg.level, line);
}
} else {
output(msg.level, message);
}
}
void usvfs::sinks::shm_sink::output(spdlog::level::level_enum lev,
const std::string& message)
{
bool sent = true;
// spdlog auto-append line breaks which we don't need. Probably would be
// better to not write the breaks to begin with?
size_t count = std::min(message.find_last_not_of("\r\n") + 1,
static_cast(m_LogQueue.get_max_msg_size()));
// depending on the log level, drop less important messages if the receiver
// can't keep up
switch (lev) {
case spdlog::level::trace:
case spdlog::level::debug:
case spdlog::level::info: {
// m_LogQueue.send(message.c_str(), count, 0);
sent = m_LogQueue.try_send(message.c_str(), static_cast(count), 0);
} break;
case spdlog::level::err:
case spdlog::level::critical: {
m_LogQueue.send(message.c_str(), static_cast(count), 0);
} break;
default: {
boost::posix_time::ptime time =
microsec_clock::universal_time() + boost::posix_time::milliseconds(200);
sent = m_LogQueue.timed_send(message.c_str(), static_cast(count), 0,
time);
} break;
}
if (!sent) {
m_DroppedMessages.fetch_add(1, std::memory_order_relaxed);
}
}
void __cdecl boost::interprocess::ipcdetail::get_shared_dir(std::string& shared_dir)
{
PWSTR path;
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &path))) {
_bstr_t bPath(path);
shared_dir = (char*)bPath;
shared_dir += "\\USVFS";
} else {
shared_dir = "C:\\ProgramData\\USVFS";
}
boost::filesystem::path boostPath(shared_dir);
if (!boost::filesystem::exists(boostPath))
boost::filesystem::create_directories(boostPath);
}