Adding support for exception instances in Services.NzbDrone

pull/3113/head
kay.one 13 years ago
parent 917ae0f999
commit 98faca17ee

@ -20,14 +20,13 @@ namespace NzbDrone.Common.Contract
{ {
var dic = new Dictionary<string, string> var dic = new Dictionary<string, string>
{ {
{"ExType", Type.NullCheck()}, {"ExType", Type.NullSafe()},
{"Logger", Logger.NullCheck()}, {"Logger", Logger.NullSafe()},
{"Message", LogMessage.NullCheck()}, {"Message", LogMessage.NullSafe()},
{"Str", String.NullCheck()} {"Str", String.NullSafe()}
}; };
return dic; return dic;
} }
} }
} }

@ -0,0 +1,11 @@
using System.Linq;
using Newtonsoft.Json;
namespace NzbDrone.Common.Contract
{
public class ExceptionReportResponse
{
[JsonProperty("id")]
public int ExceptionId { get; set; }
}
}

@ -13,7 +13,7 @@ namespace NzbDrone.Common.Contract
{ {
var dic = new Dictionary<string, string> var dic = new Dictionary<string, string>
{ {
{"Title", Title.NullCheck()}, {"Title", Title.NullSafe()},
}; };
return dic; return dic;

@ -54,6 +54,7 @@
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Contract\ExceptionReportResponse.cs" />
<Compile Include="StringExtention.cs" /> <Compile Include="StringExtention.cs" />
<Compile Include="HttpProvider.cs" /> <Compile Include="HttpProvider.cs" />
<Compile Include="ConfigFileProvider.cs" /> <Compile Include="ConfigFileProvider.cs" />

@ -6,15 +6,15 @@ namespace NzbDrone.Common
public static class StringExtention public static class StringExtention
{ {
public static object NullCheck(this object target) public static object NullSafe(this object target)
{ {
if (target != null) return target; if (target != null) return target;
return "[NULL]"; return "[NULL]";
} }
public static string NullCheck(this string target) public static string NullSafe(this string target)
{ {
return ((object)target).NullCheck().ToString(); return ((object)target).NullSafe().ToString();
} }
} }
} }

@ -0,0 +1,93 @@
using System;
using System.Linq;
using System.Web.Mvc;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Contract;
using NzbDrone.Services.Service.Repository.Reporting;
using Services.PetaPoco;
namespace NzbDrone.Services.Service.Controllers
{
public class ExceptionController : Controller
{
private readonly IDatabase _database;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
private const string OK = "OK";
public ExceptionController(IDatabase database)
{
_database = database;
}
[HttpPost]
public JsonResult ReportNew(ExceptionReport exceptionReport)
{
try
{
var exceptionId = GetExceptionDetailId(exceptionReport);
var exceptionInstance = new ExceptionInstance
{
ExceptionDetail = exceptionId,
IsProduction = exceptionReport.IsProduction,
LogMessage = exceptionReport.LogMessage,
Timestamp = DateTime.Now
};
_database.Insert(exceptionInstance);
return new JsonResult { Data = new ExceptionReportResponse { ExceptionId = exceptionId } };
}
catch (Exception e)
{
logger.FatalException("Error has occurred while logging exception", e);
throw;
}
}
private int GetExceptionDetailId(ExceptionReport exceptionReport)
{
var reportHash = Hash(exceptionReport.Version + exceptionReport.String + exceptionReport.Logger);
var id = _database.FirstOrDefault<int>("SELECT Id FROM Exceptions WHERE Hash =@0", reportHash);
if (id == 0)
{
var exeptionDetail = new ExceptionDetail();
exeptionDetail.Hash = reportHash;
exeptionDetail.Logger = exceptionReport.Logger;
exeptionDetail.String = exceptionReport.String;
exeptionDetail.Type = exceptionReport.Type;
exeptionDetail.Version = exceptionReport.Version;
id = Convert.ToInt32(_database.Insert(exeptionDetail));
}
return id;
}
private static string Hash(string input)
{
uint mCrc = 0xffffffff;
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(input);
foreach (byte myByte in bytes)
{
mCrc ^= ((uint)(myByte) << 24);
for (var i = 0; i < 8; i++)
{
if ((Convert.ToUInt32(mCrc) & 0x80000000) == 0x80000000)
{
mCrc = (mCrc << 1) ^ 0x04C11DB7;
}
else
{
mCrc <<= 1;
}
}
}
return String.Format("{0:x8}", mCrc);
}
}
}

