更改日志模块为spdlog

This commit is contained in:
csh 2025-06-24 21:57:40 +08:00
parent d9beeb82a6
commit 56b0f5473a
18 changed files with 111 additions and 242 deletions

View File

@ -5,13 +5,14 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(nlohmann_json REQUIRED)
find_package(spdlog REQUIRED)
find_package(fmt REQUIRED)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
set(SOURCES
src/main.cpp
src/language/tsl_keywords.cpp
src/lsp/dispacther.cpp
src/lsp/server.cpp
src/lsp/logger.cpp
src/provider/base/provider_registry.cpp
src/provider/initialize/initialize_provider.cpp
src/provider/initialized/initialized_provider.cpp
@ -24,5 +25,7 @@ set(SOURCES
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME}
nlohmann_json::nlohmann_json
spdlog::spdlog
fmt::fmt
)

View File

@ -1,60 +0,0 @@
#include "./logger.hpp"
namespace lsp::log
{
Logger::~Logger()
{
if (file_stream_.is_open())
file_stream_.close();
}
Logger& Logger::Instance()
{
static Logger logger;
return logger;
}
void Logger::SetLevel(LogLevel level)
{
std::lock_guard<std::mutex> lock(mutex_);
level_ = level;
}
void Logger::SetLogFile(const std::string& filename)
{
std::lock_guard<std::mutex> lock(mutex_);
if (file_stream_.is_open())
file_stream_.close();
file_stream_.open(filename, std::ios::app);
use_file_ = file_stream_.is_open();
}
void Logger::EnableStderr(bool enable)
{
std::lock_guard<std::mutex> lock(mutex_);
use_stderr_ = enable;
}
const char* Logger::LevelToString(LogLevel level)
{
switch (level)
{
case LogLevel::kOff:
return "OFF";
case LogLevel::kError:
return "ERROR";
case LogLevel::kWarn:
return "WARN";
case LogLevel::kInfo:
return "INFO";
case LogLevel::kDebug:
return "DEBUG";
case LogLevel::kVerbose:
return "VERBOSE";
default:
return "UNKNOWN";
}
}
}

View File

@ -1,93 +0,0 @@
#pragma once
#include <iostream>
#include <fstream>
#include <sstream>
#include <mutex>
namespace lsp::log
{
enum class LogLevel
{
kOff = 0,
kError = 1,
kWarn = 2,
kInfo = 3,
kDebug = 4,
kVerbose = 5
};
class Logger
{
public:
static Logger& Instance();
void SetLevel(LogLevel level);
void SetLogFile(const std::string& filename);
void EnableStderr(bool enable);
template<typename... Args>
void log(LogLevel level, Args&&... args) {
if (level > level_)
return;
std::lock_guard<std::mutex> lock(mutex_);
std::ostringstream oss;
oss << "[TSL-LSP:" << LevelToString(level) << "] ";
(oss << ... << args);
oss << std::endl;
std::string message = oss.str();
if (use_stderr_)
{
std::cerr << message;
}
if (use_file_ && file_stream_.is_open())
{
file_stream_ << message;
file_stream_.flush();
}
}
private:
Logger() = default;
~Logger();
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
const char* LevelToString(LogLevel level);
private:
LogLevel level_;
bool use_file_ = false;
bool use_stderr_ = false;
std::ofstream file_stream_;
std::mutex mutex_;
};
template<typename... Args>
void Error(Args&&... args) {
Logger::Instance().log(LogLevel::kError, std::forward<Args>(args)...);
}
template<typename... Args>
void Warn(Args&&... args) {
Logger::Instance().log(LogLevel::kWarn, std::forward<Args>(args)...);
}
template<typename... Args>
void Info(Args&&... args) {
Logger::Instance().log(LogLevel::kInfo, std::forward<Args>(args)...);
}
template<typename... Args>
void Debug(Args&&... args) {
Logger::Instance().log(LogLevel::kDebug, std::forward<Args>(args)...);
}
template<typename... Args>
void Verbose(Args&&... args) {
Logger::Instance().log(LogLevel::kVerbose, std::forward<Args>(args)...);
}
}

View File

