Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<SentryVersion>6.0.0</SentryVersion>
<SentryVersion>6.1.0-alpha.1</SentryVersion>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Sentry" Version="$(SentryVersion)" />
Expand Down
1 change: 1 addition & 0 deletions src/SymbolCollector.Android.Library/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static IHost Init(Context context, string dsn, string? sentryTrace = null
{
o.CaptureFailedRequests = true;
o.EnableLogs = true;
o.Experimental.EnableMetrics = true;

This comment was marked as outdated.


o.SetBeforeSend(@event =>
{
Expand Down
3 changes: 2 additions & 1 deletion src/SymbolCollector.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace SymbolCollector.Console;

internal class Program
{
private static readonly ClientMetrics Metrics = new ClientMetrics();
private static readonly SentryClientMetrics Metrics = new SentryClientMetrics();

static async Task<int> Main(
string? upload = null,
Expand Down Expand Up @@ -221,6 +221,7 @@ private static void Bootstrap(Args args)
o.IsGlobalModeEnabled = true;
o.CaptureFailedRequests = true;
o.EnableLogs = true;
o.Experimental.EnableMetrics = true;

#if DEBUG
o.Environment = "development";
Expand Down
28 changes: 14 additions & 14 deletions src/SymbolCollector.Core/ClientMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,72 +40,72 @@ public class ClientMetrics : IClientMetrics
public int DirectoryDoesNotExistCount => _directoryDoesNotExistCount;
public int FileDoesNotExistCount => _fileDoesNotExistCount;

public void FileProcessed()
public virtual void FileProcessed()
{
Interlocked.Increment(ref _filesProcessedCount);
}

public void MachOFileFound()
public virtual void MachOFileFound()
{
Interlocked.Increment(ref _machOFileFoundCount);
}

public void ElfFileFound()
public virtual void ElfFileFound()
{
Interlocked.Increment(ref _elfFileFoundCount);
}

public void FatMachOFileFound()
public virtual void FatMachOFileFound()
{
Interlocked.Increment(ref _fatMachOFileFoundCount);
}

public void FailedToUpload()
public virtual void FailedToUpload()
{
Interlocked.Increment(ref _failedToUploadCount);
}

public void FailedToParse()
public virtual void FailedToParse()
{
Interlocked.Increment(ref _failedToParse);
}

public void SuccessfulUpload()
public virtual void SuccessfulUpload()
{
Interlocked.Increment(ref _successfullyUploadCount);
}

public void AlreadyExisted()
public virtual void AlreadyExisted()
{
Interlocked.Increment(ref _alreadyExistedCount);
}

public void JobsInFlightRemove(int tasksCount)
public virtual void JobsInFlightRemove(int tasksCount)
{
Interlocked.Add(ref _jobsInFlightCount, -tasksCount);
}

public void JobsInFlightAdd(int tasksCount)
public virtual void JobsInFlightAdd(int tasksCount)
{
Interlocked.Add(ref _jobsInFlightCount, tasksCount);
}

public void UploadedBytesAdd(long bytes)
public virtual void UploadedBytesAdd(long bytes)
{
Interlocked.Add(ref _uploadedBytesCount, bytes);
}

public void FileOrDirectoryUnauthorizedAccess()
public virtual void FileOrDirectoryUnauthorizedAccess()
{
Interlocked.Increment(ref _fileOrDirectoryUnauthorizedAccessCount);
}

public void DirectoryDoesNotExist()
public virtual void DirectoryDoesNotExist()
{
Interlocked.Increment(ref _directoryDoesNotExistCount);
}

public void FileDoesNotExist()
public virtual void FileDoesNotExist()
{
Interlocked.Increment(ref _fileDoesNotExistCount);
}
Expand Down
171 changes: 171 additions & 0 deletions src/SymbolCollector.Core/SentryClientMetrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#pragma warning disable SENTRYTRACECONNECTEDMETRICS // IHub.Metrics is experimental
using Sentry;
using Sentry.Extensibility;

namespace SymbolCollector.Core;

/// <summary>
/// A decorator for <see cref="ClientMetrics"/> that also emits metrics to Sentry's trace-connected metrics API.
/// </summary>
/// <remarks>
/// This integrates with Sentry SDK 6.1.0's new experimental trace-connected metrics feature.
/// Metrics are attached to the current trace/span for correlation in Sentry's UI.
/// Enable with <c>options.Experimental.EnableMetrics = true</c>.
/// </remarks>
public class SentryClientMetrics : ClientMetrics
{
private readonly IHub _hub;

/// <summary>
/// Creates a new instance using the default <see cref="HubAdapter.Instance"/>.
/// </summary>
public SentryClientMetrics() : this(HubAdapter.Instance)
{
}

/// <summary>
/// Creates a new instance with the specified hub for metrics emission.
/// </summary>
/// <param name="hub">The Sentry hub to use for emitting metrics.</param>
public SentryClientMetrics(IHub hub)
{
_hub = hub;
}

private SentryTraceMetrics Metrics => _hub.Metrics;

/// <summary>
/// Records a file processed event, incrementing both local and Sentry counters.
/// </summary>
public override void FileProcessed()
{
base.FileProcessed();
Metrics.AddCounter("symbol_collector.files_processed", 1);
}

/// <summary>
/// Records a Mach-O file discovery.
/// </summary>
public override void MachOFileFound()
{
base.MachOFileFound();
Metrics.AddCounter("symbol_collector.debug_images_found", 1,
[new KeyValuePair<string, object>("format", "macho")]);
}

/// <summary>
/// Records an ELF file discovery.
/// </summary>
public override void ElfFileFound()
{
base.ElfFileFound();
Metrics.AddCounter("symbol_collector.debug_images_found", 1,
[new KeyValuePair<string, object>("format", "elf")]);
}

/// <summary>
/// Records a Fat Mach-O file discovery.
/// </summary>
public override void FatMachOFileFound()
{
base.FatMachOFileFound();
Metrics.AddCounter("symbol_collector.debug_images_found", 1,
[new KeyValuePair<string, object>("format", "fat_macho")]);
}

/// <summary>
/// Records a failed upload.
/// </summary>
public override void FailedToUpload()
{
base.FailedToUpload();
Metrics.AddCounter("symbol_collector.uploads", 1,
[new KeyValuePair<string, object>("status", "failed")]);
}

/// <summary>
/// Records a parse failure.
/// </summary>
public override void FailedToParse()
{
base.FailedToParse();
Metrics.AddCounter("symbol_collector.parse_failures", 1);
}

/// <summary>
/// Records a successful upload.
/// </summary>
public override void SuccessfulUpload()
{
base.SuccessfulUpload();
Metrics.AddCounter("symbol_collector.uploads", 1,
[new KeyValuePair<string, object>("status", "success")]);
}

/// <summary>
/// Records when a file already existed on the server.
/// </summary>
public override void AlreadyExisted()
{
base.AlreadyExisted();
Metrics.AddCounter("symbol_collector.uploads", 1,
[new KeyValuePair<string, object>("status", "already_exists")]);
}

/// <summary>
/// Removes jobs from the in-flight count.
/// </summary>
public override void JobsInFlightRemove(int tasksCount)
{
base.JobsInFlightRemove(tasksCount);
Metrics.RecordGauge("symbol_collector.jobs_in_flight", JobsInFlightCount);
}

/// <summary>
/// Adds jobs to the in-flight count.
/// </summary>
public override void JobsInFlightAdd(int tasksCount)
{
base.JobsInFlightAdd(tasksCount);
Metrics.RecordGauge("symbol_collector.jobs_in_flight", JobsInFlightCount);
}

/// <summary>
/// Records bytes uploaded, emitting as a distribution for percentile analysis.
/// </summary>
public override void UploadedBytesAdd(long bytes)
{
base.UploadedBytesAdd(bytes);
Metrics.RecordDistribution("symbol_collector.uploaded_bytes", bytes, "byte");
}

/// <summary>
/// Records an unauthorized access error.
/// </summary>
public override void FileOrDirectoryUnauthorizedAccess()
{
base.FileOrDirectoryUnauthorizedAccess();
Metrics.AddCounter("symbol_collector.access_errors", 1,
[new KeyValuePair<string, object>("type", "unauthorized")]);
}

/// <summary>
/// Records a directory not found error.
/// </summary>
public override void DirectoryDoesNotExist()
{
base.DirectoryDoesNotExist();
Metrics.AddCounter("symbol_collector.access_errors", 1,
[new KeyValuePair<string, object>("type", "directory_not_found")]);
}

/// <summary>
/// Records a file not found error.
/// </summary>
public override void FileDoesNotExist()
{
base.FileDoesNotExist();
Metrics.AddCounter("symbol_collector.access_errors", 1,
[new KeyValuePair<string, object>("type", "file_not_found")]);
}
}
4 changes: 2 additions & 2 deletions src/SymbolCollector.Core/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ private static void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Client>();
services.AddSingleton<ObjectFileParser>();
services.AddSingleton<ClientMetrics>();
services.AddSingleton<SentryClientMetrics>();
services.AddSingleton<ClientMetrics>(sp => sp.GetRequiredService<SentryClientMetrics>());
services.AddSingleton<FatBinaryReader>();
services.AddSingleton<ClientMetrics>();

services.AddOptions<SymbolClientOptions>()
.Configure<IConfiguration>((o, f) => f.Bind("SymbolClient", o))
Expand Down
1 change: 1 addition & 0 deletions src/SymbolCollector.Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ private static IHostBuilder CreateHostBuilder(string[] args) =>
o.MinimumBreadcrumbLevel = LogLevel.Debug;
o.CaptureFailedRequests = true;
o.EnableLogs = true;
o.Experimental.EnableMetrics = true;

// https://github.com/getsentry/symbol-collector/issues/205
// o.CaptureBlockingCalls = true;
Expand Down
3 changes: 2 additions & 1 deletion src/SymbolCollector.Server/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public void ConfigureServices(IServiceCollection services)

services.AddSingleton<ObjectFileParser>();
services.AddSingleton<FatBinaryReader>();
services.AddSingleton<ClientMetrics>();
services.AddSingleton<SentryClientMetrics>();
services.AddSingleton<ClientMetrics>(sp => sp.GetRequiredService<SentryClientMetrics>());
services.AddSingleton<IBatchFinalizer, SymsorterBatchFinalizer>();
services.AddSingleton<ISymbolGcsWriter, SymbolGcsWriter>();
services.AddSingleton<IStorageClientFactory, StorageClientFactory>();
Expand Down
Loading