Skip to content

Commit 10358b1

Browse files
committed
A customer received very large error reports (100Mb JSON) for certain errors. Those are now discarded.
Changed so that the new ReportReceivedAt property is used when displaying incidents.
1 parent 54e1ed8 commit 10358b1

File tree

24 files changed

+77
-39
lines changed

24 files changed

+77
-39
lines changed

src/Server/Coderr.Server.Api/Core/Incidents/Queries/FindIncidentsResultItem.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ protected FindIncidentsResultItem()
5454
public bool IsReOpened { get; set; }
5555

5656
/// <summary>
57-
/// When the last report was received (or when the last user action was made)
57+
/// When someone updated this incident (assigned/closed etc).
5858
/// </summary>
5959
public DateTime LastUpdateAtUtc { get; set; }
6060

@@ -67,5 +67,10 @@ protected FindIncidentsResultItem()
6767
/// Total number of received reports (increased even if the number of stored reports are at the limit)
6868
/// </summary>
6969
public int ReportCount { get; set; }
70+
71+
/// <summary>
72+
/// When we recieved the last report.
73+
/// </summary>
74+
public DateTime LastReportReceivedAtUtc { get; set; }
7075
}
7176
}

src/Server/Coderr.Server.Api/Core/Incidents/Queries/GetIncidentResult.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ public string Description
127127
/// </summary>
128128
public int ReportCount { get; set; }
129129

130+
/// <summary>
131+
/// When we received the last report for this incident.
132+
/// </summary>
133+
public DateTime LastReportReceivedAtUtc { get; set; }
130134

131135
/// <summary>
132136
/// Generated hash code

src/Server/Coderr.Server.App/Core/Reports/Config/ReportConfig.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ namespace codeRR.Server.App.Core.Reports.Config
88
/// </summary>
99
public class ReportConfig : IConfigurationSection
1010
{
11+
/// <summary>
12+
/// Maximum number of bytes that a uncompressed JSON report can be
13+
/// </summary>
14+
/// <remarks>
15+
/// <para>
16+
/// Used to filter out reports that are too large.
17+
/// </para>
18+
/// </remarks>
19+
public int MaxReportJsonSize { get; set; }
20+
1121
/// <summary>
1222
/// Max number of reports per incident
1323
/// </summary>
@@ -24,11 +34,7 @@ public class ReportConfig : IConfigurationSection
2434
/// </remarks>
2535
public int RetentionDays { get; set; }
2636

27-
28-
string IConfigurationSection.SectionName
29-
{
30-
get { return "ReportConfig"; }
31-
}
37+
string IConfigurationSection.SectionName => "ReportConfig";
3238

3339
IDictionary<string, string> IConfigurationSection.ToDictionary()
3440
{
@@ -38,6 +44,8 @@ IDictionary<string, string> IConfigurationSection.ToDictionary()
3844
void IConfigurationSection.Load(IDictionary<string, string> settings)
3945
{
4046
this.AssignProperties(settings);
47+
if (MaxReportJsonSize == 0)
48+
MaxReportJsonSize = 1000000;
4149
}
4250
}
4351
}

src/Server/Coderr.Server.Infrastructure/Configuration/ConfigurationStore.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Diagnostics.CodeAnalysis;
3-
using codeRR.Server.Infrastructure.Configuration.Database;
42

53
namespace codeRR.Server.Infrastructure.Configuration
64
{

src/Server/Coderr.Server.ReportAnalyzer/Handlers/Reports/ReportAnalyzer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ public void Analyze(ClaimsPrincipal user, ErrorReportEntity report)
6262
}
6363
catch (Exception ex)
6464
{
65-
_logger.Fatal("Failed to store report " + JsonConvert.SerializeObject(report), ex);
65+
var reportJson = JsonConvert.SerializeObject(report);
66+
if (reportJson.Length > 1000000)
67+
reportJson = reportJson.Substring(0, 100000) + "[....]";
68+
_logger.Fatal("Failed to init report " + reportJson, ex);
6669
return;
6770
}
6871

src/Server/Coderr.Server.ReportAnalyzer/Inbound/SaveReportHandler.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
using System.Security.Claims;
88
using System.Text;
99
using System.Threading.Tasks;
10-
using codeRR.Server.Infrastructure;
10+
using codeRR.Server.App.Core.Reports.Config;
11+
using codeRR.Server.Infrastructure.Configuration;
1112
using codeRR.Server.ReportAnalyzer.Inbound.Models;
1213
using codeRR.Server.ReportAnalyzer.LibContracts;
1314
using DotNetCqs;
1415
using DotNetCqs.Queues;
1516
using Griffin.Data;
16-
using Griffin.Data.Mapper;
1717
using log4net;
1818
using Newtonsoft.Json;
1919

