diff --git a/src-test/server/ConfigTest.cpp b/src-test/server/ConfigTest.cpp index 3aa334a53f..ad9d9e462f 100644 --- a/src-test/server/ConfigTest.cpp +++ b/src-test/server/ConfigTest.cpp @@ -33,7 +33,7 @@ #include "mc/world/actor/DataItem.h" -#include "ll/api/memory/IndirectValue.h" +#include "ll/api/data/IndirectValue.h" // [0, 8, 16, 96, 97, 98, 104, 136, 144, 160, 176, 184, 196, 208, 232, 248, 304, 328, 360, 392, 408] @@ -186,8 +186,8 @@ LL_AUTO_TYPE_INSTANCE_HOOK(ConfigTest, HookPriority::Normal, ServerInstance, &Se ll::getLogger().debug("{}", 3 / v1); - auto indirect = ll::memory::makePolymorphic(); - indirect = ll::memory::makePolymorphic(13, 23); + auto indirect = ll::makePolymorphic(); + indirect = ll::makePolymorphic(13, 23); auto indirect2 = indirect; auto indirect3 = indirect; diff --git a/src-test/server/ECSTest.cpp b/src-test/server/ECSTest.cpp index eb1e71a7df..863c49b4e7 100644 --- a/src-test/server/ECSTest.cpp +++ b/src-test/server/ECSTest.cpp @@ -5,10 +5,10 @@ #include #include -#include "ll/api/io/Logger.h" #include "ll/api/base/StdInt.h" #include "ll/api/command/CommandHandle.h" #include "ll/api/command/CommandRegistrar.h" +#include "ll/api/io/Logger.h" #include "ll/api/memory/Hook.h" #include "ll/api/thread/TickSyncSleep.h" #include "mc/entity/systems/DefaultEntitySystemsCollection.h" diff --git a/src-test/server/FormTest.cpp b/src-test/server/FormTest.cpp index 2aff731dbb..3986c28569 100644 --- a/src-test/server/FormTest.cpp +++ b/src-test/server/FormTest.cpp @@ -24,7 +24,7 @@ struct TestFormParam { void registerFormTestCommand() { static ll::io::Logger logger{"FormTest"}; - auto& cmd = + auto& cmd = ll::command::CommandRegistrar::getInstance() .getOrCreateCommand("formtest", "formtest", CommandPermissionLevel::GameDirectors, CommandFlagValue::None); cmd.overload().required("type").execute( diff --git a/src-test/server/TestNbt.cpp b/src-test/server/TestNbt.cpp index d35a2add80..e2c29789e3 100644 --- a/src-test/server/TestNbt.cpp +++ b/src-test/server/TestNbt.cpp @@ -166,8 +166,11 @@ LL_AUTO_TYPE_INSTANCE_HOOK(NbtTest, HookPriority::Normal, ServerInstance, &Serve using namespace ll::string_utils; - ll::getLogger().debug("\n{}", replaceAnsiToMcCode(nbt.toSnbt(SnbtFormat::Colored | SnbtFormat::Console))); - ll::getLogger().debug("\n{}", (nbt2.value().toSnbt(SnbtFormat::Colored))); + ll::getLogger().debug( + "\n{}", + replaceMcToAnsiCode(replaceAnsiToMcCode(nbt.toSnbt(SnbtFormat::Colored | SnbtFormat::Console))) + ); + ll::getLogger().debug("\n{}", replaceMcToAnsiCode(nbt2.value().toSnbt(SnbtFormat::Colored))); ll::getLogger().debug( diff --git a/src/ll/api/base/CompilerPredefine.h b/src/ll/api/base/CompilerPredefine.h index 26a65e7624..b963bba54d 100644 --- a/src/ll/api/base/CompilerPredefine.h +++ b/src/ll/api/base/CompilerPredefine.h @@ -45,6 +45,22 @@ #define LL_RETURN_ADDRESS _ReturnAddress() #endif +#ifndef LL_CURRENT_LINE +#define LL_CURRENT_LINE __builtin_LINE() +#endif + +#ifndef LL_CURRENT_COLUMN +#define LL_CURRENT_COLUMN __builtin_COLUMN() +#endif + +#ifndef LL_CURRENT_FILE +#define LL_CURRENT_FILE __builtin_FILE() +#endif + +#ifndef LL_CURRENT_FUNCTION +#define LL_CURRENT_FUNCTION __builtin_FUNCTION() +#endif + // MSVC has customized some functions and classes inside the compiler, but they are not included in IntelliSense. This // header file is only used for IntelliSense. #if defined(__INTELLISENSE__) || defined(__clang__) || defined(__clangd__) @@ -248,6 +264,22 @@ using FileHandleT = void*; #define LL_RETURN_ADDRESS __builtin_return_address(0) #endif +#ifndef LL_CURRENT_LINE +#define LL_CURRENT_LINE __builtin_LINE() +#endif + +#ifndef LL_CURRENT_COLUMN +#define LL_CURRENT_COLUMN 0 +#endif + +#ifndef LL_CURRENT_FILE +#define LL_CURRENT_FILE __builtin_FILE() +#endif + +#ifndef LL_CURRENT_FUNCTION +#define LL_CURRENT_FUNCTION __builtin_FUNCTION() +#endif + namespace ll::internal { [[nodiscard]] LL_FORCEINLINE void* getCurrentModuleHandle() noexcept; // Implemented in SystemUtils_linux.cpp diff --git a/src/ll/api/base/SourceLocation.h b/src/ll/api/base/SourceLocation.h index 2991472140..f657ffc2a7 100644 --- a/src/ll/api/base/SourceLocation.h +++ b/src/ll/api/base/SourceLocation.h @@ -2,21 +2,25 @@ #include +#include "ll/api/base/CompilerPredefine.h" + #include "fmt/format.h" namespace ll { class SourceLocation { public: static consteval SourceLocation current( - int line = __builtin_LINE(), - int column = __builtin_COLUMN(), - char const* file = __builtin_FILE(), - char const* function = __builtin_FUNCTION() + char const* file = LL_CURRENT_FILE, + char const* function = LL_CURRENT_FUNCTION, + int line = LL_CURRENT_LINE, + int column = LL_CURRENT_COLUMN ) noexcept { - return {line, column, file, function}; + return {file, function, line, column}; } - consteval SourceLocation(int line, int column, char const* file, char const* function) noexcept + static consteval SourceLocation unknown() noexcept { return {"", "", 0, 0}; } + + consteval SourceLocation(char const* file, char const* function, int line, int column) noexcept : mLine(line), mColumn(column), mFile(file), @@ -32,9 +36,9 @@ class SourceLocation { } private: - int mLine; - int mColumn; char const* mFile; char const* mFunction; + int mLine; + int mColumn; }; } // namespace ll diff --git a/src/ll/api/memory/IndirectValue.h b/src/ll/api/data/IndirectValue.h similarity index 77% rename from src/ll/api/memory/IndirectValue.h rename to src/ll/api/data/IndirectValue.h index f78e8f8972..b18f5a8f14 100644 --- a/src/ll/api/memory/IndirectValue.h +++ b/src/ll/api/data/IndirectValue.h @@ -7,7 +7,7 @@ #include "ll/api/base/Concepts.h" #include "ll/api/data/TightPair.h" -namespace ll::memory { +namespace ll::data { template struct defaultCopy { @@ -150,22 +150,48 @@ class IndirectValue { return *this; } - constexpr T* operator->() noexcept { return get(); } - constexpr T const* operator->() const noexcept { return get(); } - constexpr T& operator*() & noexcept { return *get(); } - constexpr T const& operator*() const& noexcept { return *get(); } - constexpr T&& operator*() && noexcept { return std::forward(*get()); } - constexpr T const&& operator*() const&& noexcept { return std::forward(*get()); } + [[nodiscard]] constexpr T* operator->() noexcept { return get(); } + [[nodiscard]] constexpr T const* operator->() const noexcept { return get(); } + [[nodiscard]] constexpr T& operator*() & noexcept { return *get(); } + [[nodiscard]] constexpr T const& operator*() const& noexcept { return *get(); } + [[nodiscard]] constexpr T&& operator*() && noexcept { return std::forward(*get()); } + [[nodiscard]] constexpr T const&& operator*() const&& noexcept { return std::forward(*get()); } }; +template +[[nodiscard]] constexpr bool operator==(IndirectValue const& lhs, IndirectValue const& rhs) { + return lhs.get() == rhs.get(); +} +template +[[nodiscard]] constexpr auto operator<=>(IndirectValue const& lhs, IndirectValue const& rhs) { + return lhs.get() <=> rhs.get(); +} +template +[[nodiscard]] constexpr bool operator==(IndirectValue const& lhs, std::nullptr_t) { + return lhs.get() == nullptr; +} +template +[[nodiscard]] constexpr auto operator<=>(IndirectValue const& lhs, std::nullptr_t) { + return lhs.get() <=> nullptr; +} +} // namespace ll::data +namespace std { +template +struct hash<::ll::data::IndirectValue> { + std::size_t operator()(::ll::data::IndirectValue const& value) const { return hash{}(value.get()); } +}; +} // namespace std + +namespace ll { template > -using Indirect = IndirectValue, D>; +using Indirect = data::IndirectValue, D>; template < class T, - class C = std::conditional_t, virtualCloneCopy, polymorphicCopy>, + class C = + std::conditional_t, data::virtualCloneCopy, data::polymorphicCopy>, class D = std::default_delete> -using Polymorphic = IndirectValue; +using Polymorphic = data::IndirectValue; template Indirect makeIndirect(Args&&... args) { @@ -176,5 +202,4 @@ template Polymorphic makePolymorphic(Args&&... args) { return Polymorphic(new T(std::forward(args)...)); } - -} // namespace ll::memory +} // namespace ll diff --git a/src/ll/api/data/TmWithMs.h b/src/ll/api/data/TmWithMs.h new file mode 100644 index 0000000000..2444485057 --- /dev/null +++ b/src/ll/api/data/TmWithMs.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "ll/api/base/StdInt.h" + +#include "fmt/chrono.h" + +namespace ll::data { +struct TmWithMs : public std::tm { + ushort ms; +}; +} // namespace ll::data + +template +struct fmt::formatter : formatter { +private: + detail::arg_ref precisionRef; + int precision{0}; + +public: + FMT_CONSTEXPR formatter() { this->format_str_ = detail::string_literal{}; } + + template + auto format(ll::data::TmWithMs const& val, FormatContext& ctx) const -> decltype(ctx.out()) { + formatter::format(val, ctx); + auto out = ctx.out(); + if (precision > 0) { + fmt::format_to(out, "{0:0>{1}}", val.ms, precision); + } + return out; + } + constexpr auto parse(basic_format_parse_context& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(), end = ctx.end(); + if (it == end || *it == '}') return it; + if (*it == '.') { + it = detail::parse_precision(it, end, precision, precisionRef, ctx); + } + ctx.advance_to(it); + return formatter::parse(ctx); + } +}; diff --git a/src/ll/api/i18n/I18n.h b/src/ll/api/i18n/I18n.h index 39c04c6e12..1fa3228e92 100644 --- a/src/ll/api/i18n/I18n.h +++ b/src/ll/api/i18n/I18n.h @@ -1,8 +1,8 @@ #pragma once +#include "ll/api/Expected.h" #include "ll/api/base/Concepts.h" #include "ll/api/base/FixedString.h" -#include "ll/api/Expected.h" #include diff --git a/src/ll/api/io/DefaultSink.cpp b/src/ll/api/io/DefaultSink.cpp new file mode 100644 index 0000000000..193bd19d17 --- /dev/null +++ b/src/ll/api/io/DefaultSink.cpp @@ -0,0 +1,40 @@ +#include "ll/api/io/DefaultSink.h" + +#include "ll/api/io/FileUtils.h" +#include "ll/api/io/PatternFormatter.h" +#include "ll/core/io/Output.h" + +#include "pl/Config.h" + +namespace ll::io { +struct DefaultSink::Impl { + std::mutex mutex; + std::ofstream logFile{file_utils::u8path(pl::pl_log_path) / u8"latest.log"}; + PatternFormatter logFileFormatter{"[{tm:.3%F %T.} {lvl}][{tit}] {msg}", false}; +}; + +DefaultSink::Impl& DefaultSink::getImpl() { + static Impl impl; + return impl; +} + +DefaultSink::DefaultSink() +: Sink(makePolymorphic("{tm:.3%T.} {lvl} {tit} {msg}", true, 0b0010)), + impl(getImpl()) {} + +DefaultSink::~DefaultSink() = default; + +void DefaultSink::setFormatter(Polymorphic fmter) { + std::lock_guard lock(impl.mutex); + formatter = std::move(fmter); +} +void DefaultSink::append(LogMessageView const& view) { + std::lock_guard lock(impl.mutex); + std::string buffer; + formatter->format(view, buffer); + defaultOutput(buffer); + buffer.clear(); + impl.logFileFormatter.format(view, buffer); + impl.logFile << buffer; +} +} // namespace ll::io diff --git a/src/ll/api/io/DefaultSink.h b/src/ll/api/io/DefaultSink.h new file mode 100644 index 0000000000..22c893e091 --- /dev/null +++ b/src/ll/api/io/DefaultSink.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "ll/api/io/Sink.h" + +namespace ll::io { +class DefaultSink : public Sink { + struct Impl; + Impl& impl; + + static Impl& getImpl(); + +public: + LLAPI DefaultSink(); + + LLAPI ~DefaultSink() override; + + LLAPI void setFormatter(Polymorphic fmter) override; + + LLAPI void append(LogMessageView const& view) override; +}; +} // namespace ll::io diff --git a/src/ll/api/io/FileSink.cpp b/src/ll/api/io/FileSink.cpp new file mode 100644 index 0000000000..6ff7383cc3 --- /dev/null +++ b/src/ll/api/io/FileSink.cpp @@ -0,0 +1,34 @@ +#include "ll/api/io/FileSink.h" + +namespace ll::io { +FileSink::FileSink(std::filesystem::path const& path, Polymorphic formatter, std::ios::openmode mode) +: Sink(std::move(formatter)), + file(path, mode), + flushLevel(LogLevel::Off) {} + +FileSink::~FileSink() = default; + +void FileSink::setFormatter(Polymorphic fmter) { + std::lock_guard lock(mutex); + formatter = std::move(fmter); +} +void FileSink::append(LogMessageView const& view) { + std::lock_guard lock(mutex); + std::string buffer; + formatter->format(view, buffer); + file << buffer; + if (view.lvl <= flushLevel) { + file.flush(); + } +} + +void FileSink::flush() { + std::lock_guard lock(mutex); + file.flush(); +} + +void FileSink::setFlushLevel(LogLevel level) { + std::lock_guard lock(mutex); + flushLevel = level; +} +} // namespace ll::io diff --git a/src/ll/api/io/FileSink.h b/src/ll/api/io/FileSink.h new file mode 100644 index 0000000000..fb7e9fdddd --- /dev/null +++ b/src/ll/api/io/FileSink.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include "ll/api/io/Sink.h" + +namespace ll::io { +class FileSink : public Sink { + std::ofstream file; + std::mutex mutex; + LogLevel flushLevel; + +public: + LLAPI + FileSink( + std::filesystem::path const& path, + Polymorphic formatter, + std::ios::openmode mode = std::ios::out + ); + + LLAPI ~FileSink() override; + + LLAPI void setFormatter(Polymorphic fmter) override; + + LLAPI void append(LogMessageView const& view) override; + + LLAPI void flush() override; + + LLAPI void setFlushLevel(LogLevel level) override; +}; +} // namespace ll::io diff --git a/src/ll/api/io/Formatter.h b/src/ll/api/io/Formatter.h new file mode 100644 index 0000000000..ae5e558991 --- /dev/null +++ b/src/ll/api/io/Formatter.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ll/api/io/LogMessage.h" + +namespace ll::io { +class Formatter { +public: + virtual ~Formatter() = default; + + virtual void format(LogMessageView const& view, std::string& buffer) const noexcept = 0; +}; +} // namespace ll::io diff --git a/src/ll/api/io/LogLevel.h b/src/ll/api/io/LogLevel.h index 33b649c2d0..0d4fd99d22 100644 --- a/src/ll/api/io/LogLevel.h +++ b/src/ll/api/io/LogLevel.h @@ -2,7 +2,8 @@ namespace ll::io { class Logger; -enum class LogLevel { +enum class LogLevel : int { + Off = -1, Fatal, Error, Warn, diff --git a/src/ll/api/io/LogMessage.h b/src/ll/api/io/LogMessage.h new file mode 100644 index 0000000000..0f8e77a3f0 --- /dev/null +++ b/src/ll/api/io/LogMessage.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include "ll/api/base/StdInt.h" +#include "ll/api/data/TmWithMs.h" +#include "ll/api/io/LogLevel.h" + +namespace ll::io { +struct LogMessageView { + std::string_view msg; + std::string_view tit; + LogLevel lvl; + data::TmWithMs tm; + + constexpr LogMessageView(std::string_view msg, std::string_view tit, LogLevel lvl, data::TmWithMs const& tm) + : msg(msg), + tit(tit), + lvl(lvl), + tm(tm) {} +}; +struct LogMessage : public LogMessageView { + std::string msgBuffer; + std::string titBuffer; + + constexpr LogMessage(std::string msg, std::string_view tit, LogLevel lvl, data::TmWithMs const& tm) + : msgBuffer(std::move(msg)), + titBuffer(tit), + LogMessageView(msgBuffer, titBuffer, lvl, tm) {} +}; +} // namespace ll::io diff --git a/src/ll/api/io/Logger.cpp b/src/ll/api/io/Logger.cpp index 6cd634a9bb..020e7e0510 100644 --- a/src/ll/api/io/Logger.cpp +++ b/src/ll/api/io/Logger.cpp @@ -1,21 +1,96 @@ #include "ll/api/io/Logger.h" + +#include "ll/api/io/DefaultSink.h" +#include "ll/api/io/LogMessage.h" +#include "ll/api/io/PatternFormatter.h" +#include "ll/api/utils/ErrorUtils.h" +#include "ll/api/utils/StringUtils.h" +#include "ll/api/utils/SystemUtils.h" #include "ll/core/io/Output.h" +#include "pl/Config.h" + namespace ll::io { struct Logger::Impl { std::string title; -}; + LogLevel level; -Logger::Logger() : impl(std::make_unique()) {} -Logger::~Logger() = default; + std::vector> sinks; -Logger::Logger(std::string_view title) : Logger() { setTitle(title); } + Impl(std::string_view title) : title(title) {} +}; +Logger::~Logger() = default; -void Logger::print(LogLevel level, std::string_view msg) const noexcept { - defaultOutput(impl->title + " " + std::string{msg} + '\n'); +Logger::Logger(std::string_view title) : impl(std::make_unique(title)) { + impl->level = (LogLevel)(std::clamp(pl::pl_log_level - 1, 0, 5)); + impl->sinks.push_back(std::make_shared()); } +Logger::Logger() : Logger(sys_utils::getCallerModuleFileName()) {} + +void Logger::printStr(LogLevel level, std::string&& msg) const noexcept { + if (level > impl->level) { + return; + } + printView(level, msg); +} +// TODO: move to threadpool? need test +void Logger::printView(LogLevel level, std::string_view msg) const noexcept try { + if (level > impl->level) { + return; + } + LogMessageView msgView(msg, impl->title, level, sys_utils::getLocalTime()); + for (auto& sink : impl->sinks) { + sink->append(msgView); + } +} catch (...) { + try { + io::defaultOutput(fmt::format( + "\x1b[31mERROR IN LOGGER API:\n{}\x1b[91m\n{}\x1b[0m\n", + error_utils::makeExceptionString(std::current_exception()), + string_utils::tou8str(msg) + )); + } catch (...) { + try { + io::defaultOutput(fmt::format("\x1b[31mUNKNOWN ERROR IN LOGGER API\x1b[0m\n")); + } catch (...) {} + } +} void Logger::setTitle(std::string_view title) { impl->title.assign(title); } +void Logger::setLevel(LogLevel level) { impl->level = level; } + +void Logger::setFlushLevel(LogLevel level) { + for (auto& sink : impl->sinks) { + sink->setFlushLevel(level); + } +} +void Logger::setFormatter(Polymorphic formatter) { + for (auto& sink : impl->sinks) { + sink->setFormatter(formatter); + } +} +void Logger::flush() const { + for (auto& sink : impl->sinks) { + sink->flush(); + } +} +void Logger::clearSink() const { impl->sinks.clear(); } + +size_t Logger::addSink(std::shared_ptr sink) const { + impl->sinks.emplace_back(std::move(sink)); + return impl->sinks.size() - 1; +} + +std::shared_ptr Logger::getSink(size_t index) const { return impl->sinks.at(index); } + +void Logger::forEachSink(std::function const& fn) const { + for (auto& sink : impl->sinks) { + if (!fn(*sink)) { + break; + } + } +} + } // namespace ll::io diff --git a/src/ll/api/io/Logger.h b/src/ll/api/io/Logger.h index ed8e297b6b..31ec3b5c4d 100644 --- a/src/ll/api/io/Logger.h +++ b/src/ll/api/io/Logger.h @@ -1,11 +1,6 @@ #pragma once -#include -#include -#include #include -#include -#include #include #include #include @@ -13,6 +8,7 @@ #include "ll/api/base/Concepts.h" // IWYU pragma: keep #include "ll/api/base/Macro.h" #include "ll/api/io/LogLevel.h" +#include "ll/api/io/Sink.h" #include "fmt/chrono.h" // IWYU pragma: keep #include "fmt/color.h" @@ -25,7 +21,8 @@ namespace ll::io { class Logger { - LLAPI void print(LogLevel, std::string_view) const noexcept; + LLAPI void printStr(LogLevel, std::string&&) const noexcept; + LLAPI void printView(LogLevel, std::string_view) const noexcept; struct Impl; @@ -34,72 +31,96 @@ class Logger { public: template void log(LogLevel level, fmt::format_string fmt, Args&&... args) const { - print(level, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); + printStr(level, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); } + void log(LogLevel level, std::string&& msg) const { printStr(level, std::move(msg)); } template void log(LogLevel level, S const& msg) const { - print(level, msg); + printView(level, msg); } template void fatal(fmt::format_string fmt, Args&&... args) const { - print(LogLevel::Fatal, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); + printStr(LogLevel::Fatal, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); } + void fatal(std::string&& msg) const { printStr(LogLevel::Fatal, std::move(msg)); } template void fatal(S const& msg) const { - print(LogLevel::Fatal, msg); + printView(LogLevel::Fatal, msg); } template void error(fmt::format_string fmt, Args&&... args) const { - print(LogLevel::Error, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); + printStr(LogLevel::Error, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); } + void error(std::string&& msg) const { printStr(LogLevel::Error, std::move(msg)); } template void error(S const& msg) const { - print(LogLevel::Error, msg); + printView(LogLevel::Error, msg); } template void warn(fmt::format_string fmt, Args&&... args) const { - print(LogLevel::Warn, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); + printStr(LogLevel::Warn, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); } + void warn(std::string&& msg) const { printStr(LogLevel::Warn, std::move(msg)); } template void warn(S const& msg) const { - print(LogLevel::Warn, msg); + printView(LogLevel::Warn, msg); } template void info(fmt::format_string fmt, Args&&... args) const { - print(LogLevel::Info, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); + printStr(LogLevel::Info, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); } + void info(std::string&& msg) const { printStr(LogLevel::Info, std::move(msg)); } template void info(S const& msg) const { - print(LogLevel::Info, msg); + printView(LogLevel::Info, msg); } template void debug(fmt::format_string fmt, Args&&... args) const { - print(LogLevel::Debug, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); + printStr(LogLevel::Debug, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); } + void debug(std::string&& msg) const { printStr(LogLevel::Debug, std::move(msg)); } template void debug(S const& msg) const { - print(LogLevel::Debug, msg); + printView(LogLevel::Debug, msg); } template void trace(fmt::format_string fmt, Args&&... args) const { - print(LogLevel::Trace, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); + printStr(LogLevel::Trace, fmt::vformat(fmt.get(), fmt::make_format_args(args...))); } + void trace(std::string&& msg) const { printStr(LogLevel::Trace, std::move(msg)); } template void trace(S const& msg) const { - print(LogLevel::Trace, msg); + printView(LogLevel::Trace, msg); } - LLNDAPI explicit Logger(); LLAPI ~Logger(); + LLNDAPI explicit Logger(); + LLNDAPI explicit Logger(std::string_view title); LLAPI void setTitle(std::string_view title); + + LLAPI void setLevel(LogLevel level); + + LLAPI void setFlushLevel(LogLevel level); + + LLAPI void setFormatter(Polymorphic formatter); + + LLAPI void flush() const; + + LLAPI void clearSink() const; + + LLAPI size_t addSink(std::shared_ptr sink) const; + + LLAPI std::shared_ptr getSink(size_t index) const; + + LLAPI void forEachSink(std::function const& fn) const; }; } // namespace ll::io diff --git a/src/ll/api/io/PatternFormatter.cpp b/src/ll/api/io/PatternFormatter.cpp new file mode 100644 index 0000000000..02d5f282ee --- /dev/null +++ b/src/ll/api/io/PatternFormatter.cpp @@ -0,0 +1,69 @@ +#include "ll/api/io/PatternFormatter.h" + +#include "ll/api/utils/StringUtils.h" + +template +struct BracketedArg { + T const& value; + bool bracketed; +}; +template +struct fmt::formatter, Char> : formatter { + template + auto format(BracketedArg const& arg, FormatContext& ctx) const -> decltype(ctx.out()) { + const auto& value = arg.value; + auto out = ctx.out(); + if (arg.bracketed) { + *out++ = static_cast('['); + out = formatter::format(value, ctx); + *out++ = static_cast(']'); + } else { + out = formatter::format(value, ctx); + } + return out; + } +}; + +namespace ll::io { + +static std::array levelNames{"FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"}; + +template +static constexpr auto makeBracketed(T const& value, bool bracketed) -> BracketedArg> { + return BracketedArg>{value, bracketed}; +} + +void PatternFormatter::format(LogMessageView const& view, std::string& buffer) const noexcept try { + auto lvlIdx = std::to_underlying(view.lvl); + if (colored) { + fmt::format_to( + std::back_inserter(buffer), + fmt::runtime(pattern), + fmt::arg("msg", fmt::styled(makeBracketed(view.msg, bracketed[0]), styles[lvlIdx][0])), + fmt::arg("tit", fmt::styled(makeBracketed(view.tit, bracketed[1]), styles[lvlIdx][1])), + fmt::arg("lvl", fmt::styled(makeBracketed(levelNames[lvlIdx], bracketed[2]), styles[lvlIdx][2])), + fmt::arg("tm", fmt::styled(makeBracketed(view.tm, bracketed[3]), styles[lvlIdx][3])) + ); + } else { + fmt::format_to( + std::back_inserter(buffer), + fmt::runtime(pattern), + fmt::arg("msg", makeBracketed(string_utils::removeEscapeCode(view.msg), bracketed[0])), + fmt::arg("tit", makeBracketed(view.tit, bracketed[1])), + fmt::arg("lvl", makeBracketed(levelNames[lvlIdx], bracketed[2])), + fmt::arg("tm", makeBracketed(view.tm, bracketed[3])) + ); + } + buffer.push_back('\n'); +} catch (...) { + try { + buffer.append("Fail to format "); + buffer.append(levelNames[std::to_underlying(view.lvl)]); + buffer.append(" ["); + buffer.append(view.tit); + buffer.append("] "); + buffer.append(view.msg); + buffer.push_back('\n'); + } catch (...) {} +} +} // namespace ll::io diff --git a/src/ll/api/io/PatternFormatter.h b/src/ll/api/io/PatternFormatter.h new file mode 100644 index 0000000000..5f9395bb79 --- /dev/null +++ b/src/ll/api/io/PatternFormatter.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +#include "ll/api/base/Macro.h" +#include "ll/api/io/Formatter.h" + +#include "fmt/color.h" + +namespace ll::io { +class PatternFormatter : public Formatter { +public: + std::string pattern; + std::bitset<4> bracketed; + bool colored; + std::array, 6> styles{ + { + // {msg, tit, lvl, tm} + {{ + fmt::fg(fmt::color::red) | fmt::emphasis::bold, + fmt::fg(fmt::color::red), + fmt::fg(fmt::color::red), + fmt::fg(fmt::color::light_blue), + }}, // Fatal + {{ + fmt::fg(fmt::terminal_color::bright_red) | fmt::emphasis::bold, + fmt::fg(fmt::terminal_color::bright_red), + fmt::fg(fmt::terminal_color::bright_red), + fmt::fg(fmt::color::light_blue), + }}, // Error + {{ + fmt::fg(fmt::terminal_color::bright_yellow) | fmt::emphasis::bold, + fmt::fg(fmt::terminal_color::bright_yellow), + fmt::fg(fmt::terminal_color::bright_yellow), + fmt::fg(fmt::color::light_blue), + }}, // Warn + {{{}, {}, fmt::fg(fmt::color::light_sea_green), fmt::fg(fmt::color::light_blue)}}, // Info + {{ + fmt::fg(fmt::color::light_golden_rod_yellow) | fmt::emphasis::italic, + fmt::fg(fmt::color::light_golden_rod_yellow), + fmt::fg(fmt::color::light_golden_rod_yellow), + fmt::fg(fmt::color::light_blue), + }}, // Debug + {{ + fmt::fg(fmt::color::light_steel_blue) | fmt::emphasis::italic, + fmt::fg(fmt::color::light_steel_blue), + fmt::fg(fmt::color::light_steel_blue), + fmt::fg(fmt::color::light_blue), + }}, // Trace + } + }; + + PatternFormatter(std::string pattern, bool colored = true, std::bitset<4> const& bracketed = 0) + : pattern(std::move(pattern)), + bracketed(bracketed), + colored(colored) {} + + virtual ~PatternFormatter() = default; + + LLAPI void format(LogMessageView const& view, std::string& buffer) const noexcept override; +}; +} // namespace ll::io diff --git a/src/ll/api/io/Sink.cpp b/src/ll/api/io/Sink.cpp new file mode 100644 index 0000000000..c176e51384 --- /dev/null +++ b/src/ll/api/io/Sink.cpp @@ -0,0 +1,9 @@ +#include "ll/api/io/Sink.h" + +#include "pl/Config.h" + +namespace ll::io { +Sink::~Sink() = default; +Sink::Sink(Polymorphic formatter) : formatter(std::move(formatter)) {} +void Sink::setFormatter(Polymorphic fmter) { formatter = std::move(fmter); } +} // namespace ll::io diff --git a/src/ll/api/io/Sink.h b/src/ll/api/io/Sink.h new file mode 100644 index 0000000000..fbb044dc3d --- /dev/null +++ b/src/ll/api/io/Sink.h @@ -0,0 +1,32 @@ +#pragma once + +#include "gsl/gsl" +#include "ll/api/data/IndirectValue.h" +#include "ll/api/io/Formatter.h" +#include + +namespace ll::io { +class SinkBase : public std::enable_shared_from_this { +public: + virtual ~SinkBase() = default; + + virtual void append(LogMessageView const& view) = 0; + + virtual void flush() {} + + virtual void setFormatter(Polymorphic fmter) = 0; + + virtual void setFlushLevel(LogLevel level) {} +}; +class Sink : public SinkBase { +protected: + Polymorphic formatter; + +public: + LLAPI Sink(Polymorphic formatter); + + LLAPI virtual ~Sink(); + + LLAPI void setFormatter(Polymorphic fmter) override; +}; +} // namespace ll::io diff --git a/src/ll/api/mod/ModManager.h b/src/ll/api/mod/ModManager.h index 9ce4d3ca13..31d58e7477 100644 --- a/src/ll/api/mod/ModManager.h +++ b/src/ll/api/mod/ModManager.h @@ -17,6 +17,8 @@ namespace ll::mod { class ModManagerRegistry; class ModManager { friend ModManagerRegistry; + struct Impl; + std::unique_ptr impl; public: LLNDAPI std::string const& getType() const; @@ -30,9 +32,6 @@ class ModManager { LLAPI void forEachMod(std::function const& fn); protected: - struct Impl; - std::unique_ptr impl; - LLNDAPI std::lock_guard lock(); LLNDAPI explicit ModManager(std::string_view type); diff --git a/src/ll/api/utils/SystemUtils.h b/src/ll/api/utils/SystemUtils.h index f158b52ce4..0290f76ad7 100644 --- a/src/ll/api/utils/SystemUtils.h +++ b/src/ll/api/utils/SystemUtils.h @@ -10,6 +10,7 @@ #include "ll/api/base/CompilerPredefine.h" #include "ll/api/base/Macro.h" #include "ll/api/base/StdInt.h" +#include "ll/api/data/TmWithMs.h" namespace ll::inline utils::sys_utils { @@ -32,7 +33,7 @@ LLNDAPI std::string getSystemLocaleCode(); LLNDAPI std::string const& getSystemName(); -LLNDAPI std::pair getLocalTime(); // tm & ms +LLNDAPI data::TmWithMs getLocalTime(); // tm & ms LLNDAPI std::string getEnvironmentVariable(std::string_view name); diff --git a/src/ll/api/utils/SystemUtils_win.cpp b/src/ll/api/utils/SystemUtils_win.cpp index fca5f1e8aa..9203cb0733 100644 --- a/src/ll/api/utils/SystemUtils_win.cpp +++ b/src/ll/api/utils/SystemUtils_win.cpp @@ -122,20 +122,22 @@ std::string getModuleFileName(void* handle, void* process) { .value_or("unknown module"); } -std::pair getLocalTime() { +data::TmWithMs getLocalTime() { SYSTEMTIME sysTime; GetLocalTime(&sysTime); - std::tm time{ - .tm_sec = sysTime.wSecond, // seconds after the minute - [0, 60] including leap second - .tm_min = sysTime.wMinute, // minutes after the hour - [0, 59] - .tm_hour = sysTime.wHour, // hours since midnight - [0, 23] - .tm_mday = sysTime.wDay, // day of the month - [1, 31] - .tm_mon = sysTime.wMonth - 1, // months since January - [0, 11] - .tm_year = sysTime.wYear - 1900, // years since 1900 - .tm_wday = sysTime.wDayOfWeek, // days since Sunday - [0, 6] - .tm_isdst = -1 // daylight savings time flag + return data::TmWithMs{ + { + .tm_sec = sysTime.wSecond, // seconds after the minute - [0, 60] including leap second + .tm_min = sysTime.wMinute, // minutes after the hour - [0, 59] + .tm_hour = sysTime.wHour, // hours since midnight - [0, 23] + .tm_mday = sysTime.wDay, // day of the month - [1, 31] + .tm_mon = sysTime.wMonth - 1, // months since January - [0, 11] + .tm_year = sysTime.wYear - 1900, // years since 1900 + .tm_wday = sysTime.wDayOfWeek, // days since Sunday - [0, 6] + .tm_isdst = -1 // daylight savings time flag + }, + sysTime.wMilliseconds }; - return {time, sysTime.wMilliseconds}; } std::string getEnvironmentVariable(std::string_view name) { diff --git a/src/ll/core/CrashLogger_win.cpp b/src/ll/core/CrashLogger_win.cpp index 94cc3fd8ab..55347a8cd2 100644 --- a/src/ll/core/CrashLogger_win.cpp +++ b/src/ll/core/CrashLogger_win.cpp @@ -5,8 +5,10 @@ #include "ll/api/Versions.h" #include "ll/api/data/Version.h" #include "ll/api/i18n/I18n.h" +#include "ll/api/io/FileSink.h" #include "ll/api/io/FileUtils.h" #include "ll/api/io/Logger.h" +#include "ll/api/io/PatternFormatter.h" #include "ll/api/utils/ErrorUtils.h" #include "ll/api/utils/StacktraceUtils.h" #include "ll/api/utils/StringUtils.h" @@ -265,23 +267,34 @@ static bool genMiniDumpFile(PEXCEPTION_POINTERS e) { static LONG unhandledExceptionFilter(_In_ struct _EXCEPTION_POINTERS* e) { try { - crashInfo.date = fmt::format("{:%Y-%m-%d_%H-%M-%S}", fmt::localtime(_time64(nullptr))); - // crashInfo.logger.fileLevel = std::numeric_limits::max(); - // crashInfo.logger.consoleLevel = std::numeric_limits::max(); - // crashInfo.logger.info.consoleFormat = {"{0} [{1}] {3}", "{:%T}.{:0>3}", "{}", "", "{}"}; - // crashInfo.logger.info.style = { - // fmt::fg(fmt::color::light_blue), - // fmt::fg(fmt::color::light_green), - // {}, - // {}, - // }; - crashInfo.settings = ll::getLeviConfig().modules.crashLogger; - crashInfo.path = file_utils::u8path(pl::pl_log_path) / u8"crash"; - // crashInfo.logger.setFile(u8str2str((crashInfo.path / ("trace_" + crashInfo.date + ".log")).u8string())); - // crashInfo.logger.ofs.value() << std::unitbuf; - - // crashInfo.logger.info.fileFormat = {"{0} [{1}] {3}", "{:%F %T}.{:0>3}", "{}", "", "{}"}; - // crashInfo.logger.error = crashInfo.logger.info; + crashInfo.date = fmt::format("{:%Y-%m-%d_%H-%M-%S}", fmt::localtime(_time64(nullptr))); + crashInfo.settings = ll::getLeviConfig().modules.crashLogger; + crashInfo.path = file_utils::u8path(pl::pl_log_path) / u8"crash"; + auto formatter = makePolymorphic("{tm:.3%T.} [{lvl}] {msg}"); + formatter->styles[1] = { + { + {}, + {}, + fmt::fg(fmt::color::crimson), + fmt::fg(fmt::color::light_blue), + } + }; + formatter->styles[3] = { + { + {}, + {}, + fmt::fg(fmt::color::light_green), + fmt::fg(fmt::color::light_blue), + } + }; + crashInfo.logger.getSink(0)->setFormatter(std::move(formatter)); + crashInfo.logger.addSink(std::make_shared( + crashInfo.path / ("trace_" + crashInfo.date + ".log"), + makePolymorphic("{tm:.3%F %T.} [{lvl}] {msg}", false) + )); + crashInfo.logger.setLevel(io::LogLevel::Trace); + crashInfo.logger.setFlushLevel(io::LogLevel::Info); + crashInfo.process = GetCurrentProcess(); crashInfo.thread = GetCurrentThread(); crashInfo.processId = GetCurrentProcessId(); diff --git a/src/ll/core/command/Crash.cpp b/src/ll/core/command/Crash_win.cpp similarity index 83% rename from src/ll/core/command/Crash.cpp rename to src/ll/core/command/Crash_win.cpp index 391daae251..dd086264f8 100644 --- a/src/ll/core/command/Crash.cpp +++ b/src/ll/core/command/Crash_win.cpp @@ -5,6 +5,8 @@ #include "ll/api/i18n/I18n.h" #include "ll/core/Config.h" +#include "windows.h" + namespace ll::command { struct Code { int exceptionCode{0}; @@ -19,8 +21,7 @@ void registerCrashCommand() { cmd.overload() .optional("exceptionCode") .execute([&](CommandOrigin const&, CommandOutput&, Code const& params) { - std::thread([&] { throw std::system_error(std::error_code{params.exceptionCode, std::system_category()}); } - ).detach(); + std::thread([&] { RaiseException(params.exceptionCode, 0, 0, nullptr); }).detach(); }); } } // namespace ll::command diff --git a/src/ll/core/tweak/ModifyInfomation.cpp b/src/ll/core/tweak/ModifyInfomation.cpp index 0f4779c766..7c3353e606 100644 --- a/src/ll/core/tweak/ModifyInfomation.cpp +++ b/src/ll/core/tweak/ModifyInfomation.cpp @@ -1,6 +1,8 @@ #include "ll/core/tweak/ModifyInfomation.h" #include "ll/api/base/Containers.h" #include "ll/api/memory/Hook.h" +#include "ll/api/utils/ErrorUtils.h" +#include "ll/api/utils/StringUtils.h" #include "ll/core/Config.h" #include "ll/core/LeviLamina.h" #include "ll/core/Version.h" @@ -39,15 +41,8 @@ LL_STATIC_HOOK( io::Logger serverLogger("Server"); -LL_STATIC_HOOK( - BedrockLogOutHook, - HookPriority::Normal, - BedrockLogOut, - void, - uint priority, - char const* pszFormat, - ... -) { +LL_STATIC_HOOK(BedrockLogOutHook, HookPriority::Normal, BedrockLogOut, void, uint priority, char const* pszFormat, ...) +try { bool success = false; std::string buffer; va_list va; @@ -101,8 +96,13 @@ LL_STATIC_HOOK( if (!knownPriority) { line = fmt::format(" {}", priority, line); } - serverLogger.log(level, line); + serverLogger.log(level, string_utils::replaceMcToAnsiCode(line)); } +} catch (...) { + try { + serverLogger.fatal("Fail to format [{}] {}", priority, pszFormat); + error_utils::printCurrentException(serverLogger, io::LogLevel::Fatal); + } catch (...) {} } // Block from adding LOG metadata diff --git a/xmake.lua b/xmake.lua index e516a1ae52..977253e601 100644 --- a/xmake.lua +++ b/xmake.lua @@ -116,6 +116,7 @@ target("LeviLamina") {public = true} ) add_defines( + "FMT_USE_FULL_CACHE_DRAGONBOX=1", "ENTT_PACKED_PAGE=128", -- public = true "LL_EXPORT" )