Skip to content

Commit f17476b

Browse files
thomhurstclaude
andcommitted
feat: Add Phase 2 Activity-based logging to FileSystem operations
- Add GetCurrentModuleName() and GetCurrentActivityId() helper methods to ModuleActivityTracing for retrieving activity context - Update File.cs logging methods to include module name and activity ID in all file operations (read, write, copy, move, delete, create, append) - Update Folder.cs logging methods to include module name and activity ID in all folder operations (create, delete, clean, copy, move, get, search) - Add private helper methods LogFileOperation, LogFileOperationWithDestination, LogFolderOperation, LogFolderOperationWithDestination, LogFolderOperationWithExpression, and LogFolderWarning for consistent activity context logging This is Phase 2 of the Activity-based refactor, which uses Activity.Current for context alongside the existing AsyncLocal pattern for backward compatibility. The log messages now include the current module name and activity ID when available. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 5127a46 commit f17476b

3 files changed

Lines changed: 129 additions & 33 deletions

File tree

src/ModularPipelines/FileSystem/File.cs

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
using System.Diagnostics;
12
using System.Diagnostics.CodeAnalysis;
23
using System.Text.Json.Serialization;
34
using Microsoft.Extensions.Logging;
45
using ModularPipelines.Logging;
6+
using ModularPipelines.Tracing;
57

68
namespace ModularPipelines.FileSystem;
79

