ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Fiddler自动保存抓包数据:脚本实现与性能优化指南

Fiddler自动保存抓包数据:脚本实现与性能优化指南 1. 项目缘起为什么需要自动保存抓包内容做网络调试、接口测试或者安全分析的朋友对Fiddler这款工具肯定不陌生。它就像是我们窥探网络世界的一扇窗HTTP/HTTPS的请求响应在它面前一览无余。但不知道你有没有遇到过这样的场景你在复现一个偶发的线上bug或者在进行一个长时间的稳定性测试Fiddler的会话列表Session List里瞬间就积累了成百上千条记录。这时候一个手滑点了“清除所有会话”Remove All或者Fiddler本身因为内存占用过高而崩溃之前辛辛苦苦抓到的、可能包含关键问题线索的所有数据瞬间就灰飞烟灭了。那种感觉就像写了几千字的文档没保存一样让人抓狂。更常见的情况是我们需要把抓包数据留存下来用于后续的分析、报告或者给其他同事共享排查。每次都手动去点“File - Save - All Sessions...”不仅麻烦而且在你专注于测试过程时很容易忘记。这就是“Fiddler自动保存抓包内容到文件”这个需求最直接的来源。它不是一个炫技的功能而是一个实实在在能提升工作效率、保障工作成果的“保险丝”。通过脚本让Fiddler在后台默默地将捕获到的会话按照我们设定的规则比如时间、数量、关键词自动保存到本地文件让我们可以心无旁骛地进行测试事后也能有据可查。这个功能的核心价值在于“自动化”和“持久化”。自动化解放了我们的双手和注意力避免了人为疏忽持久化则将内存中易失的会话数据固化到硬盘上形成了可追溯、可分析的日志文件。无论是用于故障复盘、性能分析还是安全审计这些自动保存的.saz文件Fiddler的会话存档格式或文本日志都是宝贵的第一手资料。2. 实现自动保存的两种核心路径规则引擎与脚本扩展Fiddler本身并没有在界面上提供一个直接的“自动保存”按钮但这恰恰体现了它强大的可扩展性。实现自动保存主要有两种思路它们各有优劣适用于不同的场景。2.1 路径一利用Fiddler内置的“自动响应器”与“规则”做简易定时保存这是最简单、最“图形化”的方法不需要写代码适合轻度需求或临时使用。其核心思路是利用Fiddler的“自动响应器”AutoResponder或“自定义规则”Customize Rules的定时触发机制模拟手动保存的动作。具体操作步骤如下理解机制Fiddler的“规则”Rules菜单下有一个“性能”Performance子项里面可以模拟各种网络条件。更重要的是在“自定义规则”Customize Rules的脚本中我们可以访问到FiddlerApplication对象进而调用其UI命令。Fiddler的UI操作背后大多对应着可被脚本调用的命令。定位保存命令通过Fiddler的“帮助”Help菜单选择“检查Fiddler元素”Inspect Fiddler UI可以打开一个开发者工具窗口。在这里我们可以点击界面上的“保存”按钮查看其触发的命令。通常保存所有会话的命令是exec:saveall。编写定时规则脚本打开Fiddler Script编辑器Rules Customize Rules找到OnBoot()或Main()函数。我们可以利用.NET的Timer类来创建一个定时器。例如我们希望每捕获到100个会话就自动保存一次可以这样写这是一个概念示例需调整// 在OnBoot函数内添加 static System.Threading.Timer _saveTimer; static int sessionCount 0; static int saveThreshold 100; static function OnBoot() { // ... 其他初始化代码 ... // 监听会话增加事件 FiddlerApplication.AfterSessionComplete function(session: Session) { sessionCount; if (sessionCount saveThreshold) { SaveSessions(); sessionCount 0; // 重置计数 } }; } static function SaveSessions() { // 构建一个带时间戳的文件名避免覆盖 var fileName “C:\\FiddlerCaptures\\AutoSave_” System.DateTime.Now.ToString(“yyyyMMdd_HHmmss”) “.saz”; // 确保目录存在 System.IO.Directory.CreateDirectory(“C:\\FiddlerCaptures”); // 执行保存命令。注意直接调用UI命令可能不稳定更推荐使用SAZProvider // FiddlerApplication.DoNotifyUser(“正在自动保存会话...”, “Info”); // 更可靠的方式使用SAZProvider保存当前会话列表 var oFormat FiddlerApplication.UI.GetFormat(“SAZ”); if (oFormat ! null) { var sessions FiddlerApplication.UI.GetAllSessions(); oFormat.SaveSessions(sessions, fileName, null); } }这种方法的优缺点非常明显优点无需安装额外插件利用现有功能组合实现学习成本低。缺点功能粗糙难以实现复杂的保存逻辑如按域名过滤、只保存错误请求等。稳定性存疑频繁调用UI命令或在事件中执行文件IO操作在会话量极大时可能影响Fiddler性能甚至导致卡顿、崩溃。灵活性差无法方便地定制保存格式如纯JSON、CSV。因此这种方法更适合对保存逻辑要求不高、抓包会话量不大的临时性任务。2.2 路径二使用FiddlerScriptJScript.NET编写高级保存逻辑这是真正强大和推荐的方式。FiddlerScript基于JScript.NET一种类似JavaScript的.NET语言允许我们深度介入Fiddler的生命周期和数据处理过程。我们可以编写脚本来响应几乎所有的Fiddler事件从而实现高度定制化的自动保存。核心事件与对象FiddlerApplication.AfterSessionComplete在一个会话请求响应完全结束后触发。这是最常用的钩子我们可以在这里检查会话的所有属性URL、状态码、响应体等。Session对象代表一个完整的会话包含了oRequest请求对象、oResponse响应对象、url、responseCode等所有信息。SAZProvider/ 其他格式提供者用于将会话列表保存为.saz或其他格式。一个基础的自动保存脚本框架如下import System; import System.IO; import Fiddler; class Handlers { // 定义保存路径和触发条件 static var sAutoSavePath: String “C:\\FiddlerLogs”; static var sSaveByCount: int 50; // 每50个会话保存一次 static var sSaveByTime: int 300000; // 每5分钟300秒保存一次单位毫秒 static var sTimer: System.Threading.Timer null; static var iSessionCounter: int 0; static var oSessionsToSave: Session[] []; static function OnBoot() { // 创建保存目录 if (!Directory.Exists(sAutoSavePath)) { Directory.CreateDirectory(sAutoSavePath); } // 绑定会话完成事件 FiddlerApplication.AfterSessionComplete OnAfterSessionComplete; // 初始化定时器如果启用按时间保存 if (sSaveByTime 0) { var autoSaveCallback new System.Threading.TimerCallback(OnTimedSave); sTimer new System.Threading.Timer(autoSaveCallback, null, sSaveByTime, sSaveByTime); } FiddlerApplication.Log.LogString(“FiddlerScript: 自动保存模块已加载。”); } static function OnAttach() {} static function OnDetach() {} static function OnAfterSessionComplete(oS: Session) { // 在这里实现你的过滤逻辑。例如只保存特定域名或状态码的会话 // if (oS.hostname.Contains(“api.target.com”) oS.responseCode 400) { iSessionCounter; oSessionsToSave.Add(oS); // 将符合条件的会话加入列表 if (iSessionCounter sSaveByCount) { SaveSessionsNow(); } // } } static function OnTimedSave(state: Object) { if (oSessionsToSave.Count 0) { SaveSessionsNow(); } } static function SaveSessionsNow() { if (oSessionsToSave.Count 0) return; try { var timestamp DateTime.Now.ToString(“yyyyMMdd_HHmmss”); var fileName Path.Combine(sAutoSavePath, “AutoSave_” timestamp “.saz”); // 使用SAZ格式保存 var sazFormat: ISessionExporter FiddlerApplication.UI.GetFormat(“SAZ”) as ISessionExporter; if (sazFormat ! null) { sazFormat.SaveSessions(oSessionsToSave.ToArray(), fileName, null); FiddlerApplication.Log.LogString(String.Format(“已自动保存 {0} 个会话到: {1}”, oSessionsToSave.Count, fileName)); } else { FiddlerApplication.Log.LogString(“错误无法获取SAZ格式导出器。”); } // 保存后清空列表和计数器 oSessionsToSave.Clear(); iSessionCounter 0; } catch (e: Exception) { FiddlerApplication.Log.LogString(“自动保存时发生错误: ” e.Message); } } }这个框架提供了按会话数量、按时间间隔两种触发方式并且将待保存的会话暂存到列表批量写入减少了频繁IO操作对性能的影响。你可以轻松地在OnAfterSessionComplete函数中添加过滤条件实现精准保存。3. 高级定制过滤、分片与格式转换掌握了基础脚本后我们可以玩出更多花样让自动保存功能更智能、更贴合项目需求。3.1 实现基于内容的智能过滤保存无差别的全量保存会产生大量冗余数据。我们通常只关心特定类型的流量。在OnAfterSessionComplete事件中Session对象提供了丰富的属性供我们过滤按域名/主机名过滤if (oS.hostname.Contains(“payment.example.com”)) { ... }按URL路径过滤if (oS.PathAndQuery.StartsWith(“/api/v1/order”)) { ... }按HTTP方法过滤if (oS.RequestMethod “POST”) { ... }按状态码过滤if (oS.responseCode 500 || oS.responseCode 404) { ... }只保存错误请求按请求/响应内容过滤这需要获取Body内容性能开销稍大但更精准。// 检查请求体是否包含特定关键字例如只保存执行“下单”操作的请求 if (oS.oRequest.headers.HTTPMethod “POST”) { var requestBody oS.GetRequestBodyAsString(); if (requestBody.Contains(“actioncreateOrder”)) { // 保存此会话 } } // 检查响应体是否包含特定错误信息 var responseBody oS.GetResponseBodyAsString(); if (responseBody.Contains(“Internal Server Error”)) { // 保存此会话 }注意频繁调用GetRequestBodyAsString()和GetResponseBodyAsString()会显著增加内存和CPU开销因为Fiddler需要解码和存储这些数据。在会话量巨大的情况下应谨慎使用或考虑在确认需要保存时才调用。3.2 文件分片与归档策略如果长时间运行自动保存会产生大量文件。好的归档策略能方便后续查找。按时间分片这是最常用的策略。除了在文件名中加入时间戳还可以按日期创建文件夹。static function GetDailySavePath() { var dateStr DateTime.Now.ToString(“yyyy-MM-dd”); var dailyPath Path.Combine(sAutoSavePath, dateStr); if (!Directory.Exists(dailyPath)) Directory.CreateDirectory(dailyPath); return dailyPath; } // 在SaveSessionsNow中使用 var dailyPath GetDailySavePath(); var fileName Path.Combine(dailyPath, String.Format(“Capture_{0}.saz”, DateTime.Now.ToString(“HHmmss”)));按项目/域名分片为不同项目或域名创建不同的根目录。static function GetProjectSavePath(hostname: String) { String projectFolder; if (hostname.Contains(“api.projectA.com”)) projectFolder “ProjectA”; else if (hostname.Contains(“test.projectB.com”)) projectFolder “ProjectB”; else projectFolder “Others”; var projPath Path.Combine(sAutoSavePath, projectFolder, DateTime.Now.ToString(“yyyy-MM-dd”)); if (!Directory.Exists(projPath)) Directory.CreateDirectory(projPath); return projPath; }文件滚动与清理可以编写额外的逻辑定期删除超过一定天数的旧文件防止磁盘被占满。3.3 超越SAZ保存为其他格式.saz是Fiddler的专有格式虽然方便在Fiddler中重新打开但不利于用其他工具如文本编辑器、Python Pandas、ELK进行分析。我们可以在保存时直接转换格式。保存为纯文本日志TXT/JSON:static function SaveSessionsAsText(sessions: Session[], filePath: String) { using (var writer new StreamWriter(filePath, true)) { // true表示追加模式 for (var s: Session in sessions) { writer.WriteLine(“ Session Start [{0}] ”, DateTime.Now); writer.WriteLine(“URL: ” s.fullUrl); writer.WriteLine(“Method: ” s.RequestMethod); writer.WriteLine(“Status: ” s.responseCode ” ” s.responseStatus); writer.WriteLine(“Request Headers:\n” s.oRequest.headers.ToString()); // 谨慎记录Body可能很大或包含二进制 if (s.RequestBodyLength 10240) { // 例如只记录小于10KB的请求体 writer.WriteLine(“Request Body:\n” s.GetRequestBodyAsString()); } writer.WriteLine(“Response Headers:\n” s.oResponse.headers.ToString()); if (s.ResponseBodyLength 10240) { writer.WriteLine(“Response Body:\n” s.GetResponseBodyAsString()); } writer.WriteLine(“ Session End \n”); } } }保存为JSON Lines便于程序解析:static function SaveSessionsAsJsonl(sessions: Session[], filePath: String) { using (var writer new StreamWriter(filePath, true)) { for (var s: Session in sessions) { var sessionObj { “timestamp”: s.Timers.ClientBeginRequest.ToString(“o”), “url”: s.fullUrl, “method”: s.RequestMethod, “status”: s.responseCode, “request_headers”: s.oRequest.headers.ToDictionary(), // 需实现转换方法 “response_headers”: s.oResponse.headers.ToDictionary(), // “request_body”: s.GetRequestBodyAsString(), // 可选 // “response_body”: s.GetResponseBodyAsString() // 可选 }; writer.WriteLine(JsonConvert.SerializeObject(sessionObj)); // 需要引用Newtonsoft.Json等库 } } }提示在FiddlerScript中直接使用复杂的JSON序列化库可能比较麻烦。一个更简单的方法是调用.NET的JavaScriptSerializerSystem.Web.Script.Serialization但需要注意Fiddler的脚本环境支持哪些程序集。对于轻量级需求手动拼接JSON字符串也是一个选择。4. 实战部署、调试与性能优化脚本写好了如何让它稳定可靠地在你的Fiddler中运行呢4.1 脚本的安装与加载编辑CustomRules.jsx最标准的方式是直接修改CustomRules.jsx文件。该文件通常位于C:\Users\[你的用户名]\Documents\Fiddler2\Scripts\CustomRules.jsx。将你的自动保存脚本代码整合到该文件的Handlers类中。使用Fiddler ScriptEditor在Fiddler中点击菜单栏的Rules Customize Rules...会打开ScriptEditor。你可以在这里直接编辑并保存CtrlS。保存后Fiddler会自动重新编译并加载脚本通常会弹出一个提示框显示编译结果成功或错误信息。模块化加载高级如果你有多个独立的功能脚本不希望全部混在CustomRules.jsx中可以编写独立的.jsx文件然后在CustomRules.jsx的OnBoot函数中使用FiddlerObject.eval()来加载它们。但这需要更仔细地处理作用域和依赖。4.2 调试你的FiddlerScript调试脚本是开发过程中必不可少的一环。使用Fiddler日志FiddlerApplication.Log.LogString(“你的调试信息”)是最基本的调试手段。输出的信息可以在Fiddler主界面的Log标签页中看到。这是检查脚本是否执行、变量值是否正确的首选方法。使用系统对话框谨慎MessageBox.Show(“变量值: ” someVar);可以弹窗显示信息但会阻塞Fiddler主线程在事件处理函数中弹出可能导致Fiddler无响应仅建议在脚本初始化等非关键路径临时使用。异常处理务必用try...catch包裹你的核心保存逻辑并在catch块中将异常信息写入日志。这能防止因单个会话处理出错导致整个脚本停止工作。static function SaveSessionsNow() { try { // ... 你的保存代码 ... } catch (e: Exception) { FiddlerApplication.Log.LogString(“[ERROR] SaveSessionsNow failed: ” e.Message “\nStackTrace: ” e.StackTrace); } }检查文件输出最直接的验证方式就是去看目标目录下是否有文件生成以及文件内容是否符合预期。4.3 性能考量与避坑指南自动保存脚本如果设计不当会成为Fiddler性能的瓶颈。避免在事件处理中执行耗时操作AfterSessionComplete事件触发非常频繁。如果你在这个事件里直接进行复杂的字符串处理、JSON序列化或文件写入会严重拖慢Fiddler捕获会话的速度甚至导致丢包。正确的做法是像我们框架里那样只做简单的过滤和计数将符合条件的会话添加到一个List中。批量写入减少IO次数文件IO操作尤其是写小文件是相对耗时的。不要每抓到一个会话就写一次文件。应该积累一定数量如50、100个或等待定时器触发一次性批量写入。这能极大提升效率。谨慎处理请求/响应体调用GetRequestBodyAsString()和GetResponseBodyAsString()会迫使Fiddler对可能已经压缩或编码的Body进行解码和存储消耗大量内存和CPU。除非必要不要在过滤逻辑中频繁使用。如果必须使用考虑设置大小限制只处理小体积的Body。注意内存泄漏如果你在脚本中创建了全局的集合如oSessionsToSave来存储会话对象要确保在保存后及时清空.Clear()。因为Session对象本身可能持有大量数据如Body长时间不释放会导致Fiddler内存占用持续增长。更好的做法是只存储会话的ID或关键信息需要保存时再从Fiddler的全局会话列表中根据ID获取但这更复杂。磁盘空间监控自动保存脚本如果不加限制会持续产生文件。建议在脚本中加入简单的磁盘空间检查逻辑当剩余空间低于某个阈值时停止保存或发出警告通过日志。脚本冲突如果你安装了其他Fiddler插件或扩展它们也可能修改CustomRules.jsx或监听相同的事件。要留意可能的冲突确保你的脚本逻辑是健壮的不会因为其他代码的干扰而失效。最后一个实用的建议是为你的自动保存脚本增加一个“开关”配置。比如在OnBoot里读取一个外部配置文件或者通过判断某个特定的“魔术请求”比如你访问一个特定的URL来动态启用或禁用保存功能。这样在不需要保存的时候比如日常浏览可以关闭它以提升性能在需要的时候再灵活开启。
返回列表