Apply Cleanse to Exception Data as well.
This commit is contained in:
parent
924fe80997
commit
b70d167911
|
@ -0,0 +1,55 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Extensions
|
||||||
|
{
|
||||||
|
public static class ExceptionExtensions
|
||||||
|
{
|
||||||
|
public static T WithData<T>(this T ex, string key, string value) where T : Exception
|
||||||
|
{
|
||||||
|
ex.AddData(key, value);
|
||||||
|
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
public static T WithData<T>(this T ex, string key, int value) where T : Exception
|
||||||
|
{
|
||||||
|
ex.AddData(key, value.ToString());
|
||||||
|
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T WithData<T>(this T ex, string key, Http.HttpUri value) where T : Exception
|
||||||
|
{
|
||||||
|
ex.AddData(key, value.ToString());
|
||||||
|
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static T WithData<T>(this T ex, Http.HttpResponse response, int maxSampleLength = 512) where T : Exception
|
||||||
|
{
|
||||||
|
if (response == null || response.Content == null) return ex;
|
||||||
|
|
||||||
|
var contentSample = response.Content.Substring(0, Math.Min(response.Content.Length, 512));
|
||||||
|
|
||||||
|
if (response.Headers != null)
|
||||||
|
{
|
||||||
|
ex.AddData("ContentType", response.Headers.ContentType ?? string.Empty);
|
||||||
|
}
|
||||||
|
ex.AddData("ContentLength", response.Content.Length.ToString());
|
||||||
|
ex.AddData("ContentSample", contentSample);
|
||||||
|
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void AddData(this Exception ex, string key, string value)
|
||||||
|
{
|
||||||
|
if (value.IsNullOrWhiteSpace()) return;
|
||||||
|
|
||||||
|
ex.Data[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NzbDrone.Common.Serializer;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Instrumentation
|
||||||
|
{
|
||||||
|
public class CleansingJsonVisitor : JsonVisitor
|
||||||
|
{
|
||||||
|
public override void Visit(JArray json)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < json.Count; i++)
|
||||||
|
{
|
||||||
|
if (json[i].Type == JTokenType.String)
|
||||||
|
{
|
||||||
|
var text = json[i].Value<string>();
|
||||||
|
json[i] = new JValue(CleanseLogMessage.Cleanse(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (JToken token in json)
|
||||||
|
{
|
||||||
|
Visit(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Visit(JProperty property)
|
||||||
|
{
|
||||||
|
if (property.Value.Type == JTokenType.String)
|
||||||
|
{
|
||||||
|
property.Value = CleanseValue(property.Value as JValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base.Visit(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JValue CleanseValue(JValue value)
|
||||||
|
{
|
||||||
|
var text = value.Value<string>();
|
||||||
|
var cleansed = CleanseLogMessage.Cleanse(text);
|
||||||
|
return new JValue(cleansed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
|
{
|
||||||
|
public class SentryPacketCleanser
|
||||||
|
{
|
||||||
|
public void CleansePacket(SonarrSentryPacket packet)
|
||||||
|
{
|
||||||
|
packet.Message = CleanseLogMessage.Cleanse(packet.Message);
|
||||||
|
|
||||||
|
if (packet.Fingerprint != null)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < packet.Fingerprint.Length; i++)
|
||||||
|
{
|
||||||
|
packet.Fingerprint[i] = CleanseLogMessage.Cleanse(packet.Fingerprint[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet.Extra != null)
|
||||||
|
{
|
||||||
|
var target = JObject.FromObject(packet.Extra);
|
||||||
|
new CleansingJsonVisitor().Visit(target);
|
||||||
|
packet.Extra = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -111,18 +111,10 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var extras = logEvent.Properties.ToDictionary(x => x.Key.ToString(), x => CleanseLogMessage.Cleanse(x.Value.ToString()));
|
var extras = logEvent.Properties.ToDictionary(x => x.Key.ToString(), x => x.Value.ToString());
|
||||||
_client.Logger = logEvent.LoggerName;
|
_client.Logger = logEvent.LoggerName;
|
||||||
|
|
||||||
string cleansedMessage = CleanseLogMessage.Cleanse(logEvent.Message);
|
var sentryMessage = new SentryMessage(logEvent.Message, logEvent.Parameters);
|
||||||
string cleansedFormattedMessage = cleansedMessage;
|
|
||||||
|
|
||||||
if (logEvent.Parameters != null)
|
|
||||||
{
|
|
||||||
cleansedFormattedMessage = CleanseLogMessage.Cleanse(string.Format(logEvent.Message, logEvent.Parameters));
|
|
||||||
}
|
|
||||||
|
|
||||||
var sentryMessage = new SentryMessage(cleansedFormattedMessage);
|
|
||||||
|
|
||||||
var sentryEvent = new SentryEvent(logEvent.Exception)
|
var sentryEvent = new SentryEvent(logEvent.Exception)
|
||||||
{
|
{
|
||||||
|
@ -133,7 +125,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
{
|
{
|
||||||
logEvent.Level.ToString(),
|
logEvent.Level.ToString(),
|
||||||
logEvent.LoggerName,
|
logEvent.LoggerName,
|
||||||
cleansedMessage
|
logEvent.Message
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,13 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
{
|
{
|
||||||
public class SonarrJsonPacketFactory : IJsonPacketFactory
|
public class SonarrJsonPacketFactory : IJsonPacketFactory
|
||||||
{
|
{
|
||||||
|
private readonly SentryPacketCleanser _cleanser;
|
||||||
|
|
||||||
|
public SonarrJsonPacketFactory()
|
||||||
|
{
|
||||||
|
_cleanser = new SentryPacketCleanser();
|
||||||
|
}
|
||||||
|
|
||||||
private static string ShortenPath(string path)
|
private static string ShortenPath(string path)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -37,6 +44,8 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
frame.Filename = ShortenPath(frame.Filename);
|
frame.Filename = ShortenPath(frame.Filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cleanser.CleansePacket(packet);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
@ -46,7 +55,6 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Obsolete]
|
[Obsolete]
|
||||||
public JsonPacket Create(string project, SentryMessage message, ErrorLevel level = ErrorLevel.Info, IDictionary<string, string> tags = null,
|
public JsonPacket Create(string project, SentryMessage message, ErrorLevel level = ErrorLevel.Info, IDictionary<string, string> tags = null,
|
||||||
string[] fingerprint = null, object extra = null)
|
string[] fingerprint = null, object extra = null)
|
||||||
|
|
|
@ -136,6 +136,7 @@
|
||||||
<Compile Include="Extensions\Base64Extensions.cs" />
|
<Compile Include="Extensions\Base64Extensions.cs" />
|
||||||
<Compile Include="Extensions\DateTimeExtensions.cs" />
|
<Compile Include="Extensions\DateTimeExtensions.cs" />
|
||||||
<Compile Include="Crypto\HashConverter.cs" />
|
<Compile Include="Crypto\HashConverter.cs" />
|
||||||
|
<Compile Include="Extensions\ExceptionExtensions.cs" />
|
||||||
<Compile Include="Extensions\Int64Extensions.cs" />
|
<Compile Include="Extensions\Int64Extensions.cs" />
|
||||||
<Compile Include="Extensions\ObjectExtensions.cs" />
|
<Compile Include="Extensions\ObjectExtensions.cs" />
|
||||||
<Compile Include="Extensions\StreamExtensions.cs" />
|
<Compile Include="Extensions\StreamExtensions.cs" />
|
||||||
|
@ -175,12 +176,14 @@
|
||||||
<Compile Include="Extensions\IEnumerableExtensions.cs" />
|
<Compile Include="Extensions\IEnumerableExtensions.cs" />
|
||||||
<Compile Include="Http\UserAgentBuilder.cs" />
|
<Compile Include="Http\UserAgentBuilder.cs" />
|
||||||
<Compile Include="Instrumentation\CleanseLogMessage.cs" />
|
<Compile Include="Instrumentation\CleanseLogMessage.cs" />
|
||||||
|
<Compile Include="Instrumentation\CleansingJsonVisitor.cs" />
|
||||||
<Compile Include="Instrumentation\Extensions\LoggerProgressExtensions.cs" />
|
<Compile Include="Instrumentation\Extensions\LoggerProgressExtensions.cs" />
|
||||||
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
|
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
|
||||||
<Compile Include="Instrumentation\LogEventExtensions.cs" />
|
<Compile Include="Instrumentation\LogEventExtensions.cs" />
|
||||||
<Compile Include="Instrumentation\NzbDroneFileTarget.cs" />
|
<Compile Include="Instrumentation\NzbDroneFileTarget.cs" />
|
||||||
<Compile Include="Instrumentation\NzbDroneLogger.cs" />
|
<Compile Include="Instrumentation\NzbDroneLogger.cs" />
|
||||||
<Compile Include="Instrumentation\Sentry\SentryDebounce.cs" />
|
<Compile Include="Instrumentation\Sentry\SentryDebounce.cs" />
|
||||||
|
<Compile Include="Instrumentation\Sentry\SentryPacketCleanser.cs" />
|
||||||
<Compile Include="Instrumentation\Sentry\SentryTarget.cs" />
|
<Compile Include="Instrumentation\Sentry\SentryTarget.cs" />
|
||||||
<Compile Include="Instrumentation\Sentry\MachineNameUserFactory.cs" />
|
<Compile Include="Instrumentation\Sentry\MachineNameUserFactory.cs" />
|
||||||
<Compile Include="Instrumentation\Sentry\SonarrJsonPacketFactory.cs" />
|
<Compile Include="Instrumentation\Sentry\SonarrJsonPacketFactory.cs" />
|
||||||
|
@ -205,6 +208,7 @@
|
||||||
<Compile Include="Serializer\HttpUriConverter.cs" />
|
<Compile Include="Serializer\HttpUriConverter.cs" />
|
||||||
<Compile Include="Serializer\IntConverter.cs" />
|
<Compile Include="Serializer\IntConverter.cs" />
|
||||||
<Compile Include="Serializer\Json.cs" />
|
<Compile Include="Serializer\Json.cs" />
|
||||||
|
<Compile Include="Serializer\JsonVisitor.cs" />
|
||||||
<Compile Include="ServiceFactory.cs" />
|
<Compile Include="ServiceFactory.cs" />
|
||||||
<Compile Include="ServiceProvider.cs" />
|
<Compile Include="ServiceProvider.cs" />
|
||||||
<Compile Include="Extensions\StringExtensions.cs" />
|
<Compile Include="Extensions\StringExtensions.cs" />
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Serializer
|
||||||
|
{
|
||||||
|
|
||||||
|
public class JsonVisitor
|
||||||
|
{
|
||||||
|
protected void Dispatch(JToken json)
|
||||||
|
{
|
||||||
|
switch (json.Type)
|
||||||
|
{
|
||||||
|
case JTokenType.Object:
|
||||||
|
Visit(json as JObject);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JTokenType.Array:
|
||||||
|
Visit(json as JArray);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JTokenType.Raw:
|
||||||
|
Visit(json as JRaw);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JTokenType.Constructor:
|
||||||
|
Visit(json as JConstructor);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JTokenType.Property:
|
||||||
|
Visit(json as JProperty);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JTokenType.Comment:
|
||||||
|
case JTokenType.Integer:
|
||||||
|
case JTokenType.Float:
|
||||||
|
case JTokenType.String:
|
||||||
|
case JTokenType.Boolean:
|
||||||
|
case JTokenType.Null:
|
||||||
|
case JTokenType.Undefined:
|
||||||
|
case JTokenType.Date:
|
||||||
|
case JTokenType.Bytes:
|
||||||
|
case JTokenType.Guid:
|
||||||
|
case JTokenType.Uri:
|
||||||
|
case JTokenType.TimeSpan:
|
||||||
|
Visit(json as JValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Visit(JToken json)
|
||||||
|
{
|
||||||
|
Dispatch(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Visit(JContainer json)
|
||||||
|
{
|
||||||
|
Dispatch(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Visit(JArray json)
|
||||||
|
{
|
||||||
|
foreach (JToken token in json)
|
||||||
|
{
|
||||||
|
Visit(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public virtual void Visit(JConstructor json)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Visit(JObject json)
|
||||||
|
{
|
||||||
|
foreach (JProperty property in json.Properties())
|
||||||
|
{
|
||||||
|
Visit(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Visit(JProperty property)
|
||||||
|
{
|
||||||
|
Visit(property.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Visit(JValue value)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -243,6 +243,7 @@ namespace NzbDrone.Core.Indexers
|
||||||
catch (CloudFlareCaptchaException ex)
|
catch (CloudFlareCaptchaException ex)
|
||||||
{
|
{
|
||||||
_indexerStatusService.RecordFailure(Definition.Id);
|
_indexerStatusService.RecordFailure(Definition.Id);
|
||||||
|
ex.WithData("FeedUrl", url);
|
||||||
if (ex.IsExpired)
|
if (ex.IsExpired)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "Expired CAPTCHA token for {0}, please refresh in indexer settings.", this);
|
_logger.Error(ex, "Expired CAPTCHA token for {0}, please refresh in indexer settings.", this);
|
||||||
|
@ -257,11 +258,11 @@ namespace NzbDrone.Core.Indexers
|
||||||
_indexerStatusService.RecordFailure(Definition.Id);
|
_indexerStatusService.RecordFailure(Definition.Id);
|
||||||
_logger.Warn(ex, "{0}", url);
|
_logger.Warn(ex, "{0}", url);
|
||||||
}
|
}
|
||||||
catch (Exception feedEx)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_indexerStatusService.RecordFailure(Definition.Id);
|
_indexerStatusService.RecordFailure(Definition.Id);
|
||||||
feedEx.Data.Add("FeedUrl", url);
|
ex.WithData("FeedUrl", url);
|
||||||
_logger.Error(feedEx, "An error occurred while processing feed. {0}", url);
|
_logger.Error(ex, "An error occurred while processing feed. {0}", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CleanupReleases(releases);
|
return CleanupReleases(releases);
|
||||||
|
|
|
@ -69,6 +69,8 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||||
catch (XmlException ex)
|
catch (XmlException ex)
|
||||||
{
|
{
|
||||||
_logger.Debug(ex, "Failed to parse newznab api capabilities for {0}.", indexerSettings.Url);
|
_logger.Debug(ex, "Failed to parse newznab api capabilities for {0}.", indexerSettings.Url);
|
||||||
|
|
||||||
|
ex.WithData(response);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
@ -65,12 +65,14 @@ namespace NzbDrone.Core.Indexers
|
||||||
}
|
}
|
||||||
catch (UnsupportedFeedException itemEx)
|
catch (UnsupportedFeedException itemEx)
|
||||||
{
|
{
|
||||||
itemEx.Data.Add("Item", item.Title());
|
itemEx.WithData("FeedUrl", indexerResponse.Request.Url);
|
||||||
|
itemEx.WithData("ItemTitle", item.Title());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception itemEx)
|
catch (Exception itemEx)
|
||||||
{
|
{
|
||||||
itemEx.Data.Add("Item", item.Title());
|
itemEx.WithData("FeedUrl", indexerResponse.Request.Url);
|
||||||
|
itemEx.WithData("ItemTitle", item.Title());
|
||||||
_logger.Error(itemEx, "An error occurred while processing feed item from {0}", indexerResponse.Request.Url);
|
_logger.Error(itemEx, "An error occurred while processing feed item from {0}", indexerResponse.Request.Url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,8 +97,7 @@ namespace NzbDrone.Core.Indexers
|
||||||
var contentSample = indexerResponse.Content.Substring(0, Math.Min(indexerResponse.Content.Length, 512));
|
var contentSample = indexerResponse.Content.Substring(0, Math.Min(indexerResponse.Content.Length, 512));
|
||||||
_logger.Debug("Truncated response content (originally {0} characters): {1}", indexerResponse.Content.Length, contentSample);
|
_logger.Debug("Truncated response content (originally {0} characters): {1}", indexerResponse.Content.Length, contentSample);
|
||||||
|
|
||||||
ex.Data.Add("ContentLength", indexerResponse.Content.Length);
|
ex.WithData(indexerResponse.HttpResponse);
|
||||||
ex.Data.Add("ContentSample", contentSample);
|
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,23 +40,31 @@ namespace NzbDrone.Core.Indexers.TorrentRss
|
||||||
{
|
{
|
||||||
_logger.Debug("Evaluating TorrentRss feed '{0}'", indexerSettings.BaseUrl);
|
_logger.Debug("Evaluating TorrentRss feed '{0}'", indexerSettings.BaseUrl);
|
||||||
|
|
||||||
var requestGenerator = new TorrentRssIndexerRequestGenerator { Settings = indexerSettings };
|
|
||||||
var request = requestGenerator.GetRecentRequests().GetAllTiers().First().First();
|
|
||||||
|
|
||||||
HttpResponse httpResponse = null;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
httpResponse = _httpClient.Execute(request.HttpRequest);
|
var requestGenerator = new TorrentRssIndexerRequestGenerator { Settings = indexerSettings };
|
||||||
|
var request = requestGenerator.GetRecentRequests().GetAllTiers().First().First();
|
||||||
|
|
||||||
|
HttpResponse httpResponse = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
httpResponse = _httpClient.Execute(request.HttpRequest);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, string.Format("Unable to connect to indexer {0}: {1}", request.Url, ex.Message));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexerResponse = new IndexerResponse(request, httpResponse);
|
||||||
|
return GetParserSettings(indexerResponse, indexerSettings);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Warn(ex, string.Format("Unable to connect to indexer {0}: {1}", request.Url, ex.Message));
|
ex.WithData("FeedUrl", indexerSettings.BaseUrl);
|
||||||
return null;
|
throw;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var indexerResponse = new IndexerResponse(request, httpResponse);
|
|
||||||
return GetParserSettings(indexerResponse, indexerSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TorrentRssIndexerParserSettings GetParserSettings(IndexerResponse response, TorrentRssIndexerSettings indexerSettings)
|
private TorrentRssIndexerParserSettings GetParserSettings(IndexerResponse response, TorrentRssIndexerSettings indexerSettings)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue