Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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>5.9.0</SentryVersion>
<SentryVersion>5.11.0-alpha.1</SentryVersion>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Sentry" Version="$(SentryVersion)" />
Expand Down
8 changes: 6 additions & 2 deletions src/SymbolCollector.Android.Library/AndroidUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,29 @@ public AndroidUploader(Client client, ILogger<AndroidUploader> logger)
_logger = logger;
}

public Task StartUpload(string friendlyName, CancellationToken token) =>
public Task StartUpload(string friendlyName, ISpan uploadSpan, CancellationToken token) =>
Task.Run(async () =>
{
var paths = new[] { "/system/lib", "/system/lib64", "/system/", "/vendor/lib" };

_logger.LogInformation("Using friendly name: {friendlyName} and paths: {paths}",
friendlyName, paths);

var androidUploaderSpan = uploadSpan.StartChild("androidUpload", "uploading all paths async");
try
{
await _client.UploadAllPathsAsync(friendlyName, BatchType.Android, paths, token);
await _client.UploadAllPathsAsync(friendlyName, BatchType.Android, paths, androidUploaderSpan, token);
androidUploaderSpan.Finish();
}
catch (OperationCanceledException)
{
androidUploaderSpan.Finish(SpanStatus.Cancelled);
}
catch (Exception e)
{
_logger.LogError(e, "Failed uploading {friendlyName} paths: {paths}",
friendlyName, paths);
androidUploaderSpan.Finish(e);
// Make sure event is flushed and rethrow
await SentrySdk.FlushAsync(TimeSpan.FromSeconds(3));
throw;
Expand Down
2 changes: 1 addition & 1 deletion src/SymbolCollector.Android.Library/AutoUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void Run(Context context)
s.Contexts.OperatingSystem.KernelVersion = uname.Release;
}
});
var uploadTask = uploader.StartUpload(friendlyName, source.Token);
var uploadTask = uploader.StartUpload(friendlyName, tran, source.Token);
uploadTask.ContinueWith(t =>
{
tran.Finish(t.IsCompletedSuccessfully ? SpanStatus.Ok : SpanStatus.UnknownError);
Expand Down
58 changes: 31 additions & 27 deletions src/SymbolCollector.Android/MainActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class MainActivity : Activity
private string _friendlyName = null!; // set on OnCreate
private IHost _host = null!; // set on OnCreate
private IServiceProvider _serviceProvider = null!; // set on OnCreate
private ITransactionTracer _startupTransaction = null!; // set on OnCreate
private SentryTraceHeader _parentTraceHeader = null!;

protected override void OnCreate(Bundle? savedInstanceState)
{
Expand All @@ -35,10 +35,12 @@ protected override void OnCreate(Bundle? savedInstanceState)
_serviceProvider = _host.Services;

// It's set in Host.Init above
SentrySdk.ConfigureScope(s => _startupTransaction = s.Transaction!);
ITransactionTracer startupTransaction = null!;
SentrySdk.ConfigureScope(s => startupTransaction = s.Transaction!);
_parentTraceHeader = startupTransaction.GetTraceHeader();
AddSentryContext();

var span = _startupTransaction.StartChild("OnCreate");
var onCreateSpan = startupTransaction.StartChild("OnCreate");
try
{
base.OnCreate(savedInstanceState);
Expand Down Expand Up @@ -71,10 +73,23 @@ protected override void OnCreate(Bundle? savedInstanceState)
uploadButton.Click += OnUploadButtonOnClick;
cancelButton.Click += OnCancelButtonOnClick;

onCreateSpan.Finish(SpanStatus.Ok);
startupTransaction.Finish(SpanStatus.Ok);
return;

void OnCancelButtonOnClick(object? sender, EventArgs args)
{
SentrySdk.AddBreadcrumb("OnCancelButtonOnClick", category: "ui.event");
Unfocus();
source.Cancel();
}

async void OnUploadButtonOnClick(object? sender, EventArgs args)
{
// The scope tracks the overall app transaction while this is only batch uploading
var uploadTransaction = SentrySdk.StartTransaction("BatchUpload", "batch.upload", _startupTransaction.GetTraceHeader());
var uploadTransaction = SentrySdk.StartTransaction("BatchUpload", "batch.upload", _parentTraceHeader);
// startup transaction has completed at this point, this starts when the user pressed Upload so make this transaction global:
SentrySdk.ConfigureScope(s => s.Transaction = uploadTransaction);

try
{
Expand All @@ -89,32 +104,23 @@ async void OnUploadButtonOnClick(object? sender, EventArgs args)
uploadButton.Enabled = false;
source = new CancellationTokenSource();

var uploadTask = uploader.StartUpload(_friendlyName, source.Token);
var uploadTask = uploader.StartUpload(_friendlyName, uploadTransaction, source.Token);
var updateUiTask = StartUiUpdater(source.Token, metrics);

await UploadAsync(uploadTask, updateUiTask, metrics, cancelButton, uploadButton, uploadTransaction, source);
// Successful completion (or timeout) of the transaction is handled internally.
await AwaitOnUploadAndCancellationTasks(uploadTask, updateUiTask, metrics, cancelButton, uploadButton, uploadTransaction, source);
}
catch (Exception e)
{
uploadTransaction.Finish(e);
throw;
}
}

void OnCancelButtonOnClick(object? sender, EventArgs args)
{
SentrySdk.AddBreadcrumb("OnCancelButtonOnClick", category: "ui.event");
Unfocus();
source.Cancel();
}

span.Finish(SpanStatus.Ok);
_startupTransaction.Finish(SpanStatus.Ok);
}
catch (Exception e)
{
span.Finish(e);
_startupTransaction.Finish(e);
onCreateSpan.Finish(e);
startupTransaction.Finish(e);
throw;
}
}
Expand All @@ -128,13 +134,13 @@ private void Unfocus()
}
}

