Finished Exception Controller

pull/4/head
kay.one 13 years ago
parent 98faca17ee
commit 9cebfab2f4

@ -5,7 +5,7 @@ namespace NzbDrone.Common.Contract
{ {
public class ExceptionReportResponse public class ExceptionReportResponse
{ {
[JsonProperty("id")] [JsonProperty("h")]
public int ExceptionId { get; set; } public string ExceptionHash { get; set; }
} }
} }

@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace NzbDrone.Common.Contract
{
public class ExistingExceptionReport : ReportBase
{
[JsonProperty("h")]
public string Hash { get; set; }
[JsonProperty("lm")]
public string LogMessage { get; set; }
protected override Dictionary<string, string> GetString()
{
var dic = new Dictionary<string, string>
{
{"Message", LogMessage.NullSafe()}
};
return dic;
}
}
}

@ -55,6 +55,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Contract\ExceptionReportResponse.cs" /> <Compile Include="Contract\ExceptionReportResponse.cs" />
<Compile Include="Contract\ExistingExceptionReport.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" />

@ -15,7 +15,7 @@ namespace NzbDrone.Services.Service.App_Start
public static void PreStart() public static void PreStart()
{ {
string logPath = string.Format("C:\\NLog\\{0}\\{1}\\${{shortdate}}.log", HostingEnvironment.SiteName, new EnviromentProvider().Version); string logPath = string.Format("C:\\NLog\\{0}\\{1}\\${{shortdate}}-${{logger}}.log", HostingEnvironment.SiteName, new EnviromentProvider().Version);
string error = string.Format("C:\\NLog\\{0}\\{1}\\${{shortdate}}_Error.log", HostingEnvironment.SiteName, new EnviromentProvider().Version); string error = string.Format("C:\\NLog\\{0}\\{1}\\${{shortdate}}_Error.log", HostingEnvironment.SiteName, new EnviromentProvider().Version);
LogConfiguration.RegisterUdpLogger(); LogConfiguration.RegisterUdpLogger();

@ -2,7 +2,6 @@
using System.Linq; using System.Linq;
using System.Web.Mvc; using System.Web.Mvc;
using NLog; using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Contract; using NzbDrone.Common.Contract;
using NzbDrone.Services.Service.Repository.Reporting; using NzbDrone.Services.Service.Repository.Reporting;
using Services.PetaPoco; using Services.PetaPoco;
@ -14,23 +13,53 @@ namespace NzbDrone.Services.Service.Controllers
private readonly IDatabase _database; private readonly IDatabase _database;
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
private const string OK = "OK";
public ExceptionController(IDatabase database) public ExceptionController(IDatabase database)
{ {
_database = database; _database = database;
} }
[HttpPost]
public EmptyResult ReportExisting(ExistingExceptionReport existingExceptionReport)
{
try
{
if (ExceptionHashExists(existingExceptionReport.Hash))
{
var exceptionInstance = new ExceptionInstance
{
ExceptionHash = existingExceptionReport.Hash,
IsProduction = existingExceptionReport.IsProduction,
LogMessage = existingExceptionReport.LogMessage,
Timestamp = DateTime.Now
};
_database.Insert(exceptionInstance);
}
else
{
logger.Warn("Invalid exception hash '{0}'", existingExceptionReport.Hash);
}
}
catch (Exception e)
{
logger.FatalException("Error has occurred while saving exception", e);
throw;
}
return new EmptyResult();
}
[HttpPost] [HttpPost]
public JsonResult ReportNew(ExceptionReport exceptionReport) public JsonResult ReportNew(ExceptionReport exceptionReport)
{ {
try try
{ {
var exceptionId = GetExceptionDetailId(exceptionReport); var exceptionHash = GetExceptionDetailId(exceptionReport);
var exceptionInstance = new ExceptionInstance var exceptionInstance = new ExceptionInstance
{ {
ExceptionDetail = exceptionId, ExceptionHash = exceptionHash,
IsProduction = exceptionReport.IsProduction, IsProduction = exceptionReport.IsProduction,
LogMessage = exceptionReport.LogMessage, LogMessage = exceptionReport.LogMessage,
Timestamp = DateTime.Now Timestamp = DateTime.Now
@ -38,22 +67,20 @@ namespace NzbDrone.Services.Service.Controllers
_database.Insert(exceptionInstance); _database.Insert(exceptionInstance);
return new JsonResult { Data = new ExceptionReportResponse { ExceptionId = exceptionId } }; return new JsonResult { Data = new ExceptionReportResponse { ExceptionHash = exceptionHash } };
} }
catch (Exception e) catch (Exception e)
{ {
logger.FatalException("Error has occurred while logging exception", e); logger.FatalException("Error has occurred while saving exception", e);
throw; throw;
} }
} }
private string GetExceptionDetailId(ExceptionReport exceptionReport)
private int GetExceptionDetailId(ExceptionReport exceptionReport)
{ {
var reportHash = Hash(exceptionReport.Version + exceptionReport.String + exceptionReport.Logger); var reportHash = Hash(String.Concat(exceptionReport.Version, exceptionReport.String, exceptionReport.Logger));
var id = _database.FirstOrDefault<int>("SELECT Id FROM Exceptions WHERE Hash =@0", reportHash);
if (!ExceptionHashExists(reportHash))
if (id == 0)
{ {
var exeptionDetail = new ExceptionDetail(); var exeptionDetail = new ExceptionDetail();
exeptionDetail.Hash = reportHash; exeptionDetail.Hash = reportHash;
@ -62,10 +89,15 @@ namespace NzbDrone.Services.Service.Controllers
exeptionDetail.Type = exceptionReport.Type; exeptionDetail.Type = exceptionReport.Type;
exeptionDetail.Version = exceptionReport.Version; exeptionDetail.Version = exceptionReport.Version;
id = Convert.ToInt32(_database.Insert(exeptionDetail)); _database.Insert(exeptionDetail);
} }
return id; return reportHash;
}
private bool ExceptionHashExists(string reportHash)
{
return _database.Exists<ExceptionDetail>(reportHash);
} }
private static string Hash(string input) private static string Hash(string input)

@ -10,24 +10,25 @@ namespace NzbDrone.Services.Service.Migrations
{ {
public override void Up() public override void Up()
{ {
Database.AddTable("ExceptionInstances", new Column("Id", DbType.Int64, ColumnProperty.PrimaryKeyWithIdentity), Database.AddTable("ExceptionInstances", new Column("Id", DbType.Int64, ColumnProperty.PrimaryKeyWithIdentity),
new Column("ExceptionDetail", DbType.Int16, ColumnProperty.NotNull), new Column("ExceptionHash", DbType.String, ColumnProperty.NotNull),
new Column("LogMessage", DbType.String, 3000, ColumnProperty.NotNull), new Column("LogMessage", DbType.String, 3000, ColumnProperty.NotNull),
MigrationsHelper.TimestampColumn, MigrationsHelper.TimestampColumn,
MigrationsHelper.ProductionColumn); MigrationsHelper.ProductionColumn);
Database.AddTable("Exceptions", new Column("Id", DbType.Int64, ColumnProperty.PrimaryKeyWithIdentity), Database.AddTable("Exceptions", new Column("Hash", DbType.String, ColumnProperty.Unique),
new Column("Logger", DbType.String, ColumnProperty.NotNull), new Column("Logger", DbType.String, ColumnProperty.NotNull),
new Column("Type", DbType.String, ColumnProperty.NotNull), new Column("Type", DbType.String, ColumnProperty.NotNull),
new Column("String", DbType.String, ColumnProperty.NotNull), new Column("String", DbType.String, ColumnProperty.NotNull),
new Column("Hash", DbType.String, ColumnProperty.NotNull),
MigrationsHelper.VersionColumn); MigrationsHelper.VersionColumn);
var indexName = MigrationsHelper.GetIndexName("Exceptions", "Hash");
Database.AddIndex(indexName, "Exceptions", "Hash");
//Database.AddForeignKey("FK_Exceptions_ExceptionInstances", "Exceptions", "Hash", "ExceptionInstances", "ExceptionHash", ForeignKeyConstraint.Cascade);
Database.ExecuteNonQuery("ALTER TABLE ExceptionReports ALTER COLUMN String NTEXT"); Database.ExecuteNonQuery("ALTER TABLE ExceptionReports ALTER COLUMN String NTEXT");
Database.ExecuteNonQuery("ALTER TABLE Exceptions ALTER COLUMN String NTEXT"); Database.ExecuteNonQuery("ALTER TABLE Exceptions ALTER COLUMN String NTEXT");
} }
public override void Down() public override void Down()

@ -20,7 +20,7 @@ namespace NzbDrone.Services.Service.Migrations
try try
{ {
var migrator = new Migrator.Migrator("sqlserver", connetionString, Assembly.GetAssembly(typeof(MigrationsHelper)), true, new MigrationLogger()); var migrator = new Migrator.Migrator("sqlserver", connetionString, Assembly.GetAssembly(typeof(MigrationsHelper)), true);
migrator.MigrateToLastVersion(); migrator.MigrateToLastVersion();
logger.Info("Database migration completed"); logger.Info("Database migration completed");
} }

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

@ -8,7 +8,7 @@ namespace NzbDrone.Services.Service.Repository.Reporting
public class ExceptionInstance public class ExceptionInstance
{ {
public long Id { get; set; } public long Id { get; set; }
public int ExceptionDetail { get; set; } public string ExceptionHash { get; set; }
public string LogMessage { get; set; } public string LogMessage { get; set; }
public DateTime Timestamp { get; set; } public DateTime Timestamp { get; set; }
public bool IsProduction { get; set; } public bool IsProduction { get; set; }

@ -0,0 +1,66 @@
using System;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Contract;
using NzbDrone.Services.Service.Repository.Reporting;
using NzbDrone.Services.Tests.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Services.Tests.ExceptionController
{
[TestFixture]
public class ReportExistingFixture : ServicesTestBase
{
Service.Controllers.ExceptionController Controller
{
get
{
return Mocker.Resolve<Service.Controllers.ExceptionController>();
}
}
private static ExistingExceptionReport CreateExceptionReport()
{
return new ExistingExceptionReport
{
IsProduction = true,
Version = "1.1.2.323456",
UGuid = Guid.NewGuid(),
LogMessage = @"Log message",
Hash = "ABC123"
};
}
[Test]
public void should_log_warn_if_hash_doesnt_exist()
{
WithRealDb();
Controller.ReportExisting(CreateExceptionReport());
Db.Fetch<ExceptionDetail>().Should().BeEmpty();
Db.Fetch<ExceptionInstance>().Should().BeEmpty();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_save_instance_if_hash_is_valid()
{
WithRealDb();
var existing = CreateExceptionReport();
Db.Insert(Builder<ExceptionDetail>.CreateNew().With(c => c.Hash = existing.Hash).Build());
Controller.ReportExisting(CreateExceptionReport());
Db.Fetch<ExceptionDetail>().Should().HaveCount(1);
Db.Fetch<ExceptionInstance>().Should().HaveCount(1);
}
}
}

@ -3,21 +3,20 @@ using System.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Contract; using NzbDrone.Common.Contract;
using NzbDrone.Services.Service.Controllers;
using NzbDrone.Services.Service.Repository.Reporting; using NzbDrone.Services.Service.Repository.Reporting;
using NzbDrone.Services.Tests.Framework; using NzbDrone.Services.Tests.Framework;
namespace NzbDrone.Services.Tests namespace NzbDrone.Services.Tests.ExceptionController
{ {
[TestFixture] [TestFixture]
public class ExceptionControllerFixture : ServicesTestBase public class ReportNewFixture : ServicesTestBase
{ {
ExceptionController Controller Service.Controllers.ExceptionController Controller
{ {
get get
{ {
return Mocker.Resolve<ExceptionController>(); return Mocker.Resolve<Service.Controllers.ExceptionController>();
} }
} }
@ -89,12 +88,30 @@ namespace NzbDrone.Services.Tests
var exceptionInstance = Db.Fetch<ExceptionInstance>(); var exceptionInstance = Db.Fetch<ExceptionInstance>();
exceptionInstance.Should().HaveCount(1); exceptionInstance.Should().HaveCount(1);
exceptionInstance.Single().Id.Should().BeGreaterThan(0); exceptionInstance.Single().Id.Should().BeGreaterThan(0);
exceptionInstance.Single().ExceptionDetail.Should().BeGreaterThan(0); exceptionInstance.Single().ExceptionHash.Should().NotBeBlank();
exceptionInstance.Single().IsProduction.Should().Be(exceptionReport.IsProduction); exceptionInstance.Single().IsProduction.Should().Be(exceptionReport.IsProduction);
exceptionInstance.Single().Timestamp.Should().BeWithin(TimeSpan.FromSeconds(4)).Before(DateTime.Now); exceptionInstance.Single().Timestamp.Should().BeWithin(TimeSpan.FromSeconds(4)).Before(DateTime.Now);
exceptionInstance.Single().LogMessage.Should().Be(exceptionReport.LogMessage); exceptionInstance.Single().LogMessage.Should().Be(exceptionReport.LogMessage);
} }
[Test]
public void ReportNew_should_save_detail()
{
var exceptionReport = CreateExceptionReport();
WithRealDb();
Controller.ReportNew(exceptionReport);
var exceptionDetails = Db.Fetch<ExceptionDetail>();
exceptionDetails.Should().HaveCount(1);
exceptionDetails.Single().Hash.Should().NotBeBlank();
exceptionDetails.Single().Logger.Should().Be(exceptionReport.Logger);
exceptionDetails.Single().Type.Should().Be(exceptionReport.Type);
exceptionDetails.Single().String.Should().Be(exceptionReport.String);
exceptionDetails.Single().Version.Should().Be(exceptionReport.Version);
}
[Test] [Test]
public void ReportNew_should_return_exception_id() public void ReportNew_should_return_exception_id()
{ {
@ -105,7 +122,7 @@ namespace NzbDrone.Services.Tests
var response = Controller.ReportNew(exceptionReport); var response = Controller.ReportNew(exceptionReport);
response.Data.Should().BeOfType<ExceptionReportResponse>(); response.Data.Should().BeOfType<ExceptionReportResponse>();
((ExceptionReportResponse)response.Data).ExceptionId.Should().BeGreaterThan(0); ((ExceptionReportResponse)response.Data).ExceptionHash.Should().NotBeBlank();
} }
@ -119,15 +136,77 @@ namespace NzbDrone.Services.Tests
var response1 = Controller.ReportNew(exceptionReport); var response1 = Controller.ReportNew(exceptionReport);
var response2 = Controller.ReportNew(exceptionReport); var response2 = Controller.ReportNew(exceptionReport);
var response3 = Controller.ReportNew(exceptionReport); var response3 = Controller.ReportNew(exceptionReport);
var detail = Db.Fetch<ExceptionDetail>(); var detail = Db.Fetch<ExceptionDetail>();
var instances = Db.Fetch<ExceptionInstance>(); var instances = Db.Fetch<ExceptionInstance>();
detail.Should().HaveCount(1); detail.Should().HaveCount(1);
instances.Should().HaveCount(3); instances.Should().HaveCount(3);
instances.Should().OnlyContain(c => c.ExceptionDetail == detail.Single().Id); instances.Should().OnlyContain(c => c.ExceptionHash == detail.Single().Hash);
}
[Test]
public void Reporting_exception_with_diffrent_version_should_create_new_detail()
{
var exceptionReport1 = CreateExceptionReport();
exceptionReport1.Version = "0.1.1";
var exceptionReport2 = CreateExceptionReport();
exceptionReport2.Version = "0.2.1";
WithRealDb();
Controller.ReportNew(exceptionReport1);
Controller.ReportNew(exceptionReport2);
var detail = Db.Fetch<ExceptionDetail>();
var instances = Db.Fetch<ExceptionInstance>();
detail.Should().HaveCount(2);
instances.Should().HaveCount(2);
}
[Test]
public void Reporting_exception_with_diffrent_strting_should_create_new_detail()
{
var exceptionReport1 = CreateExceptionReport();
exceptionReport1.String = "Error1";
var exceptionReport2 = CreateExceptionReport();
exceptionReport2.String = "Error2";
WithRealDb();
Controller.ReportNew(exceptionReport1);
Controller.ReportNew(exceptionReport2);
var detail = Db.Fetch<ExceptionDetail>();
var instances = Db.Fetch<ExceptionInstance>();
detail.Should().HaveCount(2);
instances.Should().HaveCount(2);
}
[Test]
public void Reporting_exception_with_diffrent_logger_should_create_new_detail()
{
var exceptionReport1 = CreateExceptionReport();
exceptionReport1.Logger = "logger1";
var exceptionReport2 = CreateExceptionReport();
exceptionReport2.Logger = "logger2";
WithRealDb();
Controller.ReportNew(exceptionReport1);
Controller.ReportNew(exceptionReport2);
var detail = Db.Fetch<ExceptionDetail>();
var instances = Db.Fetch<ExceptionInstance>();
detail.Should().HaveCount(2);
instances.Should().HaveCount(2);
} }
} }
} }

@ -4,6 +4,7 @@ using System.Configuration;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Migrator.Providers.SqlServer; using Migrator.Providers.SqlServer;
using NUnit.Framework;
using NzbDrone.Services.Service.Migrations; using NzbDrone.Services.Service.Migrations;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq; using NzbDrone.Test.Common.AutoMoq;
@ -60,5 +61,11 @@ namespace NzbDrone.Services.Tests.Framework
Mocker.SetConstant(Db); Mocker.SetConstant(Db);
} }
[TearDown]
public void ServiceTearDown()
{
ExceptionVerification.IgnoreWarns();
}
} }
} }

@ -50,6 +50,9 @@
<Reference Include="Moq"> <Reference Include="Moq">
<HintPath>..\..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath> <HintPath>..\..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\packages\NLog.2.0.0.2000\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.6.0.12051, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=2.6.0.12051, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll</HintPath> <HintPath>..\..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll</HintPath>
@ -65,7 +68,8 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ExceptionControllerFixture.cs" /> <Compile Include="ExceptionController\ReportExistingFixture.cs" />
<Compile Include="ExceptionController\ReportNewFixture.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" />

@ -3,5 +3,6 @@
<package id="FluentAssertions" version="1.7.0" /> <package id="FluentAssertions" version="1.7.0" />
<package id="Moq" version="4.0.10827" /> <package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1.1" /> <package id="NBuilder" version="3.0.1.1" />
<package id="NLog" version="2.0.0.2000" />
<package id="NUnit" version="2.6.0.12054" /> <package id="NUnit" version="2.6.0.12054" />
</packages> </packages>
Loading…
Cancel
Save