> For the complete documentation index, see [llms.txt](https://ilogtail.gitbook.io/ilogtail-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ilogtail.gitbook.io/ilogtail-docs/pre-release/developer-guide/development-environment.md).

# 开发环境

虽然[源代码编译](/ilogtail-docs/pre-release/installation/yuan-dai-ma/build.md)已经提供了方便的 LoongCollector 编译方法，但却不适合开发场景。因为开发过程中需要不断进行编译调试，重复全量编译的速度太慢，因此需要构建支持增量编译开发环境。

## 进程结构

LoongCollector 为了支持插件系统，引入了 libGoPluginAdaptor 和 libGoPluginBase（以下简称 adaptor 和 base）这两个动态库，它们与 LoongCollector 之间的关系如下：\
LoongCollector 动态依赖于这两个动态库（即 binary 中不依赖），在初始化时，LoongCollector 会尝试使用动态库接口（如 dlopen）动态加载它们，获取所需的符号。\
Adaptor 充当一个中间层，LoongCollector 和 base 均依赖它，LoongCollector 向 adaptor 注册回调，adpator 将这些回调记录下来以接口的形式暴露给 base 使用。\
Base 是插件系统的主体，它包含插件系统所必须的采集、处理、聚合以及输出（向 LoongCollector 递交可以视为其中一种）等功能。\
因此，完整的 LoongCollector 包含loongcollector 、libGoPluginAdaptor.so 和 libGoPluginBase.so 3个二进制文件。

![image.png](https://sls-opensource.oss-us-west-1.aliyuncs.com/ilogtail/ilogtail-adapter-cgo.png)

## 目录结构 <a href="#ikc61" id="ikc61"></a>

LoongCollector 的大致目录结构如下：

```shell
.
├── core                  # C++核心代码
│   ├── CMakeLists.txt    # C++项目描述文件
│   └── logtail.cpp       # C++ 主函数入口（当前仓库）
├── plugins               # Go插件代码
├── go.mod                # Go项目描述文件
├── docker                # 辅助编译的镜像描述目录
├── scripts               # 辅助编译的脚本目录
└── Makefile              # 编译描述文件
```

`core` 目录包含 LoongCollector C++ 核心代码，主函数入口为 `logtail.cpp`。C++ 工程使用 CMake 描述，`CMakeLists.txt` 为总入口，各子目录中的 `CMakeLists.txt` 描述对应编译目标。

顶层目录.本身就是一个Go项目，该项目为 LoongCollector 插件，go.mod为其描述文件。插件代码主体在plugins目录。

docker目录和scripts目录分别为辅助编译的镜像描述目录和脚本目录。Makefile为整个 LoongCollector 的编译描述文件，对编译命令进行了封装。

## 开发镜像

LoongCollector 依赖了诸多第三方库（详见[编译依赖](/ilogtail-docs/pre-release/installation/yuan-dai-ma/dependencies.md)

LoongCollector 提供了预编译依赖的镜像辅助编译。开发镜像的地址在`sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/loongcollector-community-edition/loongcollector-build-linux`，可通过下面命令获取。

```shell
docker pull sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/loongcollector-community-edition/loongcollector-build-linux
```

开发镜像预先安装了gcc和go编译器，更新了git，为了代码风格统一安装了clang-format、go-tools，为了便于调试也安装了gdb、go-delve等工具。为方便国内开发者，预先设置了GOPROXY="<https://goproxy.cn,direct">。

```shell
$ gcc --version
gcc (GCC) 9.3.1 20200408 (Red Hat 9.3.1-2)
$ go version
go version go1.25.9 linux/amd64
$ git --version
git version 2.46.2
```

C++核心的编译依赖在/opt/logtail/deps/目录下，该路径是CMakeLists.txt的DEFAULT\_DEPS\_ROOT（可以查看./core/dependencies.cmake找到修改路径的变量）。

```shell
$ ls /opt/logtail/deps/
bin  include  lib  lib64  share  ssl
```

如果需要安装更多工具或编译依赖，可以在开发镜像上镜像叠加，制作自定义的开发镜像。

```dockerfile
from sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/loongcollector-community-edition/loongcollector-build-linux
yum -y install ...
go install ...
```

## 使用VS Code构建开发环境

[VS Code](https://code.visualstudio.com/)通过[Remote Development](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)插件可以实现远程开发、在镜像中开发，甚至远程+镜像中开发，在镜像中开发的功能使得编译环境在不同部署间都能保持统一。由于VS Code免费而功能强大，因此我们选用VS Code来为 LoongCollector 创建一致的、可移植的开发环境。

### 1. 安装插件 <a href="#zpmpx" id="zpmpx"></a>

在VS Code的Marketplace中搜索“Remote Development”安装插件。\
![image.png](https://ilogtail-community-edition.oss-cn-shanghai.aliyuncs.com/images/developer-guide/development-environment/vscode-remote-development-plugin.png)

### 2. 创建镜像开发环境配置 <a href="#s3qyx" id="s3qyx"></a>

在 LoongCollector 代码库的顶层目录创建`.devcontainer`目录，并在里面创建`devcontainer.json`文件，文件的内容如下：

```json
{
  "image": "sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/loongcollector-community-edition/loongcollector-build-linux:2.1.19",
  "customizations": {
    "vscode": {
      "extensions": [
        "golang.Go",
        "ms-vscode.cpptools-extension-pack",
        "DavidAnson.vscode-markdownlint",
        "redhat.vscode-yaml"
      ]
    }
  }
}
```

其中，image指定了 LoongCollector 的开发镜像地址，customizations.vscode.extensions指定了开发环境的插件。部分插件介绍如下，开发者也可以按照自己的习惯进行修改，[欢迎讨论](https://github.com/alibaba/loongcollector/discussions/299)。

| **插件名**                           | **用途**         |
| --------------------------------- | -------------- |
| golang.Go                         | Go开发必备插件       |
| ms-vscode.cpptools-extension-pack | C++开发必备插件      |
| DavidAnson.vscode-markdownlint    | Markdown代码风格检查 |
| redhat.vscode-yaml                | YAML语言支持       |

### 3. 在容器中打开代码库 <a href="#rsqu1" id="rsqu1"></a>

使用Shift + Command + P（Mac）或Ctrl + Shift + P（Win）打开命令面板，输入`reopen`，选择`Remote-Containers: Reopen in Container`。\
![image.png](https://ilogtail-community-edition.oss-cn-shanghai.aliyuncs.com/images/developer-guide/development-environment/reopen-in-palette.png)\
或者若出现如下图提示，则可以直接点击在容器中重新打开。\
![image.png](https://ilogtail-community-edition.oss-cn-shanghai.aliyuncs.com/images/developer-guide/development-environment/reopen-in-tip.png)\
首次打开时会比较慢，因为要下载编译镜像并安装插件，后面再次打开时速度会很快。按照提示进行镜像Build。\
完成上述步骤后，我们已经可以使用VS Code进行代码编辑，并在其中进行代码编译。\
注：如果以前拉取过编译镜像，可能需要触发`Remote-Containers: Rebuild Container Without Cache`重新构建。

### 4. 在容器中进行编译 <a href="#wef4t" id="wef4t"></a>

打开新Terminal（找不到的可以在命令面板中打Terminal，选择新开一个）\
![image.png](https://ilogtail-community-edition.oss-cn-shanghai.aliyuncs.com/images/developer-guide/development-environment/terminal.png)

* 编译Go插件

```bash
go mod tidy       # 若需要更新插件库
make plugin_local # 每次更新插件代码后从这里开始
```

![image.png](https://ilogtail-community-edition.oss-cn-shanghai.aliyuncs.com/images/developer-guide/development-environment/go-compile-result.png)\
如果只是对插件代码进行了修改，则只需要执行最后一行命令即可增量编译。

* 编译C++代码

```bash
git submodule update --init # 初始化并更新所有子模块
mkdir -p core/build # 若之前没有建过
cd core/build
cmake ..            # 若增删文件，修改CMakeLists.txt后需要重新执行
make -sj$(nproc)    # 每次更新core代码后从这里开始
```

![image.png](https://ilogtail-community-edition.oss-cn-shanghai.aliyuncs.com/images/developer-guide/development-environment/c%2B%2B-compiling.png)\
![image.png](https://ilogtail-community-edition.oss-cn-shanghai.aliyuncs.com/images/developer-guide/development-environment/c%2B%2B-compile-result.png)

如果只是对core代码进行了修改，则只需要执行最后一行命令即可增量编译。

默认的编译选项代码可能被优化，若需要Debug建议修改CMAKE\_BUILD\_TYPE开关。替换上述第2行为

```bash
cmake -D CMAKE_BUILD_TYPE=Debug ..
```

同理，若需要明确需要代码优化，则将上面的Debug改为Release。

默认的编译开关没有打开UT，如果需要编译UT，需要增加BUILD\_LOGTAIL\_UT开关。替换上述第2行为

```bash
cmake -DBUILD_LOGTAIL_UT=ON ..
```

**AgentSight（`libagentsight.so` / `InputAgentSight`）**：Linux 上 `core/CMakeLists.txt` 默认 `ENABLE_AGENTSIGHT=ON`，一般 **`cmake ..` 无需额外开关** 即会编译 coolbpf 里的 AgentSight FFI。若曾显式关过或 CMake 缓存里为 OFF，需重新打开：

```bash
cmake -DENABLE_AGENTSIGHT=ON ..
```

确认是否开启：在 `core/build` 下查看 `cmake` 输出中的 `ENABLE_AGENTSIGHT=ON`，或检查是否存在 `core/build/_thirdparty/coolbpf/src/agentsight/release/libagentsight.so`。CI / `make core` 也会传 `-DENABLE_AGENTSIGHT=ON`（见 `scripts/gen_build_scripts.sh`、`Makefile`）。

* 制作镜像

目前不支持，没有安装docker，请直接在主机上进行编译。

### 5. 获取编译产出 <a href="#x0fef" id="x0fef"></a>

**编译目录与产物目录是同一个：`core/build/`。** 上述 C++ 编译步骤在容器内执行 `cd core/build && make` 后，产物直接落在该目录下；不要在仓库根目录另建 `build/` 编译。

由于 VS Code 将代码库目录挂载进镜像，主机与容器看到的是同一路径，主机上可直接访问 `core/build/` 下的文件。\
![image.png](https://ilogtail-community-edition.oss-cn-shanghai.aliyuncs.com/images/developer-guide/development-environment/artifacts-on-host.png)\
镜像内默认使用 root 编译，主机上若遇权限问题可执行 `sudo chown -R $USER .`。

#### 主要产物路径（相对仓库根目录）

| 用途             | 路径                                                                       |
| -------------- | ------------------------------------------------------------------------ |
| 主程序            | `core/build/loongcollector`                                              |
| Go 插件适配层       | `core/build/go_pipeline/libGoPluginAdapter.so`                           |
| Go 插件基础库       | `core/build/go_pipeline/libGoPluginBase.so`                              |
| eBPF 驱动        | `core/build/ebpf/driver/libeBPFDriver.so`                                |
| coolbpf 运行时    | `core/build/_thirdparty/coolbpf/src/libcoolbpf.so.1.0.0`                 |
| AgentSight FFI | `core/build/_thirdparty/coolbpf/src/agentsight/release/libagentsight.so` |
| 单元测试可执行文件      | `core/build/unittest/<模块>/<名称>_unittest`                                 |
| 跑全部 core UT    | 仓库根目录执行 `./scripts/run_core_ut.sh`（默认扫描 `core/build/unittest`）           |

单元测试必须在 `core/build` 目录下运行，例如：

```bash
cd core/build
./unittest/ebpf/agentsight_message_util_unittest
```

#### 组装可运行目录 `output/`

本地调试推荐用脚本（从 `core/build` 拷贝二进制与 so，并同步配置）：

```bash
./run_scripts/restart.sh
```

也可手动拷贝核心文件：

```bash
mkdir -p output
cp -a ./core/build/loongcollector ./output/
cp -a ./core/build/go_pipeline/libGoPluginAdapter.so ./output/
cp -a ./core/build/go_pipeline/libGoPluginBase.so ./output/
cp -a ./core/build/ebpf/driver/libeBPFDriver.so ./output/
cp -a ./core/build/_thirdparty/coolbpf/src/libcoolbpf.so.1.0.0 ./output/
cp -a ./core/build/_thirdparty/coolbpf/src/agentsight/release/libagentsight.so ./output/
```

最终 `./output` 目录结构示例：

```
./output
├── loongcollector              # 主程序（来自 core/build/loongcollector）
├── libGoPluginAdapter.so       # 插件接口
├── libGoPluginBase.so          # 插件 lib
├── libeBPFDriver.so
├── libcoolbpf.so.1.0.0
└── libagentsight.so            # AgentSight（restart.sh 会一并拷贝）
```

## 直接使用镜像编译 <a href="#vsykl" id="vsykl"></a>

如果不方便使用VS Code，也可以通过Docker手动挂载代码库目录的方式，启动编译镜像进入编译。

### 1. 进入源代码顶层目录

### 2. 创建编译容器，并挂载代码目录

```bash
docker run --name loongcollector-build -d \
  -v `pwd`:/src -w /src \
  sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/loongcollector-community-edition/loongcollector-build-linux:2.1.19 \
  bash -c "sleep infinity"
```

### 3. 进入容器

```bash
docker exec -it loongcollector-build bash
```

### 4. 在容器内编译

后续步骤均与VS Code的操作步骤相同。

## 使用编译产出

在主机上，编译的后的产出可以直接替换对应的文件使用。而对于容器场景，虽然在编译镜像内没有制作镜像能力，但通过下面的方法可以在不制作新镜像的情况下实现快速测试。

### 1. 修改官方镜像entrypoint

基于官方镜像包进行调试，首先用bash覆盖官方镜像的entrypoint，避免杀死 LoongCollector 后容器直接退出。

* docker：指定CMD

```bash
docker run -it --name docker_loongcollector -v /:/logtail_host:ro -v /var/run:/var/run aliyun/loongcollector:<VERSION> bash
```

* k8s：用command覆盖entrypoint

```yaml
   command:
        - sleep
        - 'infinity'
  # 删除livenessProbe，要不然会因为探测不到端口重启
```

### 2. 将自己编的二进制文件、so，替换到容器里

由于 LoongCollector 容器挂载了主机目录，因此将需要替换掉文件放到主机目录上容器内就能访问。

```bash
# 将开发机上编译的so scp到container所在node上
scp libGoPluginBase.so <user>@<node>:/home/<user>
```

主机的根路径在 LoongCollector 容器中位于/logtail\_host，找到对应目录进行copy即可。

```bash
cp /logtail_host/home/<user>/libGoPluginBase.so /usr/local/loongcollector
```

## 常见问题

### 1. 更新代码后编译错误

如果发生编译错误，如

```shell
/src/core/common/CompressTools.cpp:18:23: fatal error: zstd/zstd.h: No such file or directory
 #include <zstd/zstd.h>
                       ^
compilation terminated.
```

请确保本地编译镜像为最新版本。可使用以下命令更新：

```shell
docker pull sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/loongcollector-community-edition/loongcollector-build-linux
```

若使用VS Code进行开发，请重新编译开发容器镜像。使用Shift + Command + P（Mac）或Ctrl + Shift + P（Win）打开命令面板，输入rebuild，选择`Dev-Containers: Rebuild Without Cache and Reopen in Container`。

### 2. 更新代码后无法编译C++代码

如果发生编译错误，如

```shell
CMake Error at ebpf/driver/CMakeLists.txt:22 (add_subdirectory):
  The source directory

    /workspaces/loongcollector/core/_thirdparty/coolbpf

  does not contain a CMakeLists.txt file.
```

这是因为新代码增加了coolbpf子模块依赖，请使用以下命令拉取：

```bash
git submodule update --init # 初始化并更新所有子模块
```

拉取后，重新执行cmake命令即可正常编译。