@ -25,7 +25,7 @@ namespace NzbDrone.Services.Service.Controllers
[HttpPost] [HttpPost]
public JsonResult ParseError(ParseErrorReport parseErrorReport) public JsonResult ParseError(ParseErrorReport parseErrorReport)
{ {
logger.Trace(parseErrorReport.NullCheck()); logger.Trace(parseErrorReport.NullSafe());
if (ParseErrorExists(parseErrorReport.Title)) if (ParseErrorExists(parseErrorReport.Title))
return Json(OK); return Json(OK);
@ -63,7 +63,7 @@ namespace NzbDrone.Services.Service.Controllers
} }
catch (Exception) catch (Exception)
{ {
logger.Trace(exceptionReport.NullCheck()); logger.Trace(exceptionReport.NullSafe());
throw; throw;
} }

@ -0,0 +1,38 @@
using System;
using System.Data;
using System.Linq;
using Migrator.Framework;
namespace NzbDrone.Services.Service.Migrations
{
[Migration(20120229)]
public class Migration20120229 : Migration
{
public override void Up()
{
Database.AddTable("ExceptionInstances", new Column("Id", DbType.Int64, ColumnProperty.PrimaryKeyWithIdentity),
new Column("ExceptionDetail", DbType.Int16, ColumnProperty.NotNull),
new Column("LogMessage", DbType.String, 3000, ColumnProperty.NotNull),
MigrationsHelper.TimestampColumn,
MigrationsHelper.ProductionColumn);
Database.AddTable("Exceptions", new Column("Id", DbType.Int64, ColumnProperty.PrimaryKeyWithIdentity),
new Column("Logger", DbType.String, ColumnProperty.NotNull),
new Column("Type", DbType.String, ColumnProperty.NotNull),
new Column("String", DbType.String, ColumnProperty.NotNull),
new Column("Hash", DbType.String, ColumnProperty.NotNull),
MigrationsHelper.VersionColumn);
Database.ExecuteNonQuery("ALTER TABLE ExceptionReports ALTER COLUMN String NTEXT");
Database.ExecuteNonQuery("ALTER TABLE Exceptions ALTER COLUMN String NTEXT");
}
public override void Down()
{
throw new NotImplementedException();
}
}
}

@ -209,8 +209,12 @@
<ItemGroup> <ItemGroup>
<Compile Include="App_Start\Logging.cs" /> <Compile Include="App_Start\Logging.cs" />
<Compile Include="App_Start\NinjectMVC3.cs" /> <Compile Include="App_Start\NinjectMVC3.cs" />
<Compile Include="Controllers\ExceptionController.cs" />
<Compile Include="Helpers\HtmlIncludeExtentions.cs" /> <Compile Include="Helpers\HtmlIncludeExtentions.cs" />
<Compile Include="Migrations\Migration20120226.cs" /> <Compile Include="Migrations\Migration20120226.cs" />
<Compile Include="Migrations\Migration20120229.cs" />
<Compile Include="Repository\Reporting\ExceptionDetail.cs" />
<Compile Include="Repository\Reporting\ExceptionInstance.cs" />
<Compile Include="Services.PetaPoco.cs" /> <Compile Include="Services.PetaPoco.cs" />
<Compile Include="Datastore\Connection.cs" /> <Compile Include="Datastore\Connection.cs" />
<Compile Include="Controllers\DailySeriesController.cs" /> <Compile Include="Controllers\DailySeriesController.cs" />