@ -1,24 +1,24 @@
#include <exception>
#include <iostream>
#include <spdlog/spdlog.h>
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#endif
#include "../provider/base/provider_registry.hpp"
#include "./server.hpp"
#include "./logger.hpp"
namespace lsp
{
LspServer::LspServer()
{
providers::RegisterAllProviders(dispatcher_);
log::Debug("LSP server initialized with providers.");
spdlog::debug("LSP server initialized with providers.");
}
void LspServer::Run()
{
log::Info("LSP server starting main loop...");
spdlog::info("LSP server starting main loop...");
while (true)
{
try
@ -31,10 +31,10 @@ namespace lsp
}
catch(const std::exception& e)
{
log::Error("Error processing message: ", e.what());
spdlog::error("Error processing message: {}", e.what());
}
}
log::Info("LSP server main loop ended");
spdlog::info("LSP server main loop ended");
}
std::optional<std::string> LspServer::ReadMessage()
@ -45,7 +45,7 @@ namespace lsp
// 读取 LSP Header
while (std::getline(std::cin, line))
{
log::Verbose("Received header line: ", line);
spdlog::trace("Received header line: {}", line);
// 去掉尾部 \r
if (!line.empty() && line.back() == '\r')
@ -63,11 +63,11 @@ namespace lsp
try
{
content_length = std::stoul(length_str);
log::Verbose("Content-Length: ", content_length);
spdlog::trace("Content-Length: {}", content_length);
}
catch (const std::exception& e)
{
log::Error("Failed to parse Content-Length: ", e.what());
spdlog::error("Failed to parse Content-Length: {}", e.what());
return std::nullopt;
}
}
@ -76,7 +76,7 @@ namespace lsp
if (content_length == 0)
{
log::Warn("No Content-Length found in header");
spdlog::warn("No Content-Length found in header");
return std::nullopt;
}
@ -84,11 +84,11 @@ namespace lsp
std::string body(content_length, '\0');
std::cin.read(&body[0], content_length);
log::Verbose("Message body: ", body);
spdlog::trace("Message body: {}", body);
if (std::cin.gcount() != static_cast<std::streamsize>(content_length))
{
log::Error("Read incomplete message body, expected: ", content_length, ", got: ", std::cin.gcount());
spdlog::error("Read incomplete message body, expected: {}, got: {}", content_length, std::cin.gcount());
return std::nullopt;
}
return body;
@ -100,14 +100,14 @@ namespace lsp
{
nlohmann::json json = nlohmann::json::parse(raw_request);
LspRequest request(json);
log::Debug("Processing method: ", request.method);
spdlog::trace("Processing method: {}", request.method);
nlohmann::json response = dispatcher_.Dispatch(request);
return response;
}
catch (const std::exception& e)
{
log::Error("Failed to handle request: ", e.what());
spdlog::error("Failed to handle request: {}", e.what());
return nlohmann::json();
}
}
@ -118,8 +118,8 @@ namespace lsp
size_t byte_length = response_str.length();
// 调试:显示实际发送的原始内容
log::Debug("Response length: ", byte_length);
log::Debug("Raw response content: [", response_str, "]");
spdlog::debug("Response length: {}", byte_length);
spdlog::debug("Raw response content: [{}]", response_str);
// 在程序启动时设置stdout为二进制模式只需设置一次
static bool binary_mode_set = false;
@ -139,6 +139,6 @@ namespace lsp
std::cout.write(response_str.c_str(), response_str.length());
std::cout.flush();
log::Verbose("Response sent successfully");
spdlog::trace("Response sent successfully");
}
}

View File

