ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

在 Fluent Bit 中编写 Rust + C 混合 WASM 过滤器:filter_rust_clib 示例完整实战指南

2026/9/16 19:36:36 拓冰建站 浏览量
在 Fluent Bit 中编写 Rust + C 混合 WASM 过滤器:filter_rust_clib 示例完整实战指南 在 Fluent Bit 中编写 Rust C 混合 WASM 过滤器filter_rust_clib 示例完整实战指南【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit导读本指南以 Fluent Bit 官方示例filter_rust_clib为核心讲解如何在 Fluent Bit 中构建并运行一个以 Rust 编写核心逻辑、以 C 导出 WASI 入口的 WASM 过滤器Rust 侧负责 JSON 解析与字段重组C 侧负责把 Fluent Bit 传入的参数适配为 Rust 的 C ABI 调用最终编译为单文件.wasm通过filter_wasm插件挂载进 Fluent Bit 数据管道。读完本文你将掌握完整的环境准备、构建命令、配置参数含义以及从源码层面理解 WASM 过滤器与 Fluent Bit 的调用契约。示例源码位于 examples/filter_rust_clib/与本示例配套的纯 C 版本见 examples/filter_wasm_c/更精简的纯 Rust 版本见 examples/filter_rust/。示例定位Rust 写逻辑、C 做桥接的 WASI 过滤器filter_rust_clib在官方示例中承担一个独特角色它演示了Rust 与 C 混编的 WASM 过滤器形态。README 中明确说明该源码树提供的正是一个以 Rust 为主编写的、运行在 WASI 模式下的 WASM 程序见 README 首段。整个示例只有 4 个文件分工清晰文件作用src/lib.rsRust 侧核心过滤器解析传入的 JSON 记录、附加时间与 tag 信息后重新输出 JSONrust_clib_filter.cC 侧桥接函数把char*风格参数换算为长度后转调 Rust 导出函数Cargo.tomlRust crate 配置声明cdylib/staticlib/rlib三种库形态Makefile一键构建脚本Rust 编译 → cbindgen 生成头文件 → WASI SDK 的 clang 链接出.wasm这种Rust 核心 C 壳的结构使 Rust 开发者可以完全使用自己熟悉的 crate 生态本例用到了serde_json与chrono同时又通过 C 层保持与 Fluent Bit WASI 运行时 ABI 的稳定兼容。环境准备编译器、工具链与 WASI SDK需要安装的工具工具用途版本要求Rust / rustc编译 Rust 过滤器README 注明使用 rustc 1.61.0fe5b13d68 2022-05-18rustup管理 Rust 工具链与 target用于添加wasm32-unknown-unknown目标cbindgen由 Rust crate 自动生成 C 头文件用于声明 Rust 导出的 C 风格函数WASI SDK提供 WASI 版 clang负责最终链接生成.wasm示例使用 WASI SDK 14Ubuntu 下安装 WASI SDKREADME 给出了基于wget的安装方式把 SDK 释放到/opt/wasi-sdk$ export WASI_VERSION14 $ export WASI_VERSION_FULL${WASI_VERSION}.0 $ wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION_FULL}-linux.tar.gz $ sudo mkdir -p /opt/wasi-sdk/ $ sudo tar xvf wasi-sdk-${WASI_VERSION_FULL}-linux.tar.gz --strip-components1 -C /opt/wasi-sdk--strip-components1的作用是把压缩包根目录剥掉一层让bin/clang、share等直接落在/opt/wasi-sdk下与 Makefile 中WASI_SDK_ROOT ? /opt/wasi-sdk的默认值保持一致。如果你安装在其他目录通过WASI_SDK_ROOT/your/path make build覆盖即可。构建步骤从 Rust 源码到单文件 wasm第一步添加 wasm 编译目标Rust 默认不包含 WebAssembly 后端需要用 rustup 显式添加$ rustup target add wasm32-unknown-unknown第二步安装 cbindgencbindgen用于从 Rust 侧导出符号自动生成 C 头文件filter_rust_clib.h$ cargo install --force cbindgen第三步执行 make build$ make build构建完成后当前目录会生成最终产物$ ls *.wasm rust_clib_filter.wasmMakefile 背后的三步流水线从 Makefile 可以看到build目标实际串联了两个子目标rustclib: cargo build --target wasm32-unknown-unknown --release build: wasm rustclib wasm: rustclib rust_clib_filter.h ${WASI_SDK_ROOT}/bin/clang -O3 -nostdlib \ -z stack-size${STACK_SIZE} -Wl,--initial-memory${INITIAL_MEMORY_SIZE} \ -o rust_clib_filter.wasm rust_clib_filter.c \ -L ./target/wasm32-unknown-unknown/release -lfilter_rust_clib \ -Wl,--export__heap_base -Wl,--export__data_end -Wl,--exportrust_clib_filter \ -Wl,--no-entry -Wl,--strip-all -Wl,--allow-undefined rust_clib_filter.h: cbindgen --crate filter_rust_clib --output filter_rust_clib.h --lang crustclibcargo build --release --target wasm32-unknown-unknown把 src/lib.rs 编译为target/wasm32-unknown-unknown/release/libfilter_rust_clib.a静态库形态由 Cargo.toml 中的crate-type [cdylib, staticlib, rlib]决定。rust_clib_filter.hcbindgen --crate filter_rust_clib --output filter_rust_clib.h --lang c根据 Rust 源码中#[no_mangle] pub extern C的导出函数生成 C 声明。wasm用 WASI SDK 的clang -O3 -nostdlib编译 rust_clib_filter.c再-L ... -lfilter_rust_clib链接 Rust 静态库。注意-Wl,--exportrust_clib_filter显式导出过滤器入口-Wl,--no-entry表示无 main 入口、-Wl,--allow-undefined容忍未定义符号由 WASI 运行时补齐、-Wl,--strip-all缩减产物体积并设置了STACK_SIZE默认 8192与INITIAL_MEMORY_SIZE默认 65536两个内存参数。源码剖析Rust 过滤器与 C 桥接层Rust 侧rust_filter导出函数核心逻辑位于 src/lib.rs 的rust_filter#[no_mangle] pub extern C fn rust_filter(tag: *const c_char, tag_len: u32, time_sec: u32, time_nsec: u32, record: *const c_char, record_len: u32) - *const u8 { let slice_tag: [u8] unsafe { slice::from_raw_parts(tag as *const u8, tag_len as usize) }; let slice_record: [u8] unsafe { slice::from_raw_parts(record as *const u8, record_len as usize) }; // ... let v: Value serde_json::from_slice(slice_record).unwrap(); let dt Utc.timestamp(time_sec as i64, time_nsec); let time dt.format(%Y-%m-%dT%H:%M:%S.%9f %z).to_string(); let message json!({ message: v[message], time: format!({}, time), tag: vtag, original: v.to_string(), lang: Rust, }); let buf: String message.to_string(); buf.as_ptr() }几个关键点#[no_mangle] pub extern C保证符号名不被修饰且按 C ABI 传参这是 WASI 运行时能够定位并调用它的前提函数签名携带tag、tag_len、time_sec、time_nsec、record、record_len六个参数与 Fluent Bit 传入记录的构成tag、时间戳、msgpack/JSON 载荷一一对应内部用serde_json把记录解析为Value再用chrono把秒/纳秒时间戳格式化为%Y-%m-%dT%H:%M:%S.%9f %z输出一个重组后的 JSON 对象保留原message字段、追加格式化time、来源tag、完整original原文与标记lang: Rust返回buf.as_ptr()——这里必须理解 WASM 线性内存模型Rust 侧分配字符串后返回的只是 WASM 堆上的指针Fluent Bit 侧会依据返回指针在共享线性内存中读取结果因此返回指针前字符串必须仍存活buf在该函数返回后由运行时读取示例以简化方式演示了这一契约。C 侧rust_clib_filter桥接rust_clib_filter.c 提供了一个薄封装把 指针 长度 的调用换算成 C 字符串再转交 Rust#include filter_rust_clib.h char* rust_clib_filter(char* tag, int len, uint32_t sec, uint32_t nsec, char* record, int record_len) { return (char *)rust_filter(tag, strlen(tag), sec, nsec, record, strlen(record)); }它包含filter_rust_clib.h——正是构建流程中由 cbindgen 自动生成的头文件从而保证 C 侧对 Rust 函数签名的声明与 Rust 定义严格一致。这个 C 层同时也是-Wl,--exportrust_clib_filter导出到.wasm的最终入口对应 Fluent Bit 配置中的Function_Name。在 Fluent Bit 中运行WASI 集成验证构建出rust_clib_filter.wasm后按 README 提供的方式编写如下 Fluent Bit 配置进行验证[SERVICE] Flush 1 Daemon Off Log_Level info HTTP_Server Off HTTP_Listen 0.0.0.0 HTTP_Port 2020 [INPUT] Name dummy Tag dummy.local [FILTER] Name wasm match dummy.* WASM_Path /path/to/rust_clib_filter.wasm Function_Name rust_clib_filter accessible_paths .,/path/to/fluent-bit [OUTPUT] Name stdout Match *配置链路为dummy输入插件按Flush 1每秒生成一条带dummy.localtag 的测试记录 →wasm过滤器匹配dummy.*并把记录送入rust_clib_filter.wasm→stdout输出插件打印处理结果。启动后可在终端看到每条记录都被重写为包含message、time、tag、original、lang: Rust字段的 JSON从而验证 WASI 集成与 Rust 过滤器确实生效。关键配置参数详解下表结合 filter_wasm.c 的config_map第 454 行起逐项说明本示例涉及的参数参数示例取值含义与默认值WASM_Path/path/to/rust_clib_filter.wasm待执行的.wasm文件路径必填插件在cb_wasm_pre_run阶段会用access(path, R_OK)校验可读性Function_Namerust_clib_filter.wasm中要调用的导出函数名必填对应 C 入口rust_clib_filteraccessible_paths.,/path/to/fluent-bitWASM 程序可访问的目录列表逗号分隔默认值是当前工作目录.event_formatjson默认/msgpack传给 WASM 程序的事件编码格式本示例按默认 JSON 路径工作cb_wasm_filter中通过flb_msgpack_to_json_str将 msgpack 记录转为 JSON 再调用 WASMwasm_heap_size可选默认8192WASM 运行时堆大小KB仅当配置值大于默认值时才覆盖wasm_stack_size可选默认8192WASM 运行时栈大小KB覆盖逻辑同上从源码看调用链路filter_wasm.c 揭示了过滤器与 WASM 程序的实际交互流程初始化cb_wasm_init读取配置通过flb_wasm_instantiate把.wasm实例化一次并持久保存在过滤器上下文中后续每条记录复用同一实例逐记录处理cb_wasm_filter先用flb_log_event_decoder解码输入事件在 JSON 模式下将事件 body 经flb_msgpack_to_json_str编码为 JSON 字符串连同tag、时间戳一起交给flb_wasm_call_function_format_json执行 WASM 函数结果回填WASM 返回的 JSON 字符串再经flb_pack_json转回 msgpack通过flb_log_event_encoder_set_body_from_raw_msgpack写回事件体若返回值为空或 JSON 非法则跳过该记录FLB_FILTER_NOTOUCH。这套msgpack 解码 → JSON 入 WASM → JSON 出 WASM → msgpack 回填的契约正是filter_rust_clib中 Rust 函数接收字符串、返回字符串的原因——在默认event_format json下Rust 过滤器无需感知 msgpack专注处理 JSON 即可。常见问题与排查建议cannot access wasm program报错WASM_Path指向的文件不可读检查路径与权限插件在cb_wasm_pre_run中执行access(R_OK)校验见 filter_wasm.c 第 334-339 行。failed to instantiate wasm program.wasm文件缺失导出符号或格式不兼容确认make build成功且Function_Name与-Wl,--export导出的名称一致。记录被跳过无输出WASM 返回空字符串或非法 JSON 时插件会跳过记录可用Log_Level debug观察filter_wasm的调试日志定位。清理构建产物make clean会删除*.wasm、*.h并执行cargo clean。小结filter_rust_clib完整演示了一条Rust 核心逻辑 C ABI 桥接 WASI SDK 链接 filter_wasm 插件挂载的过滤器开发路径Rust 侧借助serde_json、chrono等成熟库实现记录改写C 侧保证与 Fluent Bit 调用契约的稳定对接最终以单文件.wasm交付、由accessible_paths控制文件系统访问边界。在此基础上你可以参考 examples/filter_wasm_c/ 对比纯 C 实现或参考 examples/filter_rust/ 与 examples/filter_rust_msgpack/ 探索更纯粹的 Rust 写法与 msgpack 模式从而根据团队技术栈选择最合适的 WASM 过滤器开发方式。【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考