@@ -24,20 +24,23 @@ namespace codeRR.Server.ReportAnalyzer.Inbound
2424
/// </summary>
2525
public class SaveReportHandler
2626
{
27-
private readonly IAdoNetUnitOfWork _unitOfWork;
27+
private readonly List<Func<NewReportDTO, bool>> _filters = new List<Func<NewReportDTO, bool>>();
2828
private readonly ILog _logger = LogManager.GetLogger(typeof(SaveReportHandler));
2929
private readonly IMessageQueue _queue;
30-
private readonly List<Func<NewReportDTO, bool>> _filters = new List<Func<NewReportDTO, bool>>();
30+
private readonly IAdoNetUnitOfWork _unitOfWork;
31+
private readonly int _maxSizeForJsonErrorReport;
3132

3233
/// <summary>
3334
/// Creates a new instance of <see cref="SaveReportHandler" />.
3435
/// </summary>
3536
/// <param name="queue">Queue to store inbound reports in</param>
3637
/// <exception cref="ArgumentNullException">queueProvider;connectionFactory</exception>
37-
public SaveReportHandler(IMessageQueue queue, IAdoNetUnitOfWork unitOfWork)
38+
public SaveReportHandler(IMessageQueue queue, IAdoNetUnitOfWork unitOfWork, ConfigurationStore configStore)
3839
{
3940
_unitOfWork = unitOfWork;
4041
_queue = queue ?? throw new ArgumentNullException(nameof(queue));
42+
var config= configStore.Load<ReportConfig>();
43+
_maxSizeForJsonErrorReport = config?.MaxReportJsonSize ?? 1000000;
4144
}
4245

4346
public void AddFilter(Func<NewReportDTO, bool> filter)
@@ -46,7 +49,8 @@ public void AddFilter(Func<NewReportDTO, bool> filter)
4649
_filters.Add(filter);
4750
}
4851

49-
public async Task BuildReportAsync(ClaimsPrincipal user, string appKey, string signatureProvidedByTheClient, string remoteAddress,
52+
public async Task BuildReportAsync(ClaimsPrincipal user, string appKey, string signatureProvidedByTheClient,
53+
string remoteAddress,
5054
byte[] reportBody)
5155
{
5256
if (!Guid.TryParse(appKey, out var tempKey))
@@ -70,6 +74,8 @@ public async Task BuildReportAsync(ClaimsPrincipal user, string appKey, string s
7074
}
7175

7276
var report = DeserializeBody(reportBody);
77+
if (report == null)
78+
return;
7379

7480
// correct incorrect clients
7581
if (report.CreatedAtUtc > DateTime.UtcNow)
@@ -130,6 +136,10 @@ private NewReportDTO DeserializeBody(byte[] body)
130136
json = Encoding.UTF8.GetString(body);
131137
}
132138

139+
// protection against very large error reports.
140+
if (json.Length > _maxSizeForJsonErrorReport)
141+
return null;
142+
133143
//to support clients that still use the OneTrueError client library.
134144
json = json.Replace("OneTrueError", "codeRR");
135145

@@ -160,15 +170,14 @@ private async Task<AppInfo> GetAppAsync(string appKey)
160170
};
161171
}
162172
}
163-
164173
}
165174

166175
private async Task StoreInvalidReportAsync(string appKey, string sig, string remoteAddress, byte[] reportBody)
167176
{
168177
try
169178
{
170179
//TODO: Make something generic.
171-
using (var cmd = (SqlCommand)_unitOfWork.CreateCommand())
180+
using (var cmd = (SqlCommand) _unitOfWork.CreateCommand())
172181
{
173182
cmd.CommandText =
174183
@"INSERT INTO InvalidReports(appkey, signature, reportbody, errormessage, createdatutc)
@@ -185,7 +194,6 @@ private async Task StoreInvalidReportAsync(string appKey, string sig, string rem
185194
cmd.AddParameter("createdatutc", DateTime.UtcNow);
186195
await cmd.ExecuteNonQueryAsync();
187196
}
188-
189197
}
190198
catch (Exception ex)
191199
{
@@ -206,7 +214,7 @@ private Task StoreReportAsync(ClaimsPrincipal user, ProcessReport report)
206214
catch (Exception ex)
207215
{
208216
_logger.Error(
209-
"Failed to StoreReport: " + JsonConvert.SerializeObject(new { model = report }), ex);
217+
"Failed to StoreReport: " + JsonConvert.SerializeObject(new {model = report}), ex);
210218
}
211219
return Task.FromResult<object>(null);
212220
}

src/Server/Coderr.Server.SqlServer/Core/Incidents/Queries/FindIncidentResultItemMapper.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public void Map(IDataRecord source, FindIncidentsResultItem destination)
2424
destination.IsReOpened = source["IsReopened"].Equals(1);
2525
destination.ReportCount = (int) source["ReportCount"];
2626
destination.LastUpdateAtUtc = (DateTime) source["UpdatedAtUtc"];
27+
28+
var value = source["LastReportAtUtc"];
29+
destination.LastReportReceivedAtUtc = (DateTime) (value is DBNull ? destination.LastUpdateAtUtc : value);
30+
2731
destination.CreatedAtUtc = (DateTime) source["CreatedAtUtc"];
2832
}
2933
}

