Mirror minidumps to Sentry for easier analysis.

This commit is contained in:
Nick Blakely 2020-09-01 11:04:22 -07:00
parent 4877479528
commit 1f4d4e846c
5 changed files with 78 additions and 8 deletions

View File

@ -1,5 +1,7 @@
namespace Giants.Services
{
using System;
using System.Net.Http.Headers;
using Giants.Services.Core;
using Giants.Services.Services;
using Giants.Services.Store;
@ -22,6 +24,14 @@
services.AddHostedService<InitializerService>();
services.AddHostedService<ServerRegistryCleanupService>();
services.AddHttpClient("Sentry", c =>
{
c.BaseAddress = new Uri(configuration["SentryBaseUri"]);
string sentryAuthenticationToken = configuration["SentryAuthenticationToken"];
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", sentryAuthenticationToken);
});
}
}
}

View File

@ -8,11 +8,13 @@
<PackageReference Include="AutoMapper" Version="10.0.0" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.5.1" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.12.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.7" />
<PackageReference Include="System.Runtime.Caching" Version="4.7.0" />
</ItemGroup>

View File

@ -2,6 +2,9 @@
{
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Microsoft.Extensions.Configuration;
@ -12,18 +15,53 @@
private readonly BlobServiceClient blobServiceClient;
private readonly IConfiguration configuration;
private readonly ILogger<CrashReportService> logger;
private readonly IHttpClientFactory clientFactory;
private const string SentryMinidumpUploadFileKey = "upload_file_minidump";
public CrashReportService(
IConfiguration configuration,
ILogger<CrashReportService> logger)
ILogger<CrashReportService> logger,
IHttpClientFactory clientFactory)
{
this.configuration = configuration;
this.logger = logger;
this.clientFactory = clientFactory;
string blobConnectionString = configuration["BlobConnectionString"];
this.blobServiceClient = new BlobServiceClient(blobConnectionString);
}
public async Task UploadMinidumpToSentry(string fileName, Stream stream)
{
string minidumpUri = this.configuration["SentryMinidumpUri"];
if (string.IsNullOrEmpty(minidumpUri))
{
throw new InvalidOperationException("Minidump URI is not defined.");
}
var httpClient = this.clientFactory.CreateClient("Sentry");
using var zipArchive = new ZipArchive(stream);
var zipEntry = zipArchive.Entries.FirstOrDefault(e => e.Name == "crashdump.dmp");
if (zipEntry == null)
{
throw new InvalidOperationException("No crash dump found in archive.");
}
using var dumpStream = zipEntry.Open();
using var formData = new MultipartFormDataContent
{
{ new StreamContent(dumpStream), SentryMinidumpUploadFileKey, fileName }
};
var response = await httpClient.PostAsync(minidumpUri, formData).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException();
}
}
public async Task ProcessReport(string fileName, string senderIpAddress, Stream stream)
{
this.logger.LogInformation("Processing crash report file {FileName} from {IP}", fileName, senderIpAddress);

View File

@ -5,6 +5,7 @@
public interface ICrashReportService
{
Task UploadMinidumpToSentry(string fileName, Stream stream);
Task ProcessReport(string fileName, string senderIpAddress, Stream stream);
}
}

View File

@ -8,6 +8,8 @@
using Giants.Services.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
[ApiController]
[Route("api/[controller]")]
@ -15,15 +17,20 @@
{
private readonly ICrashReportService crashReportService;
private readonly IHttpContextAccessor httpContextAccessor;
private readonly ILogger<CrashReportsController> logger;
private readonly IConfiguration configuration;
private const long MaximumSizeInBytes = 5242880; // 5MB
public CrashReportsController(
ICrashReportService crashReportService,
IHttpContextAccessor httpContextAccessor)
IHttpContextAccessor httpContextAccessor,
ILogger<CrashReportsController> logger,
IConfiguration configuration)
{
this.crashReportService = crashReportService;
this.httpContextAccessor = httpContextAccessor;
this.logger = logger;
this.configuration = configuration;
}
[HttpPost]
@ -36,6 +43,18 @@
{
await this.crashReportService.ProcessReport(file.FileName, this.GetRequestIpAddress(), stream).ConfigureAwait(false);
}
bool sentryEnabled = Convert.ToBoolean(this.configuration["SentryEnabled"]);
if (!sentryEnabled)
{
this.logger.LogInformation("Skipping Sentry upload; disabled.");
return;
}
using (var stream = file.OpenReadStream())
{
await this.crashReportService.UploadMinidumpToSentry(file.FileName, stream).ConfigureAwait(false);
}
}
private void ValidateFiles(IEnumerable<IFormFile> formFiles)