@@ -35,22 +37,22 @@ internal File(FileInfo fileInfo)
3537
/// <inheritdoc cref="System.IO.File.ReadAllTextAsync(string,System.Text.Encoding,System.Threading.CancellationToken)"/>>
3638
public Task<string> ReadAsync(CancellationToken cancellationToken = default)
3739
{
38-
ModuleLogger.Current.LogInformation("Reading File: {Path}", this);
40+
LogFileOperation("Reading File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
3941

4042
return System.IO.File.ReadAllTextAsync(Path, cancellationToken);
4143
}
4244

4345
/// <inheritdoc cref="System.IO.File.ReadLinesAsync(string,System.Threading.CancellationToken)"/>
4446
public IAsyncEnumerable<string> ReadLinesAsync(CancellationToken cancellationToken = default)
4547
{
46-
ModuleLogger.Current.LogInformation("Reading File: {Path}", this);
48+
LogFileOperation("Reading File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
4749

4850
return System.IO.File.ReadLinesAsync(Path, cancellationToken);
4951
}
5052

5153
public Task<byte[]> ReadBytesAsync(CancellationToken cancellationToken = default)
5254
{
53-
ModuleLogger.Current.LogInformation("Reading File: {Path}", this);
55+
LogFileOperation("Reading File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
5456

5557
return System.IO.File.ReadAllBytesAsync(Path, cancellationToken);
5658
}
@@ -62,28 +64,28 @@ public FileStream GetStream(FileAccess fileAccess = FileAccess.ReadWrite)
6264

6365
public Task WriteAsync(string contents, CancellationToken cancellationToken = default)
6466
{
65-
ModuleLogger.Current.LogInformation("Writing to File: {Path}", this);
67+
LogFileOperation("Writing to File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
6668

6769
return System.IO.File.WriteAllTextAsync(Path, contents, cancellationToken);
6870
}
6971

7072
public Task WriteAsync(byte[] contents, CancellationToken cancellationToken = default)
7173
{
72-
ModuleLogger.Current.LogInformation("Writing to File: {Path}", this);
74+
LogFileOperation("Writing to File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
7375

7476
return System.IO.File.WriteAllBytesAsync(Path, contents, cancellationToken);
7577
}
7678

7779
public Task WriteAsync(IEnumerable<string> contents, CancellationToken cancellationToken = default)
7880
{
79-
ModuleLogger.Current.LogInformation("Writing to File: {Path}", this);
81+
LogFileOperation("Writing to File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
8082

8183
return System.IO.File.WriteAllLinesAsync(Path, contents, cancellationToken);
8284
}
8385

8486
public async Task WriteAsync(ReadOnlyMemory<byte> contents, CancellationToken cancellationToken = default)
8587
{
86-
ModuleLogger.Current.LogInformation("Writing to File: {Path}", this);
88+
LogFileOperation("Writing to File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
8789

8890
var fileStream = System.IO.File.Create(Path);
8991
await using (fileStream.ConfigureAwait(false))
@@ -94,7 +96,7 @@ public async Task WriteAsync(ReadOnlyMemory<byte> contents, CancellationToken ca
9496

9597
public async Task WriteAsync(Stream contents, CancellationToken cancellationToken = default)
9698
{
97-
ModuleLogger.Current.LogInformation("Writing to File: {Path}", this);
99+
LogFileOperation("Writing to File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
98100

99101
var fileStream = System.IO.File.Create(Path);
100102
await using (fileStream.ConfigureAwait(false))
@@ -110,14 +112,14 @@ public async Task WriteAsync(Stream contents, CancellationToken cancellationToke
110112

111113
public Task AppendAsync(string contents, CancellationToken cancellationToken = default)
112114
{
113-
ModuleLogger.Current.LogInformation("Writing to File: {Path}", this);
115+
LogFileOperation("Appending to File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
114116

115117
return System.IO.File.AppendAllTextAsync(Path, contents, cancellationToken);
116118
}
117119

118120
public Task AppendAsync(IEnumerable<string> contents, CancellationToken cancellationToken = default)
119121
{
120-
ModuleLogger.Current.LogInformation("Writing to File: {Path}", this);
122+
LogFileOperation("Appending to File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
121123

122124
return System.IO.File.AppendAllLinesAsync(Path, contents, cancellationToken);
123125
}
@@ -143,7 +145,7 @@ public Task AppendAsync(IEnumerable<string> contents, CancellationToken cancella
143145

144146
public File Create()
145147
{
146-
ModuleLogger.Current.LogInformation("Creating File: {Path}", this);
148+
LogFileOperation("Creating File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
147149

148150
var fileStream = System.IO.File.Create(Path);
149151
fileStream.Dispose();
@@ -174,15 +176,15 @@ public FileAttributes Attributes
174176
/// <inheritdoc cref="FileInfo.Delete"/>>
175177
public void Delete()
176178
{
177-
ModuleLogger.Current.LogInformation("Deleting File: {File}", this);
179+
LogFileOperation("Deleting File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
178180

179181
FileInfo.Delete();
180182
}
181183

182184
/// <inheritdoc cref="FileInfo.MoveTo(string)"/>>
183185
public File MoveTo(string path)
184186
{
185-
ModuleLogger.Current.LogInformation("Moving File: {Source} > {Destination}", this, path);
187+
LogFileOperationWithDestination("Moving File: {Source} > {Destination} [Module: {ModuleName}, Activity: {ActivityId}]", this, path);
186188

187189
FileInfo.MoveTo(path);
188190
return this;
@@ -191,7 +193,7 @@ public File MoveTo(string path)
191193
/// <inheritdoc cref="FileInfo.MoveTo(string)"/>>
192194
public File MoveTo(Folder folder)
193195
{
194-
ModuleLogger.Current.LogInformation("Moving File: {Source} > {Destination}", this, folder);
196+
LogFileOperationWithDestination("Moving File: {Source} > {Destination} [Module: {ModuleName}, Activity: {ActivityId}]", this, folder);
195197

196198
folder.Create();
197199
return MoveTo(System.IO.Path.Combine(folder.Path, Name));
@@ -200,14 +202,14 @@ public File MoveTo(Folder folder)
200202
/// <inheritdoc cref="FileInfo.CopyTo(string)"/>>
201203
public File CopyTo(string path)
202204
{
203-
ModuleLogger.Current.LogInformation("Copying File: {Source} > {Destination}", this, path);
205+
LogFileOperationWithDestination("Copying File: {Source} > {Destination} [Module: {ModuleName}, Activity: {ActivityId}]", this, path);
204206

205207
return FileInfo.CopyTo(path);
206208
}
207209

208210
public File CopyTo(Folder folder)
209211
{
210-
ModuleLogger.Current.LogInformation("Copying File: {Source} > {Destination}", this, folder);
212+
LogFileOperationWithDestination("Copying File: {Source} > {Destination} [Module: {ModuleName}, Activity: {ActivityId}]", this, folder);
211213

212214
folder.Create();
213215
return CopyTo(System.IO.Path.Combine(folder.Path, Name));
@@ -217,7 +219,7 @@ public static File GetNewTemporaryFilePath()
217219
{
218220
var path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
219221

220-
ModuleLogger.Current.LogInformation("Temporary File Path: {Path}", path);
222+
LogFileOperation("Temporary File Path: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", path);
221223

222224
return path!;
223225
}
@@ -301,4 +303,30 @@ public override int GetHashCode()
301303
{
302304
return !Equals(left, right);
303305
}
304-
}
306+
307+
/// <summary>
308+
/// Logs a file operation with Activity context information.
309+
/// </summary>
310+
/// <remarks>
311+
/// Phase 2: Uses Activity.Current for context alongside AsyncLocal for backward compatibility.
312+
/// The log message includes the current module name and activity ID when available.
313+
/// </remarks>
314+
private static void LogFileOperation(string messageTemplate, object? arg1)
315+
{
316+
var moduleName = ModuleActivityTracing.GetCurrentModuleName() ?? "Unknown";
317+
var activityId = ModuleActivityTracing.GetCurrentActivityId();
318+
319+
ModuleLogger.Current.LogInformation(messageTemplate, arg1, moduleName, activityId);
320+
}
321+
322+
/// <summary>
323+
/// Logs a file operation with Activity context information for operations with source and destination.
324+
/// </summary>
325+
private static void LogFileOperationWithDestination(string messageTemplate, object? source, object? destination)
326+
{
327+
var moduleName = ModuleActivityTracing.GetCurrentModuleName() ?? "Unknown";
328+
var activityId = ModuleActivityTracing.GetCurrentActivityId();
329+
330+
ModuleLogger.Current.LogInformation(messageTemplate, source, destination, moduleName, activityId);
331+
}
332+
}

src/ModularPipelines/FileSystem/Folder.cs

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics;
12
using System.Diagnostics.CodeAnalysis;
23
using System.Runtime.CompilerServices;
34
using System.Text.Json.Serialization;
@@ -6,6 +7,7 @@
67
using Microsoft.Extensions.Logging;
78
using ModularPipelines.JsonUtils;
89
using ModularPipelines.Logging;
10+
using ModularPipelines.Tracing;
911

1012
namespace ModularPipelines.FileSystem;
1113

@@ -77,15 +79,15 @@ public Folder Root
7779

7880
public Folder Create()
7981
{
80-
ModuleLogger.Current.LogInformation("Creating Folder: {Path}", this);
82+
LogFolderOperation("Creating Folder: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
8183

8284
Directory.CreateDirectory(Path);
8385
return this;
8486
}
8587

8688
public void Delete()
8789
{
88-
ModuleLogger.Current.LogInformation("Deleting Folder: {Path}", this);
90+
LogFolderOperation("Deleting Folder: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
8991

9092
DirectoryInfo.Delete(true);
9193
}
@@ -107,7 +109,7 @@ public void Clean()
107109
/// </param>
108110
public void Clean(bool removeReadOnlyAttribute)
109111
{
110-
ModuleLogger.Current.LogInformation("Cleaning Folder: {Path}", this);
112+
LogFolderOperation("Cleaning Folder: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", this);
111113

112114
foreach (var directory in DirectoryInfo.EnumerateDirectories("*", SearchOption.TopDirectoryOnly))
113115
{
@@ -122,7 +124,7 @@ public void Clean(bool removeReadOnlyAttribute)
122124
}
123125
catch (Exception ex)
124126
{
125-
ModuleLogger.Current.LogWarning(ex, "Failed to delete directory: {Path}", directory.FullName);
127+
LogFolderWarning(ex, "Failed to delete directory: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", directory.FullName);
126128
}
127129
}
128130

@@ -139,7 +141,7 @@ public void Clean(bool removeReadOnlyAttribute)
139141
}
140142
catch (Exception ex)
141143
{
142-
ModuleLogger.Current.LogWarning(ex, "Failed to delete file: {Path}", file.FullName);
144+
LogFolderWarning(ex, "Failed to delete file: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", file.FullName);
143145
}
144146
}
145147
}
@@ -218,14 +220,14 @@ public Folder CopyTo(string targetPath, bool preserveTimestamps)
218220
targetRootDir.LastAccessTimeUtc = DirectoryInfo.LastAccessTimeUtc;
219221
}
220222

221-
ModuleLogger.Current.LogInformation("Copying Folder: {Source} > {Destination}", this, targetPath);
223+
LogFolderOperationWithDestination("Copying Folder: {Source} > {Destination} [Module: {ModuleName}, Activity: {ActivityId}]", this, targetPath);
222224

223225
return new Folder(targetPath);
224226
}
225227

226228
public Folder MoveTo(string path)
227229
{
228-
ModuleLogger.Current.LogInformation("Moving Folder: {Source} > {Destination}", this, path);
230+
LogFolderOperationWithDestination("Moving Folder: {Source} > {Destination} [Module: {ModuleName}, Activity: {ActivityId}]", this, path);
229231

230232
DirectoryInfo.MoveTo(path);
231233
return this;
@@ -235,7 +237,7 @@ public Folder GetFolder(string name)
235237
{
236238
var directoryInfo = new DirectoryInfo(System.IO.Path.Combine(Path, name));
237239

238-
ModuleLogger.Current.LogInformation("Getting Folder: {Path}", directoryInfo.FullName);
240+
LogFolderOperation("Getting Folder: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", directoryInfo.FullName);
239241

240242
return directoryInfo;
241243
}
@@ -244,7 +246,7 @@ public Folder CreateFolder(string name)
244246
{
245247
var folder = GetFolder(name).Create();
246248

247-
ModuleLogger.Current.LogInformation("Creating Folder: {Path}", folder);
249+
LogFolderOperation("Creating Folder: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", folder);
248250

249251
return folder;
250252
}
@@ -253,7 +255,7 @@ public File GetFile(string name)
253255
{
254256
var fileInfo = new FileInfo(System.IO.Path.Combine(Path, name));
255257

256-
ModuleLogger.Current.LogInformation("Getting File: {Path}", fileInfo.FullName);
258+
LogFolderOperation("Getting File: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", fileInfo.FullName);
257259

258260
return fileInfo;
259261
}
@@ -269,7 +271,7 @@ public File CreateFile(string name)
269271

270272
public IEnumerable<Folder> GetFolders(Func<Folder, bool> predicate, Func<Folder, bool> exclusionFilters, [CallerArgumentExpression("predicate")] string predicateExpression = "")
271273
{
272-
ModuleLogger.Current.LogInformation("Searching Folders in: {Path} > {Expression}", this, predicateExpression);
274+
LogFolderOperationWithExpression("Searching Folders in: {Path} > {Expression} [Module: {ModuleName}, Activity: {ActivityId}]", this, predicateExpression);
273275

274276
return SafeWalk.EnumerateFolders(this, exclusionFilters)
275277
.Select(x => new Folder(x))
@@ -279,7 +281,7 @@ public IEnumerable<Folder> GetFolders(Func<Folder, bool> predicate, Func<Folder,
279281

280282
public IEnumerable<File> GetFiles(Func<File, bool> predicate, Func<Folder, bool> directoryExclusionFilters, [CallerArgumentExpression("predicate")] string predicateExpression = "")
281283
{
282-
ModuleLogger.Current.LogInformation("Searching Files in: {Path} > {Expression}", this, predicateExpression);
284+
LogFolderOperationWithExpression("Searching Files in: {Path} > {Expression} [Module: {ModuleName}, Activity: {ActivityId}]", this, predicateExpression);
283285

284286
return SafeWalk.EnumerateFiles(this, directoryExclusionFilters)
285287
.Select(x => new File(x))
@@ -289,7 +291,7 @@ public IEnumerable<File> GetFiles(Func<File, bool> predicate, Func<Folder, bool>
289291

290292
public IEnumerable<File> GetFiles(string globPattern)
291293
{
292-
ModuleLogger.Current.LogInformation("Searching Files in: {Path} > {Glob}", this, globPattern);
294+
LogFolderOperationWithExpression("Searching Files in: {Path} > {Glob} [Module: {ModuleName}, Activity: {ActivityId}]", this, globPattern);
293295

294296
return new Matcher(StringComparison.OrdinalIgnoreCase)
295297
.AddInclude(globPattern)
@@ -326,7 +328,7 @@ public static Folder CreateTemporaryFolder()
326328
var tempDirectory = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName().Replace(".", string.Empty));
327329
Directory.CreateDirectory(tempDirectory);
328330

329-
ModuleLogger.Current.LogInformation("Creating Temporary Folder: {Path}", tempDirectory);
331+
LogFolderOperation("Creating Temporary Folder: {Path} [Module: {ModuleName}, Activity: {ActivityId}]", tempDirectory);
330332

331333
return tempDirectory!;
332334
}
@@ -434,4 +436,52 @@ private static void RemoveReadOnlyAttributeRecursively(DirectoryInfo directory)
434436
}
435437
}
436438
}
437-
}
439+
440+
/// <summary>
441+
/// Logs a folder operation with Activity context information.
442+
/// </summary>
443+
/// <remarks>
444+
/// Phase 2: Uses Activity.Current for context alongside AsyncLocal for backward compatibility.
445+
/// The log message includes the current module name and activity ID when available.
446+
/// </remarks>
447+
private static void LogFolderOperation(string messageTemplate, object? arg1)
448+
{
449+
var moduleName = ModuleActivityTracing.GetCurrentModuleName() ?? "Unknown";
450+
var activityId = ModuleActivityTracing.GetCurrentActivityId();
451+
452+
ModuleLogger.Current.LogInformation(messageTemplate, arg1, moduleName, activityId);
453+
}
454+
455+
/// <summary>
456+
/// Logs a folder operation with Activity context information for operations with source and destination.
457+
/// </summary>
458+
private static void LogFolderOperationWithDestination(string messageTemplate, object? source, object? destination)
459+
{
460+
var moduleName = ModuleActivityTracing.GetCurrentModuleName() ?? "Unknown";
461+
var activityId = ModuleActivityTracing.GetCurrentActivityId();
462+
463+
ModuleLogger.Current.LogInformation(messageTemplate, source, destination, moduleName, activityId);
464+
}
465+
466+
/// <summary>
467+
/// Logs a folder operation with Activity context information for operations with path and expression/glob.
468+
/// </summary>
469+
private static void LogFolderOperationWithExpression(string messageTemplate, object? path, object? expression)
470+
{
471+
var moduleName = ModuleActivityTracing.GetCurrentModuleName() ?? "Unknown";
472+
var activityId = ModuleActivityTracing.GetCurrentActivityId();
473+
474+
ModuleLogger.Current.LogInformation(messageTemplate, path, expression, moduleName, activityId);
475+
}
476+
477+
/// <summary>
478+
/// Logs a folder warning with Activity context information.
479+
/// </summary>
480+
private static void LogFolderWarning(Exception ex, string messageTemplate, object? arg1)
481+
{
482+
var moduleName = ModuleActivityTracing.GetCurrentModuleName() ?? "Unknown";
483+
var activityId = ModuleActivityTracing.GetCurrentActivityId();
484+
485+
ModuleLogger.Current.LogWarning(ex, messageTemplate, arg1, moduleName, activityId);
486+
}
487+
}

0 commit comments

Comments
 (0)