src/Server/Coderr.Server.SqlServer/Core/Incidents/Queries/GetIncidentResultMapper.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public GetIncidentResultMapper()
2525
Property(x => x.Solution)
2626
.ToPropertyValue(x => EntitySerializer.Deserialize<IncidentSolution>(x)?.Description);
2727

28+
Property(x => x.LastReportReceivedAtUtc)
29+
.ToPropertyValue(DbConverters.ToEntityDate)
30+
.ColumnName("LastReportAtUtc");
31+
2832
Property(x => x.IsSolved).Ignore();
2933
Property(x => x.IsIgnored).Ignore();
3034

src/Server/Coderr.Server.Web/Areas/Receiver/Controllers/ReportController.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Web;
99
using System.Web.Http;
1010
using codeRR.Server.Infrastructure;
11+
using codeRR.Server.Infrastructure.Configuration;
1112
using codeRR.Server.ReportAnalyzer.Inbound;
1213
using codeRR.Server.Web.Areas.Receiver.Helpers;
1314
using codeRR.Server.Web.Areas.Receiver.Models;
@@ -24,15 +25,17 @@ public class ReportController : ApiController
2425
private static readonly SamplingCounter _samplingCounter = new SamplingCounter();
2526
private readonly ILog _logger = LogManager.GetLogger(typeof(ReportController));
2627
private readonly IMessageQueue _messageQueue;
28+
private readonly ConfigurationStore _configStore;
2729

2830
static ReportController()
2931
{
3032
_samplingCounter.Load();
3133
}
3234

33-
public ReportController(IMessageQueueProvider queueProvider, IAdoNetUnitOfWork unitOfWork)
35+
public ReportController(IMessageQueueProvider queueProvider, IAdoNetUnitOfWork unitOfWork, ConfigurationStore configStore)
3436
{
3537
_unitOfWork = unitOfWork;
38+
_configStore = configStore;
3639
_messageQueue = queueProvider.Open("Reports");
3740
}
3841

@@ -51,7 +54,7 @@ public HttpResponseMessage Index()
5154
[HttpPost, Route("receiver/report/{appKey}")]
5255
public async Task<HttpResponseMessage> Post(string appKey, string sig)
5356
{
54-
if (HttpContext.Current.Request.InputStream.Length > 20000000)
57+
if (HttpContext.Current.Request.InputStream.Length > 2000000)
5558
{
5659
return await KillLargeReportAsync(appKey);
5760
}
@@ -64,7 +67,7 @@ public async Task<HttpResponseMessage> Post(string appKey, string sig)
6467
{
6568
var buffer = new byte[HttpContext.Current.Request.InputStream.Length];
6669
HttpContext.Current.Request.InputStream.Read(buffer, 0, buffer.Length);
67-
var handler = new SaveReportHandler(_messageQueue, _unitOfWork);
70+
var handler = new SaveReportHandler(_messageQueue, _unitOfWork, _configStore);
6871
await handler.BuildReportAsync(User as ClaimsPrincipal, appKey, sig, Request.GetClientIpAddress(), buffer);
6972
return Request.CreateResponse(HttpStatusCode.OK);
7073
}

src/Server/Coderr.Server.Web/Scripts/Models/AllModels.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Server/Coderr.Server.Web/Scripts/Models/AllModels.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -762,19 +762,20 @@ module codeRR.Core.Incidents.Queries {
762762
export class GetIncidentResult {
763763
public static TYPE_NAME: string = 'GetIncidentResult';
764764
public ApplicationId: number;
765+
public AssignedAtUtc: any;
766+
public AssignedTo: string;
767+
public AssignedToId: number;
765768
public ContextCollections: string[];
766769
public CreatedAtUtc: any;
767770
public DayStatistics: codeRR.Core.Incidents.Queries.ReportDay[];
768771
public Description: string;
769772
public FeedbackCount: number;
770-
public AssignedToId: number;
771773
public FullName: string;
772774
public HashCodeIdentifier: string;
773775
public Id: number;
776+
public IncidentState: number;
774777
public IsIgnored: boolean;
775778
public IsReOpened: boolean;
776-
public AssignedTo: string;
777-
public AssignedAtUtc: any;
778779
public IsSolutionShared: boolean;
779780
public IsSolved: boolean;
780781
public PreviousSolutionAtUtc: any;

src/Server/Coderr.Server.Web/ViewModels/Incident/Components/IncidentTable.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)