diff --git a/src/Exceptionless.Tests/Plugins/PluginTests.cs b/src/Exceptionless.Tests/Plugins/PluginTests.cs
index 8554b676..6897226d 100644
--- a/src/Exceptionless.Tests/Plugins/PluginTests.cs
+++ b/src/Exceptionless.Tests/Plugins/PluginTests.cs
@@ -726,7 +726,7 @@ public void VerifyDeduplication() {
var errorPlugin = new ErrorPlugin();
EventPluginContext mergedContext = null;
- using (var duplicateCheckerPlugin = new DuplicateCheckerPlugin(TimeSpan.FromMilliseconds(20))) {
+ using (var duplicateCheckerPlugin = new DuplicateCheckerPlugin(TimeSpan.FromMilliseconds(40))) {
for (int index = 0; index < 10; index++) {
var builder = GetException().ToExceptionless();
var context = new EventPluginContext(client, builder.Target, builder.PluginContextData);
@@ -745,7 +745,7 @@ public void VerifyDeduplication() {
}
}
- Thread.Sleep(50);
+ Thread.Sleep(100);
Assert.Equal(9, mergedContext.Event.Count.GetValueOrDefault());
}
diff --git a/src/Exceptionless.Tests/project.json b/src/Exceptionless.Tests/project.json
index dedbde4e..f0cee9db 100644
--- a/src/Exceptionless.Tests/project.json
+++ b/src/Exceptionless.Tests/project.json
@@ -8,7 +8,7 @@
}
},
"dependencies": {
- "BenchmarkDotNet": "0.9.8",
+ "BenchmarkDotNet": "0.9.9",
"Exceptionless": {
"target": "project"
},
diff --git a/src/Exceptionless/Configuration/ExceptionlessConfiguration.cs b/src/Exceptionless/Configuration/ExceptionlessConfiguration.cs
index d7d3bc75..301cb17a 100644
--- a/src/Exceptionless/Configuration/ExceptionlessConfiguration.cs
+++ b/src/Exceptionless/Configuration/ExceptionlessConfiguration.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using System.Net;
using System.Reflection;
using Exceptionless.Dependency;
using Exceptionless.Plugins;
@@ -99,11 +100,6 @@ public string HeartbeatServerUrl {
}
}
- ///
- /// Used to identify the client that sent the events to the server.
- ///
- public string UserAgent { get; set; }
-
///
/// The API key that will be used when sending events to the server.
///
@@ -122,6 +118,16 @@ public string ApiKey {
}
}
+ ///
+ /// Used to identify the client that sent the events to the server.
+ ///
+ public string UserAgent { get; set; }
+
+ ///
+ /// Ability to set a custom proxy. By default, .NET will use any system or configuration defined proxy settings.
+ ///
+ public IWebProxy Proxy { get; set; }
+
///
/// Whether the client is currently enabled or not. If it is disabled, submitted errors will be discarded and no data will be sent to the server.
///
diff --git a/src/Exceptionless/Configuration/SettingsManager.cs b/src/Exceptionless/Configuration/SettingsManager.cs
index 0ee4384d..e0800fde 100644
--- a/src/Exceptionless/Configuration/SettingsManager.cs
+++ b/src/Exceptionless/Configuration/SettingsManager.cs
@@ -71,7 +71,7 @@ public static void CheckVersion(int version, ExceptionlessConfiguration config)
public static void UpdateSettings(ExceptionlessConfiguration config, int? version = null) {
if (config == null || !config.IsValid || !config.Enabled)
return;
-
+
try {
if (!version.HasValue || version < 0)
version = GetVersion(config);
diff --git a/src/Exceptionless/ExceptionlessClient.cs b/src/Exceptionless/ExceptionlessClient.cs
index 737d5e91..38a7ee3a 100644
--- a/src/Exceptionless/ExceptionlessClient.cs
+++ b/src/Exceptionless/ExceptionlessClient.cs
@@ -203,7 +203,7 @@ public void SubmitEvent(Event ev, ContextData pluginContextData = null) {
_queue.Value.Enqueue(ev);
if (!String.IsNullOrEmpty(ev.ReferenceId)) {
- _log.Value.FormattedTrace(typeof(ExceptionlessClient), "Setting last reference id '{0}'", ev.ReferenceId);
+ _log.Value.FormattedTrace(typeof(ExceptionlessClient), "Setting last reference id: {0}", ev.ReferenceId);
_lastReferenceIdManager.Value.SetLast(ev.ReferenceId);
}
diff --git a/src/Exceptionless/Logging/ExceptionlessLogExtensions.cs b/src/Exceptionless/Logging/ExceptionlessLogExtensions.cs
index 3e2ca691..f9fd11c5 100644
--- a/src/Exceptionless/Logging/ExceptionlessLogExtensions.cs
+++ b/src/Exceptionless/Logging/ExceptionlessLogExtensions.cs
@@ -11,11 +11,11 @@ public static void Error(this IExceptionlessLog log, Type source, Exception exce
}
public static void FormattedError(this IExceptionlessLog log, Type source, Exception exception, string format, params object[] args) {
- log.Error(String.Format(format, args), GetSourceName(source), exception);
+ log.Error(GetMessage(format, args), GetSourceName(source), exception);
}
public static void FormattedError(this IExceptionlessLog log, Type source, string format, params object[] args) {
- log.Error(String.Format(format, args), GetSourceName(source));
+ log.Error(GetMessage(format, args), GetSourceName(source));
}
public static void Info(this IExceptionlessLog log, Type source, string message) {
@@ -23,7 +23,7 @@ public static void Info(this IExceptionlessLog log, Type source, string message)
}
public static void FormattedInfo(this IExceptionlessLog log, Type source, string format, params object[] args) {
- log.Info(String.Format(format, args), GetSourceName(source));
+ log.Info(GetMessage(format, args), GetSourceName(source));
}
public static void Debug(this IExceptionlessLog log, Type source, string message) {
@@ -31,7 +31,7 @@ public static void Debug(this IExceptionlessLog log, Type source, string message
}
public static void FormattedDebug(this IExceptionlessLog log, Type source, string format, params object[] args) {
- log.Debug(String.Format(format, args), GetSourceName(source));
+ log.Debug(GetMessage(format, args), GetSourceName(source));
}
public static void Warn(this IExceptionlessLog log, Type source, string message) {
@@ -39,7 +39,7 @@ public static void Warn(this IExceptionlessLog log, Type source, string message)
}
public static void FormattedWarn(this IExceptionlessLog log, Type source, string format, params object[] args) {
- log.Warn(String.Format(format, args), GetSourceName(source));
+ log.Warn(GetMessage(format, args), GetSourceName(source));
}
public static void Trace(this IExceptionlessLog log, Type source, string message) {
@@ -47,7 +47,7 @@ public static void Trace(this IExceptionlessLog log, Type source, string message
}
public static void FormattedTrace(this IExceptionlessLog log, Type source, string format, params object[] args) {
- log.Trace(String.Format(format, args), GetSourceName(source));
+ log.Trace(GetMessage(format, args), GetSourceName(source));
}
public static void Error(this IExceptionlessLog log, Exception exception, string message) {
@@ -55,27 +55,35 @@ public static void Error(this IExceptionlessLog log, Exception exception, string
}
public static void FormattedError(this IExceptionlessLog log, Exception exception, string format, params object[] args) {
- log.Error(String.Format(format, args), exception: exception);
+ log.Error(GetMessage(format, args), exception: exception);
}
public static void FormattedError(this IExceptionlessLog log, string format, params object[] args) {
- log.Error(String.Format(format, args));
+ log.Error(GetMessage(format, args));
}
public static void FormattedInfo(this IExceptionlessLog log, string format, params object[] args) {
- log.Info(String.Format(format, args));
+ log.Info(GetMessage(format, args));
}
public static void FormattedDebug(this IExceptionlessLog log, string format, params object[] args) {
- log.Debug(String.Format(format, args));
+ log.Debug(GetMessage(format, args));
}
public static void FormattedWarn(this IExceptionlessLog log, string format, params object[] args) {
- log.Warn(String.Format(format, args));
+ log.Warn(GetMessage(format, args));
}
private static string GetSourceName(Type type) {
return type.Name;
}
+
+ private static string GetMessage(string format, params object[] args) {
+ try {
+ return String.Format(format, args);
+ } catch (Exception) {
+ return format;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Exceptionless/Plugins/Default/1010_DuplicateCheckerPlugin.cs b/src/Exceptionless/Plugins/Default/1010_DuplicateCheckerPlugin.cs
index f6fd0cdc..d42ce3e4 100644
--- a/src/Exceptionless/Plugins/Default/1010_DuplicateCheckerPlugin.cs
+++ b/src/Exceptionless/Plugins/Default/1010_DuplicateCheckerPlugin.cs
@@ -32,7 +32,7 @@ public DuplicateCheckerPlugin(TimeSpan? interval) {
public void Run(EventPluginContext context) {
int hashCode = context.Event.GetHashCode();
int count = context.Event.Count ?? 1;
- context.Log.FormattedTrace(typeof(DuplicateCheckerPlugin), String.Concat("Checking event: ", context.Event.Message, " with hash: ", hashCode));
+ context.Log.FormattedTrace(typeof(DuplicateCheckerPlugin), "Checking event: {0} with hash: {1}", context.Event.Message, hashCode);
lock (_lock) {
// Increment the occurrence count if the event is already queued for submission.
@@ -40,21 +40,21 @@ public void Run(EventPluginContext context) {
if (merged != null) {
merged.IncrementCount(count);
merged.UpdateDate(context.Event.Date);
- context.Log.FormattedInfo(typeof(DuplicateCheckerPlugin), String.Concat("Ignoring duplicate event with hash:", hashCode));
+ context.Log.FormattedInfo(typeof(DuplicateCheckerPlugin), "Ignoring duplicate event with hash: {0}", hashCode);
context.Cancel = true;
return;
}
DateTimeOffset repeatWindow = DateTimeOffset.UtcNow.Subtract(_interval);
if (_processed.Any(s => s.Item1 == hashCode && s.Item2 >= repeatWindow)) {
- context.Log.FormattedInfo(typeof(DuplicateCheckerPlugin), String.Concat("Adding event with hash:", hashCode, " to cache."));
+ context.Log.FormattedInfo(typeof(DuplicateCheckerPlugin), "Adding event with hash: {0} to cache.", hashCode);
// This event is a duplicate for the first time, lets save it so we can delay it while keeping count
_mergedEvents.Enqueue(new MergedEvent(hashCode, context, count));
context.Cancel = true;
return;
}
- context.Log.FormattedInfo(typeof(DuplicateCheckerPlugin), String.Concat("Enqueueing event with hash:", hashCode, " to cache."));
+ context.Log.FormattedInfo(typeof(DuplicateCheckerPlugin), "Enqueueing event with hash: {0} to cache.", hashCode);
_processed.Enqueue(Tuple.Create(hashCode, DateTimeOffset.UtcNow));
while (_processed.Count > 50)
@@ -116,7 +116,7 @@ public void Resubmit() {
_context.Resolver.GetEventQueue().Enqueue(_context.Event);
if (!String.IsNullOrEmpty(_context.Event.ReferenceId)) {
- _context.Log.FormattedTrace(typeof(DuplicateCheckerPlugin), "Setting last reference id '{0}'", _context.Event.ReferenceId);
+ _context.Log.FormattedTrace(typeof(DuplicateCheckerPlugin), "Setting last reference id: {0}", _context.Event.ReferenceId);
_context.Resolver.GetLastReferenceIdManager().SetLast(_context.Event.ReferenceId);
}
diff --git a/src/Exceptionless/Services/DefaultEnvironmentInfoCollector.cs b/src/Exceptionless/Services/DefaultEnvironmentInfoCollector.cs
index d2f09ee6..6dd7633e 100644
--- a/src/Exceptionless/Services/DefaultEnvironmentInfoCollector.cs
+++ b/src/Exceptionless/Services/DefaultEnvironmentInfoCollector.cs
@@ -24,7 +24,7 @@ public DefaultEnvironmentInfoCollector(IExceptionlessLog log) {
public EnvironmentInfo GetEnvironmentInfo() {
if (_environmentInfo != null) {
PopulateThreadInfo(_environmentInfo);
- PopulateMemoryInfo(_environmentInfo);
+ PopulateMemoryInfo(_environmentInfo);
return _environmentInfo;
}
@@ -178,7 +178,7 @@ private void PopulateRuntimeInfo(EnvironmentInfo info) {
#if NETSTANDARD
computerInfo = Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default;
#elif NET45
- computerInfo = new Microsoft.VisualBasic.Devices.ComputerInfo();
+ computerInfo = new Microsoft.VisualBasic.Devices.ComputerInfo();
#endif
} catch (Exception ex) {
_log.FormattedInfo(typeof(DefaultEnvironmentInfoCollector), "Unable to get computer info. Error message: {0}", ex.Message);
diff --git a/src/Exceptionless/Submission/DefaultSubmissionClient.cs b/src/Exceptionless/Submission/DefaultSubmissionClient.cs
index 3fef0167..d9d36cba 100644
--- a/src/Exceptionless/Submission/DefaultSubmissionClient.cs
+++ b/src/Exceptionless/Submission/DefaultSubmissionClient.cs
@@ -15,10 +15,10 @@
namespace Exceptionless.Submission {
public class DefaultSubmissionClient : ISubmissionClient, IDisposable {
- private readonly HttpClient _client;
+ private readonly Lazy _client;
public DefaultSubmissionClient(ExceptionlessConfiguration config) {
- _client = CreateHttpClient(config.UserAgent);
+ _client = new Lazy(() => CreateHttpClient(config));
}
public SubmissionResponse PostEvents(IEnumerable events, ExceptionlessConfiguration config, IJsonSerializer serializer) {
@@ -36,8 +36,8 @@ public SubmissionResponse PostEvents(IEnumerable events, ExceptionlessCon
if (data.Length > 1024 * 4)
content = new GzipContent(content);
- _client.AddAuthorizationHeader(config.ApiKey);
- response = _client.PostAsync(url, content).ConfigureAwait(false).GetAwaiter().GetResult();
+ _client.Value.AddAuthorizationHeader(config.ApiKey);
+ response = _client.Value.PostAsync(url, content).ConfigureAwait(false).GetAwaiter().GetResult();
} catch (Exception ex) {
return new SubmissionResponse(500, message: ex.Message);
}
@@ -48,7 +48,7 @@ public SubmissionResponse PostEvents(IEnumerable events, ExceptionlessCon
return new SubmissionResponse((int)response.StatusCode, GetResponseMessage(response));
}
-
+
public SubmissionResponse PostUserDescription(string referenceId, UserDescription description, ExceptionlessConfiguration config, IJsonSerializer serializer) {
if (!config.IsValid)
return new SubmissionResponse(500, message: "Invalid client configuration settings.");
@@ -64,8 +64,8 @@ public SubmissionResponse PostUserDescription(string referenceId, UserDescriptio
if (data.Length > 1024 * 4)
content = new GzipContent(content);
- _client.AddAuthorizationHeader(config.ApiKey);
- response = _client.PostAsync(url, content).ConfigureAwait(false).GetAwaiter().GetResult();
+ _client.Value.AddAuthorizationHeader(config.ApiKey);
+ response = _client.Value.PostAsync(url, content).ConfigureAwait(false).GetAwaiter().GetResult();
} catch (Exception ex) {
return new SubmissionResponse(500, message: ex.Message);
}
@@ -85,8 +85,8 @@ public SettingsResponse GetSettings(ExceptionlessConfiguration config, int versi
HttpResponseMessage response;
try {
- _client.AddAuthorizationHeader(config.ApiKey);
- response = _client.GetAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
+ _client.Value.AddAuthorizationHeader(config.ApiKey);
+ response = _client.Value.GetAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
} catch (Exception ex) {
var message = String.Concat("Unable to retrieve configuration settings. Exception: ", ex.GetMessage());
return new SettingsResponse(false, message: message);
@@ -105,22 +105,22 @@ public SettingsResponse GetSettings(ExceptionlessConfiguration config, int versi
var settings = serializer.Deserialize(json);
return new SettingsResponse(true, settings.Settings, settings.Version);
}
-
+
public void SendHeartbeat(string sessionIdOrUserId, bool closeSession, ExceptionlessConfiguration config) {
if (!config.IsValid)
return;
string url = String.Format("{0}/events/session/heartbeat?id={1}&close={2}", GetHeartbeatServiceEndPoint(config), sessionIdOrUserId, closeSession);
try {
- _client.AddAuthorizationHeader(config.ApiKey);
- _client.GetAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
+ _client.Value.AddAuthorizationHeader(config.ApiKey);
+ _client.Value.GetAsync(url).ConfigureAwait(false).GetAwaiter().GetResult();
} catch (Exception ex) {
var log = config.Resolver.GetLog();
log.Error(String.Concat("Error submitting heartbeat: ", ex.GetMessage()));
}
}
- private HttpClient CreateHttpClient(string userAgent) {
+ protected virtual HttpClient CreateHttpClient(ExceptionlessConfiguration config) {
#if NET45
var handler = new WebRequestHandler { UseDefaultCredentials = true };
handler.ServerCertificateValidationCallback = delegate { return true; };
@@ -130,17 +130,19 @@ private HttpClient CreateHttpClient(string userAgent) {
//handler.ServerCertificateCustomValidationCallback = delegate { return true; };
#endif
#endif
-
if (handler.SupportsAutomaticDecompression)
handler.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip | DecompressionMethods.None;
if (handler.SupportsRedirectConfiguration)
handler.AllowAutoRedirect = true;
-
+
+ if (handler.SupportsProxy && config.Proxy != null)
+ handler.Proxy = config.Proxy;
+
var client = new HttpClient(handler, true);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.ExpectContinue = false;
- client.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);
+ client.DefaultRequestHeaders.UserAgent.ParseAdd(config.UserAgent);
return client;
}
@@ -205,7 +207,8 @@ private Uri GetHeartbeatServiceEndPoint(ExceptionlessConfiguration config) {
}
public void Dispose() {
- _client.Dispose();
+ if (_client.IsValueCreated)
+ _client.Value.Dispose();
}
}
}
\ No newline at end of file
diff --git a/src/Platforms/Exceptionless.NLog/project.json b/src/Platforms/Exceptionless.NLog/project.json
index e2ecdc4a..da3c93eb 100644
--- a/src/Platforms/Exceptionless.NLog/project.json
+++ b/src/Platforms/Exceptionless.NLog/project.json
@@ -48,7 +48,7 @@
"define": [ "NET45" ]
},
"dependencies": {
- "NLog": "4.3.6"
+ "NLog": "4.3.8"
}
}
}