@ -1,42 +1,65 @@
#include <iostream>
#include <exception>
#include <memory>
#include <vector>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include "./lsp/server.hpp"
#include "./lsp/logger.hpp"
void ParseArgs(int argc, char* argv[])
{
std::vector<spdlog::sink_ptr> sinks;
bool use_stderr = false;
std::string log_file;
spdlog::level::level_enum log_level = spdlog::level::info;
for (int i = 1; i < argc; ++i)
{
std::string arg = argv[i];
if (arg == "--log=verbose")
{
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kVerbose);
} else if (arg == "--log=debug")
{
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kDebug);
} else if (arg == "--log=info")
{
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kInfo);
} else if (arg == "--log=warn")
{
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kWarn);
} else if (arg == "--log=error")
{
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kError);
} else if (arg == "--log=off")
{
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kOff);
} else if (arg.find("--log-file=") == 0)
{
std::string filename = arg.substr(11); // 跳过 "--log-file="
lsp::log::Logger::Instance().SetLogFile(filename);
}
if (arg == "--log=trace")
log_level = spdlog::level::trace;
else if (arg == "--log=debug")
log_level = spdlog::level::debug;
else if (arg == "--log=info")
log_level = spdlog::level::info;
else if (arg == "--log=warn")
log_level = spdlog::level::warn;
else if (arg == "--log=error")
log_level = spdlog::level::err;
else if (arg == "--log=off")
log_level = spdlog::level::off;
else if (arg.find("--log-file=") == 0)
log_file = arg.substr(11); // 跳过 "--log-file="
else if (arg == "--log-stderr")
use_stderr = true;
}
sinks.clear();
if (use_stderr)
sinks.push_back(std::make_shared<spdlog::sinks::stderr_sink_mt>());
// 如果指定了日志文件,添加文件 sink
if (!log_file.empty())
{
lsp::log::Logger::Instance().EnableStderr(true);
try
{
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_file, true));
}
catch (const std::exception& e)
{
std::cerr << "[TSL-LSP] Failed to create log file: " << e.what() << std::endl;
}
}
std::shared_ptr<spdlog::logger> logger = std::make_shared<spdlog::logger>("tsl_lsp", sinks.begin(), sinks.end());
logger->set_level(log_level);
logger->set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
// 设置为默认日志器
spdlog::set_default_logger(logger);
spdlog::flush_on(spdlog::level::warn);
}
int main(int argc, char* argv[])
@ -44,23 +67,24 @@ int main(int argc, char* argv[])
ParseArgs(argc, argv);
try
{
lsp::log::Info("TSL-LSP server starting...");
spdlog::info("TSL-LSP server starting...");
lsp::LspServer server;
server.Run();
}
catch (const std::exception& e)
{
std::cerr << "[TSL-LSP] Server fatal error: " << e.what() << std::endl;
lsp::log::Error("Server fatal error: ", e.what());
spdlog::error("Server fatal error: ", e.what());
return 1;
}
catch (...)
{
std::cerr << "[TSL-LSP] Server unknown fatal error" << std::endl;
lsp::log::Error("Server unknown fatal error");
spdlog::error("Server unknown fatal error");
return 1;
}
lsp::log::Info("TSL-LSP server stopped normally");
spdlog::info("TSL-LSP server stopped normally");
spdlog::shutdown();
return 0;
}

View File

@ -1,3 +1,4 @@
#include <spdlog/spdlog.h>
#include "./provider_registry.hpp"
#include "../initialize/initialize_provider.hpp"
#include "../initialized/initialized_provider.hpp"
@ -5,14 +6,13 @@
#include "../text_document/did_change_provider.hpp"
#include "../text_document/completion_provider.hpp"
#include "../trace/set_trace_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers
{
void RegisterAllProviders(RequestDispatcher& dispatcher)
{
log::Info("Registering LSP providers...");
spdlog::info("Registering LSP providers...");
RegisterProvider<initialize::InitializeProvider>(dispatcher);
RegisterProvider<initialized::InitializedProvider>(dispatcher);
@ -21,7 +21,7 @@ namespace lsp::providers
RegisterProvider<text_document::CompletionProvider>(dispatcher);
RegisterProvider<trace::SetTraceProvider>(dispatcher);
log::Info("Successfully registered ", dispatcher.GetSupportedMethods().size(), " LSP providers");
spdlog::info("Successfully registered {} LSP providers", dispatcher.GetSupportedMethods().size());
}
}

View File

