@@ -9,6 +9,7 @@ namespace Microsoft.ComponentDetection.Detectors.Rust;
99using System . Threading ;
1010using System . Threading . Tasks ;
1111using global ::DotNet . Globbing ;
12+ using Microsoft . ComponentDetection . Common . Telemetry . Records ;
1213using Microsoft . ComponentDetection . Contracts ;
1314using Microsoft . ComponentDetection . Contracts . Internal ;
1415using Microsoft . ComponentDetection . Contracts . TypedComponent ;
@@ -43,6 +44,14 @@ public class RustSbomDetector : FileComponentDetector
4344 private Dictionary < string , Contracts . CargoMetadata > manifestMetadataCache ;
4445 private DetectionMode mode ;
4546
47+ // Telemetry counters
48+ private int skippedCargoTomlCount ;
49+ private int skippedCargoLockCount ;
50+ private int processedCargoTomlCount ;
51+ private int processedCargoLockCount ;
52+ private int processedSbomCount ;
53+ private int totalPackagesInOwnershipMap ;
54+
4655 public RustSbomDetector (
4756 IComponentStreamEnumerableFactory componentStreamEnumerableFactory ,
4857 IObservableDirectoryWalkerFactory walkerFactory ,
@@ -72,6 +81,14 @@ public RustSbomDetector(
7281 this . visitedDirs = new HashSet < string > ( this . pathComparer ) ;
7382 this . visitedGlobRules = [ ] ;
7483 this . manifestMetadataCache = new Dictionary < string , Contracts . CargoMetadata > ( this . pathComparer ) ;
84+
85+ // Initialize telemetry counters
86+ this . skippedCargoTomlCount = 0 ;
87+ this . skippedCargoLockCount = 0 ;
88+ this . processedCargoTomlCount = 0 ;
89+ this . processedCargoLockCount = 0 ;
90+ this . processedSbomCount = 0 ;
91+ this . totalPackagesInOwnershipMap = 0 ;
7592 }
7693
7794 /// <summary>
@@ -159,9 +176,11 @@ protected override async Task<IObservable<ProcessRequest>> OnPrepareDetectionAsy
159176 var ownership = await this . metadataContextBuilder . BuildPackageOwnershipMapAsync ( tomlPaths , cancellationToken ) ;
160177 this . ownershipMap = ownership . PackageToTomls ;
161178 this . manifestMetadataCache = ownership . ManifestToMetadata ;
179+ this . totalPackagesInOwnershipMap = this . ownershipMap ? . Count ?? 0 ;
180+
162181 this . Logger . LogInformation (
163182 "Loaded Rust ownership (packages: {PkgCount}) and metadata cache (manifests: {ManifestCount})" ,
164- this . ownershipMap ? . Count ?? 0 ,
183+ this . totalPackagesInOwnershipMap ,
165184 this . manifestMetadataCache ? . Count ?? 0 ) ;
166185
167186 if ( ownership . FailedManifests ? . Count > 0 )
@@ -177,13 +196,15 @@ protected override async Task<IObservable<ProcessRequest>> OnPrepareDetectionAsy
177196 this . Logger . LogWarning ( ex , "Failed to compute Rust ownership/metadata cache; proceeding without cache" ) ;
178197 this . ownershipMap = null ;
179198 this . manifestMetadataCache = null ;
199+ this . totalPackagesInOwnershipMap = 0 ;
180200 }
181201 }
182202 else
183203 {
184204 this . Logger . LogInformation ( "No Cargo.toml files found; ownership and metadata cache unavailable" ) ;
185205 this . ownershipMap = null ;
186206 this . manifestMetadataCache = null ;
207+ this . totalPackagesInOwnershipMap = 0 ;
187208 }
188209
189210 IEnumerable < ProcessRequest > filteredRequests ;
@@ -251,27 +272,75 @@ protected override async Task OnFileFoundAsync(
251272 if ( this . ShouldSkip ( directory , fileKind , location ) )
252273 {
253274 this . Logger . LogInformation ( "Skipping file due to skip rules: {Location}" , normLocation ) ;
275+
276+ // Increment skip counters
277+ switch ( fileKind )
278+ {
279+ case FileKind . CargoToml :
280+ Interlocked . Increment ( ref this . skippedCargoTomlCount ) ;
281+ break ;
282+ case FileKind . CargoLock :
283+ Interlocked . Increment ( ref this . skippedCargoLockCount ) ;
284+ break ;
285+ case FileKind . CargoSbom :
286+ break ;
287+ default :
288+ break ;
289+ }
290+
254291 return ;
255292 }
256293
257294 if ( this . mode == DetectionMode . SBOM_ONLY )
258295 {
259296 await this . ProcessSbomFileAsync ( processRequest , cancellationToken ) ;
297+ Interlocked . Increment ( ref this . processedSbomCount ) ;
260298 }
261299 else
262300 {
263301 // FALLBACK mode
264302 if ( fileKind == FileKind . CargoToml )
265303 {
266304 await this . ProcessCargoTomlAsync ( processRequest , directory , cancellationToken ) ;
305+ Interlocked . Increment ( ref this . processedCargoTomlCount ) ;
267306 }
268307 else if ( fileKind == FileKind . CargoLock )
269308 {
270309 await this . ProcessCargoLockAsync ( processRequest , directory , cancellationToken ) ;
310+ Interlocked . Increment ( ref this . processedCargoLockCount ) ;
271311 }
272312 }
273313 }
274314
315+ /// <inheritdoc />
316+ protected override Task OnDetectionFinishedAsync ( )
317+ {
318+ // Record telemetry using the using pattern
319+ var totalSkippedFiles = this . skippedCargoTomlCount + this . skippedCargoLockCount ;
320+ var totalProcessedFiles = this . processedCargoTomlCount + this . processedCargoLockCount + this . processedSbomCount ;
321+ var totalFiles = totalSkippedFiles + totalProcessedFiles ;
322+ var skipRatio = totalFiles > 0
323+ ? $ "{ 100.0 * totalSkippedFiles / totalFiles : 0.00} %"
324+ : "0.00%" ;
325+
326+ using var telemetryRecord = new RustDetectionTelemetryRecord
327+ {
328+ DetectionMode = this . mode . ToString ( ) ,
329+ SkippedCargoTomlCount = this . skippedCargoTomlCount ,
330+ SkippedCargoLockCount = this . skippedCargoLockCount ,
331+ TotalSkippedFiles = totalSkippedFiles ,
332+ ProcessedCargoTomlCount = this . processedCargoTomlCount ,
333+ ProcessedCargoLockCount = this . processedCargoLockCount ,
334+ ProcessedSbomCount = this . processedSbomCount ,
335+ TotalProcessedFiles = totalProcessedFiles ,
336+ OwnershipMapPackageCount = this . totalPackagesInOwnershipMap ,
337+ OwnershipMapAvailable = this . ownershipMap != null ,
338+ SkipRatio = skipRatio ,
339+ } ;
340+
341+ return Task . CompletedTask ;
342+ }
343+
275344 /// <summary>
276345 /// Calculates the depth of a directory path by counting the number of directory separators.
277346 /// </summary>
0 commit comments