diff --git a/bin/clice.cc b/bin/clice.cc index 76466152..eac56e11 100644 --- a/bin/clice.cc +++ b/bin/clice.cc @@ -48,13 +48,6 @@ cl::opt port{ cl::desc("The port to connect to"), }; -cl::opt resource_dir{ - "resource-dir", - cl::cat(category), - cl::value_desc("path"), - cl::desc(R"(The path of the clang resource directory, default is "../../lib/clang/version")"), -}; - cl::opt log_color{ "log-color", cl::cat(category), @@ -69,75 +62,43 @@ cl::opt log_color{ cl::opt log_level{ "log-level", cl::cat(category), - cl::value_desc("trace|debug|info|warn|fatal"), + cl::value_desc("trace|debug|info|warn|error"), cl::init(logging::Level::info), cl::values(clEnumValN(logging::Level::trace, "trace", ""), clEnumValN(logging::Level::debug, "debug", ""), clEnumValN(logging::Level::info, "info", ""), clEnumValN(logging::Level::warn, "warn", ""), - clEnumValN(logging::Level::err, "fatal", "")), + clEnumValN(logging::Level::err, "error", ""), + clEnumValN(logging::Level::off, "off", "")), cl::desc("The log level, default is info"), }; -void init_log() { - using namespace logging; - options.color = log_color; - options.level = log_level; - logging::create_stderr_logger("clice", logging::options); -} - -/// Check the command line arguments and initialize the clice. -bool check_arguments(int argc, const char** argv) { - /// Hide unrelated options. - cl::HideUnrelatedOptions(category); - - // Set version printer and parse command line options - cl::SetVersionPrinter([](llvm::raw_ostream& os) { - os << std::format("clice version: {}\nllvm version: {}\n", - clice::config::version, - clice::config::llvm_version); - }); - cl::ParseCommandLineOptions(argc, - argv, - "clice is a new generation of language server for C/C++"); - - init_log(); - - for(int i = 0; i < argc; ++i) { - logging::info("argv[{}] = {}", i, argv[i]); - } - - // Initialize resource directory - if(resource_dir.empty()) { - logging::info("No resource directory specified, using default resource directory"); - // Try to initialize default resource directory - if(auto result = fs::init_resource_dir(argv[0]); !result) { - logging::warn("Cannot find default resource directory, because {}", result.error()); - return false; - } - } else { - // Set and check the specified resource directory - fs::resource_dir = resource_dir.getValue(); - if(fs::exists(fs::resource_dir)) { - logging::info("Resource directory found: {}", fs::resource_dir); - } else { - logging::warn("Resource directory not found: {}", fs::resource_dir); - return false; - } - } - - return true; -} - } // namespace int main(int argc, const char** argv) { llvm::InitLLVM guard(argc, argv); llvm::setBugReportMsg( "Please report bugs to https://github.com/clice-io/clice/issues and include the crash backtrace"); + cl::SetVersionPrinter([](llvm::raw_ostream& os) { + os << std::format("clice version: {}\nllvm version: {}\n", + clice::config::version, + clice::config::llvm_version); + }); + cl::HideUnrelatedOptions(category); + cl::ParseCommandLineOptions(argc, + argv, + "clice is a new generation of language server for C/C++"); - if(!check_arguments(argc, argv)) { - return 1; + logging::options.color = log_color; + logging::options.level = log_level; + logging::stderr_logger("clice", logging::options); + + if(auto result = fs::init_resource_dir(argv[0]); !result) { + LOGGING_FATAL("Cannot find default resource directory, because {}", result.error()); + } + + for(int i = 0; i < argc; ++i) { + LOGGING_INFO("argv[{}] = {}", i, argv[i]); } async::init(); @@ -151,13 +112,13 @@ int main(int argc, const char** argv) { switch(mode) { case Mode::Pipe: { async::net::listen(loop); - logging::info("Server starts listening on stdin/stdout"); + LOGGING_INFO("Server starts listening on stdin/stdout"); break; } case Mode::Socket: { async::net::listen(host.c_str(), port, loop); - logging::info("Server starts listening on {}:{}", host.getValue(), port.getValue()); + LOGGING_INFO("Server starts listening on {}:{}", host.getValue(), port.getValue()); break; } @@ -169,7 +130,7 @@ int main(int argc, const char** argv) { async::run(); - logging::info("clice exit normally!"); + LOGGING_INFO("clice exit normally!"); return 0; } diff --git a/bin/unit_tests.cc b/bin/unit_tests.cc index 6a56e40f..aee1d1a8 100644 --- a/bin/unit_tests.cc +++ b/bin/unit_tests.cc @@ -18,7 +18,7 @@ namespace { namespace cl = llvm::cl; -cl::OptionCategory unittest_category("Clice Unittest Options"); +cl::OptionCategory unittest_category("clice Unittest Options"); cl::opt test_dir{ "test-dir", @@ -28,12 +28,6 @@ cl::opt test_dir{ cl::cat(unittest_category), }; -cl::opt resource_dir{ - "resource-dir", - cl::desc("Resource dir path"), - cl::cat(unittest_category), -}; - cl::opt test_filter{ "test-filter", cl::desc("A glob pattern to run subset of tests"), @@ -190,7 +184,7 @@ int main(int argc, const char* argv[]) { llvm::cl::HideUnrelatedOptions(unittest_category); llvm::cl::ParseCommandLineOptions(argc, argv, "clice test\n"); - logging::create_stderr_logger("clice", logging::options); + logging::stderr_logger("clice", logging::options); if(!test_filter.empty()) { if(auto result = GlobPattern::create(test_filter)) { @@ -198,13 +192,9 @@ int main(int argc, const char* argv[]) { } } - if(!resource_dir.empty()) { - fs::resource_dir = resource_dir; - } else { - if(auto result = fs::init_resource_dir(argv[0]); !result) { - std::println("Failed to get resource directory, because {}", result.error()); - return 1; - } + if(auto result = fs::init_resource_dir(argv[0]); !result) { + std::println("Failed to get resource directory, because {}", result.error()); + return 1; } using namespace clice::testing; diff --git a/include/Support/Format.h b/include/Support/Format.h index 12565767..e6ec79fd 100644 --- a/include/Support/Format.h +++ b/include/Support/Format.h @@ -73,16 +73,31 @@ template <> struct std::formatter : std::formatter { using Base = std::formatter; + int indent = 0; + template constexpr auto parse(ParseContext& ctx) { - return Base::parse(ctx); + auto it = ctx.begin(); + auto end = ctx.end(); + if(it == end) { + return it; + } + + int parsed_indent = 0; + while(it != end && *it >= '0' && *it <= '9') { + parsed_indent = parsed_indent * 10 + (*it - '0'); + ++it; + } + indent = parsed_indent; + + return it; } template auto format(const clice::json::Value& value, FormatContext& ctx) const { llvm::SmallString<128> buffer; llvm::raw_svector_ostream os{buffer}; - os << value; + llvm::json::OStream(os, indent).value(value); return Base::format(buffer, ctx); } }; diff --git a/include/Support/Logging.h b/include/Support/Logging.h index b3aa7bc5..b36e3767 100644 --- a/include/Support/Logging.h +++ b/include/Support/Logging.h @@ -9,15 +9,22 @@ using Level = spdlog::level::level_enum; using ColorMode = spdlog::color_mode; struct Options { + /// The logging level. Level level = Level::info; + + /// The logging color. ColorMode color = ColorMode::automatic; + + /// If enable, we will record the logs of console sink and replay it + /// when create a new sink, + bool replay_console = true; }; extern Options options; -void create_stderr_logger(std::string_view name, const Options& options); +void stderr_logger(std::string_view name, const Options& options); -void create_file_loggger(std::string_view name, std::string_view dir, const Options& options); +void file_loggger(std::string_view name, std::string_view dir, const Options& options); template struct logging_rformat { @@ -72,10 +79,27 @@ void warn(logging_format fmt, Args&&... args) { } template -void fatal [[noreturn]] (logging_format fmt, Args&&... args) { +void err(logging_format fmt, Args&&... args) { logging::log(spdlog::level::err, fmt.location, fmt.str, std::forward(args)...); +} + +template +void critical [[noreturn]] (logging_format fmt, Args&&... args) { + logging::log(spdlog::level::critical, fmt.location, fmt.str, std::forward(args)...); spdlog::shutdown(); std::exit(1); } } // namespace clice::logging + +#define LOGGING_MESSAGE(name, fmt, ...) \ + if(clice::logging::options.level <= clice::logging::Level::name) { \ + clice::logging::name(fmt __VA_OPT__(, ) __VA_ARGS__); \ + } + +#define LOGGING_TRACE(fmt, ...) LOGGING_MESSAGE(trace, fmt, __VA_ARGS__) +#define LOGGING_DEBUG(fmt, ...) LOGGING_MESSAGE(debug, fmt, __VA_ARGS__) +#define LOGGING_INFO(fmt, ...) LOGGING_MESSAGE(info, fmt, __VA_ARGS__) +#define LOGGING_WARN(fmt, ...) LOGGING_MESSAGE(warn, fmt, __VA_ARGS__) +#define LOGGING_ERROR(fmt, ...) LOGGING_MESSAGE(err, fmt, __VA_ARGS__) +#define LOGGING_FATAL(fmt, ...) clice::logging::critical(fmt __VA_OPT__(, ) __VA_ARGS__); diff --git a/src/AST/Selection.cpp b/src/AST/Selection.cpp index 21dd7f2e..6b490582 100644 --- a/src/AST/Selection.cpp +++ b/src/AST/Selection.cpp @@ -24,12 +24,6 @@ namespace clice { -#ifdef NDEBUG -#define LOGGING_DEBUG(...) -#else -#define LOGGING_DEBUG(...) logging::debug(__VA_ARGS__) -#endif - namespace { using Node = SelectionTree::Node; diff --git a/src/Async/FileSystem.cpp b/src/Async/FileSystem.cpp index 03a37c07..89f2592a 100644 --- a/src/Async/FileSystem.cpp +++ b/src/Async/FileSystem.cpp @@ -122,7 +122,7 @@ handle::~handle() { uv_fs_t request; int error = uv_fs_close(async::loop, &request, file, nullptr); if(error < 0) { - logging::warn("Failed to close file: {}", uv_strerror(error)); + LOGGING_WARN("Failed to close file: {}", uv_strerror(error)); } uv_fs_req_cleanup(&request); } diff --git a/src/Async/Network.cpp b/src/Async/Network.cpp index 8d30ac8e..6cdb7515 100644 --- a/src/Async/Network.cpp +++ b/src/Async/Network.cpp @@ -30,7 +30,7 @@ void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { /// If an error occurred while reading, we can't continue. if(nread < 0) [[unlikely]] { - logging::fatal("An error occurred while reading: {0}", uv_strerror(nread)); + LOGGING_FATAL("An error occurred while reading: {0}", uv_strerror(nread)); } /// We have at most one connection and use default event loop. So there is no data race @@ -57,7 +57,7 @@ void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { task.dispose(); } else { /// If the message is invalid, we can't continue. - logging::fatal("Unexpected JSON input: {0}", result); + LOGGING_FATAL("Unexpected JSON input: {0}", result); } /// Remove the processed message from the buffer. @@ -130,7 +130,7 @@ struct write { uv_write(&req, writer, buf, 2, [](uv_write_t* req, int status) { if(status < 0) { - logging::fatal("An error occurred while writing: {0}", uv_strerror(status)); + LOGGING_FATAL("An error occurred while writing: {0}", uv_strerror(status)); } auto& awaiter = uv_cast(req); diff --git a/src/Async/libuv.cpp b/src/Async/libuv.cpp index ef623964..3527710b 100644 --- a/src/Async/libuv.cpp +++ b/src/Async/libuv.cpp @@ -29,11 +29,11 @@ const std::error_category& category() { /// Use source_location to log the file, line, and function name where the error occurred. void uv_check_result(const int result, const std::source_location location) { if(result < 0) { - logging::warn("libuv error: {}", uv_strerror(result)); - logging::warn("At {}:{}:{}", - location.file_name(), - location.line(), - location.function_name()); + LOGGING_WARN("libuv error: {}", uv_strerror(result)); + LOGGING_WARN("At {}:{}:{}", + location.file_name(), + location.line(), + location.function_name()); } } diff --git a/src/Compiler/Command.cpp b/src/Compiler/Command.cpp index 831a02db..dd8b3a27 100644 --- a/src/Compiler/Command.cpp +++ b/src/Compiler/Command.cpp @@ -176,10 +176,10 @@ struct CompilationDatabase::Impl { using Arg = std::unique_ptr; auto on_error = [&](int index, int count) { - logging::warn("missing argument index: {}, count: {} when parse: {}", - index, - count, - file); + LOGGING_WARN("missing argument index: {}, count: {} when parse: {}", + index, + count, + file); }; /// Prepare for removing arguments. @@ -284,7 +284,7 @@ struct CompilationDatabase::Impl { // /path/to/foobar if(other.starts_with(dir) && (other.size() == dir.size() || path::is_separator(other[dir.size()]))) { - logging::info("Guess command for:{}, from existed file: {}", file, other_file); + LOGGING_INFO("Guess command for:{}, from existed file: {}", file, other_file); return LookupInfo{info.directory, info.arguments}; } } @@ -492,7 +492,7 @@ auto CompilationDatabase::update_command(llvm::StringRef directory, /// Handle response file. if(argument.starts_with("@")) { if(!response_file.empty()) { - logging::warn( + LOGGING_WARN( "clice currently supports only one response file in the command, when loads {}", file); } @@ -624,17 +624,17 @@ auto CompilationDatabase::load_compile_database(llvm::ArrayRef comp std::string filepath = path::join(dir, "compile_commands.json"); auto content = fs::read(filepath); if(!content) { - logging::warn("Failed to read CDB file: {}, {}", filepath, content.error()); + LOGGING_WARN("Failed to read CDB file: {}, {}", filepath, content.error()); return false; } auto load = this->load_commands(*content, workspace); if(!load) { - logging::warn("Failed to load CDB file: {}. {}", filepath, load.error()); + LOGGING_WARN("Failed to load CDB file: {}. {}", filepath, load.error()); return false; } - logging::info("Load CDB file: {} successfully, {} items loaded", filepath, load->size()); + LOGGING_INFO("Load CDB file: {} successfully, {} items loaded", filepath, load->size()); return true; }; @@ -642,7 +642,7 @@ auto CompilationDatabase::load_compile_database(llvm::ArrayRef comp return; } - logging::warn( + LOGGING_WARN( "Can not found any valid CDB file from given directories, search recursively from workspace: {} ...", workspace); @@ -673,7 +673,7 @@ auto CompilationDatabase::load_compile_database(llvm::ArrayRef comp } /// TODO: Add a default command in clice.toml. Or load commands from .clangd ? - logging::warn( + LOGGING_WARN( "Can not found any valid CDB file in current workspace, fallback to default mode."); } @@ -711,7 +711,7 @@ auto CompilationDatabase::lookup(llvm::StringRef file, CommandOptions options) - record(system_header); } } else if(!options.suppress_logging) { - logging::warn("Failed to query driver:{}, error:{}", driver, driver_info.error()); + LOGGING_WARN("Failed to query driver:{}, error:{}", driver, driver_info.error()); } } diff --git a/src/Compiler/Tidy.cpp b/src/Compiler/Tidy.cpp index 4cb5fa03..07812033 100644 --- a/src/Compiler/Tidy.cpp +++ b/src/Compiler/Tidy.cpp @@ -334,11 +334,11 @@ std::unique_ptr configure(clang::CompilerInstance& instance, return nullptr; } auto file_name = input.getFile(); - logging::info("Tidy configure file: {}", file_name); + LOGGING_INFO("Tidy configure file: {}", file_name); tidy::ClangTidyOptions opts = create_options(); if(opts.Checks) { - logging::info("Tidy configure checks: {}", *opts.Checks); + LOGGING_INFO("Tidy configure checks: {}", *opts.Checks); } { @@ -391,7 +391,7 @@ std::unique_ptr configure(clang::CompilerInstance& instance, checker->context.setCurrentFile(file_name); checker->context.setSelfContainedDiags(true); checker->checks = factories.createChecksForLanguage(&checker->context); - logging::info("Tidy configure checks: {}", checker->checks.size()); + LOGGING_INFO("Tidy configure checks: {}", checker->checks.size()); clang::Preprocessor* pp = &instance.getPreprocessor(); for(const auto& check: checker->checks) { check->registerPPCallbacks(instance.getSourceManager(), pp, pp); diff --git a/src/Compiler/Toolchain.cpp b/src/Compiler/Toolchain.cpp index 0fdcd84d..8a2ccdec 100644 --- a/src/Compiler/Toolchain.cpp +++ b/src/Compiler/Toolchain.cpp @@ -193,12 +193,12 @@ auto query_driver(llvm::StringRef driver) -> std::expected document_format(llvm::StringRef file, range ? tooling::Range(range->begin, range->length()) : tooling::Range(0, content.size()); auto replacements = format(file, content, selection); if(!replacements) { - logging::info("Fail to format for {}\n{}", file, replacements.error()); + LOGGING_INFO("Fail to format for {}\n{}", file, replacements.error()); return edits; } diff --git a/src/Server/Document.cpp b/src/Server/Document.cpp index e5556c7f..40bf0161 100644 --- a/src/Server/Document.cpp +++ b/src/Server/Document.cpp @@ -12,14 +12,14 @@ void Server::load_cache_info() { auto path = path::join(config.project.cache_dir, "cache.json"); auto file = llvm::MemoryBuffer::getFile(path); if(!file) { - logging::warn("Fail to load cache info, because: {}", file.getError()); + LOGGING_WARN("Fail to load cache info, because: {}", file.getError()); return; } llvm::StringRef content = file.get()->getBuffer(); auto json = json::parse(content); if(!json) { - logging::warn("Fail to load cache info, invalid json: {}", json.takeError()); + LOGGING_WARN("Fail to load cache info, invalid json: {}", json.takeError()); return; } @@ -30,7 +30,7 @@ void Server::load_cache_info() { auto version = object->getString("version"); if(!version) { - logging::info("Fail to load cache info, the cache info is outdated"); + LOGGING_INFO("Fail to load cache info, the cache info is outdated"); return; } @@ -75,7 +75,7 @@ void Server::load_cache_info() { } } - logging::info("Load cache info successfully"); + LOGGING_INFO("Load cache info successfully"); } void Server::save_cache_info() { @@ -105,20 +105,20 @@ void Server::save_cache_info() { llvm::SmallString<128> temp_path; if(auto error = llvm::sys::fs::createTemporaryFile("cache", "json", temp_path)) { - logging::warn("Fail to create temporary file for cache info: {}", error.message()); + LOGGING_WARN("Fail to create temporary file for cache info: {}", error.message()); return; } auto clean_up = llvm::make_scope_exit([&temp_path]() { if(auto errc = llvm::sys::fs::remove(temp_path)) { - logging::warn("Fail to remove temporary file: {}", errc.message()); + LOGGING_WARN("Fail to remove temporary file: {}", errc.message()); } }); std::error_code EC; llvm::raw_fd_ostream os(temp_path, EC, llvm::sys::fs::OF_None); if(EC) { - logging::warn("Fail to open temporary file for writing: {}", EC.message()); + LOGGING_WARN("Fail to open temporary file for writing: {}", EC.message()); return; } @@ -127,18 +127,18 @@ void Server::save_cache_info() { os.close(); if(os.has_error()) { - logging::warn("Fail to write cache info to temporary file"); + LOGGING_WARN("Fail to write cache info to temporary file"); return; } if(auto error = llvm::sys::fs::rename(temp_path, final_path)) { - logging::warn("Fail to rename temporary file to final cache file: {}", error.message()); + LOGGING_WARN("Fail to rename temporary file to final cache file: {}", error.message()); return; } clean_up.release(); - logging::info("Save cache info successfully"); + LOGGING_INFO("Save cache info successfully"); } namespace { @@ -178,7 +178,7 @@ async::Task build_pch_task(LookupInfo& info, if(!fs::exists(cache_dir)) { auto error = fs::create_directories(cache_dir); if(error) { - logging::warn("Fail to create directory for PCH building: {}", cache_dir); + LOGGING_WARN("Fail to create directory for PCH building: {}", cache_dir); co_return false; } } @@ -199,7 +199,7 @@ async::Task build_pch_task(LookupInfo& info, command += argument; } - logging::info("Start building PCH for {}, command: [{}]", path, command); + LOGGING_INFO("Start building PCH for {}, command: [{}]", path, command); command.clear(); PCHInfo pch; @@ -220,14 +220,14 @@ async::Task build_pch_task(LookupInfo& info, }); if(!success) { - logging::warn("Building PCH fails for {}, Because: {}", path, message); + LOGGING_WARN("Building PCH fails for {}, Because: {}", path, message); for(auto& diagnostic: *diagnostics) { - logging::warn("{}", diagnostic.message); + LOGGING_WARN("{}", diagnostic.message); } co_return false; } - logging::info("Building PCH successfully for {}", path); + LOGGING_INFO("Building PCH successfully for {}", path); /// Update the built PCH info. open_file->pch = std::move(pch); @@ -254,7 +254,7 @@ async::Task Server::build_pch(std::string file, std::string content) { /// Check update ... if(open_file->pch && !check_pch_update(content, bound, info, *open_file->pch)) { /// If not need update, return directly. - logging::info("PCH is already up-to-date for {}", file); + LOGGING_INFO("PCH is already up-to-date for {}", file); co_return true; } @@ -263,12 +263,12 @@ async::Task Server::build_pch(std::string file, std::string content) { if(!task.empty()) { if(task.finished()) { task.release().destroy(); - logging::info("Release old pch task!"); + LOGGING_INFO("Release old pch task!"); } else { task.cancel(); task.dispose(); } - logging::info("Cancel old PCH building task!"); + LOGGING_INFO("Cancel old PCH building task!"); } /// Schedule the new building task. @@ -304,7 +304,7 @@ async::Task<> Server::build_ast(std::string path, std::string content) { auto pch = file->pch; if(!pch) { - logging::fatal("Expected PCH built at this point"); + LOGGING_FATAL("Expected PCH built at this point"); } CommandOptions options; @@ -324,9 +324,9 @@ async::Task<> Server::build_ast(std::string path, std::string content) { auto ast = co_await async::submit([&] { return compile(params); }); if(!ast) { /// FIXME: Fails needs cancel waiting tasks. - logging::warn("Building AST fails for {}, Beacuse: {}", path, ast.error()); + LOGGING_WARN("Building AST fails for {}, Beacuse: {}", path, ast.error()); for(auto& diagnostic: *file->diagnostics) { - logging::warn("{}", diagnostic.message); + LOGGING_WARN("{}", diagnostic.message); } co_return; } @@ -349,7 +349,7 @@ async::Task<> Server::build_ast(std::string path, std::string content) { /// Dispose the task so that it will destroyed when task complete. file->ast_build_task.dispose(); - logging::info("Building AST successfully for {}", path); + LOGGING_INFO("Building AST successfully for {}", path); } async::Task> Server::add_document(std::string path, std::string content) { @@ -363,12 +363,12 @@ async::Task> Server::add_document(std::string path, st if(!task.empty()) { if(task.finished()) { task.release().destroy(); - logging::info("Release old AST building Task!"); + LOGGING_INFO("Release old AST building Task!"); } else { task.cancel(); task.dispose(); } - logging::info("Cancel old AST building Task!"); + LOGGING_INFO("Cancel old AST building Task!"); } /// Create and schedule a new task. diff --git a/src/Server/Indexer.cpp b/src/Server/Indexer.cpp index fe5e4e46..4ba15961 100644 --- a/src/Server/Indexer.cpp +++ b/src/Server/Indexer.cpp @@ -15,7 +15,7 @@ async::Task<> Indexer::index(llvm::StringRef path) { auto path_id = project_index.path_pool.path_id(path); auto& merged_index = get_index(path_id); if(!merged_index.need_update(project_index.path_pool.paths)) { - logging::info("Check update for {}, not need to update", path); + LOGGING_INFO("Check update for {}, not need to update", path); co_return; } @@ -25,7 +25,7 @@ async::Task<> Indexer::index(llvm::StringRef path) { auto tu_index = co_await async::submit([&]() -> std::optional { auto unit = compile(params); if(!unit) { - logging::info("Fail to index for {}, because: {}", path, unit.error()); + LOGGING_INFO("Fail to index for {}, because: {}", path, unit.error()); return std::nullopt; } @@ -55,7 +55,7 @@ async::Task<> Indexer::index(llvm::StringRef path) { std::move(tu_index->graph.locations), tu_index->main_file_index); - logging::info("Successfully index {}", path); + LOGGING_INFO("Successfully index {}", path); } async::Task<> Indexer::schedule_next() { @@ -107,9 +107,9 @@ void Indexer::load_from_disk() { if(auto content = fs::read(output_path); content && !content->empty()) { /// FIXME: from should return a expected ... project_index = index::ProjectIndex::from(content->data()); - logging::info("Load project index form {} successfully", output_path); + LOGGING_INFO("Load project index form {} successfully", output_path); } else { - logging::info("Fail to load project index form {}", output_path); + LOGGING_INFO("Fail to load project index form {}", output_path); } /// FIXME: check indices update .... @@ -117,9 +117,9 @@ void Indexer::load_from_disk() { void Indexer::save_to_disk() { if(auto err = fs::create_directories(config.project.index_dir)) { - logging::warn("Fail to create index output dir: {}, because: {}", - config.project.index_dir, - err); + LOGGING_WARN("Fail to create index output dir: {}, because: {}", + config.project.index_dir, + err); return; } @@ -139,9 +139,7 @@ void Indexer::save_to_disk() { std::error_code err; llvm::raw_fd_ostream os(output_path, err, fs::CreationDisposition::CD_CreateAlways); if(err) { - logging::info("Fail to create output index file: {}, because: {}", - output_path, - err); + LOGGING_INFO("Fail to create output index file: {}, because: {}", output_path, err); continue; } @@ -149,7 +147,7 @@ void Indexer::save_to_disk() { auto opath_id = project_index.path_pool.path_id(output_path); project_index.indices.try_emplace(path_id, opath_id); - logging::info("Successfully save index for {} to {}", path, output_path); + LOGGING_INFO("Successfully save index for {} to {}", path, output_path); } } @@ -158,12 +156,12 @@ void Indexer::save_to_disk() { std::error_code err; llvm::raw_fd_ostream os(output_path, err, fs::CreationDisposition::CD_CreateAlways); if(err) { - logging::info("Fail to create output index file: {}, because: {}", output_path, err); + LOGGING_INFO("Fail to create output index file: {}, because: {}", output_path, err); return; } project_index.serialize(os); - logging::info("Successfully save project index to {}", output_path); + LOGGING_INFO("Successfully save project index to {}", output_path); } auto Indexer::lookup(llvm::StringRef path, std::uint32_t offset, RelationKind kind) -> Result { diff --git a/src/Server/Lifecycle.cpp b/src/Server/Lifecycle.cpp index 489e23f1..395fad04 100644 --- a/src/Server/Lifecycle.cpp +++ b/src/Server/Lifecycle.cpp @@ -3,9 +3,9 @@ namespace clice { async::Task Server::on_initialize(proto::InitializeParams params) { - logging::info("Initialize from client: {}, version: {}", - params.clientInfo.name, - params.clientInfo.version); + LOGGING_INFO("Initialize from client: {}, version: {}", + params.clientInfo.name, + params.clientInfo.version); /// FIXME: adjust position encoding. kind = PositionEncodingKind::UTF16; @@ -17,15 +17,19 @@ async::Task Server::on_initialize(proto::InitializeParams params) { return *params.rootUri; } - logging::fatal("The client should provide one workspace folder or rootUri at least!"); + LOGGING_FATAL("The client should provide one workspace folder or rootUri at least!"); })()); /// Initialize configuration. if(auto result = config.parse(workspace)) { - logging::info("Config initialized successfully: {0}", json::serialize(config)); + LOGGING_INFO("Config initialized successfully: {0:4}", json::serialize(config)); } else { - logging::warn("Fail to load config, because: {0}", result.error()); - logging::info("Use default config: {0}", json::serialize(config)); + LOGGING_WARN("Fail to load config, because: {0}", result.error()); + LOGGING_INFO("Use default config: {0:4}", json::serialize(config)); + } + + if(!config.project.logging_dir.empty()) { + logging::file_loggger("clice", config.project.logging_dir, logging::options); } /// Set server options. diff --git a/src/Server/Server.cpp b/src/Server/Server.cpp index a1c44234..ab6ed3c8 100644 --- a/src/Server/Server.cpp +++ b/src/Server/Server.cpp @@ -124,7 +124,7 @@ Server::Server() : indexer(database, config, kind) { async::Task<> Server::on_receive(json::Value value) { auto object = value.getAsObject(); if(!object) [[unlikely]] { - logging::fatal("Invalid LSP message, not an object: {}", value); + LOGGING_FATAL("Invalid LSP message, not an object: {}", value); } /// If the json object has an `id`, it's a request, @@ -135,7 +135,7 @@ async::Task<> Server::on_receive(json::Value value) { if(auto result = object->getString("method")) { method = *result; } else [[unlikely]] { - logging::warn("Invalid LSP message, method not found: {}", value); + LOGGING_WARN("Invalid LSP message, method not found: {}", value); if(id) { co_await response(std::move(*id), proto::ErrorCodes::InvalidRequest, @@ -152,7 +152,7 @@ async::Task<> Server::on_receive(json::Value value) { /// Handle request and notification separately. auto it = callbacks.find(method); if(it == callbacks.end()) { - logging::info("Ignore unhandled method: {}", method); + LOGGING_INFO("Ignore unhandled method: {}", method); co_return; } @@ -160,24 +160,24 @@ async::Task<> Server::on_receive(json::Value value) { auto current_id = client_request_id++; auto start_time = std::chrono::steady_clock::now(); - logging::info("<-- Handling request: {}({})", method, current_id); + LOGGING_INFO("<-- Handling request: {}({})", method, current_id); auto result = co_await it->second(*this, std::move(params)); co_await response(std::move(*id), std::move(result)); auto end_time = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast(end_time - start_time); - logging::info("--> Handled request: {}({}) {}ms", method, current_id, duration.count()); + LOGGING_INFO("--> Handled request: {}({}) {}ms", method, current_id, duration.count()); } else { auto start_time = std::chrono::steady_clock::now(); - logging::info("<-- Handling notification: {}", method); + LOGGING_INFO("<-- Handling notification: {}", method); auto result = co_await it->second(*this, std::move(params)); auto end_time = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast(end_time - start_time); - logging::info("--> Handled notification: {} {}ms", method, duration.count()); + LOGGING_INFO("--> Handled notification: {} {}ms", method, duration.count()); } co_return; diff --git a/src/Support/Logging.cpp b/src/Support/Logging.cpp index 403a6704..2bfeac95 100644 --- a/src/Support/Logging.cpp +++ b/src/Support/Logging.cpp @@ -4,27 +4,54 @@ #include "spdlog/sinks/stdout_sinks.h" #include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/sinks/basic_file_sink.h" +#include "spdlog/sinks/ringbuffer_sink.h" namespace clice::logging { Options options; -void create_stderr_logger(std::string_view name, const Options& options) { +static std::shared_ptr ringbuffer_sink; + +constexpr static auto pattern = "[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [thread %t] [%s:%#] %v"; + +void stderr_logger(std::string_view name, const Options& options) { std::shared_ptr logger; - logger = spdlog::stderr_color_mt(std::string(name), options.color); + + auto console_sink = std::make_shared(options.color); + if(options.replay_console) { + ringbuffer_sink = std::make_shared(128); + std::array sinks = {console_sink, ringbuffer_sink}; + logger = std::make_shared(std::string(name), sinks.begin(), sinks.end()); + } else { + logger = std::make_shared(std::string(name), console_sink); + } + logger->set_level(options.level); - logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [thread %t] [%s:%#] %v"); + logger->set_pattern(pattern); + logger->flush_on(Level::trace); spdlog::set_default_logger(std::move(logger)); } -void create_file_loggger(std::string_view name, std::string_view dir, const Options& options) { +void file_loggger(std::string_view name, std::string_view dir, const Options& options) { auto now = std::chrono::system_clock::now(); auto filename = std::format("{:%Y-%m-%d_%H-%M-%S}.log", now); - auto path = path::join(dir, filename); + auto sink = std::make_shared(path::join(dir, filename)); - auto logger = spdlog::basic_logger_mt(std::string(name), path); + if(options.replay_console && ringbuffer_sink) { + sink->set_level(options.level); + sink->set_pattern(pattern); + + for(auto& log: ringbuffer_sink->last_raw()) { + sink->log(log); + } + + ringbuffer_sink.reset(); + } + + auto logger = std::make_shared(std::string(name), std::move(sink)); logger->set_level(options.level); - logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [thread %t] [%s:%#] %v"); + logger->set_pattern(pattern); + logger->flush_on(Level::trace); spdlog::set_default_logger(std::move(logger)); } diff --git a/tests/conftest.py b/tests/conftest.py index 0d669b92..95f8a0d8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,12 +35,6 @@ def pytest_addoption(parser: pytest.Parser): help="The port to connect to", ) - parser.addoption( - "--resource-dir", - required=False, - help="Path to the of the clang resource directory.", - ) - @pytest.fixture(scope="session") def executable(request) -> Path | None: @@ -59,14 +53,6 @@ def executable(request) -> Path | None: return path.resolve() -@pytest.fixture(scope="session") -def resource_dir(request) -> Path | None: - path = request.config.getoption("--resource-dir") - if not path: - return None - return Path(path).resolve() - - @pytest.fixture(scope="session") def test_data_dir(request): path = os.path.join(os.path.dirname(__file__), "data") @@ -74,9 +60,7 @@ def test_data_dir(request): @pytest_asyncio.fixture(scope="function") -async def client( - request, executable: Path | None, resource_dir: Path | None, test_data_dir: Path -): +async def client(request, executable: Path | None, test_data_dir: Path): config = request.config mode = config.getoption("--mode") @@ -85,9 +69,6 @@ async def client( f"--mode={mode}", ] - if resource_dir: - cmd.append(f"--resource-dir={resource_dir}") - client = LSPClient( cmd, mode, diff --git a/tests/unit/Index/USR.cpp b/tests/unit/Index/USR.cpp index 9dafb1d8..348cb0c5 100644 --- a/tests/unit/Index/USR.cpp +++ b/tests/unit/Index/USR.cpp @@ -36,7 +36,7 @@ struct GetUSRVisitor : public clang::RecursiveASTVisitor { if(offset.has_value() && USR.has_value()) { USRs[*offset] = USRInfo{*USR, *offset, decl}; - // logging::info("USR: {} at {}:{}", USR->str(), pos->line, pos->character); + // LOGGING_INFO("USR: {} at {}:{}", USR->str(), pos->line, pos->character); } return true; @@ -61,7 +61,7 @@ struct USRTester : public Tester { llvm::StringRef lookup(llvm::StringRef key) { auto iter = USRs.find((*this)["main.cpp", key]); if(iter == USRs.end()) { - logging::fatal("USR not found for key: {}", key); + LOGGING_FATAL("USR not found for key: {}", key); } return iter->second.USR; }