Skip to content

Commit

Permalink
feat: Add extension for FileManager to track file I/O operations with…
Browse files Browse the repository at this point in the history
… Sentry
  • Loading branch information
philprime committed Feb 18, 2025
1 parent b1e4874 commit 4182d91
Show file tree
Hide file tree
Showing 5 changed files with 1,192 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
### Improvements

- Add experimental flag to disable swizzling of `NSData` individually (#4859)
### Features

- Add extension for `FileManager` to track file I/O operations with Sentry (#4862)

## 8.45.0

Expand Down
20 changes: 20 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,9 @@
D42E48572D48DF1600D251BC /* SentryBuildAppStartSpansTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D42E48562D48DF1600D251BC /* SentryBuildAppStartSpansTests.swift */; };
D43647EF2D5CF9E3001468E0 /* SentrySpanKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43647EE2D5CF9DC001468E0 /* SentrySpanKey.swift */; };
D43647F12D5CFB71001468E0 /* SentrySpanKeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43647F02D5CFB71001468E0 /* SentrySpanKeyTests.swift */; };
D43647AB2D5CAA32001468E0 /* FileManager+SentryTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43647AA2D5CAA32001468E0 /* FileManager+SentryTracing.swift */; };
D43647F32D5CFBC7001468E0 /* FileManagerTracingIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43647F22D5CFBC2001468E0 /* FileManagerTracingIntegrationTests.swift */; };
D468C0622D3669A200964230 /* SentryFileIOTracker+SwiftHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D468C0612D3669A200964230 /* SentryFileIOTracker+SwiftHelpers.swift */; };
D48724DB2D352597005DE483 /* SentryTraceOrigin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48724DA2D352591005DE483 /* SentryTraceOrigin.swift */; };
D48724DD2D354939005DE483 /* SentrySpanOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48724DC2D354934005DE483 /* SentrySpanOperation.swift */; };
D48724E02D3549CA005DE483 /* SentrySpanOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48724DF2D3549C6005DE483 /* SentrySpanOperationTests.swift */; };
Expand Down Expand Up @@ -1953,6 +1956,9 @@
D42E48582D48FC8F00D251BC /* SentryNSDictionarySanitizeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryNSDictionarySanitizeTests.swift; sourceTree = "<group>"; };
D43647EE2D5CF9DC001468E0 /* SentrySpanKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySpanKey.swift; sourceTree = "<group>"; };
D43647F02D5CFB71001468E0 /* SentrySpanKeyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SentrySpanKeyTests.swift; sourceTree = "<group>"; };
D43647AA2D5CAA32001468E0 /* FileManager+SentryTracing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+SentryTracing.swift"; sourceTree = "<group>"; };
D43647F22D5CFBC2001468E0 /* FileManagerTracingIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerTracingIntegrationTests.swift; sourceTree = "<group>"; };
D468C0612D3669A200964230 /* SentryFileIOTracker+SwiftHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryFileIOTracker+SwiftHelpers.swift"; sourceTree = "<group>"; };
D48724DA2D352591005DE483 /* SentryTraceOrigin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTraceOrigin.swift; sourceTree = "<group>"; };
D48724DC2D354934005DE483 /* SentrySpanOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySpanOperation.swift; sourceTree = "<group>"; };
D48724DF2D3549C6005DE483 /* SentrySpanOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySpanOperationTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3784,6 +3790,16 @@
name = Transaction;
sourceTree = "<group>";
};
D468C0602D36699700964230 /* IO */ = {
isa = PBXGroup;
children = (
D4EDF9832D0B2A1D0071E7B3 /* Data+SentryTracing.swift */,
D43647AA2D5CAA32001468E0 /* FileManager+SentryTracing.swift */,
D468C0612D3669A200964230 /* SentryFileIOTracker+SwiftHelpers.swift */,
);
path = IO;
sourceTree = "<group>";
};
D48724D92D35258A005DE483 /* Transactions */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4015,6 +4031,8 @@
D875ED09276CC83200422FAC /* IO */ = {
isa = PBXGroup;
children = (
D43647F22D5CFBC2001468E0 /* FileManagerTracingIntegrationTests.swift */,
D4C5F5992D4249E0002A9BF6 /* DataSentryTracingIntegrationTests.swift */,
D875ED0A276CC84700422FAC /* SentryFileIOTrackerTests.swift */,
D8CE69BB277E39C700C6EC5C /* SentryFileIOTrackingIntegrationObjCTests.m */,
D885266327739D01001269FC /* SentryFileIOTrackingIntegrationTests.swift */,
Expand Down Expand Up @@ -4943,6 +4961,7 @@
843FB3232D0CD04D00558F18 /* SentryUserAccess.m in Sources */,
63FE716720DA4C1100CDBAE8 /* SentryCrashCPU.c in Sources */,
63FE717320DA4C1100CDBAE8 /* SentryCrashC.c in Sources */,
D43647AB2D5CAA32001468E0 /* FileManager+SentryTracing.swift in Sources */,
6293F5752D422A95002BC3BD /* SentryStacktraceCodable.swift in Sources */,
63FE712120DA4C1000CDBAE8 /* SentryCrashSymbolicator.c in Sources */,
627C77892D50B6840055E966 /* SentryBreadcrumbCodable.swift in Sources */,
Expand Down Expand Up @@ -5348,6 +5367,7 @@
8F73BC312B02B87E00C3CEF4 /* SentryInstallationTests.swift in Sources */,
7B569E002590EEF600B653FC /* SentryScope+Equality.m in Sources */,
D8BFE37929A76666002E73F3 /* SentryTimeToDisplayTrackerTest.swift in Sources */,
D43647F32D5CFBC7001468E0 /* FileManagerTracingIntegrationTests.swift in Sources */,
D84541182A2DC2CD00E2B11C /* SentryBinaryImageCacheTests.swift in Sources */,
7BF536D424BEF255004FA6A2 /* SentryAssertions.swift in Sources */,
7BC6EC14255C415E0059822A /* SentryExceptionTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
@_implementationOnly import _SentryPrivate
import Foundation

/// A ``FileManager`` extension that tracks read and write operations with Sentry.
///
/// - Note: Methods provided by this extension reflect the same functionality as the original ``FileManager`` methods, but they track the operation with Sentry.
public extension FileManager {

// MARK: - Creating and Deleting Items

/// Creates a file with the specified content and attributes at the given location, tracking the operation with Sentry.
///
/// - Important: Using this method with auto-instrumentation for file operations enabled can lead to duplicate spans on older operating system versions.
/// It is recommended to use either automatic or manual instrumentation. You can disable automatic instrumentation by setting
/// `options.enableSwizzling` to `false` when initializing Sentry.
/// - Parameters:
/// - path: The path for the new file.
/// - data: A data object containing the contents of the new file.
/// - attr: A dictionary containing the attributes to associate with the new file.
/// You can use these attributes to set the owner and group numbers, file permissions, and modification date.
/// For a list of keys, see ``FileAttributeKey``. If you specify `nil` for attributes, the file is created with a set of default attributes.
/// - Returns: `true` if the operation was successful or if the item already exists, otherwise `false`.
/// - Note: See ``FileManager.createFile(atPath:contents:attributes:)`` for more information.
func createFileWithSentryTracing(atPath path: String, contents data: Data?, attributes attr: [FileAttributeKey: Any]? = nil) -> Bool {
let tracker = SentryFileIOTracker.sharedInstance()
return tracker
.measureCreatingFile(
atPath: path,
contents: data,
attributes: attr,
origin: SentryTraceOrigin.manualFileData) { path, data, attr in
self.createFile(atPath: path, contents: data, attributes: attr)
}
}

/// Removes the file or directory at the specified URL, tracking the operation with Sentry.
///
/// - Important: Using this method with auto-instrumentation for file operations enabled can lead to duplicate spans on older operating system versions.
/// It is recommended to use either automatic or manual instrumentation. You can disable automatic instrumentation by setting
/// `options.enableSwizzling` to `false` when initializing Sentry.
/// - Parameter url: A file URL specifying the file or directory to remove.
/// If the URL specifies a directory, the contents of that directory are recursively removed.
/// - Note: See ``FileManager.removeItem(at:)`` for more information.
func removeItemWithSentryTracing(at url: URL) throws {
let tracker = SentryFileIOTracker.sharedInstance()
try tracker.measureRemovingItem(at: url, origin: SentryTraceOrigin.manualFileData) { url in
try self.removeItem(at: url)
}
}

/// Removes the file or directory at the specified path, tracking the operation with Sentry.
///
/// - Important: Using this method with auto-instrumentation for file operations enabled can lead to duplicate spans on older operating system versions.
/// It is recommended to use either automatic or manual instrumentation. You can disable automatic instrumentation by setting
/// `options.enableSwizzling` to `false` when initializing Sentry.
/// - Parameter path: A path string indicating the file or directory to remove.
/// If the path specifies a directory, the contents of that directory are recursively removed.
/// - Note: See ``FileManager.removeItem(atPath:)`` for more information.
func removeItemWithSentryTracing(atPath path: String) throws {
let tracker = SentryFileIOTracker.sharedInstance()
try tracker.measureRemovingItem(atPath: path, origin: SentryTraceOrigin.manualFileData) { path in
try self.removeItem(atPath: path)
}
}

// MARK: - Moving and Copying Items

/// Copies the file at the specified URL to a new location synchronously, tracking the operation with Sentry.
///
/// - Important: Using this method with auto-instrumentation for file operations enabled can lead to duplicate spans on older operating system versions.
/// It is recommended to use either automatic or manual instrumentation. You can disable automatic instrumentation by setting
/// `options.enableSwizzling` to `false` when initializing Sentry.
/// - Parameters:
/// - srcURL: The file URL that identifies the file you want to copy.
/// The URL in this parameter must not be a file reference URL.
/// - dstURL: The URL at which to place the copy of `srcURL`.
/// The URL in this parameter must not be a file reference URL and must include the name of the file in its new location.
/// - Note: See ``FileManager.copyItem(at:to:)`` for more information.
func copyItemWithSentryTracing(at srcURL: URL, to dstURL: URL) throws {
let tracker = SentryFileIOTracker.sharedInstance()
try tracker.measureCopyingItem(at: srcURL, to: dstURL, origin: SentryTraceOrigin.manualFileData) { srcURL, dstURL in
try self.copyItem(at: srcURL, to: dstURL)
}
}

/// Copies the item at the specified path to a new location synchronously, tracking the operation with Sentry.
///
/// - Important: Using this method with auto-instrumentation for file operations enabled can lead to duplicate spans on older operating system versions.
/// It is recommended to use either automatic or manual instrumentation. You can disable automatic instrumentation by setting
/// `options.enableSwizzling` to `false` when initializing Sentry.
/// - Parameters:
/// - srcPath: The path to the file or directory you want to move.
/// - dstPath: The path at which to place the copy of `srcPath`.
/// This path must include the name of the file or directory in its new location.
/// - Note: See ``FileManager.copyItem(atPath:toPath:)`` for more information.
func copyItemWithSentryTracing(at srcPath: String, to dstPath: String) throws {
let tracker = SentryFileIOTracker.sharedInstance()
try tracker.measureCopyingItem(atPath: srcPath, toPath: dstPath, origin: SentryTraceOrigin.manualFileData) { srcPath, dstPath in
try self.copyItem(atPath: srcPath, toPath: dstPath)
}
}

/// Moves the file or directory at the specified URL to a new location synchronously, tracking the operation with Sentry.
///
/// - Important: Using this method with auto-instrumentation for file operations enabled can lead to duplicate spans on older operating system versions.
/// It is recommended to use either automatic or manual instrumentation. You can disable automatic instrumentation by setting
/// `options.enableSwizzling` to `false` when initializing Sentry.
/// - Parameters:
/// - srcURL: The file URL that identifies the file or directory you want to move.
/// The URL in this parameter must not be a file reference URL.
/// - dstURL: The new location for the item in `srcURL`.
/// The URL in this parameter must not be a file reference URL and must include the name of the file or directory in its new location.
/// - Note: See ``FileManager.moveItem(at:to:)`` for more information.
func moveItemWithSentryTracing(at srcURL: URL, to dstURL: URL) throws {
let tracker = SentryFileIOTracker.sharedInstance()
try tracker.measureMovingItem(
at: srcURL,
to: dstURL,
origin: SentryTraceOrigin.manualFileData) { srcURL, dstURL in
try self.moveItem(at: srcURL, to: dstURL)
}
}

/// Moves the file or directory at the specified path to a new location synchronously, tracking the operation with Sentry.
///
/// - Important: Using this method with auto-instrumentation for file operations enabled can lead to duplicate spans on older operating system versions.
/// It is recommended to use either automatic or manual instrumentation. You can disable automatic instrumentation by setting
/// `options.enableSwizzling` to `false` when initializing Sentry.
/// - Parameters:
/// - srcPath: The path to the file or directory you want to move.
/// - dstPath: The new path for the item in `srcPath`.
/// This path must include the name of the file or directory in its new location.
/// - Note: See ``FileManager.moveItem(atPath:toPath:)`` for more information.
func moveItemWithSentryTracing(at srcPath: String, to dstPath: String) throws {
let tracker = SentryFileIOTracker.sharedInstance()
try tracker.measureMovingItem(atPath: srcPath, toPath: dstPath, origin: SentryTraceOrigin.manualFileData) { srcPath, dstPath in
try self.moveItem(atPath: srcPath, toPath: dstPath)
}
}
}
Loading

0 comments on commit 4182d91

Please sign in to comment.