@ -1,7 +1,7 @@
#pragma once
#include <spdlog/spdlog.h>
#include "../../lsp/dispacther.hpp"
#include "./provider_interface.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers
{
@ -15,7 +15,7 @@ namespace lsp::providers
auto provider = std::make_shared<ProviderClass>();
log::Info("Registering ", provider->GetProviderName(), " for method: ", provider->GetMethod());
spdlog::info("Registering {} for method: {}", provider->GetProviderName(), provider->GetMethod());
dispatcher.RegisterProvider(
provider->GetMethod(),

View File

@ -1,16 +1,16 @@
#include <spdlog/spdlog.h>
#include "./initialize_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::initialize
{
nlohmann::json InitializeProvider::ProvideResponse(const LspRequest& request)
{
spdlog::debug("InitializeProvider: Providing response for method {}", request.method);
nlohmann::json response;
response["jsonrpc"] = "2.0";
response["id"] = request.id;
response["result"] = BuildInitializeResult();
log::Debug("InitializeProvider: Providing response for method '", request.method, "' with id ", request.id);
return response;
}

View File

@ -1,12 +1,12 @@
#include <spdlog/spdlog.h>
#include "./initialized_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::initialized
{
nlohmann::json InitializedProvider::ProvideResponse(const LspRequest& request)
{
log::Debug("InitializedProvider: Providing response for method '", request.method, "' with id ", request.id);
spdlog::debug("InitializeProvider: Providing response for method {}", request.method);
return nlohmann::json();
}

View File

@ -1,11 +1,11 @@
#include <spdlog/spdlog.h>
#include "./completion_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::text_document
{
nlohmann::json CompletionProvider::ProvideResponse(const LspRequest& request)
{
log::Debug("CompletionProvider: Providing response for method '", request.method, "' with id ", request.id);
spdlog::debug("CompletionProvider: Providing response for method {}", request.method);
try
{
nlohmann::json response = BuildCompletionResponse(request);
@ -13,7 +13,7 @@ namespace lsp::providers::text_document
}
catch (const std::exception& e)
{
log::Error(GetProviderName(), ": Error - ", e.what());
spdlog::error("{}: Error - ", GetProviderName(), e.what());
nlohmann::json errorResponse = CreateErrorResponse(request.id, -32603, e.what());
return errorResponse;
}
@ -38,7 +38,7 @@ namespace lsp::providers::text_document
// 验证必要参数
if (!request.params.contains("textDocument") || !request.params.contains("position"))
{
log::Warn(GetProviderName(), ": Missing required parameters in request");
spdlog::warn("{}: Missing required parameters in request", GetProviderName());
// 返回空补全列表而非错误
response["result"] = BuildCompletionResult({});
return response;
@ -49,8 +49,7 @@ namespace lsp::providers::text_document
nlohmann::json position = ExtractPosition(request.params);
std::string prefix = ExtractPrefix(request.params);
log::Verbose(
GetProviderName(), ": Processing completion request for URI='", uri, "', Position=", position.dump(), ", Prefix='", prefix, "'");
spdlog::trace("{}: Processing completion request for URI='{}', Position={}, prefix='{}'", GetProviderName(), uri, position.dump(), prefix);
// 收集所有补全项
std::vector<CompletionItem> allItems;
@ -66,7 +65,7 @@ namespace lsp::providers::text_document
// 构建响应
response["result"] = BuildCompletionResult(allItems);
log::Info(GetProviderName(), ": Provided ", allItems.size(), " completion items");
spdlog::info("{}: Provided {}", GetProviderName(), allItems.size());
return response;
}
@ -88,10 +87,10 @@ namespace lsp::providers::text_document
if (params.contains("textDocument") && params["textDocument"].contains("uri"))
{
std::string uri = params["textDocument"]["uri"].get<std::string>();
log::Verbose("ExtractDocumentUri: Found URI: ", uri);
spdlog::trace("ExtractDocumentUri: Found URI: ", uri);
return uri;
}
log::Warn("ExtractDocumentUri: No URI found in parameters");
spdlog::warn("ExtractDocumentUri: No URI found in parameters");
return "";
}
@ -100,14 +99,14 @@ namespace lsp::providers::text_document
if (params.contains("position"))
{
nlohmann::json pos = params["position"];
log::Verbose("ExtractPosition: Found position: ", pos.dump());
spdlog::trace("ExtractPosition: Found position: ", pos.dump());
return pos;
}
// 返回默认位置
nlohmann::json defaultPos;
defaultPos["line"] = 0;
defaultPos["character"] = 0;
log::Warn("ExtractPosition: No position found in parameters, using default (0, 0)");
spdlog::warn("ExtractPosition: No position found in parameters, using default (0, 0)");
return defaultPos;
}
@ -117,7 +116,7 @@ namespace lsp::providers::text_document
if (params.contains("prefix"))
{
std::string prefix = params["prefix"].get<std::string>();
log::Verbose("ExtractPrefix: Found prefix form params: '", prefix, "'");
spdlog::trace("ExtractPrefix: Found prefix form params: '", prefix, "'");
return prefix;
}
@ -125,13 +124,13 @@ namespace lsp::providers::text_document
if (params.contains("context") && params["context"].contains("prefix"))
{
std::string prefix = params["context"]["prefix"].get<std::string>();
log::Verbose("ExtractPrefix: Found prefix form params: '", prefix, "'");
spdlog::trace("ExtractPrefix: Found prefix form params: '", prefix, "'");
return prefix;
}
// TODO: 理想情况下,应该从文档内容和位置计算前缀
// 这需要维护文档内容的状态
log::Verbose("ExtractPrefix: No prefix found, returning empty string");
spdlog::trace("ExtractPrefix: No prefix found, returning empty string");
return "";
}
@ -153,7 +152,7 @@ namespace lsp::providers::text_document
items.push_back(item);
}
log::Debug("ProvideKeywordCompletions: Found ", items.size(), " keyword completions");
spdlog::debug("ProvideKeywordCompletions: Found ", items.size(), " keyword completions");
return items;
}
@ -162,7 +161,7 @@ namespace lsp::providers::text_document
const nlohmann::json& position,
const std::string& prefix)
{
log::Debug("ProvideContextualCompletions: Processing contextual completions for URI: ", uri);
spdlog::debug("ProvideContextualCompletions: Processing contextual completions for URI: ", uri);
std::vector<CompletionItem> items;
// TODO: 基于上下文提供补全
@ -171,7 +170,7 @@ namespace lsp::providers::text_document
// - 函数名补全
// - 类型补全
// - 属性补全等
log::Debug("ProvideContextualCompletions: Found ", items.size(), " contextual completions");
spdlog::debug("ProvideContextualCompletions: Found ", items.size(), " contextual completions");
return items;
}
@ -205,7 +204,7 @@ namespace lsp::providers::text_document
error["message"] = GetProviderName() + ": " + message;
response["error"] = error;
log::Error("CreateErrorResponse: Created error response with code ", code, " and message: ", message);
spdlog::error("CreateErrorResponse: Created error response with code {} and message {}", code, message);
return response;
}

