Skip to content

Commit 444b531

Browse files
IronsDuclaude
andauthored
Refactor/decouple drogon remove singleton (#35)
* refactor: decouple Drogon/spdlog, remove singletons (WIP) - Remove spdlog dependency: DefaultLogSink now uses std::cout/std::cerr - Replace spdlog/fmt with C++20 std::format in log macros - Add HttpServer abstraction interface (include/profiler/http_server.h) - Remove singleton from LogManager, make it instance-owned - Update log macros to use log_manager_ member instead of global singleton - Update plan.md with full design document for the refactoring Still TODO: ProfilerManager singleton removal, Drogon adapter, web_server refactoring, CMake target split, example/test updates Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: add detailed progress tracking and continuation guide Record completed tasks (spdlog removal, HttpServer interface, LogManager de-singleton) and remaining tasks with specific file-level change instructions for next development session. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: decouple Drogon, remove singleton, split CMake targets - Remove ProfilerManager singleton: delete getInstance(), make constructor public, use unique_ptr<LogManager> (PIMPL) for per-instance log config - Create HttpServer abstraction: pure C++ interface (Request/Response structs) in include/profiler/http_server.h, no framework dependency - Create Drogon adapter: src/backends/drogon/drogon_http_server.{h,cpp} implements HttpServer interface, only place that #includes <drogon/drogon.h> - Rewrite web_server.cpp: all routes use HttpServer abstraction instead of Drogon directly, remove Json::Value dependency (manual JSON string building) - Remove spdlog dependency: DefaultLogSink uses std::cout/std::cerr, log macros use std::format instead of fmt - Split CMake targets: profiler_core (no Drogon) + profiler_web (optional, requires Drogon), add REMOTE_PROFILER_ENABLE_WEB option - Update logger.h: global setSink/setLogLevel removed, now ProfilerManager instance methods - Signal handler: lazy install on first captureAllThreadStacks() call - Update example and tests for non-singleton API - Remove spdlog from vcpkg.json All 18 tests pass (LoggerTest: 7, FullFlowTest: 6, CPUProfileTest: 5). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: replace HttpServer abstraction with ProfilerHttpHandlers Replace the HttpServer interface pattern with a simpler, more flexible design: ProfilerHttpHandlers provides framework-agnostic handler methods that return HandlerResponse structs. Users with any web framework can call handlers and wrap responses with their own request/response types. - Add include/profiler/http_handlers.h (HandlerResponse + ProfilerHttpHandlers) - Add src/http_handlers.cpp (all profiler endpoint business logic) - Simplify web_server.cpp to a thin Drogon adapter - Delete include/profiler/http_server.h and src/backends/drogon/ - Update example to use registerHttpHandlers(profiler) directly - Move page handlers (WebResources) to profiler_web, data handlers to profiler_core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: suppress unused parameter warning in DefaultLogSink Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update user guides for non-singleton ProfilerHttpHandlers API - Replace all getInstance() singleton references with local instances - Add ProfilerHttpHandlers and HandlerResponse API documentation - Add integration examples for any web framework (not just Drogon) - Update setSink/setLogLevel as ProfilerManager instance methods - Remove spdlog references (now uses std::cout/cerr) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: apply clang-format 18 to fix CI format check Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: rename web_server to drogon_adapter, fix outdated docs - Rename web_server.h/cpp → profiler/drogon_adapter.h/src/drogon_adapter.cpp - Rename registerHttpHandlers() → registerDrogonHandlers() - Fix all getInstance() references in docs/examples → instance creation - Fix profiler_lib references → profiler_core - Update log_sink.h doc example to use instance method - Update plan.md: remove outdated HttpServer/singleton/spdlog descriptions - Update CMakeLists.txt install target for new header path Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve CI format check and pmf-conversions warning - Fix test_cpu_profile.cpp: replace member function pointer cast with free function pointer (avoids -Werror=pmf-conversions) - Apply clang-format-18 to profiler_manager.h/cpp, log_macros.h Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 1733427 commit 444b531

29 files changed

Lines changed: 2097 additions & 2808 deletions

CMakeLists.txt

Lines changed: 392 additions & 319 deletions
Large diffs are not rendered by default.

docs/user_guide/01_quick_start.md

Lines changed: 57 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,14 @@ if [ ! -d "vcpkg" ]; then
4343
cd ..
4444
fi
4545

46-
# 4. 安装 vcpkg 依赖
47-
cd vcpkg
48-
./vcpkg install --triplet=x64-linux-release
49-
cd ..
50-
51-
# 5. 编译库
46+
# 4. 编译库
5247
mkdir build && cd build
5348
cmake .. \
5449
-DCMAKE_BUILD_TYPE=Release \
5550
-DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake \
5651
-DVCPKG_TARGET_TRIPLET=x64-linux-release
5752

58-
# 6. 安装到系统(可选)
59-
sudo make install
53+
make -j$(nproc)
6054
```
6155

6256
### 方法 2: 直接集成源码
@@ -82,7 +76,8 @@ void doSomeWork() {
8276
}
8377

8478
int main() {
85-
auto& profiler = profiler::ProfilerManager::getInstance();
79+
// 创建 ProfilerManager 实例(非单例模式)
80+
profiler::ProfilerManager profiler;
8681

8782
std::cout << "开始 CPU profiling..." << std::endl;
8883

@@ -108,46 +103,26 @@ int main() {
108103

109104
### CMakeLists.txt 配置
110105

111-
在你的项目根目录创建 `CMakeLists.txt`:
112-
113106
```cmake
114107
cmake_minimum_required(VERSION 3.15)
115108
project(MyProfilerApp VERSION 1.0.0)
116109
117110
set(CMAKE_CXX_STANDARD 20)
118111
set(CMAKE_CXX_STANDARD_REQUIRED ON)
119112
120-
# 方法 1: 如果已安装到系统
121-
find_package(PkgConfig REQUIRED)
122-
pkg_check_modules(GPERFTOOLS REQUIRED libprofiler libtcmalloc)
113+
# 链接 profiler 核心库(不需要 Drogon)
114+
find_package(cpp-remote-profiler REQUIRED)
123115
124116
add_executable(my_app my_profiler_app.cpp)
125-
126-
# 链接 profiler 库
127-
target_link_libraries(my_app
128-
profiler_lib # C++ Remote Profiler 库
129-
${GPERFTOOLS_LIBRARIES} # gperftools
130-
pthread
131-
)
132-
133-
# 方法 2: 如果使用源码直接编译
134-
# add_subdirectory(path/to/cpp-remote-profiler)
135-
# target_link_libraries(my_app profiler_lib)
117+
target_link_libraries(my_app cpp-remote-profiler::profiler_core)
136118
```
137119

138120
### 编译命令
139121

140122
```bash
141-
# 如果使用 vcpkg
142123
mkdir build && cd build
143-
cmake .. \
144-
-DCMAKE_BUILD_TYPE=Release \
145-
-DCMAKE_TOOLCHAIN_FILE=../cpp-remote-profiler/vcpkg/scripts/buildsystems/vcpkg.cmake \
146-
-DVCPKG_TARGET_TRIPLET=x64-linux-release
147-
124+
cmake .. -DCMAKE_BUILD_TYPE=Release
148125
make
149-
150-
# 运行
151126
./my_app
152127
```
153128

@@ -164,42 +139,20 @@ make
164139
#### 使用 Go pprof (推荐)
165140

166141
```bash
167-
# 安装 Go (如果还没有)
168-
wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz
169-
sudo tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
170-
export PATH=$PATH:/usr/local/go/bin
171-
172142
# 使用 pprof 分析
173143
go tool pprof -http=:8080 my_profile.prof
174144
```
175145

176-
然后在浏览器中打开 `http://localhost:8080`
177-
178-
#### 使用 FlameGraph
179-
180-
```bash
181-
# 1. 安装 FlameGraph 工具
182-
git clone https://github.com/brendangregg/FlameGraph /tmp/FlameGraph
183-
184-
# 2. 使用 pprof 转换 profile 为 collapsed 格式
185-
pprof -raw my_profile.app > /tmp/profile.raw
186-
187-
# 3. 生成火焰图
188-
perl /tmp/FlameGraph/flamegraph.pl /tmp/profile.raw > flamegraph.svg
189-
190-
# 4. 在浏览器中查看
191-
firefox flamegraph.svg
192-
```
193-
194146
## 一键生成火焰图 (API 方式)
195147

196-
如果你想直接在代码中生成火焰图,可以使用 `analyzeCPUProfile()`:
148+
如果你想直接在代码中生成火焰图
197149

198150
```cpp
199151
#include "profiler_manager.h"
152+
#include <fstream>
200153

201154
int main() {
202-
auto& profiler = profiler::ProfilerManager::getInstance();
155+
profiler::ProfilerManager profiler;
203156

204157
// 采样 10 秒并生成火焰图
205158
std::string svg = profiler.analyzeCPUProfile(10, "flamegraph");
@@ -209,40 +162,65 @@ int main() {
209162
out << svg;
210163
out.close();
211164

212-
std::cout << "火焰图已生成: flamegraph.svg" << std::endl;
213-
214165
return 0;
215166
}
216167
```
217168

218169
## 带有 Web 界面的完整示例
219170

220-
如果你想要一个完整的 Web 界面来查看 profiling 结果
171+
如果你使用 Drogon 框架,可以使用一键注册函数
221172

222173
```cpp
223-
#include <drogon/drogon.h>
224174
#include "profiler_manager.h"
225-
#include "web_server.h"
175+
#include "profiler/drogon_adapter.h"
176+
#include <drogon/drogon.h>
226177

227178
int main() {
228-
// 启动 Drogon 服务器
229-
profiler::ProfilerManager& profiler = profiler::ProfilerManager::getInstance();
230-
231-
// 注册所有 profiling 相关的 HTTP 端点
232-
profiler::registerHttpHandlers(profiler);
179+
profiler::ProfilerManager profiler;
233180

234-
// 监听 8080 端口
235-
drogon::app().addListener("0.0.0.0", 8080);
236-
std::cout << "Profiler Web UI: http://localhost:8080" << std::endl;
181+
// 注册所有 profiling 相关的 HTTP 端点到 Drogon
182+
profiler::registerDrogonHandlers(profiler);
237183

238184
// 启动服务器
239-
drogon::app().run();
185+
drogon::app().addListener("0.0.0.0", 8080).run();
240186

241187
return 0;
242188
}
243189
```
244190

245-
访问 `http://localhost:8080` 即可看到 Web 界面,点击按钮即可生成火焰图。
191+
**CMake 配置**:
192+
```cmake
193+
target_link_libraries(my_app
194+
cpp-remote-profiler::profiler_web
195+
Drogon::Drogon
196+
)
197+
```
198+
199+
## 使用其他 Web 框架
200+
201+
如果你使用的是 Drogon 以外的 Web 框架,可以使用 `ProfilerHttpHandlers`
202+
203+
```cpp
204+
#include "profiler_manager.h"
205+
#include "profiler/http_handlers.h"
206+
207+
int main() {
208+
profiler::ProfilerManager profiler;
209+
profiler::ProfilerHttpHandlers handlers(profiler);
210+
211+
// 调用任意 handler,获得框架无关的响应
212+
profiler::HandlerResponse resp = handlers.handleCpuAnalyze(10, "flamegraph");
213+
214+
// resp.status, resp.content_type, resp.body
215+
// 用你自己的 Web 框架包装这些数据
216+
}
217+
```
218+
219+
**CMake 配置**:
220+
```cmake
221+
target_link_libraries(my_app cpp-remote-profiler::profiler_core)
222+
# 不需要 Drogon
223+
```
246224

247225
## Heap Profiling 示例
248226

@@ -256,15 +234,13 @@ int main() {
256234
// 设置环境变量 (在程序启动前)
257235
setenv("TCMALLOC_SAMPLE_PARAMETER", "524288", 1);
258236

259-
auto& profiler = profiler::ProfilerManager::getInstance();
237+
profiler::ProfilerManager profiler;
260238

261-
// 启动 heap profiler
262239
profiler.startHeapProfiler("heap.prof");
263240

264241
// 分配一些内存
265242
int* data = new int[1000];
266243

267-
// 停止 heap profiler
268244
profiler.stopHeapProfiler();
269245

270246
delete[] data;
@@ -286,9 +262,9 @@ export TCMALLOC_SAMPLE_PARAMETER=524288
286262

287263
## 下一步
288264

289-
- 📖 阅读 [API 参考手册](02_api_reference.md) 了解所有可用的 API
290-
- 💡 查看 [集成示例](03_integration_examples.md) 学习更多使用场景
291-
- 🔧 遇到问题?查看 [故障排除指南](04_troubleshooting.md)
265+
- 阅读 [API 参考手册](02_api_reference.md) 了解所有可用的 API
266+
- 查看 [集成示例](03_integration_examples.md) 学习更多使用场景
267+
- 遇到问题?查看 [故障排除指南](04_troubleshooting.md)
292268

293269
## 常见问题
294270

@@ -307,8 +283,5 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
307283
### Q: Heap profiling 不工作
308284
**A**: 确保设置了 `TCMALLOC_SAMPLE_PARAMETER` 环境变量,并且链接了 tcmalloc 库。
309285

310-
### Q: 性能开销太大
311-
**A**: CPU profiling 通常有 1-5% 的性能开销。如果开销过大,可以:
312-
- 降低采样频率
313-
- 仅在需要时启用 profiling
314-
- 使用更长的采样间隔
286+
### Q: 如何在非 Drogon 的 Web 框架中使用?
287+
**A**: 使用 `ProfilerHttpHandlers` 类。每个 handler 方法返回 `HandlerResponse` 结构体,你只需将其包装到你框架的 response 对象中。详见 [场景 3: 与任意 Web 框架集成](03_integration_examples.md#场景-3-与任意-web-框架集成)

0 commit comments

Comments
 (0)