GH-926 implement log cleaning functionality
Also adds gzip compressed log support
This commit is contained in:
@ -26,6 +26,7 @@
|
||||
#include "BaseVersionList.h"
|
||||
#include "auth/MojangAccount.h"
|
||||
#include "launch/MessageLevel.h"
|
||||
#include "pathmatcher/IPathMatcher.h"
|
||||
|
||||
class QDir;
|
||||
class Task;
|
||||
@ -158,12 +159,16 @@ public:
|
||||
*/
|
||||
virtual std::shared_ptr<Task> createJarModdingTask() = 0;
|
||||
|
||||
|
||||
/*!
|
||||
* Create envrironment variables for running the instance
|
||||
*/
|
||||
virtual QProcessEnvironment createEnvironment() = 0;
|
||||
|
||||
/*!
|
||||
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
|
||||
*/
|
||||
virtual IPathMatcher::Ptr getLogFileMatcher() = 0;
|
||||
|
||||
/*!
|
||||
* does any necessary cleanups after the instance finishes. also runs before\
|
||||
* TODO: turn into a task that can run asynchronously
|
||||
|
@ -59,6 +59,16 @@ set(LOGIC_SOURCES
|
||||
resources/ResourceProxyModel.h
|
||||
resources/ResourceProxyModel.cpp
|
||||
|
||||
# Path matchers
|
||||
pathmatcher/FSTreeMatcher.h
|
||||
pathmatcher/IPathMatcher.h
|
||||
pathmatcher/MultiMatcher.h
|
||||
pathmatcher/RegexpMatcher.h
|
||||
|
||||
# Compression support
|
||||
GZip.h
|
||||
GZip.cpp
|
||||
|
||||
# network stuffs
|
||||
net/NetAction.h
|
||||
net/MD5EtagDownload.h
|
||||
@ -293,12 +303,28 @@ set(LOGIC_SOURCES
|
||||
)
|
||||
################################ COMPILE ################################
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DZ_PREFIX)
|
||||
endif()
|
||||
|
||||
# Add common library
|
||||
add_library(MultiMC_logic STATIC ${LOGIC_SOURCES})
|
||||
|
||||
# Use system zlib on unix and Qt ZLIB on Windows
|
||||
if(UNIX)
|
||||
find_package(ZLIB REQUIRED)
|
||||
else(UNIX)
|
||||
get_filename_component(ZLIB_FOUND_DIR "${Qt5Core_DIR}/../../../include/QtZlib" ABSOLUTE)
|
||||
set(ZLIB_INCLUDE_DIRS ${ZLIB_FOUND_DIR} CACHE PATH "Path to ZLIB headers of Qt")
|
||||
set(ZLIB_LIBRARIES "")
|
||||
if(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
message("Please specify a valid zlib include dir")
|
||||
endif(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
endif(UNIX)
|
||||
|
||||
# Link
|
||||
target_link_libraries(MultiMC_logic xz-embedded unpack200 iconfix libUtil LogicalGui ${QUAZIP_LIBRARIES}
|
||||
Qt5::Core Qt5::Xml Qt5::Widgets Qt5::Network Qt5::Concurrent
|
||||
${MultiMC_LINK_ADDITIONAL_LIBS})
|
||||
${ZLIB_LIBRARIES} ${MultiMC_LINK_ADDITIONAL_LIBS})
|
||||
|
||||
add_dependencies(MultiMC_logic QuaZIP)
|
||||
|
71
logic/GZip.cpp
Normal file
71
logic/GZip.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "GZip.h"
|
||||
#include <zlib.h>
|
||||
#include <QByteArray>
|
||||
|
||||
// HACK: workaround for terrible macro crap on Windows
|
||||
int wrap_inflate (z_streamp strm, int flush)
|
||||
{
|
||||
return inflate(strm, flush);
|
||||
}
|
||||
|
||||
#ifdef inflate
|
||||
#undef inflate
|
||||
#endif
|
||||
|
||||
bool GZip::inflate(const QByteArray &compressedBytes, QByteArray &uncompressedBytes)
|
||||
{
|
||||
if (compressedBytes.size() == 0)
|
||||
{
|
||||
uncompressedBytes = compressedBytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned uncompLength = compressedBytes.size();
|
||||
unsigned half_length = compressedBytes.size() / 2;
|
||||
uncompressedBytes.clear();
|
||||
uncompressedBytes.resize(uncompLength);
|
||||
|
||||
z_stream strm;
|
||||
strm.next_in = (Bytef *)compressedBytes.data();
|
||||
strm.avail_in = compressedBytes.size();
|
||||
strm.total_out = 0;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
|
||||
bool done = false;
|
||||
|
||||
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!done)
|
||||
{
|
||||
// If our output buffer is too small
|
||||
if (strm.total_out >= uncompLength)
|
||||
{
|
||||
uncompressedBytes.resize(uncompLength + half_length);
|
||||
uncompLength += half_length;
|
||||
}
|
||||
|
||||
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
|
||||
strm.avail_out = uncompLength - strm.total_out;
|
||||
|
||||
// Inflate another chunk.
|
||||
int err = wrap_inflate(&strm, Z_SYNC_FLUSH);
|
||||
if (err == Z_STREAM_END)
|
||||
done = true;
|
||||
else if (err != Z_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inflateEnd(&strm) != Z_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uncompressedBytes.resize(strm.total_out);
|
||||
return true;
|
||||
}
|
9
logic/GZip.h
Normal file
9
logic/GZip.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <QByteArray>
|
||||
|
||||
class GZip
|
||||
{
|
||||
public:
|
||||
static bool inflate(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
|
||||
};
|
||||
|
@ -70,4 +70,8 @@ public:
|
||||
{
|
||||
return QMap<QString, QString>();
|
||||
}
|
||||
virtual IPathMatcher::Ptr getLogFileMatcher()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <QDebug>
|
||||
|
||||
RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent)
|
||||
: QObject(parent), m_exp(".*"), m_watcher(new QFileSystemWatcher(this))
|
||||
: QObject(parent), m_watcher(new QFileSystemWatcher(this))
|
||||
{
|
||||
connect(m_watcher, &QFileSystemWatcher::fileChanged, this,
|
||||
&RecursiveFileSystemWatcher::fileChange);
|
||||
@ -82,16 +82,20 @@ void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir)
|
||||
QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory)
|
||||
{
|
||||
QStringList ret;
|
||||
QRegularExpression exp(m_exp);
|
||||
if(!m_matcher)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||
{
|
||||
ret.append(scanRecursive(directory.absoluteFilePath(dir)));
|
||||
}
|
||||
for (const QString &file : directory.entryList(QDir::Files))
|
||||
{
|
||||
if (exp.match(file).hasMatch())
|
||||
auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file));
|
||||
if (m_matcher->matches(relPath))
|
||||
{
|
||||
ret.append(m_root.relativeFilePath(directory.absoluteFilePath(file)));
|
||||
ret.append(relPath);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QDir>
|
||||
#include "pathmatcher/IPathMatcher.h"
|
||||
|
||||
class RecursiveFileSystemWatcher : public QObject
|
||||
{
|
||||
@ -10,16 +11,27 @@ public:
|
||||
RecursiveFileSystemWatcher(QObject *parent);
|
||||
|
||||
void setRootDir(const QDir &root);
|
||||
QDir rootDir() const { return m_root; }
|
||||
QDir rootDir() const
|
||||
{
|
||||
return m_root;
|
||||
}
|
||||
|
||||
// WARNING: setting this to true may be bad for performance
|
||||
void setWatchFiles(const bool watchFiles);
|
||||
bool watchFiles() const { return m_watchFiles; }
|
||||
bool watchFiles() const
|
||||
{
|
||||
return m_watchFiles;
|
||||
}
|
||||
|
||||
void setFileExpression(const QString &exp) { m_exp = exp; }
|
||||
QString fileExpression() const { return m_exp; }
|
||||
void setMatcher(IPathMatcher::Ptr matcher)
|
||||
{
|
||||
m_matcher = matcher;
|
||||
}
|
||||
|
||||
QStringList files() const { return m_files; }
|
||||
QStringList files() const
|
||||
{
|
||||
return m_files;
|
||||
}
|
||||
|
||||
signals:
|
||||
void filesChanged();
|
||||
@ -33,7 +45,7 @@ private:
|
||||
QDir m_root;
|
||||
bool m_watchFiles = false;
|
||||
bool m_isEnabled = false;
|
||||
QString m_exp;
|
||||
IPathMatcher::Ptr m_matcher;
|
||||
|
||||
QFileSystemWatcher *m_watcher;
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "Env.h"
|
||||
#include "minecraft/MinecraftVersionList.h"
|
||||
#include <MMCStrings.h>
|
||||
#include <pathmatcher/RegexpMatcher.h>
|
||||
#include <pathmatcher/MultiMatcher.h>
|
||||
|
||||
#define IBUS "@im=ibus"
|
||||
|
||||
@ -277,4 +279,12 @@ MessageLevel::Enum MinecraftInstance::guessLevel(const QString &line, MessageLev
|
||||
return level;
|
||||
}
|
||||
|
||||
IPathMatcher::Ptr MinecraftInstance::getLogFileMatcher()
|
||||
{
|
||||
auto combined = std::make_shared<MultiMatcher>();
|
||||
combined->add(std::make_shared<RegexpMatcher>(".*\\.log(\\.[0-9]*)?(\\.gz)?$"));
|
||||
combined->add(std::make_shared<RegexpMatcher>("crash-.*\\.txt"));
|
||||
return combined;
|
||||
}
|
||||
|
||||
#include "MinecraftInstance.moc"
|
||||
|
@ -44,6 +44,8 @@ public:
|
||||
/// guess log level from a line of minecraft log
|
||||
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level);
|
||||
|
||||
virtual IPathMatcher::Ptr getLogFileMatcher() override;
|
||||
|
||||
protected:
|
||||
QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
|
||||
};
|
||||
|
19
logic/pathmatcher/FSTreeMatcher.h
Normal file
19
logic/pathmatcher/FSTreeMatcher.h
Normal file
@ -0,0 +1,19 @@
|
||||
#include "IPathMatcher.h"
|
||||
#include <SeparatorPrefixTree.h>
|
||||
#include <QRegularExpression>
|
||||
|
||||
class FSTreeMatcher : public IPathMatcher
|
||||
{
|
||||
public:
|
||||
virtual ~FSTreeMatcher() {};
|
||||
FSTreeMatcher(SeparatorPrefixTree<'/'> & tree) : m_fsTree(tree)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool matches(const QString &string) override
|
||||
{
|
||||
return m_fsTree.covers(string);
|
||||
}
|
||||
|
||||
SeparatorPrefixTree<'/'> & m_fsTree;
|
||||
};
|
12
logic/pathmatcher/IPathMatcher.h
Normal file
12
logic/pathmatcher/IPathMatcher.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
class IPathMatcher
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<IPathMatcher> Ptr;
|
||||
|
||||
public:
|
||||
virtual ~IPathMatcher(){};
|
||||
virtual bool matches(const QString &string) = 0;
|
||||
};
|
31
logic/pathmatcher/MultiMatcher.h
Normal file
31
logic/pathmatcher/MultiMatcher.h
Normal file
@ -0,0 +1,31 @@
|
||||
#include "IPathMatcher.h"
|
||||
#include <SeparatorPrefixTree.h>
|
||||
#include <QRegularExpression>
|
||||
|
||||
class MultiMatcher : public IPathMatcher
|
||||
{
|
||||
public:
|
||||
virtual ~MultiMatcher() {};
|
||||
MultiMatcher()
|
||||
{
|
||||
}
|
||||
MultiMatcher &add(Ptr add)
|
||||
{
|
||||
m_matchers.append(add);
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual bool matches(const QString &string) override
|
||||
{
|
||||
for(auto iter: m_matchers)
|
||||
{
|
||||
if(iter->matches(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<Ptr> m_matchers;
|
||||
};
|
29
logic/pathmatcher/RegexpMatcher.h
Normal file
29
logic/pathmatcher/RegexpMatcher.h
Normal file
@ -0,0 +1,29 @@
|
||||
#include "IPathMatcher.h"
|
||||
#include <QRegularExpression>
|
||||
|
||||
class RegexpMatcher : public IPathMatcher
|
||||
{
|
||||
public:
|
||||
virtual ~RegexpMatcher() {};
|
||||
RegexpMatcher(QString regexp)
|
||||
{
|
||||
m_regexp.setPattern(regexp);
|
||||
m_onlyFilenamePart = !regexp.contains('/');
|
||||
}
|
||||
|
||||
virtual bool matches(const QString &string) override
|
||||
{
|
||||
if(m_onlyFilenamePart)
|
||||
{
|
||||
auto slash = string.lastIndexOf('/');
|
||||
if(slash != -1)
|
||||
{
|
||||
auto part = string.mid(slash + 1);
|
||||
return m_regexp.match(part).hasMatch();
|
||||
}
|
||||
}
|
||||
return m_regexp.match(string).hasMatch();
|
||||
}
|
||||
QRegularExpression m_regexp;
|
||||
bool m_onlyFilenamePart = false;
|
||||
};
|
Reference in New Issue
Block a user