private async Task UploadAsync(
private async Task AwaitOnUploadAndCancellationTasks(
Task uploadTask,
Task updateUiTask,
ClientMetrics metrics,
View cancelButton,
View uploadButton,
ISpan span,
ISpan uploadTransaction,
CancellationTokenSource source)
{
var container = base.FindViewById(Resource.Id.metrics_container)!;
Expand All @@ -159,28 +165,28 @@ private async Task UploadAsync(
ranForContainer.Visibility = ViewStates.Visible;

ranForLabel.Text = metrics.RanFor.ToString();
span.Finish(SpanStatus.Ok);
uploadTransaction.Finish(SpanStatus.Ok);
}
else if (uploadTask.IsFaulted)
{
await ShowError(uploadTask.Exception);
span.Finish(SpanStatus.InternalError);
uploadTransaction.Finish(SpanStatus.InternalError);
}
else
{
cancelButton.Enabled = false;
uploadButton.Enabled = true;
span.Finish(SpanStatus.Cancelled);
uploadTransaction.Finish(SpanStatus.Cancelled);
}
}
catch (Exception e)
{
await ShowError(e);
span.Finish(e);
uploadTransaction.Finish(e);
}
finally
{
source.Cancel();
await source.CancelAsync();
}
}

Expand Down Expand Up @@ -278,8 +284,6 @@ private void AddSentryContext()

SentrySdk.ConfigureScope(s =>
{
s.Transaction = _startupTransaction;

if (uname is { })
{
s.Contexts["uname"] = new
Expand Down
4 changes: 2 additions & 2 deletions src/SymbolCollector.Console/ConsoleUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public async Task StartUploadSymbols(IEnumerable<string> paths, string bundleId,
var type = batchType ?? DeviceBatchType();
_logger.LogInformation("Uploading bundle {bundleId} of type {type} and paths: {paths}",
bundleId, type, paths);
await _client.UploadAllPathsAsync(bundleId, type, paths, token);
await _client.UploadAllPathsAsync(bundleId, type, paths, transaction, token);
transaction.Finish(SpanStatus.Ok);
}
catch (Exception e)
Expand Down Expand Up @@ -87,4 +87,4 @@ static BatchType DeviceBatchType()

throw new InvalidOperationException("No BatchType available for the current device.");
}
}
}
86 changes: 50 additions & 36 deletions src/SymbolCollector.Core/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,59 +30,73 @@ public Client(
SentrySdk.ConfigureScope(s => s.SetExtra(nameof(Metrics), Metrics));
}

