Finished Exception Controller

This commit is contained in:
kay.one 2012-02-29 21:10:56 -08:00
parent 98faca17ee
commit 9cebfab2f4
14 changed files with 255 additions and 39 deletions

View File

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

View File

@ -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;
}
}
}

View File

@ -55,6 +55,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Contract\ExceptionReportResponse.cs" />
<Compile Include="Contract\ExistingExceptionReport.cs" />
<Compile Include="StringExtention.cs" />
<Compile Include="HttpProvider.cs" />
<Compile Include="ConfigFileProvider.cs" />

View File

@ -15,7 +15,7 @@ namespace NzbDrone.Services.Service.App_Start
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);
LogConfiguration.RegisterUdpLogger();

View File

@ -2,7 +2,6 @@
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;
@ -14,23 +13,53 @@ namespace NzbDrone.Services.Service.Controllers
private readonly IDatabase _database;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
private const string OK = "OK";
public ExceptionController(IDatabase 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]
public JsonResult ReportNew(ExceptionReport exceptionReport)
{
try
{
var exceptionId = GetExceptionDetailId(exceptionReport);
var exceptionHash = GetExceptionDetailId(exceptionReport);
var exceptionInstance = new ExceptionInstance
{
ExceptionDetail = exceptionId,
ExceptionHash = exceptionHash,
IsProduction = exceptionReport.IsProduction,
LogMessage = exceptionReport.LogMessage,
Timestamp = DateTime.Now
@ -38,22 +67,20 @@ namespace NzbDrone.Services.Service.Controllers
_database.Insert(exceptionInstance);
return new JsonResult { Data = new ExceptionReportResponse { ExceptionId = exceptionId } };
return new JsonResult { Data = new ExceptionReportResponse { ExceptionHash = exceptionHash } };
}
catch (Exception e)
{
logger.FatalException("Error has occurred while logging exception", e);
logger.FatalException("Error has occurred while saving exception", e);
throw;
}
}
private int GetExceptionDetailId(ExceptionReport exceptionReport)
private string 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 reportHash = Hash(String.Concat(exceptionReport.Version, exceptionReport.String, exceptionReport.Logger));
if (!ExceptionHashExists(reportHash))
{
var exeptionDetail = new ExceptionDetail();
exeptionDetail.Hash = reportHash;
@ -62,10 +89,15 @@ namespace NzbDrone.Services.Service.Controllers
exeptionDetail.Type = exceptionReport.Type;
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)

View File

@ -10,24 +10,25 @@ namespace NzbDrone.Services.Service.Migrations
{
public override void Up()
{
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),
MigrationsHelper.TimestampColumn,
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("Type", DbType.String, ColumnProperty.NotNull),
new Column("String", DbType.String, ColumnProperty.NotNull),
new Column("Hash", DbType.String, ColumnProperty.NotNull),
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 Exceptions ALTER COLUMN String NTEXT");
}
public override void Down()

View File

@ -20,7 +20,7 @@ namespace NzbDrone.Services.Service.Migrations
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();
logger.Info("Database migration completed");
}

View File

@ -1,17 +1,16 @@
using System;
using System.Linq;
using Services.PetaPoco;
namespace NzbDrone.Services.Service.Repository.Reporting
{
[TableName("Exceptions")]
[PrimaryKey("Hash", autoIncrement = false)]
public class ExceptionDetail
{
public int Id { get; set; }
public string Hash { 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; }
}
}

View File

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

View File

@ -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);
}
}
}

View File

@ -3,21 +3,20 @@ 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
namespace NzbDrone.Services.Tests.ExceptionController
{
[TestFixture]
public class ExceptionControllerFixture : ServicesTestBase
public class ReportNewFixture : ServicesTestBase
{
ExceptionController Controller
Service.Controllers.ExceptionController Controller
{
get
{
return Mocker.Resolve<ExceptionController>();
return Mocker.Resolve<Service.Controllers.ExceptionController>();
}
}
@ -89,12 +88,30 @@ namespace NzbDrone.Services.Tests
var exceptionInstance = Db.Fetch<ExceptionInstance>();
exceptionInstance.Should().HaveCount(1);
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().Timestamp.Should().BeWithin(TimeSpan.FromSeconds(4)).Before(DateTime.Now);
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]
public void ReportNew_should_return_exception_id()
{
@ -105,7 +122,7 @@ namespace NzbDrone.Services.Tests
var response = Controller.ReportNew(exceptionReport);
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 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);
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);
}
}
}

View File

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

View File

@ -50,6 +50,9 @@
<Reference Include="Moq">
<HintPath>..\..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</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">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll</HintPath>
@ -65,7 +68,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ExceptionControllerFixture.cs" />
<Compile Include="ExceptionController\ReportExistingFixture.cs" />
<Compile Include="ExceptionController\ReportNewFixture.cs" />
<Compile Include="ReportingControllerFixture.cs" />
<Compile Include="Framework\ServicesTestBase.cs" />
<Compile Include="Framework\TestDbHelper.cs" />

View File

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