ARTICLE DETAIL

资讯详情

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

Pulse 的 LoggerStore 全面解析:Core Data 存储引擎、日志写入与导出实战指南

Pulse 的 LoggerStore 全面解析:Core Data 存储引擎、日志写入与导出实战指南 开发工具可观测性【免费下载链接】PulseNetwork logger for Apple platforms项目地址https://gitcode.com/gh_mirrors/pul/Pulse点击查看免费下载LoggerStore是 Pulse 网络日志框架Network logger for Apple platforms的持久化存储核心它负责把日志消息、网络请求/响应与响应体blob写入以 SQLite 为底层、Core Data 为模型的本地存储中并提供查询、导出、清理与直接数据库访问能力。本文以 LoggerStore 的 DocC 扩展文档 为骨架结合 LoggerStore.swift、LoggerStoreConfiguration.swift 等源码完整讲解 LoggerStore 的初始化选项、存储与访问 API、导出流程、实体模型以及底层实现原理帮助你掌握在自有 App 中集成与二次开发 Pulse 存储层的完整方案。LoggerStore 是什么LoggerStore是 Pulse 中最核心的持久化组件源码注释将其定义为Persistently stores logs, network requests, and response blobsLoggerStore.swift 第 9 行。它是一个以 Swift Package 形式分发的类底层存储Core Data SQLitelogs.sqlite响应体等大块二进制数据存储在blobs/目录能力边界负责日志的写入、查询、导出、清理与销毁同时暴露 Core Data 容器供上层 UIPulseUI和开发者直接访问线程模型LoggerStore被标记为unchecked Sendable写入全部在后台backgroundContext中进行主线程通过viewContext读取事件机制内部通过PassthroughSubjectEvent, Never重传事件LoggerStore.swift 第 38 行RemoteLogger正是借此实现远程日志同步。从 DocC 索引文档LoggerStore-Extension.md可以看到其 API 被分为 Initializers、Storing Logs、Accessing Logs、Exporting Logs、Managing the Store、Direct Database Access、Core Data Entities 与 Nested Types 等若干主题本文即沿此脉络展开。初始化 LoggerStore构造函数签名与默认参数LoggerStore通过以下指定构造器初始化public init(storeURL: URL, options: Options [.create, .sweep], configuration: Configuration .init()) throws参数含义LoggerStore.swift 第 107-168 行参数说明storeURL指向一个目录package内部包含logs.sqlite数据库、blobs/二进制目录和manifest.json清单文件options存储打开选项默认[.create, .sweep]configuration存储配置默认Configuration()256 MB 大小上限等初始化流程中值得注意的几个行为目录校验如果未开启.create则要求storeURL目录必须已存在否则抛出LoggerStore.Error.fileDoesntExist版本迁移读取manifest.json后若发现内部版本与当前版本currentStoreVersion见下文不一致会直接移除旧存储目录重建源码注释说明对日志数据而言删除旧数据是最可靠安全的迁移方案会话自动创建当开启.create且未开启.readonly、且configuration.isAutoStartingSession为真时会自动写入当前Session实体LoggerStore.swift 第 183-189 行自动清扫调度开启.sweep且距离上次清扫超过sweepInterval默认 1 小时时会在 10 秒后于后台执行sweep()LoggerStore.swift 第 199-206 行。共享实例 sharedLoggerStore.shared是框架提供的默认单例LoggerStore.swift 第 70-103 行public static var shared: LoggerStore { get set }默认实现位于URL.logs.appending(directory: current.pulse)以[.create, .sweep]打开你可以替换它为自定义 store替换时会自动注册为RemoteLogger的默认存储若其尚未初始化它同时也是NetworkLogger、URLSessionProxy等组件的默认落库目标。Options 选项详解Options是一个OptionSetLoggerStoreConfiguration.swift 第 12-55 行选项位值作用与注意事项.create1 0目录不存在时创建 store中间目录必须已存在.sweep1 1达到大小上限时通过删除最旧的 message/blob 来压缩存储.synchronous1 2所有写入立即同步落盘会显著降低批量写入效率官方不推荐常规使用也不会加速远程日志.readonly1 3只读打开不执行清扫、禁止任何修改.inMemory1 4完全不写盘storeURL可以是任意路径甚至/dev/null.sweep仍生效.unsafe1 5关闭 SQLite 持久化特性WAL、fsync、共享锁换取原始写入速度面向一次性批量导入/模拟数据生成等场景中途崩溃可能导致存储损坏.unsafe在源码中会设置四条 pragmaLoggerStore.swift 第 251-260 行journal_modeOFF、synchronousOFF、temp_storeMEMORY、locking_modeEXCLUSIVE这正是牺牲崩溃安全换取吞吐的底层依据。Configuration 配置详解Configuration决定存储的大小上限、写入频率与数据保留策略LoggerStoreConfiguration.swift 第 57-115 行public struct Configuration { public var sizeLimit: Int64 // 默认 256 MB含数据库与 blobs public var saveInterval: DispatchTimeInterval // 默认 300ms消息落库的合并写入窗口 public var isStoringOnlyImageThumbnails: Bool // 默认 true网络响应图片仅存缩略图 public var imageThumbnailOptions ThumbnailOptions()// 缩略图参数 public var responseBodySizeLimit: Int 8 * 1048576 // 默认 8 MB请求/响应体上限 public var maxAge: TimeInterval 14 * 86400 // 默认两周过期消息自动删除 public var willHandleEvent: Sendable (Event) - Event? // 事件预处理钩子返回 nil 则忽略 }Configuration(sizeLimit:)是唯一对外公开的指定构造器默认256 * 1_000_000字节内部私有字段还包括blobSizeLimit大小上限的 70% 用于 blobs、trimRatio 0.7清扫时裁剪比例、sweepInterval 3600秒、isBlobCompressionEnabled trueblob 默认压缩存储、inlineLimit 1638416 KB 以下 blob 直接内联进数据库willHandleEvent是一个强大的脱敏入口你可以在事件落库前修改或丢弃它例如过滤敏感信息。注意configuration属性不是线程安全的官方警告需在应用启动、发送任何日志之前完成修改LoggerStore.swift 第 19-23 行。实际初始化示例// 使用默认共享实例绝大多数场景 let store LoggerStore.shared // 自定义 store let url try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) .appendingPathComponent(my-logs.pulse) var configuration LoggerStore.Configuration() configuration.sizeLimit 512 * 1_000_000 // 512 MB configuration.maxAge 30 * 86400 // 保留 30 天 let custom try LoggerStore(storeURL: url, options: [.create, .sweep], configuration: configuration)写入日志Storing Logs存储消息 storeMessagepublic func storeMessage( createdAt: Date? nil, label: String, level: Level, message: String, metadata: [String: MetadataValue]? nil, file: String #file, function: String #function, line: UInt #line )签名见 LoggerStore.swift 第 292-312 行createdAt缺省时取当前时间file/function/line默认自动捕获调用点其中文件名在落库时只保留lastPathComponentmetadata使用[String: MetadataValue]MetadataValue是枚举.string(String)或.stringConvertible(CustomStringConvertible)LoggerStoreLevel.swift 第 6-11 行内部通过unpack()转成[String: String]后以键值对编码存储Level与 SwiftLog 的Logger.Level兼容共 7 级trace(1) debug(2) info(3) notice(4) warning(5) error(6) critical(7)LoggerStoreLevel.swift 第 14-40 行。底层实现storeMessage构造Event.messageStored事件经handle(_:)处理后落入LoggerMessageEntityLoggerStore.swift 第 368-381 行并同步通过events发布给远程日志等观察者。存储网络请求 storeRequestpublic func storeRequest( _ request: URLRequest, response: URLResponse?, error: Swift.Error?, data: Data?, metrics: URLSessionTaskMetrics? nil, label: String? nil, taskDescription: String? nil )LoggerStore.swift 第 318-341 行一次性记录完整请求生命周期请求体取request.httpBody ?? request.httpBodyStreamData()响应体即data源码注释提醒如果需要增量更新任务进度、分阶段指标应使用NetworkLogger而非此方法——storeRequest是一次完成式的快捷 API落库时handle(.networkTaskCompleted(...))会创建/复用NetworkTaskEntity同步写入状态码、内容类型、请求/响应体 blob、URLSessionTaskMetrics转换后的交易记录与错误信息LoggerStore.swift 第 410-507 行。查询日志Accessing Logs查询消息与任务public func messages(sortDescriptors: [SortDescriptorLoggerMessageEntity] [SortDescriptor(\.createdAt, order: .forward)], predicate: NSPredicate? nil, context: NSManagedObjectContext? nil) throws - [LoggerMessageEntity] public func tasks(sortDescriptors: [SortDescriptorNetworkTaskEntity] [SortDescriptor(\.createdAt, order: .forward)], predicate: NSPredicate? nil, context: NSManagedObjectContext? nil) throws - [NetworkTaskEntity]LoggerStore.swift 第 779-804 行两个 API 默认按createdAt正序排列默认在viewContext上执行可通过context参数指定其他 context// 仅取普通日志消息排除与网络任务关联的技术性消息 let messages try store.messages(predicate: NSPredicate(format: task NULL)) // 取最近的网络请求任务 let tasks try store.tasks(sortDescriptors: [SortDescriptor(\.createdAt, order: .reverse)], predicate: NSPredicate(format: requestState %d, NetworkTaskEntity.State.success.rawValue))已废弃的旧 APIallMessages()与allTasks()已在 Pulse 5.1 中废弃LoggerStore.swift 第 806-816 行由messages(sortDescriptors:predicate:)/tasks(sortDescriptors:predicate:)取代新代码请勿再使用。导出与分享Exporting Logsexport(to:options:)public func export(to targetURL: URL, options: ExportOptions .init()) async throwsLoggerStore.swift 第 913-929 行生成的副本具有.pulse扩展名是一种归档格式PulseDocument将 SQLite 数据库与所有 blob 一并压缩进单个文档便于通过 AirDrop、邮件等方式分享给开发者用 Pulse 桌面工具/控制台查看目标目录必须已存在且目标文件已存在时抛出LoggerStore.Error.fileAlreadyExists归档内部结构databaseblob压缩后的 SQLite 副本、blobs批量插入每 100 个文件一组、infoblob存储Info元数据见 LoggerStore.swift 第 1013-1067 行。ExportOptionspublic struct ExportOptions { public var predicate: NSPredicate? // 导出满足条件的 LoggerMessageEntity public var sessions: SetUUID? // 仅导出指定会话 public init(predicate: NSPredicate? nil, sessions: SetUUID? nil) }LoggerStore.swift 第 900-911 行当指定筛选条件时导出会先在临时目录生成一份包格式副本剔除不匹配的会话与消息再打包为归档LoggerStore.swift 第 996-1011 行。注意导出副本会获得全新的storeId。let url FileManager.default.temporaryDirectory.appendingPathComponent(logs.pulse) try await store.export(to: url, options: ExportOptions(sessions: [sessionID]))管理存储Managing the Storeinfo()读取统计信息public func info() async throws - InfoLoggerStore.swift 第 1208-1236 行线程安全但严禁在backgroundContext队列内调用。返回的Info结构LoggerStoreInfo.swift 第 10-72 行包含标识storeId、storeVersion内部版本非框架版本时间creationDate、modifiedDate取自数据库文件属性统计messageCount已剔除与网络任务关联的技术消息、taskCount、blobCount、totalStoreSize、blobsSize、blobsDecompressedSizeblob 默认压缩存储因此解压后体积更大上下文appInfobundle id、名称、版本、build、base64 应用图标与deviceInfo设备名、型号、系统名称与版本平台差异见 LoggerStoreInfo.swift 第 107-168 行。removeAll / removeSessions / close / destroypublic func removeAll() // 删除全部消息、blob 与会话重建当前会话 public func removeSessions(withIDs: SetUUID)// 删除指定会话及其关联消息 public func close() throws // 安全关闭数据库移除 persistent store public func destroy() throws // 关闭并删除 store 目录全部数据之后不可再写入LoggerStore.swift 第 818-893 行removeAll通过NSBatchDeleteRequest批量删除三类实体后重建会话目录LoggerStore.swift 第 857-867 行destroy先destroyPersistentStore再删除整个storeURL。getBlobData(forKey:)public func getBlobData(forKey key: String) - Data?LoggerStore.swift 第 710-713 行按 blob 的 SHA1 十六进制 key 读取原始二进制数据。由于 blob 默认压缩存储此方法会自动解压源码注释提醒在个别场景如直接手改配置文件关闭压缩下可能失效最稳妥的方式是始终通过实体关系如task.responseBody?.data访问数据。直接数据库访问Direct Database AccessLoggerStore将 Core Data 容器与上下文直接暴露给调用方public let container: NSPersistentContainer // 底层容器 public var viewContext: NSManagedObjectContext // 主线程读上下文 public let backgroundContext: NSManagedObjectContext // 全部写操作的后台上下文 public func newBackgroundContext() - NSManagedObjectContext // 额外后台上下文LoggerStore.swift 第 29-36、226-232 行viewContext设置了automaticallyMergesChangesFromParent true与mergeByPropertyObjectTrump合并策略LoggerStore.swift 第 210-213 行因此后台写入会自动合并到主线程上下文所有写操作统一走backgroundContext配合默认 300ms 的saveInterval做合并写入setNeedsSave()调度、flush()落库LoggerStore.swift 第 725-765 行开启.synchronous则每次写入立即performAndWait 保存每个 context 的userInfo中都会挂载LoggerBlogDataStoreLoggerBlobHandleEntity.data依赖它才能解出 blob 数据LoggerStoreEntities.swift 第 335-340 行。// 在后台线程做只读查询 let context store.newBackgroundContext() let tasks try context.performAndWait { try store.tasks(sortDescriptors: [SortDescriptor(\.createdAt, order: .reverse)], context: context) }Core Data 实体模型DocC 文档列出的 7 个实体全部在 LoggerStoreEntities.swift 中定义其属性与关系在 LoggerStoreModel.swift 中程序化构建8 个实体除文档列出的 7 个外progress实体对应NetworkTaskProgressEntity实体职责关键属性LoggerSessionEntity会话id、createdAt、version、buildLoggerMessageEntity日志消息createdAt、level、text、label、file/function/line、rawMetadata、isPinned、sessiontask一对一关联NetworkTaskEntity网络任务taskId、taskType、url/host/path、httpMethod、statusCode、requestState、startDate/duration、isFromCache/isMocked、errorCode/errorDomain、requestBody/responseBody关联 blob等NetworkTaskProgressEntity任务进度completedUnitCount、totalUnitCount惰性创建NetworkRequestEntity请求详情url、httpMethod、httpHeaders、超时与缓存策略、网络访问选项等NetworkResponseEntity响应详情statusCode、httpHeadersNetworkTransactionMetricsEntity单次交易指标完整时间线fetch/domainLookup/connect/secure/request/response 各阶段起止、字节数、TLS 协议与套件、地址端口、网络条件标记等LoggerBlobHandleEntity请求/响应体句柄keySHA1、size、decompressedSize、inlineData、linkCount、contentType几个值得注意的实现细节Blob 的去重与内联策略storeBlob以 SHA1 为 key 去重重复数据仅递增linkCount16 KB 以内的小 blob 内联进数据库inlineData更大的写入blobs/目录按 key 命名文件LoggerStore.swift 第 643-684 行图片缩略图超过 15 KB 的图片响应体在isStoringOnlyImageThumbnails开启时会被压缩为最大 512 px、质量 0.5 的缩略图再存储LoggerStore.swift 第 509-522 行消息与任务合一每个网络任务都自动关联一条技术性LoggerMessageEntity默认label network、line字段复用为任务状态存储以节省空间因此messageCount需要减去taskCount才是纯日志消息数LoggerStore.swift 第 554-565 行。嵌套类型与辅助结构DocC 文档将以下嵌套类型列为独立主题均位于LoggerStore命名空间下ErrorfileDoesntExist、storeInvalid、unsupportedVersion(version:minimumSupportedVersion:)、fileAlreadyExists、unknownErrorLoggerStore.swift 第 1265-1281 行EventmessageStored、networkTaskCreated、networkTaskProgressUpdated、networkTaskCompleted四类全部Codable Sendable用于 store 间数据同步与远程日志LoggerStoreEvent.swiftInfo见上文info()一节Level / MetadataValue / Metadata日志级别与元数据值类型SessionSession(id:startDate:)结构Session.current即当前会话LoggerSession.swiftVersion语义化版本号Version(major:minor:patch)可Codable、Comparable、LosslessStringConvertible解析失败的字符串返回 nilLoggerStoreVersion.swift。内部版本常量LoggerStore.swift 第 1307-1311 行minimumSupportedVersion 3.1.0、currentStoreVersion 3.7.0、currentProtocolVersion 4.0.0——它们决定了旧 store 的迁移与远程日志协议版本。存储布局与数据流总览一个.pulsestore 目录包含三个组成部分常量定义于 LoggerStore.swift 第 1315-1318 行my-logs.pulse/ ├── logs.sqlite # Core Data 数据库实体、内联 blob ├── manifest.json # 清单storeId、内部版本、上次清扫时间 └── blobs/ # 大于 16 KB 的二进制响应/请求体以 SHA1 命名完整数据流写入侧调用方NetworkLogger、storeMessage等构造Eventhandle(_:)先经configuration.willHandleEvent过滤再调度到backgroundContext执行_handleLoggerStore.swift 第 344-366 行同时通过events发布事件供 RemoteLogger 转发实体在backgroundContext中创建/更新默认延迟 300ms 合并保存大 blob 写入blobs/目录小 blob 内联图片自动生成缩略图达到sizeLimit/maxAge时由sweep()在后台裁剪LoggerStore.swift 第 1091-1099 行。结语LoggerStore以Core Data 实体 SQLite 数据库 blobs 文件目录 manifest 清单的复合结构为 Pulse 提供了高性能、可压缩、可导出、可清理的日志持久化方案。理解它的初始化选项、写入/查询 API、导出能力与底层实体模型是你在自有 App 中嵌入 Pulse、定制存储策略大小上限、保留周期、图片缩略图、事件脱敏以及对接远程日志功能的前提。需要进一步深入时可以继续阅读NetworkLogger.swift增量式记录网络请求的完整生命周期RemoteLogger.swift基于Event的远程日志同步MockStore.swift使用Options.unsafe进行批量模拟数据写入的参考实现。赞分享开发工具可观测性【免费下载链接】PulseNetwork logger for Apple platforms项目地址https://gitcode.com/gh_mirrors/pul/Pulse点击查看免费下载相关推荐Pulse 进阶配置指南LoggerStore 存储调优、日志导出与网络调试Pulse 进阶配置指南LoggerStore 存储调优、日志导出与网络调试 本文是 Pulse 官方文档 NextSteps https://link.gi开发工具可观测性highlight.io 日志存储引擎剖析基于 ClickHouse 的结构化日志注入与键值检索实战highlight.io 日志存储引擎剖析基于 ClickHouse 的结构化日志注入与键值检索实战 本指南围绕 highlight.io 开源全栈监控平台在可观测性后端5个理由告诉你为什么Montserrat字体是现代设计的完美选择5个理由告诉你为什么Montserrat字体是现代设计的完美选择 Montserrat字体是一款完全免费开源的几何无衬线字体家族以其优雅的现代设计和丰富的字重设计系统上一篇告别手动刷歌网易云音乐自动打卡工具让你轻松冲击LV10等级下一篇5分钟快速上手TrollInstallerXiOS设备终极安装指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表