mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-05 02:22:31 +01:00
Apply Cleanse to Exception Data as well.
This commit is contained in:
parent
924fe80997
commit
b70d167911
55
src/NzbDrone.Common/Extensions/ExceptionExtensions.cs
Normal file
55
src/NzbDrone.Common/Extensions/ExceptionExtensions.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
47
src/NzbDrone.Common/Instrumentation/CleansingJsonVisitor.cs
Normal file
47
src/NzbDrone.Common/Instrumentation/CleansingJsonVisitor.cs
Normal file
@ -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 @@ protected override void Write(LogEventInfo logEvent)
|
||||
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;
|
||||
|
||||
string cleansedMessage = CleanseLogMessage.Cleanse(logEvent.Message);
|
||||
string cleansedFormattedMessage = cleansedMessage;
|
||||
|
||||
if (logEvent.Parameters != null)
|
||||
{
|
||||
cleansedFormattedMessage = CleanseLogMessage.Cleanse(string.Format(logEvent.Message, logEvent.Parameters));
|
||||
}
|
||||
|
||||
var sentryMessage = new SentryMessage(cleansedFormattedMessage);
|
||||
var sentryMessage = new SentryMessage(logEvent.Message, logEvent.Parameters);
|
||||
|
||||
var sentryEvent = new SentryEvent(logEvent.Exception)
|
||||
{
|
||||
@ -133,7 +125,7 @@ protected override void Write(LogEventInfo logEvent)
|
||||
{
|
||||
logEvent.Level.ToString(),
|
||||
logEvent.LoggerName,
|
||||
cleansedMessage
|
||||
logEvent.Message
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,13 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||
{
|
||||
public class SonarrJsonPacketFactory : IJsonPacketFactory
|
||||
{
|
||||
private readonly SentryPacketCleanser _cleanser;
|
||||
|
||||
public SonarrJsonPacketFactory()
|
||||
{
|
||||
_cleanser = new SentryPacketCleanser();
|
||||
}
|
||||
|
||||
private static string ShortenPath(string path)
|
||||
{
|
||||
|
||||
@ -37,6 +44,8 @@ public JsonPacket Create(string project, SentryEvent @event)
|
||||
frame.Filename = ShortenPath(frame.Filename);
|
||||
}
|
||||
}
|
||||
|
||||
_cleanser.CleansePacket(packet);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@ -46,7 +55,6 @@ public JsonPacket Create(string project, SentryEvent @event)
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
[Obsolete]
|
||||
public JsonPacket Create(string project, SentryMessage message, ErrorLevel level = ErrorLevel.Info, IDictionary<string, string> tags = null,
|
||||
string[] fingerprint = null, object extra = null)
|
||||
@ -61,4 +69,4 @@ public JsonPacket Create(string project, Exception exception, SentryMessage mess
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,6 +136,7 @@
|
||||
<Compile Include="Extensions\Base64Extensions.cs" />
|
||||
<Compile Include="Extensions\DateTimeExtensions.cs" />
|
||||
<Compile Include="Crypto\HashConverter.cs" />
|
||||
<Compile Include="Extensions\ExceptionExtensions.cs" />
|
||||
<Compile Include="Extensions\Int64Extensions.cs" />
|
||||
<Compile Include="Extensions\ObjectExtensions.cs" />
|
||||
<Compile Include="Extensions\StreamExtensions.cs" />
|
||||
@ -175,12 +176,14 @@
|
||||
<Compile Include="Extensions\IEnumerableExtensions.cs" />
|
||||
<Compile Include="Http\UserAgentBuilder.cs" />
|
||||
<Compile Include="Instrumentation\CleanseLogMessage.cs" />
|
||||
<Compile Include="Instrumentation\CleansingJsonVisitor.cs" />
|
||||
<Compile Include="Instrumentation\Extensions\LoggerProgressExtensions.cs" />
|
||||
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
|
||||
<Compile Include="Instrumentation\LogEventExtensions.cs" />
|
||||
<Compile Include="Instrumentation\NzbDroneFileTarget.cs" />
|
||||
<Compile Include="Instrumentation\NzbDroneLogger.cs" />
|
||||
<Compile Include="Instrumentation\Sentry\SentryDebounce.cs" />
|
||||
<Compile Include="Instrumentation\Sentry\SentryPacketCleanser.cs" />
|
||||
<Compile Include="Instrumentation\Sentry\SentryTarget.cs" />
|
||||
<Compile Include="Instrumentation\Sentry\MachineNameUserFactory.cs" />
|
||||
<Compile Include="Instrumentation\Sentry\SonarrJsonPacketFactory.cs" />
|
||||
@ -205,6 +208,7 @@
|
||||
<Compile Include="Serializer\HttpUriConverter.cs" />
|
||||
<Compile Include="Serializer\IntConverter.cs" />
|
||||
<Compile Include="Serializer\Json.cs" />
|
||||
<Compile Include="Serializer\JsonVisitor.cs" />
|
||||
<Compile Include="ServiceFactory.cs" />
|
||||
<Compile Include="ServiceProvider.cs" />
|
||||
<Compile Include="Extensions\StringExtensions.cs" />
|
||||
|
95
src/NzbDrone.Common/Serializer/JsonVisitor.cs
Normal file
95
src/NzbDrone.Common/Serializer/JsonVisitor.cs
Normal file
@ -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 @@ protected virtual IList<ReleaseInfo> FetchReleases(Func<IIndexerRequestGenerator
|
||||
catch (CloudFlareCaptchaException ex)
|
||||
{
|
||||
_indexerStatusService.RecordFailure(Definition.Id);
|
||||
ex.WithData("FeedUrl", url);
|
||||
if (ex.IsExpired)
|
||||
{
|
||||
_logger.Error(ex, "Expired CAPTCHA token for {0}, please refresh in indexer settings.", this);
|
||||
@ -257,11 +258,11 @@ protected virtual IList<ReleaseInfo> FetchReleases(Func<IIndexerRequestGenerator
|
||||
_indexerStatusService.RecordFailure(Definition.Id);
|
||||
_logger.Warn(ex, "{0}", url);
|
||||
}
|
||||
catch (Exception feedEx)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_indexerStatusService.RecordFailure(Definition.Id);
|
||||
feedEx.Data.Add("FeedUrl", url);
|
||||
_logger.Error(feedEx, "An error occurred while processing feed. {0}", url);
|
||||
ex.WithData("FeedUrl", url);
|
||||
_logger.Error(ex, "An error occurred while processing feed. {0}", url);
|
||||
}
|
||||
|
||||
return CleanupReleases(releases);
|
||||
|
@ -69,6 +69,8 @@ private NewznabCapabilities FetchCapabilities(NewznabSettings indexerSettings)
|
||||
catch (XmlException ex)
|
||||
{
|
||||
_logger.Debug(ex, "Failed to parse newznab api capabilities for {0}.", indexerSettings.Url);
|
||||
|
||||
ex.WithData(response);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -65,12 +65,14 @@ public virtual IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
}
|
||||
catch (UnsupportedFeedException itemEx)
|
||||
{
|
||||
itemEx.Data.Add("Item", item.Title());
|
||||
itemEx.WithData("FeedUrl", indexerResponse.Request.Url);
|
||||
itemEx.WithData("ItemTitle", item.Title());
|
||||
throw;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -95,8 +97,7 @@ protected virtual XDocument LoadXmlDocument(IndexerResponse indexerResponse)
|
||||
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);
|
||||
|
||||
ex.Data.Add("ContentLength", indexerResponse.Content.Length);
|
||||
ex.Data.Add("ContentSample", contentSample);
|
||||
ex.WithData(indexerResponse.HttpResponse);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
@ -40,23 +40,31 @@ public TorrentRssIndexerParserSettings Detect(TorrentRssIndexerSettings indexerS
|
||||
{
|
||||
_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
|
||||
{
|
||||
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)
|
||||
{
|
||||
_logger.Warn(ex, string.Format("Unable to connect to indexer {0}: {1}", request.Url, ex.Message));
|
||||
return null;
|
||||
ex.WithData("FeedUrl", indexerSettings.BaseUrl);
|
||||
throw;
|
||||
}
|
||||
|
||||
var indexerResponse = new IndexerResponse(request, httpResponse);
|
||||
return GetParserSettings(indexerResponse, indexerSettings);
|
||||
}
|
||||
}
|
||||
|
||||
private TorrentRssIndexerParserSettings GetParserSettings(IndexerResponse response, TorrentRssIndexerSettings indexerSettings)
|
||||
{
|
||||
@ -140,7 +148,7 @@ private TorrentRssIndexerParserSettings GetGenericTorrentRssParserSettings(Index
|
||||
_logger.Trace("Feed doesn't have Seeders in Description, disabling option.");
|
||||
parser.ParseSeedersInDescription = settings.ParseSeedersInDescription = false;
|
||||
}
|
||||
|
||||
|
||||
if (!releases.Any(r => r.Size < ValidSizeThreshold))
|
||||
{
|
||||
_logger.Trace("Feed has valid size in enclosure.");
|
||||
|
Loading…
Reference in New Issue
Block a user