public async Task UploadAllPathsAsync(
string friendlyName,
public async Task UploadAllPathsAsync(string friendlyName,
BatchType type,
IEnumerable<string> topLevelPaths,
ISpan span,
CancellationToken cancellationToken)
{
var groupsSpan = SentrySdk.GetSpan()?.StartChild("group.get", "Get the group of directories to search in parallel");
List<List<string>> groups;
var counter = 0;
var groups =
(from topPath in topLevelPaths
from lookupDirectory in SafeGetDirectories(topPath)
where _blockListedPaths?.Contains(lookupDirectory) != true
let c = counter++
group lookupDirectory by c / ParallelTasks
into grp
select grp.ToList()).ToList();
groupsSpan?.Finish();

var startSpan = SentrySdk.GetSpan()?.StartChild("batch.start");
Guid batchId;
try
{
batchId = await _symbolClient.Start(friendlyName, type, cancellationToken);
startSpan?.Finish(SpanStatus.Ok);
var groupsSpan = span.StartChild("group.get", "Get the group of directories to search in parallel");
groups =
(from topPath in topLevelPaths
from lookupDirectory in SafeGetDirectories(topPath)
where _blockListedPaths?.Contains(lookupDirectory) != true
let c = counter++
group lookupDirectory by c / ParallelTasks
into grp
select grp.ToList()).ToList();
groupsSpan.Finish();
}
catch (Exception e)

Guid batchId;
{
startSpan?.Finish(e);
throw;
var startSpan = span.StartChild("batch.start");
try
{
batchId = await _symbolClient.Start(friendlyName, type, cancellationToken);
startSpan.Finish(SpanStatus.Ok);
}
catch (Exception e)
{
startSpan.Finish(e);
throw;
}
}

var uploadSpan = SentrySdk.GetSpan()?.StartChild("batch.upload");
uploadSpan?.SetTag("groups", groups.Count.ToString());
uploadSpan?.SetTag("total_items", counter.ToString());
try
{
foreach (var group in groups)
var uploadSpan = span.StartChild("batch.upload", "concurrent batch upload");
uploadSpan.SetData("groups", groups.Count.ToString());
uploadSpan.SetData("total_items", counter.ToString());

// use this as parent to all outgoing HTTP requests now:
SentrySdk.ConfigureScope(s => s.Span = uploadSpan);
try
{
await UploadParallel(batchId, group, cancellationToken);
foreach (var group in groups)
{
await UploadParallel(batchId, group, cancellationToken);
}
uploadSpan.Finish(SpanStatus.Ok);
}
catch (Exception e)
{
_logger.LogError(e, "Failed processing files for {batchId}. Rethrowing and leaving the batch open.",
batchId);
uploadSpan.Finish(e);
throw;
}
uploadSpan?.Finish(SpanStatus.Ok);
}
catch (Exception e)

{
uploadSpan?.Finish(e);
_logger.LogError(e, "Failed processing files for {batchId}. Rethrowing and leaving the batch open.",
batchId);
throw;
var stopSpan = span.StartChild("batch.close");
await _symbolClient.Close(batchId, cancellationToken);
stopSpan.Finish(SpanStatus.Ok);
}

var stopSpan = SentrySdk.GetSpan()?.StartChild("batch.close");
await _symbolClient.Close(batchId, cancellationToken);
stopSpan?.Finish(SpanStatus.Ok);
return;

IEnumerable<string> SafeGetDirectories(string path)
{
Expand Down
4 changes: 2 additions & 2 deletions test/SymbolCollector.Core.Tests/ClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public async Task UploadAllPathsAsync_TestFilesDirectory_FilesDetected()
new HttpClient(_fixture.HttpMessageHandler));

var sut = _fixture.GetSut();
await sut.UploadAllPathsAsync("friendly name", BatchType.IOS, new[] {"TestFiles"}, CancellationToken.None);
await sut.UploadAllPathsAsync("friendly name", BatchType.IOS, new[] {"TestFiles"}, SentrySdk.StartTransaction("test", "test-op"), CancellationToken.None);

// number of valid test files in TestFiles
Assert.Equal(12, counter);
Expand All @@ -84,7 +84,7 @@ public async Task UploadAllPathsAsync_TestFilesDirectory_FileCorrectlySent()
Substitute.For<ILogger<ObjectFileParser>>(), new FatBinaryReader());

var sut = _fixture.GetSut();
await sut.UploadAllPathsAsync("friendly name", BatchType.IOS, new[] {"TestFiles"}, CancellationToken.None);
await sut.UploadAllPathsAsync("friendly name", BatchType.IOS, new[] {"TestFiles"}, SentrySdk.StartTransaction("test", "test-op"), CancellationToken.None);

// Make sure all valid test files were picked up
var testFiles = new ObjectFileResultTestCases()
Expand Down