View File

@ -1,13 +1,13 @@
#include <spdlog/spdlog.h>
#include "./did_change_provider.hpp"
#include "./did_open_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::text_document
{
nlohmann::json DidChangeProvider::ProvideResponse(const LspRequest& request)
{
log::Debug("DidChangeProvider: Providing response for method '", request.method, "' with id ", request.id);
spdlog::debug("DidChangeProvider: Providing response for method {}", request.method);
try
{
auto params = request.params;

View File

@ -1,5 +1,5 @@
#include <spdlog/spdlog.h>
#include "./did_open_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::text_document
{
@ -7,7 +7,7 @@ namespace lsp::providers::text_document
nlohmann::json DidOpenProvider::ProvideResponse(const LspRequest& request)
{
log::Debug("DidOpenProvider: Providing response for method '", request.method, "' with id ", request.id);
spdlog::debug("DidOpenProvider: Providing response for method {}", request.method);
try
{
auto params = request.params;

View File

@ -2,6 +2,10 @@
Notable changes to the `TSL` extension will be documented in this file.
## [2.1.0]: 2025-06-25
- 更换`LSP`日志模块,启动参数`verbose -> trace`
## [2.0.0]: 2025-06-22
- 初步支持`LSP`--关键字补全

Binary file not shown.

Binary file not shown.

View File

@ -1,12 +1,12 @@
{
"name": "tsl-devkit",
"version": "1.4.0",
"version": "2.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tsl-devkit",
"version": "1.4.0",
"version": "2.1.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
"vscode-languageclient": "^9.0.1"
@ -15,7 +15,6 @@
"@types/node": "^24.0.3",
"@types/vscode": "^1.101.0",
"@vscode/vsce": "^3.5.0",
"cp": "^0.2.0",
"prettier": "^3.5.3",
"typescript": "^5.8.3"
},
@ -1298,13 +1297,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/cp": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/cp/-/cp-0.2.0.tgz",
"integrity": "sha512-4ftCvShHjIZG/zzomHyunNpBof3sOFTTmU6s6q9DdqAL/ANqrKV3pr6Z6kVfBI4hjn59DFLImrBqn7GuuMqSZA==",
"dev": true,
"license": "MIT"
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",

View File

@ -1,7 +1,7 @@
{
"name": "tsl-devkit",
"displayName": "TSL",
"version": "2.0.0",
"version": "2.1.0",
"description": "TSL syntax highlighter support for VSCode.",
"publisher": "csh",
"homepage": "https://git.mytsl.cn/csh/tsl-devkit",

Binary file not shown.