From b2f328392297b866902029fe670f8e176d365eba Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 18 Sep 2024 21:50:04 +0200 Subject: [PATCH 01/18] make use of 'filesystem' --- src/Sentry/GlobalSessionManager.cs | 12 ++- src/Sentry/Http/HttpTransportBase.cs | 27 +++++- src/Sentry/ISentryJsonSerializable.cs | 10 ++- src/Sentry/Internal/FileSystem.cs | 95 +++++++++++++++++---- src/Sentry/Internal/IFileSystem.cs | 9 +- src/Sentry/Internal/InstallationIdHelper.cs | 19 ++++- src/Sentry/Internal/SdkComposer.cs | 18 +++- src/Sentry/SentryOptions.cs | 5 ++ test/Sentry.Testing/FakeFileSystem.cs | 32 +++++-- 9 files changed, 190 insertions(+), 37 deletions(-) diff --git a/src/Sentry/GlobalSessionManager.cs b/src/Sentry/GlobalSessionManager.cs index cfaf68af70..71eea23265 100644 --- a/src/Sentry/GlobalSessionManager.cs +++ b/src/Sentry/GlobalSessionManager.cs @@ -53,14 +53,20 @@ private void PersistSession(SessionUpdate update, DateTimeOffset? pauseTimestamp try { - Directory.CreateDirectory(_persistenceDirectoryPath); + _options.LogDebug("Creating persistence directory for session file at '{0}'.", _persistenceDirectoryPath); - _options.LogDebug("Created persistence directory for session file '{0}'.", _persistenceDirectoryPath); + if (_options.FileSystem.CreateDirectory(_persistenceDirectoryPath)) + { + _options.LogInfo("Failed to create persistent directory for session file."); + return; + } + + _options.LogDebug("Created persistence directory for session file"); var filePath = Path.Combine(_persistenceDirectoryPath, PersistedSessionFileName); var persistedSessionUpdate = new PersistedSessionUpdate(update, pauseTimestamp); - persistedSessionUpdate.WriteToFile(filePath, _options.DiagnosticLogger); + persistedSessionUpdate.WriteToFile(_options.FileSystem, filePath, _options.DiagnosticLogger); _options.LogDebug("Persisted session to a file '{0}'.", filePath); } diff --git a/src/Sentry/Http/HttpTransportBase.cs b/src/Sentry/Http/HttpTransportBase.cs index b11d436b37..45aef5ad44 100644 --- a/src/Sentry/Http/HttpTransportBase.cs +++ b/src/Sentry/Http/HttpTransportBase.cs @@ -386,9 +386,18 @@ private void HandleFailure(HttpResponseMessage response, Envelope envelope) var destination = Path.Combine(destinationDirectory, "envelope_too_large", (eventId ?? SentryId.Create()).ToString()); - Directory.CreateDirectory(Path.GetDirectoryName(destination)!); + if (!_options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!)) + { + _options.LogError("Failed to create directory to store the envelope."); + return; + } - var envelopeFile = File.Create(destination); + var envelopeFile = _options.FileSystem.CreateFileForWriting(destination); + if (envelopeFile == Stream.Null) + { + _options.LogError("Failed to create envelope file."); + return; + } using (envelopeFile) { @@ -442,9 +451,19 @@ private async Task HandleFailureAsync(HttpResponseMessage response, Envelope env var destination = Path.Combine(destinationDirectory, "envelope_too_large", (eventId ?? SentryId.Create()).ToString()); - Directory.CreateDirectory(Path.GetDirectoryName(destination)!); + if (!_options.FileSystem.CreateDirectory(Path.GetDirectoryName(destination)!)) + { + _options.LogError("Failed to create directory to store the envelope."); + return; + } + + var envelopeFile = _options.FileSystem.CreateFileForWriting(destination); + if (envelopeFile == Stream.Null) + { + _options.LogError("Failed to create envelope file."); + return; + } - var envelopeFile = File.Create(destination); #if NETFRAMEWORK || NETSTANDARD2_0 using (envelopeFile) #else diff --git a/src/Sentry/ISentryJsonSerializable.cs b/src/Sentry/ISentryJsonSerializable.cs index da3923b224..e03b81f56c 100644 --- a/src/Sentry/ISentryJsonSerializable.cs +++ b/src/Sentry/ISentryJsonSerializable.cs @@ -1,4 +1,5 @@ using Sentry.Extensibility; +using Sentry.Internal; namespace Sentry; @@ -19,9 +20,14 @@ public interface ISentryJsonSerializable internal static class JsonSerializableExtensions { - public static void WriteToFile(this ISentryJsonSerializable serializable, string filePath, IDiagnosticLogger? logger) + public static void WriteToFile(this ISentryJsonSerializable serializable, IFileSystem fileSystem, string filePath, IDiagnosticLogger? logger) { - using var file = File.Create(filePath); + using var file = fileSystem.CreateFileForWriting(filePath); + if (file == Stream.Null) + { + return; + } + using var writer = new Utf8JsonWriter(file); serializable.WriteTo(writer, logger); diff --git a/src/Sentry/Internal/FileSystem.cs b/src/Sentry/Internal/FileSystem.cs index 2013a38db6..160fae0c76 100644 --- a/src/Sentry/Internal/FileSystem.cs +++ b/src/Sentry/Internal/FileSystem.cs @@ -1,11 +1,16 @@ +using Sentry.Extensibility; + namespace Sentry.Internal; internal class FileSystem : IFileSystem { - public static IFileSystem Instance { get; } = new FileSystem(); + public static IFileSystem Instance { get; } = new FileSystem(SentrySdk.CurrentOptions); + + private readonly SentryOptions? _options; - private FileSystem() + private FileSystem(SentryOptions? options) { + _options = options; } public IEnumerable EnumerateFiles(string path) => Directory.EnumerateFiles(path); @@ -16,32 +21,69 @@ public IEnumerable EnumerateFiles(string path, string searchPattern) => public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => Directory.EnumerateFiles(path, searchPattern, searchOption); - public void CreateDirectory(string path) => Directory.CreateDirectory(path); + public bool CreateDirectory(string path) + { + if (_options?.DisableFileWrite is false) + { + _options?.LogWarning("Skipping creating directory. Writing to file system has been explicitly disabled."); + return false; + } + + Directory.CreateDirectory(path); + return true; + } + + public bool DeleteDirectory(string path, bool recursive = false) + { + if (_options?.DisableFileWrite is false) + { + _options?.LogWarning("Skipping deleting directory. Writing to file system has been explicitly disabled."); + return false; + } - public void DeleteDirectory(string path, bool recursive = false) => Directory.Delete(path, recursive); + Directory.Delete(path, recursive); + return true; + } public bool DirectoryExists(string path) => Directory.Exists(path); public bool FileExists(string path) => File.Exists(path); - public void MoveFile(string sourceFileName, string destFileName, bool overwrite = false) + public bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false) { + if (_options?.DisableFileWrite is false) + { + _options?.LogWarning("Skipping moving file. Writing to file system has been explicitly disabled."); + return false; + } + #if NETCOREAPP3_0_OR_GREATER File.Move(sourceFileName, destFileName, overwrite); #else - if (overwrite) - { - File.Copy(sourceFileName, destFileName, overwrite: true); - File.Delete(sourceFileName); - } - else - { - File.Move(sourceFileName, destFileName); - } + if (overwrite) + { + File.Copy(sourceFileName, destFileName, overwrite: true); + File.Delete(sourceFileName); + } + else + { + File.Move(sourceFileName, destFileName); + } #endif + return true; } - public void DeleteFile(string path) => File.Delete(path); + public bool DeleteFile(string path) + { + if (_options?.DisableFileWrite is false) + { + _options?.LogWarning("Skipping deleting file. Writing to file system has been explicitly disabled."); + return false; + } + + File.Delete(path); + return true; + } public DateTimeOffset GetFileCreationTime(string path) => new FileInfo(path).CreationTimeUtc; @@ -49,5 +91,26 @@ public void MoveFile(string sourceFileName, string destFileName, bool overwrite public Stream OpenFileForReading(string path) => File.OpenRead(path); - public Stream CreateFileForWriting(string path) => File.Create(path); + public Stream CreateFileForWriting(string path) + { + if (_options?.DisableFileWrite is false) + { + _options?.LogWarning("Skipping file for writing. Writing to file system has been explicitly disabled."); + return Stream.Null; + } + + return File.Create(path); + } + + public bool WriteAllTextToFile(string path, string contents) + { + if (_options?.DisableFileWrite is false) + { + _options?.LogWarning("Skipping writing all text to file. Writing to file system has been explicitly disabled."); + return false; + } + + File.WriteAllText(path, contents); + return true; + } } diff --git a/src/Sentry/Internal/IFileSystem.cs b/src/Sentry/Internal/IFileSystem.cs index 6dc2073c91..4a7cab96bf 100644 --- a/src/Sentry/Internal/IFileSystem.cs +++ b/src/Sentry/Internal/IFileSystem.cs @@ -8,14 +8,15 @@ internal interface IFileSystem IEnumerable EnumerateFiles(string path); IEnumerable EnumerateFiles(string path, string searchPattern); IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption); - void CreateDirectory(string path); - void DeleteDirectory(string path, bool recursive = false); + bool CreateDirectory(string path); + bool DeleteDirectory(string path, bool recursive = false); bool DirectoryExists(string path); bool FileExists(string path); - void MoveFile(string sourceFileName, string destFileName, bool overwrite = false); - void DeleteFile(string path); + bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false); + bool DeleteFile(string path); DateTimeOffset GetFileCreationTime(string path); string ReadAllTextFromFile(string file); Stream OpenFileForReading(string path); Stream CreateFileForWriting(string path); + bool WriteAllTextToFile(string path, string contents); } diff --git a/src/Sentry/Internal/InstallationIdHelper.cs b/src/Sentry/Internal/InstallationIdHelper.cs index c3159d8d93..cf0c5d01d1 100644 --- a/src/Sentry/Internal/InstallationIdHelper.cs +++ b/src/Sentry/Internal/InstallationIdHelper.cs @@ -47,13 +47,24 @@ internal class InstallationIdHelper(SentryOptions options) private string? TryGetPersistentInstallationId() { + if (options.DisableFileWrite) + { + options.LogDebug("File write has been disabled via the options. Skipping trying to get persistent installation ID."); + return null; + } + try { var rootPath = options.CacheDirectoryPath ?? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var directoryPath = Path.Combine(rootPath, "Sentry", options.Dsn!.GetHashString()); + var fileSystem = options.FileSystem; - Directory.CreateDirectory(directoryPath); + if (!fileSystem.CreateDirectory(directoryPath)) + { + options.LogDebug("Failed to create a directory for installation ID file ({0}).", directoryPath); + return null; + } options.LogDebug("Created directory for installation ID file ({0}).", directoryPath); @@ -68,7 +79,11 @@ internal class InstallationIdHelper(SentryOptions options) // Generate new installation ID and store it in a file var id = Guid.NewGuid().ToString(); - File.WriteAllText(filePath, id); + if (!fileSystem.WriteAllTextToFile(filePath, id)) + { + options.LogDebug("Failed to write Installation ID to file ({0}).", filePath); + return null; + } options.LogDebug("Saved installation ID '{0}' to file '{1}'.", id, filePath); return id; diff --git a/src/Sentry/Internal/SdkComposer.cs b/src/Sentry/Internal/SdkComposer.cs index 9c907565e4..c2e9935849 100644 --- a/src/Sentry/Internal/SdkComposer.cs +++ b/src/Sentry/Internal/SdkComposer.cs @@ -20,13 +20,29 @@ public SdkComposer(SentryOptions options) private ITransport CreateTransport() { + _options.LogDebug("Creating transport."); + // Start from either the transport given on options, or create a new HTTP transport. var transport = _options.Transport ?? CreateHttpTransport(); // When a cache directory path is given, wrap the transport in a caching transport. if (!string.IsNullOrWhiteSpace(_options.CacheDirectoryPath)) { - transport = CachingTransport.Create(transport, _options); + _options.LogDebug("Cache directory path is specified."); + + if (_options.DisableFileWrite) + { + _options.LogInfo("File writing is disabled, Skipping caching transport creation."); + } + else + { + _options.LogDebug("File writing is enabled, wrapping transport in caching transport."); + transport = CachingTransport.Create(transport, _options); + } + } + else + { + _options.LogDebug("No cache directory path specified. Skipping caching transport creation."); } // Wrap the transport with the Spotlight one that double sends the envelope: Sentry + Spotlight diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 46ec865561..2803f6ac37 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -728,6 +728,11 @@ public IList FailedRequestTargets /// internal IFileSystem FileSystem { get; set; } = Internal.FileSystem.Instance; + /// + /// Allows to disable the SDKs writing to disk operations + /// + public bool DisableFileWrite { get; set; } + /// /// If set to a positive value, Sentry will attempt to flush existing local event cache when initializing. /// Set to to disable this feature. diff --git a/test/Sentry.Testing/FakeFileSystem.cs b/test/Sentry.Testing/FakeFileSystem.cs index a218c830e6..18d56c8287 100644 --- a/test/Sentry.Testing/FakeFileSystem.cs +++ b/test/Sentry.Testing/FakeFileSystem.cs @@ -15,16 +15,23 @@ public IEnumerable EnumerateFiles(string path, string searchPattern) => public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => _mockFileSystem.Directory.EnumerateFiles(path, searchPattern, searchOption); - public void CreateDirectory(string path) => _mockFileSystem.Directory.CreateDirectory(path); + public bool CreateDirectory(string path) + { + _mockFileSystem.Directory.CreateDirectory(path); + return true; + } - public void DeleteDirectory(string path, bool recursive = false) => + public bool DeleteDirectory(string path, bool recursive = false) + { _mockFileSystem.Directory.Delete(path, recursive); + return true; + } public bool DirectoryExists(string path) => _mockFileSystem.Directory.Exists(path); public bool FileExists(string path) => _mockFileSystem.File.Exists(path); - public void MoveFile(string sourceFileName, string destFileName, bool overwrite = false) + public bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false) { #if NET5_0_OR_GREATER _mockFileSystem.File.Move(sourceFileName, destFileName, overwrite); @@ -39,9 +46,15 @@ public void MoveFile(string sourceFileName, string destFileName, bool overwrite _mockFileSystem.File.Move(sourceFileName, destFileName); } #endif + + return true; } - public void DeleteFile(string path) => _mockFileSystem.File.Delete(path); + public bool DeleteFile(string path) + { + _mockFileSystem.File.Delete(path); + return true; + } public DateTimeOffset GetFileCreationTime(string path) => _mockFileSystem.FileInfo.New(path).CreationTimeUtc; @@ -50,5 +63,14 @@ public DateTimeOffset GetFileCreationTime(string path) => public Stream OpenFileForReading(string path) => _mockFileSystem.File.OpenRead(path); - public Stream CreateFileForWriting(string path) => _mockFileSystem.File.Create(path); + public Stream CreateFileForWriting(string path) + { + return _mockFileSystem.File.Create(path); + } + + public bool WriteAllTextToFile(string path, string contents) + { + _mockFileSystem.File.WriteAllText(path, contents); + return true; + } } From ab7c7e886b4c12c252d120a39985cb8b19dd5058 Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Thu, 19 Sep 2024 18:04:21 +0200 Subject: [PATCH 02/18] Apply suggestions from code review Co-authored-by: Bruno Garcia --- src/Sentry/GlobalSessionManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sentry/GlobalSessionManager.cs b/src/Sentry/GlobalSessionManager.cs index 71eea23265..6996376506 100644 --- a/src/Sentry/GlobalSessionManager.cs +++ b/src/Sentry/GlobalSessionManager.cs @@ -55,9 +55,9 @@ private void PersistSession(SessionUpdate update, DateTimeOffset? pauseTimestamp { _options.LogDebug("Creating persistence directory for session file at '{0}'.", _persistenceDirectoryPath); - if (_options.FileSystem.CreateDirectory(_persistenceDirectoryPath)) + if (!_options.FileSystem.CreateDirectory(_persistenceDirectoryPath)) { - _options.LogInfo("Failed to create persistent directory for session file."); + _options.LogError("Failed to create persistent directory for session file."); return; } From eea14506be715417c1dbdb4b3e0109bbd5bfac65 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Thu, 19 Sep 2024 18:13:36 +0200 Subject: [PATCH 03/18] loggign --- src/Sentry/GlobalSessionManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Sentry/GlobalSessionManager.cs b/src/Sentry/GlobalSessionManager.cs index 6996376506..689a488a1d 100644 --- a/src/Sentry/GlobalSessionManager.cs +++ b/src/Sentry/GlobalSessionManager.cs @@ -61,8 +61,6 @@ private void PersistSession(SessionUpdate update, DateTimeOffset? pauseTimestamp return; } - _options.LogDebug("Created persistence directory for session file"); - var filePath = Path.Combine(_persistenceDirectoryPath, PersistedSessionFileName); var persistedSessionUpdate = new PersistedSessionUpdate(update, pauseTimestamp); From 3cfce14bfa6dd8aa816aa1484aeca62f0ffaaa81 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Thu, 19 Sep 2024 19:14:10 +0200 Subject: [PATCH 04/18] use filesystem everywhere --- src/Sentry/BindableSentryOptions.cs | 2 ++ src/Sentry/GlobalSessionManager.cs | 6 +++--- src/Sentry/Internal/DebugStackTrace.cs | 3 ++- src/Sentry/Internal/InstallationIdHelper.cs | 4 ++-- src/Sentry/Internal/Json.cs | 4 ++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Sentry/BindableSentryOptions.cs b/src/Sentry/BindableSentryOptions.cs index 4630f053c0..2fe8d85d32 100644 --- a/src/Sentry/BindableSentryOptions.cs +++ b/src/Sentry/BindableSentryOptions.cs @@ -35,6 +35,7 @@ internal partial class BindableSentryOptions public string? CacheDirectoryPath { get; set; } public bool? CaptureFailedRequests { get; set; } public List? FailedRequestTargets { get; set; } + public bool? DisableFileWrite { get; set; } public TimeSpan? InitCacheFlushTimeout { get; set; } public Dictionary? DefaultTags { get; set; } public bool? EnableTracing { get; set; } @@ -81,6 +82,7 @@ public void ApplyTo(SentryOptions options) options.CacheDirectoryPath = CacheDirectoryPath ?? options.CacheDirectoryPath; options.CaptureFailedRequests = CaptureFailedRequests ?? options.CaptureFailedRequests; options.FailedRequestTargets = FailedRequestTargets?.Select(s => new SubstringOrRegexPattern(s)).ToList() ?? options.FailedRequestTargets; + options.DisableFileWrite = DisableFileWrite ?? options.DisableFileWrite; options.InitCacheFlushTimeout = InitCacheFlushTimeout ?? options.InitCacheFlushTimeout; options.DefaultTags = DefaultTags ?? options.DefaultTags; #pragma warning disable CS0618 // Type or member is obsolete diff --git a/src/Sentry/GlobalSessionManager.cs b/src/Sentry/GlobalSessionManager.cs index 689a488a1d..af1d10db96 100644 --- a/src/Sentry/GlobalSessionManager.cs +++ b/src/Sentry/GlobalSessionManager.cs @@ -32,7 +32,7 @@ public GlobalSessionManager( _options = options; _clock = clock ?? SystemClock.Clock; _persistedSessionProvider = persistedSessionProvider - ?? (filePath => Json.Load(filePath, PersistedSessionUpdate.FromJson)); + ?? (filePath => Json.Load(_options.FileSystem, filePath, PersistedSessionUpdate.FromJson)); // TODO: session file should really be process-isolated, but we // don't have a proper mechanism for that right now. @@ -90,7 +90,7 @@ private void DeletePersistedSession() { try { - var contents = File.ReadAllText(filePath); + var contents = _options.FileSystem.ReadAllTextFromFile(filePath); _options.LogDebug("Deleting persisted session file with contents: {0}", contents); } catch (Exception ex) @@ -99,7 +99,7 @@ private void DeletePersistedSession() } } - File.Delete(filePath); + _options.FileSystem.DeleteFile(filePath); _options.LogInfo("Deleted persisted session file '{0}'.", filePath); } diff --git a/src/Sentry/Internal/DebugStackTrace.cs b/src/Sentry/Internal/DebugStackTrace.cs index 26c198c83d..c048d78b75 100644 --- a/src/Sentry/Internal/DebugStackTrace.cs +++ b/src/Sentry/Internal/DebugStackTrace.cs @@ -526,7 +526,8 @@ private static void DemangleLambdaReturnType(SentryStackFrame frame) { return reader.Invoke(assemblyName); } - var assembly = File.OpenRead(assemblyName); + + var assembly = options.FileSystem.OpenFileForReading(assemblyName); return new PEReader(assembly); } catch (Exception) diff --git a/src/Sentry/Internal/InstallationIdHelper.cs b/src/Sentry/Internal/InstallationIdHelper.cs index cf0c5d01d1..36d0200497 100644 --- a/src/Sentry/Internal/InstallationIdHelper.cs +++ b/src/Sentry/Internal/InstallationIdHelper.cs @@ -71,9 +71,9 @@ internal class InstallationIdHelper(SentryOptions options) var filePath = Path.Combine(directoryPath, ".installation"); // Read installation ID stored in a file - if (File.Exists(filePath)) + if (fileSystem.FileExists(filePath)) { - return File.ReadAllText(filePath); + return fileSystem.ReadAllTextFromFile(filePath); } options.LogDebug("File containing installation ID does not exist ({0}).", filePath); diff --git a/src/Sentry/Internal/Json.cs b/src/Sentry/Internal/Json.cs index 77c9bd2138..779a291ca2 100644 --- a/src/Sentry/Internal/Json.cs +++ b/src/Sentry/Internal/Json.cs @@ -14,9 +14,9 @@ public static T Parse(string json, Func factory) return factory.Invoke(jsonDocument.RootElement); } - public static T Load(string filePath, Func factory) + public static T Load(IFileSystem fileSystem, string filePath, Func factory) { - using var file = File.OpenRead(filePath); + using var file = fileSystem.OpenFileForReading(filePath); using var jsonDocument = JsonDocument.Parse(file); return factory.Invoke(jsonDocument.RootElement); } From 967e0062946d9a43b331e515e3f12664853367dd Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Thu, 19 Sep 2024 19:16:16 +0200 Subject: [PATCH 05/18] fix tests to use fakefilesystem --- test/Sentry.Testing/FakeFileSystem.cs | 36 +++++++++---------- .../Sentry.Tests/GlobalSessionManagerTests.cs | 12 ++++--- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/test/Sentry.Testing/FakeFileSystem.cs b/test/Sentry.Testing/FakeFileSystem.cs index 18d56c8287..50e44aa0e2 100644 --- a/test/Sentry.Testing/FakeFileSystem.cs +++ b/test/Sentry.Testing/FakeFileSystem.cs @@ -5,45 +5,45 @@ namespace Sentry.Testing; public class FakeFileSystem : IFileSystem { // This is an in-memory implementation provided by https://github.com/TestableIO/System.IO.Abstractions - private readonly MockFileSystem _mockFileSystem = new(); + public readonly MockFileSystem MockFileSystem = new(); - public IEnumerable EnumerateFiles(string path) => _mockFileSystem.Directory.EnumerateFiles(path); + public IEnumerable EnumerateFiles(string path) => MockFileSystem.Directory.EnumerateFiles(path); public IEnumerable EnumerateFiles(string path, string searchPattern) => - _mockFileSystem.Directory.EnumerateFiles(path, searchPattern); + MockFileSystem.Directory.EnumerateFiles(path, searchPattern); public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => - _mockFileSystem.Directory.EnumerateFiles(path, searchPattern, searchOption); + MockFileSystem.Directory.EnumerateFiles(path, searchPattern, searchOption); public bool CreateDirectory(string path) { - _mockFileSystem.Directory.CreateDirectory(path); + MockFileSystem.Directory.CreateDirectory(path); return true; } public bool DeleteDirectory(string path, bool recursive = false) { - _mockFileSystem.Directory.Delete(path, recursive); + MockFileSystem.Directory.Delete(path, recursive); return true; } - public bool DirectoryExists(string path) => _mockFileSystem.Directory.Exists(path); + public bool DirectoryExists(string path) => MockFileSystem.Directory.Exists(path); - public bool FileExists(string path) => _mockFileSystem.File.Exists(path); + public bool FileExists(string path) => MockFileSystem.File.Exists(path); public bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false) { #if NET5_0_OR_GREATER - _mockFileSystem.File.Move(sourceFileName, destFileName, overwrite); + MockFileSystem.File.Move(sourceFileName, destFileName, overwrite); #else if (overwrite) { - _mockFileSystem.File.Copy(sourceFileName, destFileName, overwrite: true); - _mockFileSystem.File.Delete(sourceFileName); + MockFileSystem.File.Copy(sourceFileName, destFileName, overwrite: true); + MockFileSystem.File.Delete(sourceFileName); } else { - _mockFileSystem.File.Move(sourceFileName, destFileName); + MockFileSystem.File.Move(sourceFileName, destFileName); } #endif @@ -52,25 +52,25 @@ public bool MoveFile(string sourceFileName, string destFileName, bool overwrite public bool DeleteFile(string path) { - _mockFileSystem.File.Delete(path); + MockFileSystem.File.Delete(path); return true; } public DateTimeOffset GetFileCreationTime(string path) => - _mockFileSystem.FileInfo.New(path).CreationTimeUtc; + MockFileSystem.FileInfo.New(path).CreationTimeUtc; - public string ReadAllTextFromFile(string file) => _mockFileSystem.File.ReadAllText(file); + public string ReadAllTextFromFile(string file) => MockFileSystem.File.ReadAllText(file); - public Stream OpenFileForReading(string path) => _mockFileSystem.File.OpenRead(path); + public Stream OpenFileForReading(string path) => MockFileSystem.File.OpenRead(path); public Stream CreateFileForWriting(string path) { - return _mockFileSystem.File.Create(path); + return MockFileSystem.File.Create(path); } public bool WriteAllTextToFile(string path, string contents) { - _mockFileSystem.File.WriteAllText(path, contents); + MockFileSystem.File.WriteAllText(path, contents); return true; } } diff --git a/test/Sentry.Tests/GlobalSessionManagerTests.cs b/test/Sentry.Tests/GlobalSessionManagerTests.cs index 8e0fd0143b..f2373d18ec 100644 --- a/test/Sentry.Tests/GlobalSessionManagerTests.cs +++ b/test/Sentry.Tests/GlobalSessionManagerTests.cs @@ -14,19 +14,21 @@ private class Fixture : IDisposable public Func PersistedSessionProvider { get; set; } + public FakeFileSystem FileSystem { get; } + public Fixture(Action configureOptions = null) { Clock.GetUtcNow().Returns(DateTimeOffset.Now); Logger = new InMemoryDiagnosticLogger(); - var fileSystem = new FakeFileSystem(); - _cacheDirectory = new TempDirectory(fileSystem); + FileSystem = new FakeFileSystem(); + _cacheDirectory = new TempDirectory(FileSystem); Options = new SentryOptions { Dsn = ValidDsn, CacheDirectoryPath = _cacheDirectory.Path, - FileSystem = fileSystem, + FileSystem = FileSystem, Release = "test", Debug = true, DiagnosticLogger = Logger @@ -77,7 +79,7 @@ public void StartSession_CacheDirectoryProvided_InstallationIdFileCreated() sut.StartSession(); // Assert - File.Exists(filePath).Should().BeTrue(); + _fixture.FileSystem.MockFileSystem.FileExists(filePath).Should().BeTrue(); } [Fact] @@ -97,7 +99,7 @@ public void StartSession_CacheDirectoryNotProvided_InstallationIdFileCreated() sut.StartSession(); // Assert - File.Exists(filePath).Should().BeTrue(); + _fixture.FileSystem.MockFileSystem.FileExists(filePath).Should().BeTrue(); } [Fact] From cdde5d5a16536f81358d569cc78ae2c3bda32e54 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Thu, 19 Sep 2024 19:17:05 +0200 Subject: [PATCH 06/18] verify --- test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt | 1 + test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt | 1 + test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt | 1 + test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt | 1 + 4 files changed, 4 insertions(+) diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 9d3f44a7aa..974789e645 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -664,6 +664,7 @@ namespace Sentry public Sentry.StartupTimeDetectionMode DetectStartupTime { get; set; } public Sentry.SentryLevel DiagnosticLevel { get; set; } public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } + public bool DisableFileWrite { get; set; } public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 9d3f44a7aa..974789e645 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -664,6 +664,7 @@ namespace Sentry public Sentry.StartupTimeDetectionMode DetectStartupTime { get; set; } public Sentry.SentryLevel DiagnosticLevel { get; set; } public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } + public bool DisableFileWrite { get; set; } public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 5fa4dfbeed..0ff6209f22 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -665,6 +665,7 @@ namespace Sentry public Sentry.StartupTimeDetectionMode DetectStartupTime { get; set; } public Sentry.SentryLevel DiagnosticLevel { get; set; } public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } + public bool DisableFileWrite { get; set; } public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index b60f14938d..81083846d4 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -662,6 +662,7 @@ namespace Sentry public Sentry.StartupTimeDetectionMode DetectStartupTime { get; set; } public Sentry.SentryLevel DiagnosticLevel { get; set; } public Sentry.Extensibility.IDiagnosticLogger? DiagnosticLogger { get; set; } + public bool DisableFileWrite { get; set; } public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } From ba1df680923a4372c1782c6ee0afab09de198c31 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 20 Sep 2024 11:23:10 +0200 Subject: [PATCH 07/18] debug instead of warning --- src/Sentry/Internal/FileSystem.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Sentry/Internal/FileSystem.cs b/src/Sentry/Internal/FileSystem.cs index 160fae0c76..2b927d800d 100644 --- a/src/Sentry/Internal/FileSystem.cs +++ b/src/Sentry/Internal/FileSystem.cs @@ -25,7 +25,7 @@ public bool CreateDirectory(string path) { if (_options?.DisableFileWrite is false) { - _options?.LogWarning("Skipping creating directory. Writing to file system has been explicitly disabled."); + _options?.LogDebug("Skipping creating directory. Writing to file system has been explicitly disabled."); return false; } @@ -37,7 +37,7 @@ public bool DeleteDirectory(string path, bool recursive = false) { if (_options?.DisableFileWrite is false) { - _options?.LogWarning("Skipping deleting directory. Writing to file system has been explicitly disabled."); + _options?.LogDebug("Skipping deleting directory. Writing to file system has been explicitly disabled."); return false; } @@ -53,7 +53,7 @@ public bool MoveFile(string sourceFileName, string destFileName, bool overwrite { if (_options?.DisableFileWrite is false) { - _options?.LogWarning("Skipping moving file. Writing to file system has been explicitly disabled."); + _options?.LogDebug("Skipping moving file. Writing to file system has been explicitly disabled."); return false; } @@ -77,7 +77,7 @@ public bool DeleteFile(string path) { if (_options?.DisableFileWrite is false) { - _options?.LogWarning("Skipping deleting file. Writing to file system has been explicitly disabled."); + _options?.LogDebug("Skipping deleting file. Writing to file system has been explicitly disabled."); return false; } @@ -95,7 +95,7 @@ public Stream CreateFileForWriting(string path) { if (_options?.DisableFileWrite is false) { - _options?.LogWarning("Skipping file for writing. Writing to file system has been explicitly disabled."); + _options?.LogDebug("Skipping file for writing. Writing to file system has been explicitly disabled."); return Stream.Null; } @@ -106,7 +106,7 @@ public bool WriteAllTextToFile(string path, string contents) { if (_options?.DisableFileWrite is false) { - _options?.LogWarning("Skipping writing all text to file. Writing to file system has been explicitly disabled."); + _options?.LogDebug("Skipping writing all text to file. Writing to file system has been explicitly disabled."); return false; } From 21ac35b7b6ec934ae98c709c2c9667a96e3f5a10 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 20 Sep 2024 17:04:45 +0200 Subject: [PATCH 08/18] make it work --- src/Sentry/ISentryJsonSerializable.cs | 2 +- src/Sentry/Internal/Http/CachingTransport.cs | 2 +- .../{IFileSystem.cs => ISentryFileSystem.cs} | 2 +- src/Sentry/Internal/Json.cs | 2 +- .../{FileSystem.cs => SentryFileSystem.cs} | 54 ++++++----- src/Sentry/Sentry.csproj | 8 ++ src/Sentry/SentryOptions.cs | 5 +- .../SentryMauiAppBuilderExtensionsTests.cs | 4 +- .../SamplingTransactionProfilerTests.cs | 8 +- test/Sentry.Testing/FakeFileSystem.cs | 76 --------------- test/Sentry.Testing/TempDirectory.cs | 14 +-- .../Sentry.Tests/GlobalSessionManagerTests.cs | 75 ++++++++++++--- test/Sentry.Tests/HubTests.cs | 12 +-- .../Internals/BackgroundWorkerTests.cs | 7 +- .../Internals/Http/CachingTransportTests.cs | 96 ++++++++----------- .../Internals/InstallationIdHelperTests.cs | 9 +- test/Sentry.Tests/SentryClientTests.cs | 6 +- test/Sentry.Tests/SentryFileSystemTests.cs | 59 ++++++++++++ test/Sentry.Tests/SentrySdkTests.cs | 5 +- 19 files changed, 232 insertions(+), 214 deletions(-) rename src/Sentry/Internal/{IFileSystem.cs => ISentryFileSystem.cs} (96%) rename src/Sentry/Internal/{FileSystem.cs => SentryFileSystem.cs} (55%) delete mode 100644 test/Sentry.Testing/FakeFileSystem.cs create mode 100644 test/Sentry.Tests/SentryFileSystemTests.cs diff --git a/src/Sentry/ISentryJsonSerializable.cs b/src/Sentry/ISentryJsonSerializable.cs index e03b81f56c..a73c753b57 100644 --- a/src/Sentry/ISentryJsonSerializable.cs +++ b/src/Sentry/ISentryJsonSerializable.cs @@ -20,7 +20,7 @@ public interface ISentryJsonSerializable internal static class JsonSerializableExtensions { - public static void WriteToFile(this ISentryJsonSerializable serializable, IFileSystem fileSystem, string filePath, IDiagnosticLogger? logger) + public static void WriteToFile(this ISentryJsonSerializable serializable, ISentryFileSystem fileSystem, string filePath, IDiagnosticLogger? logger) { using var file = fileSystem.CreateFileForWriting(filePath); if (file == Stream.Null) diff --git a/src/Sentry/Internal/Http/CachingTransport.cs b/src/Sentry/Internal/Http/CachingTransport.cs index 395438b57b..f2ffad9c38 100644 --- a/src/Sentry/Internal/Http/CachingTransport.cs +++ b/src/Sentry/Internal/Http/CachingTransport.cs @@ -52,7 +52,7 @@ internal class CachingTransport : ITransport, IDisposable // Inner transport exposed internally primarily for testing internal ITransport InnerTransport => _innerTransport; - private readonly IFileSystem _fileSystem; + private readonly ISentryFileSystem _fileSystem; public static CachingTransport Create(ITransport innerTransport, SentryOptions options, bool startWorker = true, diff --git a/src/Sentry/Internal/IFileSystem.cs b/src/Sentry/Internal/ISentryFileSystem.cs similarity index 96% rename from src/Sentry/Internal/IFileSystem.cs rename to src/Sentry/Internal/ISentryFileSystem.cs index 4a7cab96bf..927eb9eed7 100644 --- a/src/Sentry/Internal/IFileSystem.cs +++ b/src/Sentry/Internal/ISentryFileSystem.cs @@ -1,6 +1,6 @@ namespace Sentry.Internal; -internal interface IFileSystem +internal interface ISentryFileSystem { // Note: This is not comprehensive. If you need other filesystem methods, add to this interface, // then implement in both Sentry.Internal.FileSystem and Sentry.Testing.FakeFileSystem. diff --git a/src/Sentry/Internal/Json.cs b/src/Sentry/Internal/Json.cs index 779a291ca2..a6c85ce5c0 100644 --- a/src/Sentry/Internal/Json.cs +++ b/src/Sentry/Internal/Json.cs @@ -14,7 +14,7 @@ public static T Parse(string json, Func factory) return factory.Invoke(jsonDocument.RootElement); } - public static T Load(IFileSystem fileSystem, string filePath, Func factory) + public static T Load(ISentryFileSystem fileSystem, string filePath, Func factory) { using var file = fileSystem.OpenFileForReading(filePath); using var jsonDocument = JsonDocument.Parse(file); diff --git a/src/Sentry/Internal/FileSystem.cs b/src/Sentry/Internal/SentryFileSystem.cs similarity index 55% rename from src/Sentry/Internal/FileSystem.cs rename to src/Sentry/Internal/SentryFileSystem.cs index 2b927d800d..e3601e3019 100644 --- a/src/Sentry/Internal/FileSystem.cs +++ b/src/Sentry/Internal/SentryFileSystem.cs @@ -1,73 +1,75 @@ +using System.IO.Abstractions; using Sentry.Extensibility; namespace Sentry.Internal; -internal class FileSystem : IFileSystem +internal class SentryFileSystem : ISentryFileSystem { - public static IFileSystem Instance { get; } = new FileSystem(SentrySdk.CurrentOptions); - private readonly SentryOptions? _options; - private FileSystem(SentryOptions? options) + private readonly IFileSystem _fileSystem; + + public SentryFileSystem(SentryOptions? options, IFileSystem? fileSystem = null) { _options = options; + _fileSystem = fileSystem ?? new FileSystem(); } - public IEnumerable EnumerateFiles(string path) => Directory.EnumerateFiles(path); + public IEnumerable EnumerateFiles(string path) => _fileSystem.Directory.EnumerateFiles(path); public IEnumerable EnumerateFiles(string path, string searchPattern) => - Directory.EnumerateFiles(path, searchPattern); + _fileSystem.Directory.EnumerateFiles(path, searchPattern); public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => - Directory.EnumerateFiles(path, searchPattern, searchOption); + _fileSystem.Directory.EnumerateFiles(path, searchPattern, searchOption); public bool CreateDirectory(string path) { - if (_options?.DisableFileWrite is false) + if (!_options?.DisableFileWrite is false) { _options?.LogDebug("Skipping creating directory. Writing to file system has been explicitly disabled."); return false; } - Directory.CreateDirectory(path); + _fileSystem.Directory.CreateDirectory(path); return true; } public bool DeleteDirectory(string path, bool recursive = false) { - if (_options?.DisableFileWrite is false) + if (!_options?.DisableFileWrite is false) { _options?.LogDebug("Skipping deleting directory. Writing to file system has been explicitly disabled."); return false; } - Directory.Delete(path, recursive); + _fileSystem.Directory.Delete(path, recursive); return true; } - public bool DirectoryExists(string path) => Directory.Exists(path); + public bool DirectoryExists(string path) => _fileSystem.Directory.Exists(path); - public bool FileExists(string path) => File.Exists(path); + public bool FileExists(string path) => _fileSystem.File.Exists(path); public bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false) { - if (_options?.DisableFileWrite is false) + if (!_options?.DisableFileWrite is false) { _options?.LogDebug("Skipping moving file. Writing to file system has been explicitly disabled."); return false; } #if NETCOREAPP3_0_OR_GREATER - File.Move(sourceFileName, destFileName, overwrite); + _fileSystem.File.Move(sourceFileName, destFileName, overwrite); #else if (overwrite) { - File.Copy(sourceFileName, destFileName, overwrite: true); - File.Delete(sourceFileName); + _fileSystem.File.Copy(sourceFileName, destFileName, overwrite: true); + _fileSystem.File.Delete(sourceFileName); } else { - File.Move(sourceFileName, destFileName); + _fileSystem.File.Move(sourceFileName, destFileName); } #endif return true; @@ -75,42 +77,42 @@ public bool MoveFile(string sourceFileName, string destFileName, bool overwrite public bool DeleteFile(string path) { - if (_options?.DisableFileWrite is false) + if (!_options?.DisableFileWrite is false) { _options?.LogDebug("Skipping deleting file. Writing to file system has been explicitly disabled."); return false; } - File.Delete(path); + _fileSystem.File.Delete(path); return true; } public DateTimeOffset GetFileCreationTime(string path) => new FileInfo(path).CreationTimeUtc; - public string ReadAllTextFromFile(string path) => File.ReadAllText(path); + public string ReadAllTextFromFile(string path) => _fileSystem.File.ReadAllText(path); - public Stream OpenFileForReading(string path) => File.OpenRead(path); + public Stream OpenFileForReading(string path) => _fileSystem.File.OpenRead(path); public Stream CreateFileForWriting(string path) { - if (_options?.DisableFileWrite is false) + if (!_options?.DisableFileWrite is false) { _options?.LogDebug("Skipping file for writing. Writing to file system has been explicitly disabled."); return Stream.Null; } - return File.Create(path); + return _fileSystem.File.Create(path); } public bool WriteAllTextToFile(string path, string contents) { - if (_options?.DisableFileWrite is false) + if (!_options?.DisableFileWrite is false) { _options?.LogDebug("Skipping writing all text to file. Writing to file system has been explicitly disabled."); return false; } - File.WriteAllText(path, contents); + _fileSystem.File.WriteAllText(path, contents); return true; } } diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 3dc0a6dc0a..df2266b170 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -59,6 +59,14 @@ + + + + + From 9f77eb7580a169ed432053eed4d13d2a631c3dac Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 23 Sep 2024 12:46:37 +0200 Subject: [PATCH 11/18] kiss --- .../Sentry.Samples.Console.Basic/Program.cs | 61 ---------- src/Sentry/ISentryJsonSerializable.cs | 2 +- src/Sentry/Internal/FileSystem.cs | 114 ++++++++++++++++++ src/Sentry/Internal/Http/CachingTransport.cs | 2 +- .../{ISentryFileSystem.cs => IFileSystem.cs} | 2 +- src/Sentry/Internal/Json.cs | 2 +- src/Sentry/Sentry.csproj | 8 -- src/Sentry/SentryOptions.cs | 6 +- .../SamplingTransactionProfilerTests.cs | 4 +- .../Sentry.Testing/FakeFileSystem.cs | 13 +- ...yFileSystemTests.cs => FileSystemTests.cs} | 4 +- .../Sentry.Tests/GlobalSessionManagerTests.cs | 4 +- test/Sentry.Tests/HubTests.cs | 4 +- .../Internals/BackgroundWorkerTests.cs | 4 +- .../Internals/Http/CachingTransportTests.cs | 5 +- .../Internals/InstallationIdHelperTests.cs | 4 +- 16 files changed, 140 insertions(+), 99 deletions(-) create mode 100644 src/Sentry/Internal/FileSystem.cs rename src/Sentry/Internal/{ISentryFileSystem.cs => IFileSystem.cs} (96%) rename src/Sentry/Internal/SentryFileSystem.cs => test/Sentry.Testing/FakeFileSystem.cs (90%) rename test/Sentry.Tests/{SentryFileSystemTests.cs => FileSystemTests.cs} (94%) diff --git a/samples/Sentry.Samples.Console.Basic/Program.cs b/samples/Sentry.Samples.Console.Basic/Program.cs index 0cd25ae811..73d2916161 100644 --- a/samples/Sentry.Samples.Console.Basic/Program.cs +++ b/samples/Sentry.Samples.Console.Basic/Program.cs @@ -35,64 +35,3 @@ options.TracesSampleRate = 1.0; }); -// This starts a new transaction and attaches it to the scope. -var transaction = SentrySdk.StartTransaction("Program Main", "function"); -SentrySdk.ConfigureScope(scope => scope.Transaction = transaction); - -// Do some work. (This is where you'd have your own application logic.) -await FirstFunction(); -await SecondFunction(); -await ThirdFunction(); - -// Always try to finish the transaction successfully. -// Unhandled exceptions will fail the transaction automatically. -// Optionally, you can try/catch the exception, and call transaction.Finish(exception) on failure. -transaction.Finish(); - -async Task FirstFunction() -{ - // This is an example of making an HttpRequest. A trace us automatically captured by Sentry for this. - var messageHandler = new SentryHttpMessageHandler(); - var httpClient = new HttpClient(messageHandler, true); - var html = await httpClient.GetStringAsync("https://example.com/"); - Console.WriteLine(html); -} - -async Task SecondFunction() -{ - var span = transaction.StartChild("function", nameof(SecondFunction)); - - try - { - // Simulate doing some work - await Task.Delay(100); - - // Throw an exception - throw new ApplicationException("Something happened!"); - } - catch (Exception exception) - { - // This is an example of capturing a handled exception. - SentrySdk.CaptureException(exception); - span.Finish(exception); - } - - span.Finish(); -} - -async Task ThirdFunction() -{ - var span = transaction.StartChild("function", nameof(ThirdFunction)); - try - { - // Simulate doing some work - await Task.Delay(100); - - // This is an example of an unhandled exception. It will be captured automatically. - throw new InvalidOperationException("Something happened that crashed the app!"); - } - finally - { - span.Finish(); - } -} diff --git a/src/Sentry/ISentryJsonSerializable.cs b/src/Sentry/ISentryJsonSerializable.cs index a73c753b57..e03b81f56c 100644 --- a/src/Sentry/ISentryJsonSerializable.cs +++ b/src/Sentry/ISentryJsonSerializable.cs @@ -20,7 +20,7 @@ public interface ISentryJsonSerializable internal static class JsonSerializableExtensions { - public static void WriteToFile(this ISentryJsonSerializable serializable, ISentryFileSystem fileSystem, string filePath, IDiagnosticLogger? logger) + public static void WriteToFile(this ISentryJsonSerializable serializable, IFileSystem fileSystem, string filePath, IDiagnosticLogger? logger) { using var file = fileSystem.CreateFileForWriting(filePath); if (file == Stream.Null) diff --git a/src/Sentry/Internal/FileSystem.cs b/src/Sentry/Internal/FileSystem.cs new file mode 100644 index 0000000000..bd8a3b5e04 --- /dev/null +++ b/src/Sentry/Internal/FileSystem.cs @@ -0,0 +1,114 @@ +using Sentry.Extensibility; + +namespace Sentry.Internal; + +internal class FileSystem : IFileSystem +{ + private readonly SentryOptions? _options; + + public FileSystem(SentryOptions? options) + { + _options = options; + } + + public IEnumerable EnumerateFiles(string path) => Directory.EnumerateFiles(path); + + public IEnumerable EnumerateFiles(string path, string searchPattern) => + Directory.EnumerateFiles(path, searchPattern); + + public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => + Directory.EnumerateFiles(path, searchPattern, searchOption); + + public bool CreateDirectory(string path) + { + if (!_options?.DisableFileWrite is false) + { + _options?.LogDebug("Skipping creating directory. Writing to file system has been explicitly disabled."); + return false; + } + + Directory.CreateDirectory(path); + return true; + } + + public bool DeleteDirectory(string path, bool recursive = false) + { + if (!_options?.DisableFileWrite is false) + { + _options?.LogDebug("Skipping deleting directory. Writing to file system has been explicitly disabled."); + return false; + } + + Directory.Delete(path, recursive); + return true; + } + + public bool DirectoryExists(string path) => Directory.Exists(path); + + public bool FileExists(string path) => File.Exists(path); + + public bool MoveFile(string sourceFileName, string destFileName, bool overwrite = false) + { + if (!_options?.DisableFileWrite is false) + { + _options?.LogDebug("Skipping moving file. Writing to file system has been explicitly disabled."); + return false; + } + +#if NETCOREAPP3_0_OR_GREATER + File.Move(sourceFileName, destFileName, overwrite); +#else + if (overwrite) + { + File.Copy(sourceFileName, destFileName, overwrite: true); + File.Delete(sourceFileName); + } + else + { + File.Move(sourceFileName, destFileName); + } +#endif + return true; + } + + public bool DeleteFile(string path) + { + if (!_options?.DisableFileWrite is false) + { + _options?.LogDebug("Skipping deleting file. Writing to file system has been explicitly disabled."); + return false; + } + + File.Delete(path); + return true; + } + + public DateTimeOffset GetFileCreationTime(string path) => new FileInfo(path).CreationTimeUtc; + + public string ReadAllTextFromFile(string path) => File.ReadAllText(path); + + public Stream OpenFileForReading(string path) => File.OpenRead(path); + + public Stream CreateFileForWriting(string path) + { + if (!_options?.DisableFileWrite is false) + { + _options?.LogDebug("Skipping file for writing. Writing to file system has been explicitly disabled."); + return Stream.Null; + } + + return File.Create(path); + } + + public bool WriteAllTextToFile(string path, string contents) + { + if (!_options?.DisableFileWrite is false) + { + _options?.LogDebug("Skipping writing all text to file. Writing to file system has been explicitly disabled."); + return false; + } + + File.WriteAllText(path, contents); + return true; + } +} diff --git a/src/Sentry/Internal/Http/CachingTransport.cs b/src/Sentry/Internal/Http/CachingTransport.cs index f2ffad9c38..395438b57b 100644 --- a/src/Sentry/Internal/Http/CachingTransport.cs +++ b/src/Sentry/Internal/Http/CachingTransport.cs @@ -52,7 +52,7 @@ internal class CachingTransport : ITransport, IDisposable // Inner transport exposed internally primarily for testing internal ITransport InnerTransport => _innerTransport; - private readonly ISentryFileSystem _fileSystem; + private readonly IFileSystem _fileSystem; public static CachingTransport Create(ITransport innerTransport, SentryOptions options, bool startWorker = true, diff --git a/src/Sentry/Internal/ISentryFileSystem.cs b/src/Sentry/Internal/IFileSystem.cs similarity index 96% rename from src/Sentry/Internal/ISentryFileSystem.cs rename to src/Sentry/Internal/IFileSystem.cs index 927eb9eed7..4a7cab96bf 100644 --- a/src/Sentry/Internal/ISentryFileSystem.cs +++ b/src/Sentry/Internal/IFileSystem.cs @@ -1,6 +1,6 @@ namespace Sentry.Internal; -internal interface ISentryFileSystem +internal interface IFileSystem { // Note: This is not comprehensive. If you need other filesystem methods, add to this interface, // then implement in both Sentry.Internal.FileSystem and Sentry.Testing.FakeFileSystem. diff --git a/src/Sentry/Internal/Json.cs b/src/Sentry/Internal/Json.cs index a6c85ce5c0..779a291ca2 100644 --- a/src/Sentry/Internal/Json.cs +++ b/src/Sentry/Internal/Json.cs @@ -14,7 +14,7 @@ public static T Parse(string json, Func factory) return factory.Invoke(jsonDocument.RootElement); } - public static T Load(ISentryFileSystem fileSystem, string filePath, Func factory) + public static T Load(IFileSystem fileSystem, string filePath, Func factory) { using var file = fileSystem.OpenFileForReading(filePath); using var jsonDocument = JsonDocument.Parse(file); diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 634ea69869..3dc0a6dc0a 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -59,14 +59,6 @@ - - - - -