@ -0,0 +1,17 @@
using System;
using System.Linq;
using Services.PetaPoco;
namespace NzbDrone.Services.Service.Repository.Reporting
{
[TableName("Exceptions")]
public class ExceptionDetail
{
public int Id { get; set; }
public string Logger { get; set; }
public string Type { get; set; }
public string String { get; set; }
public string Hash { get; set; }
public string Version { get; set; }
}
}

@ -0,0 +1,16 @@
using System;
using System.Linq;
using Services.PetaPoco;
namespace NzbDrone.Services.Service.Repository.Reporting
{
[TableName("ExceptionInstances")]
public class ExceptionInstance
{
public long Id { get; set; }
public int ExceptionDetail { get; set; }
public string LogMessage { get; set; }
public DateTime Timestamp { get; set; }
public bool IsProduction { get; set; }
}
}

@ -0,0 +1,133 @@
using System;
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Contract;
using NzbDrone.Services.Service.Controllers;
using NzbDrone.Services.Service.Repository.Reporting;
using NzbDrone.Services.Tests.Framework;
namespace NzbDrone.Services.Tests
{
[TestFixture]
public class ExceptionControllerFixture : ServicesTestBase
{
ExceptionController Controller
{
get
{
return Mocker.Resolve<ExceptionController>();
}
}
private static ExceptionReport CreateExceptionReport()
{
return new ExceptionReport
{
IsProduction = true,
Version = "1.1.2.323456",
UGuid = Guid.NewGuid(),
Logger = "NzbDrone.Logger.Name",
LogMessage = @"Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message",
String = @"Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message",
Type = typeof(InvalidOperationException).Name
};
}
[Test]
public void ReportNew_should_save_instance()
{
var exceptionReport = CreateExceptionReport();
WithRealDb();
Controller.ReportNew(exceptionReport);
var exceptionInstance = Db.Fetch<ExceptionInstance>();
exceptionInstance.Should().HaveCount(1);
exceptionInstance.Single().Id.Should().BeGreaterThan(0);
exceptionInstance.Single().ExceptionDetail.Should().BeGreaterThan(0);
exceptionInstance.Single().IsProduction.Should().Be(exceptionReport.IsProduction);
exceptionInstance.Single().Timestamp.Should().BeWithin(TimeSpan.FromSeconds(4)).Before(DateTime.Now);
exceptionInstance.Single().LogMessage.Should().Be(exceptionReport.LogMessage);
}
[Test]
public void ReportNew_should_return_exception_id()
{
var exceptionReport = CreateExceptionReport();
WithRealDb();
var response = Controller.ReportNew(exceptionReport);
response.Data.Should().BeOfType<ExceptionReportResponse>();
((ExceptionReportResponse)response.Data).ExceptionId.Should().BeGreaterThan(0);
}
[Test]
public void Reporting_exception_more_than_once_should_create_single_detail_with_multiple_instances()
{
var exceptionReport = CreateExceptionReport();
WithRealDb();
var response1 = Controller.ReportNew(exceptionReport);
var response2 = Controller.ReportNew(exceptionReport);
var response3 = Controller.ReportNew(exceptionReport);
var detail = Db.Fetch<ExceptionDetail>();
var instances = Db.Fetch<ExceptionInstance>();
detail.Should().HaveCount(1);
instances.Should().HaveCount(3);
instances.Should().OnlyContain(c => c.ExceptionDetail == detail.Single().Id);
}
}
}

@ -65,6 +65,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ExceptionControllerFixture.cs" />
<Compile Include="ReportingControllerFixture.cs" /> <Compile Include="ReportingControllerFixture.cs" />
<Compile Include="Framework\ServicesTestBase.cs" /> <Compile Include="Framework\ServicesTestBase.cs" />
<Compile Include="Framework\TestDbHelper.cs" /> <Compile Include="Framework\TestDbHelper.cs" />

@ -48,6 +48,24 @@ namespace NzbDrone.Services.Tests
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message", Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message",
String = @"Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message String = @"Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message
Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message Long message Long message Long messageLong messageLong messageLong messageLong messageLong messageLong messageLong messageLong message

Loading…
Cancel
Save