diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/releases/NLog.Targets.Splunk.3.0.0.nupkg b/releases/NLog.Targets.Splunk.3.0.0.nupkg new file mode 100644 index 0000000..1c7c458 Binary files /dev/null and b/releases/NLog.Targets.Splunk.3.0.0.nupkg differ diff --git a/releases/NLog.Targets.Splunk.3.0.0.symbols.nupkg b/releases/NLog.Targets.Splunk.3.0.0.symbols.nupkg new file mode 100644 index 0000000..3c132d6 Binary files /dev/null and b/releases/NLog.Targets.Splunk.3.0.0.symbols.nupkg differ diff --git a/src/NLog.Targets.Splunk.sln b/src/NLog.Targets.Splunk.sln index 47dfc3b..94bd5f3 100644 --- a/src/NLog.Targets.Splunk.sln +++ b/src/NLog.Targets.Splunk.sln @@ -7,10 +7,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Splunk", "NLog EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestApp", "ConsoleTestApp\ConsoleTestApp.csproj", "{1FC3A684-B53E-4010-A6EE-FE2A137F2CA2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.Splunk.Tests", "NLog.Targets.Splunk.Tests\NLog.Targets.Splunk.Tests.csproj", "{4D322CDC-932F-4DB5-B608-B868DDA452CC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Splunk.Tests", "NLog.Targets.Splunk.Tests\NLog.Targets.Splunk.Tests.csproj", "{4D322CDC-932F-4DB5-B608-B868DDA452CC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCoreWebApp", "AspNetCoreWebApp\AspNetCoreWebApp.csproj", "{BA3CA56B-18B4-43F9-A15D-1F79796515D9}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildFiles", "BuildFiles", "{50C33E7F-7A6D-4FB7-8F9F-1E46B506CB3D}" + ProjectSection(SolutionItems) = preProject + build-nuget-packages.cmd = build-nuget-packages.cmd + build-nuget-packages.ps1 = build-nuget-packages.ps1 + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test Applications", "Test Applications", "{ED979648-978F-4F12-AB23-215A5DC83B15}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -37,6 +45,10 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1FC3A684-B53E-4010-A6EE-FE2A137F2CA2} = {ED979648-978F-4F12-AB23-215A5DC83B15} + {BA3CA56B-18B4-43F9-A15D-1F79796515D9} = {ED979648-978F-4F12-AB23-215A5DC83B15} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1352023C-12FA-4D5B-92BB-3027501BF536} EndGlobalSection diff --git a/src/NLog.Targets.Splunk/NLog.Targets.Splunk.csproj b/src/NLog.Targets.Splunk/NLog.Targets.Splunk.csproj index e849ce0..d52e62b 100644 --- a/src/NLog.Targets.Splunk/NLog.Targets.Splunk.csproj +++ b/src/NLog.Targets.Splunk/NLog.Targets.Splunk.csproj @@ -1,63 +1,102 @@  - net45;net472;netstandard2.0 + net45;net462;net472;net48;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0 NLog.Targets.Splunk A NLog target for Splunk's Http Event Collector (HEC) Sender Alan Barber $([System.DateTime]::Now.ToString(yyyy)) - Copyright (c) $(CurrentYear) Alan Barber, Copyright 2013-2015 Splunk, Inc. + Copyright (c) 2017-$(CurrentYear) Alan Barber, Copyright 2013-2015 Splunk, Inc. nlog;nlog-target;splunk;splunk-enterprise;logging https://raw.githubusercontent.com/AlanBarber/NLog.Targets.Splunk/master/docs/nugeticon.png https://github.com/AlanBarber/NLog.Targets.Splunk https://github.com/AlanBarber/NLog.Targets.Splunk/blob/master/LICENSE git git://github.com/AlanBarber/NLog.Targets.Splunk - 2.2.1 - Verison 2.2.1 -- Introduced MaxConnectionsPerServer property to limit number of connections to splunk server by Rolf Kristensen (https://github.com/snakefoot) -Version 2.2.0 -- Updated NLog version from 4.5.6 to 4.5.9 -- Added support for Splunk HEC Data channel by David Matz (https://github.com/davidmatz) -Version 2.1.0 -- Updated NLog version from 4.5.3 to 4.5.6 -- Adding 'IgnoreSslErrors' property to config to allow for using Splunk with self signed certs -- updated to work with NLog's paremeter tooling -- adding abilty to toggle logging of parameters on or off -- several performance updates -- Speical thanks to Rolf Kristensen (https://github.com/snakefoot) for help with this release! -Version 2.0.0 -- Updated to support .Net Standard 2.0 and .Net 4.5 w/ NLog 4.5.3 -Version 1.0.0 -- Code cleanup and performance improvements -Verison 0.0.1 -- First publish to NuGet + 3.0.0 + + Version 3.3.0 + - Updated NLog version to 4.7.7 + - Adding support for .NET Standard 2.1, .NET Core 3.1, and .NET 5.0 + - Adding support for custom web proxy configuration by Darek Dan (https://github.com/DarekDan) + - Updating test projects to Net 5.0 by Hangy (https://github.com/hangy) + - Several other minor changes + Verison 2.2.1 + - Introduced MaxConnectionsPerServer property to limit number of connections to splunk server by Rolf Kristensen (https://github.com/snakefoot) + Version 2.2.0 + - Updated NLog version from 4.5.6 to 4.5.9 + - Added support for Splunk HEC Data channel by David Matz (https://github.com/davidmatz) + Version 2.1.0 + - Updated NLog version from 4.5.3 to 4.5.6 + - Adding 'IgnoreSslErrors' property to config to allow for using Splunk with self signed certs + - updated to work with NLog's paremeter tooling + - adding abilty to toggle logging of parameters on or off + - several performance updates + - Speical thanks to Rolf Kristensen (https://github.com/snakefoot) for help with this release! + Version 2.0.0 + - Updated to support .Net Standard 2.0 and .Net 4.5 w/ NLog 4.5.3 + Version 1.0.0 + - Code cleanup and performance improvements + Verison 0.0.1 + - First publish to NuGet + - - + + + + + NLog.Targets.Splunk .NET 5.0 + NET50 + + + + + NLog.Targets.Splunk .NET Core 3.1 + NETCORE;NETSTANDARD + + NLog.Targets.Splunk .NET Standard 2.0 NETCORE;NETSTANDARD + + + NLog.Targets.Splunk .NET Standard 2.1 + NETCORE;NETSTANDARD + + NLog.Targets.Splunk .NET 4.5 NETFULL + + + NLog.Targets.Splunk .NET 4.6.2 + NETFULL + + + NLog.Targets.Splunk .NET 4.7.2 NETFULL - + + + NLog.Targets.Splunk .NET 4.8 + NETFULL + + + diff --git a/src/NLog.Targets.Splunk/Splunk.Logging.Common/HttpEventCollectorSender.cs b/src/NLog.Targets.Splunk/Splunk.Logging.Common/HttpEventCollectorSender.cs index 2f4a97c..fa2cb90 100644 --- a/src/NLog.Targets.Splunk/Splunk.Logging.Common/HttpEventCollectorSender.cs +++ b/src/NLog.Targets.Splunk/Splunk.Logging.Common/HttpEventCollectorSender.cs @@ -1,4 +1,4 @@ -/** +/** * @copyright * * Copyright 2013-2015 Splunk, Inc. @@ -26,6 +26,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; + // ReSharper disable CheckNamespace namespace Splunk.Logging @@ -153,10 +154,12 @@ public enum SendMode /// Batch max size. /// Max number of individual events in batch. /// Server validation callback should always return true - /// Default web proxy is used if set to true; otherwise, no proxy is used + /// Default web proxy is used if set to true; otherwise, no proxy is used + /// /// HTTP client middleware. This allows to plug an HttpClient handler that /// intercepts logging HTTP traffic. /// The formatter. + /// Fix for http version 1.0 servers /// /// Zero values for the batching params mean that batching is off. /// @@ -170,7 +173,7 @@ public HttpEventCollectorSender( int batchSizeBytes, int batchSizeCount, bool ignoreSslErrors, - bool useProxy, + ProxyConfiguration proxyConfig, int maxConnectionsPerServer, HttpEventCollectorMiddleware middleware, HttpEventCollectorFormatter formatter = null, @@ -221,7 +224,7 @@ public HttpEventCollectorSender( // setup HTTP client try { - var httpMessageHandler = BuildHttpMessageHandler(ignoreSslErrors, useProxy, maxConnectionsPerServer); + var httpMessageHandler = BuildHttpMessageHandler(ignoreSslErrors, maxConnectionsPerServer, proxyConfig); httpClient = new HttpClient(httpMessageHandler); } catch @@ -251,32 +254,42 @@ public HttpEventCollectorSender( /// Builds the HTTP message handler. /// /// if set to true [ignore SSL errors]. + /// + /// /// - private HttpMessageHandler BuildHttpMessageHandler(bool ignoreSslErrors, bool useProxy, int maxConnectionsPerServer) + private HttpMessageHandler BuildHttpMessageHandler(bool ignoreSslErrors, int maxConnectionsPerServer, ProxyConfiguration proxyConfig) { -#if NET45 - +#if NET45 || NET462 + // Uses the WebRequestHandler for .NET 4.5 - 4.7.0 var httpMessageHandler = new WebRequestHandler(); if (ignoreSslErrors) { httpMessageHandler.ServerCertificateValidationCallback = IgnoreServerCertificateCallback; } - - httpMessageHandler.UseProxy = useProxy; #else + // Uses the new and improved HttpClientHandler() for .NET 4.7.1+ and .NET Standard 2.0+ var httpMessageHandler = new HttpClientHandler(); if (ignoreSslErrors) { httpMessageHandler.ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => IgnoreServerCertificateCallback(msg, cert, chain, errors); } - httpMessageHandler.UseProxy = useProxy; - if (maxConnectionsPerServer > 0) { httpMessageHandler.MaxConnectionsPerServer = maxConnectionsPerServer; } #endif + // Setup proxy + httpMessageHandler.UseProxy = proxyConfig.UseProxy; + if (proxyConfig.UseProxy && !string.IsNullOrWhiteSpace(proxyConfig.ProxyUrl)) + { + httpMessageHandler.Proxy = new WebProxy(new Uri(proxyConfig.ProxyUrl)); + if (!String.IsNullOrWhiteSpace(proxyConfig.ProxyUser) && !String.IsNullOrWhiteSpace(proxyConfig.ProxyPassword)) + { + httpMessageHandler.Proxy.Credentials = new NetworkCredential(proxyConfig.ProxyUser, proxyConfig.ProxyPassword); + } + } + return httpMessageHandler; } @@ -493,8 +506,7 @@ private Task FlushInternalSingleBatch( /// /// The serialized events. /// - private async Task PostEvents( - byte[] serializedEvents) + private async Task PostEvents(byte[] serializedEvents) { // encode data HttpResponseMessage response = null; @@ -632,4 +644,4 @@ protected virtual void Dispose(bool disposing) #endregion } -} \ No newline at end of file +} diff --git a/src/NLog.Targets.Splunk/Splunk.Logging.Common/ProxyConfiguration.cs b/src/NLog.Targets.Splunk/Splunk.Logging.Common/ProxyConfiguration.cs new file mode 100644 index 0000000..79acf99 --- /dev/null +++ b/src/NLog.Targets.Splunk/Splunk.Logging.Common/ProxyConfiguration.cs @@ -0,0 +1,12 @@ +using System; + +namespace Splunk.Logging +{ + public class ProxyConfiguration + { + public bool UseProxy { get; set; } = true; + public string ProxyUrl { get; set; } = String.Empty; + public string ProxyUser { get; set; } = String.Empty; + public string ProxyPassword { get; set; } = String.Empty; + } +} diff --git a/src/NLog.Targets.Splunk/SplunkHttpEventCollector.cs b/src/NLog.Targets.Splunk/SplunkHttpEventCollector.cs index c22f925..586b582 100644 --- a/src/NLog.Targets.Splunk/SplunkHttpEventCollector.cs +++ b/src/NLog.Targets.Splunk/SplunkHttpEventCollector.cs @@ -1,324 +1,355 @@ -using NLog.Common; -using NLog.Config; +using NLog.Common; +using NLog.Config; using NLog.Layouts; -using Splunk.Logging; -using System; -using System.Collections.Generic; - -namespace NLog.Targets.Splunk -{ - /// - /// Splunk Http Event Collector - /// - /// - [Target("SplunkHttpEventCollector")] - public sealed class SplunkHttpEventCollector : TargetWithContext - { - private HttpEventCollectorSender _hecSender; - - /// - /// Gets or sets the Splunk HTTP Event Collector server URL. - /// - /// - /// The Splunk HTTP Event Collector server URL. - /// - [RequiredParameter] +using Splunk.Logging; +using System; +using System.Collections.Generic; + +namespace NLog.Targets.Splunk +{ + /// + /// Splunk Http Event Collector + /// + /// + [Target("SplunkHttpEventCollector")] + public sealed class SplunkHttpEventCollector : TargetWithContext + { + private HttpEventCollectorSender _hecSender; + + /// + /// Gets or sets the Splunk HTTP Event Collector server URL. + /// + /// + /// The Splunk HTTP Event Collector server URL. + /// + [RequiredParameter] public Layout ServerUrl { get; set; } - /// - /// Gets or sets the Splunk HTTP Event Collector token. - /// - /// - /// The Splunk HTTP Event Collector token. - /// - [RequiredParameter] - public Layout Token { get; set; } - - /// - /// Gets or sets the Splunk source type metadata. - /// - /// - /// The Splunk metadata source type. - /// - public Layout SourceType { get; set; } = "_json"; - - /// - /// Gets or sets the Splunk source metadata. - /// - /// - /// The Splunk metadata source. - /// - public Layout Source { get; set; } = "${logger}"; - - /// - /// Gets or sets the Splunk index metadata. - /// - /// - /// The Splunk metadata index. - /// - public Layout Index { get; set; } - - /// - /// Gets or sets the optional Splunk HTTP Event Collector data channel. - /// - /// - /// The Splunk HTTP Event Collector data channel. - /// - public Layout Channel { get; set; } - - /// - /// Gets or sets the number of retries on error. - /// - /// - /// The number of retries on error. - /// - public int RetriesOnError { get; set; } = 0; - - /// - /// Gets or sets the number of bytes to include before sending a batch - /// - /// - /// The batch size in bytes. - /// - public int BatchSizeBytes { get; set; } = 0; // 0 = No batching - - /// - /// Gets or sets the number of logevents to include before sending a batch - /// - /// - /// The batch size count. - /// - public int BatchSizeCount { get; set; } = 0; // 0 = No batching - - /// - /// Gets or sets whether to include positional parameters - /// - /// - /// true if [include positional parameters]; otherwise, false. - /// - public bool IncludePositionalParameters { get; set; } - - /// - /// Ignore SSL errors when using homemade Ssl Certificates - /// - /// - /// true if [ignore SSL errors]; otherwise, false. - /// - public bool IgnoreSslErrors { get; set; } - - /// - /// Gets or sets the maximum number of concurrent connections (per server endpoint) allowed when making requests - /// - /// 0 = Use default limit. Default = 10 - public int MaxConnectionsPerServer { get; set; } = 10; - - public bool UseHttpVersion10Hack { get; set; } = false; - - /// - /// Gets or sets whether to use default web proxy. - /// - /// - /// true = use default web proxy. false = use no proxy. Default is true - /// - public bool UseProxy { get; set; } = true; - - /// - /// Configuration of additional properties to include with each LogEvent (Ex. ${logger}, ${machinename}, ${threadid} etc.) - /// - public override IList ContextProperties { get; } = new List(); - - private readonly Dictionary _metaData = new Dictionary(); - - private string _hostName; - - /// - /// Initializes a new instance of the class. - /// - public SplunkHttpEventCollector() - { - OptimizeBufferReuse = true; - IncludeEventProperties = true; - Layout = "${message}"; - } - - /// - /// Initializes the target. Can be used by inheriting classes - /// to initialize logging. - /// - /// - /// SplunkHttpEventCollector ServerUrl is not set! - /// or - /// SplunkHttpEventCollector Token is not set! - /// - protected override void InitializeTarget() - { - base.InitializeTarget(); - NLog.Common.InternalLogger.Debug("Initializing SplunkHttpEventCollector"); - - _metaData.Clear(); - + /// + /// Gets or sets the Splunk HTTP Event Collector token. + /// + /// + /// The Splunk HTTP Event Collector token. + /// + [RequiredParameter] + public Layout Token { get; set; } + + /// + /// Gets or sets the Splunk source type metadata. + /// + /// + /// The Splunk metadata source type. + /// + public Layout SourceType { get; set; } = "_json"; + + /// + /// Gets or sets the Splunk source metadata. + /// + /// + /// The Splunk metadata source. + /// + public Layout Source { get; set; } = "${logger}"; + + /// + /// Gets or sets the Splunk index metadata. + /// + /// + /// The Splunk metadata index. + /// + public Layout Index { get; set; } + + /// + /// Gets or sets the optional Splunk HTTP Event Collector data channel. + /// + /// + /// The Splunk HTTP Event Collector data channel. + /// + public Layout Channel { get; set; } + + /// + /// Gets or sets the number of retries on error. + /// + /// + /// The number of retries on error. + /// + public int RetriesOnError { get; set; } = 0; + + /// + /// Gets or sets the number of bytes to include before sending a batch + /// + /// + /// The batch size in bytes. + /// + public int BatchSizeBytes { get; set; } = 0; // 0 = No batching + + /// + /// Gets or sets the number of logevents to include before sending a batch + /// + /// + /// The batch size count. + /// + public int BatchSizeCount { get; set; } = 0; // 0 = No batching + + /// + /// Gets or sets whether to include positional parameters + /// + /// + /// true if [include positional parameters]; otherwise, false. + /// + public bool IncludePositionalParameters { get; set; } + + /// + /// Ignore SSL errors when using homemade Ssl Certificates + /// + /// + /// true if [ignore SSL errors]; otherwise, false. + /// + public bool IgnoreSslErrors { get; set; } + + /// + /// Gets or sets the maximum number of concurrent connections (per server endpoint) allowed when making requests + /// + /// 0 = Use default limit. Default = 10 + public int MaxConnectionsPerServer { get; set; } = 10; + + /// + /// Fix for connecting to server running HTTP Version 1.0 + /// + /// + /// + public bool UseHttpVersion10Hack { get; set; } = false; + + /// + /// Gets or sets whether to use default web proxy. + /// + /// + /// true = use default web proxy. false = use no proxy. Default is true + /// + public bool UseProxy { get; set; } = true; + + /// + /// Gets or sets Proxy URL + /// Default is empty. URL must include protocol and port, i.e. http://proxy:5555/. + /// If no URL specified, the default system proxy will be used, unless UseProxy is set to false. + /// + public Layout ProxyUrl { get; set; } = String.Empty; + + /// + /// Gets or set user name to use for authentication with proxy + /// + public Layout ProxyUser { get; set; } = String.Empty; + + /// + /// Gets or sets user password to use for authentication with proxy + /// + public Layout ProxyPassword { get; set; } = String.Empty; + + /// + /// Configuration of additional properties to include with each LogEvent (Ex. ${logger}, ${machinename}, ${threadid} etc.) + /// + public override IList ContextProperties { get; } = new List(); + + private readonly Dictionary _metaData = new Dictionary(); + + private string _hostName; + + /// + /// Initializes a new instance of the class. + /// + public SplunkHttpEventCollector() + { + OptimizeBufferReuse = true; + IncludeEventProperties = true; + Layout = "${message}"; + } + + /// + /// Initializes the target. Can be used by inheriting classes + /// to initialize logging. + /// + /// + /// SplunkHttpEventCollector ServerUrl is not set! + /// or + /// SplunkHttpEventCollector Token is not set! + /// + protected override void InitializeTarget() + { + base.InitializeTarget(); + NLog.Common.InternalLogger.Debug("Initializing SplunkHttpEventCollector"); + + _metaData.Clear(); + var serverUri = RenderLogEvent(ServerUrl, LogEventInfo.CreateNullEvent()); - if (string.IsNullOrEmpty(serverUri)) - { - throw new NLogConfigurationException("SplunkHttpEventCollector ServerUrl is not set!"); + if (string.IsNullOrEmpty(serverUri)) + { + throw new NLogConfigurationException("SplunkHttpEventCollector ServerUrl is not set!"); + } + + var token = RenderLogEvent(Token, LogEventInfo.CreateNullEvent()); + if (string.IsNullOrEmpty(token)) + { + throw new NLogConfigurationException("SplunkHttpEventCollector Token is not set!"); } - var token = RenderLogEvent(Token, LogEventInfo.CreateNullEvent()); - if (string.IsNullOrEmpty(token)) - { - throw new NLogConfigurationException("SplunkHttpEventCollector Token is not set!"); - } - - var channel = RenderLogEvent(Channel, LogEventInfo.CreateNullEvent()); - var index = RenderLogEvent(Index, LogEventInfo.CreateNullEvent()); - var source = RenderLogEvent(Source, LogEventInfo.CreateNullEvent()); - var sourceType = RenderLogEvent(SourceType, LogEventInfo.CreateNullEvent()); - - _hecSender = new HttpEventCollectorSender( - new Uri(serverUri), // Splunk HEC URL - token, // Splunk HEC token *GUID* - channel, // Splunk HEC data channel *GUID* - GetMetaData(index, source, sourceType), // Metadata - HttpEventCollectorSender.SendMode.Sequential, // Sequential sending to keep message in order - BatchSizeBytes == 0 && BatchSizeCount == 0 ? 0 : 250, // BatchInterval - Set to 0 to disable - BatchSizeBytes, // BatchSizeBytes - Set to 0 to disable - BatchSizeCount, // BatchSizeCount - Set to 0 to disable - IgnoreSslErrors, // Enable Ssl Error ignore for self singed certs *BOOL* - UseProxy, // UseProxy - Set to false to disable - MaxConnectionsPerServer, - new HttpEventCollectorResendMiddleware(RetriesOnError).Plugin, // Resend Middleware with retry - httpVersion10Hack: UseHttpVersion10Hack - ); - _hecSender.OnError += (e) => { InternalLogger.Error(e, "SplunkHttpEventCollector(Name={0}): Failed to send LogEvents", Name); }; - } - - /// - /// Disposes the initialized HttpEventCollectorSender - /// - protected override void CloseTarget() - { - try - { - _hecSender?.Dispose(); - base.CloseTarget(); - } - finally - { - _hecSender = null; - } - } - - /// - /// Writes the specified log event information. - /// - /// The log event information. - /// logEventInfo - /// SplunkHttpEventCollector SendEventToServer() called before InitializeTarget() - protected override void Write(LogEventInfo logEventInfo) - { - // Sanity check for LogEventInfo - if (logEventInfo == null) - { - throw new ArgumentNullException(nameof(logEventInfo)); - } - - // Make sure we have a properly setup HttpEventCollectorSender - if (_hecSender == null) - { - throw new NLogRuntimeException("SplunkHttpEventCollector SendEventToServer() called before InitializeTarget()"); - } - - // Build MetaData - var index = RenderLogEvent(Index, logEventInfo); - var source = RenderLogEvent(Source, logEventInfo); - var sourceType = RenderLogEvent(SourceType, logEventInfo); - var metaData = GetMetaData(index, source, sourceType); - - // Use NLog's built in tooling to get properties - var properties = GetAllProperties(logEventInfo); - - if (IncludePositionalParameters && logEventInfo.Parameters != null) - { - for (var i = 0; i < logEventInfo.Parameters.Length; ++i) - { - properties[string.Concat("{", i.ToString(), "}")] = logEventInfo.Parameters[i]; - } - } - - // Send the event to splunk - string renderedMessage = RenderLogEvent(Layout, logEventInfo); - _hecSender.Send(logEventInfo.TimeStamp, null, logEventInfo.Level.Name, logEventInfo.Message, renderedMessage, logEventInfo.Exception, properties, metaData); - if (BatchSizeBytes == 0 && BatchSizeCount == 0) - { - _hecSender.FlushSync(); - } - } - - /// - /// Flush any pending log messages asynchronously (in case of asynchronous targets). - /// - /// The asynchronous continuation. - protected override void FlushAsync(AsyncContinuation asyncContinuation) - { - try - { - _hecSender?.FlushSync(); - asyncContinuation(null); - } - catch (Exception ex) - { - asyncContinuation(ex); - } - } - - /// - /// Gets the meta data. - /// - /// Name of the logger. - /// - private HttpEventCollectorEventInfo.Metadata GetMetaData(string index, string source, string sourcetype) - { - var hostName = _hostName ?? (_hostName = GetMachineName()); - if (!_metaData.TryGetValue(source ?? string.Empty, out var metaData)) - { - if (_metaData.Count > 1000) - _metaData.Clear(); // Extreme case that should never happen - metaData = new HttpEventCollectorEventInfo.Metadata(string.IsNullOrEmpty(index) ? null : index, string.IsNullOrEmpty(source) ? null : source, sourcetype, hostName); - _metaData[source ?? string.Empty] = metaData; - } - - return metaData; - } - - /// - /// Gets the machine name - /// - private static string GetMachineName() - { - return TryLookupValue(() => Environment.GetEnvironmentVariable("COMPUTERNAME"), "COMPUTERNAME") - ?? TryLookupValue(() => Environment.GetEnvironmentVariable("HOSTNAME"), "HOSTNAME") - ?? TryLookupValue(() => Environment.MachineName, "MachineName") - ?? TryLookupValue(() => System.Net.Dns.GetHostName(), "DnsHostName"); - } - - /// - /// Tries the lookup value. - /// - /// The lookup function. - /// Type of the lookup. - /// - private static string TryLookupValue(Func lookupFunc, string lookupType) - { - try - { - string lookupValue = lookupFunc()?.Trim(); - return string.IsNullOrEmpty(lookupValue) ? null : lookupValue; - } - catch (Exception ex) - { - NLog.Common.InternalLogger.Warn(ex, "SplunkHttpEventCollector: Failed to lookup {0}", lookupType); - return null; - } - } - } -} + var proxyConfig = UseProxy + ? new ProxyConfiguration + { + ProxyUrl = RenderLogEvent(ProxyUrl, LogEventInfo.CreateNullEvent()), + ProxyUser = RenderLogEvent(ProxyUser, LogEventInfo.CreateNullEvent()), + ProxyPassword = RenderLogEvent(ProxyPassword, LogEventInfo.CreateNullEvent()) + } + : new ProxyConfiguration { UseProxy = false }; + + var channel = RenderLogEvent(Channel, LogEventInfo.CreateNullEvent()); + var index = RenderLogEvent(Index, LogEventInfo.CreateNullEvent()); + var source = RenderLogEvent(Source, LogEventInfo.CreateNullEvent()); + var sourceType = RenderLogEvent(SourceType, LogEventInfo.CreateNullEvent()); + + _hecSender = new HttpEventCollectorSender( + new Uri(serverUri), // Splunk HEC URL + token, // Splunk HEC token *GUID* + channel, // Splunk HEC data channel *GUID* + GetMetaData(index, source, sourceType), // Metadata + HttpEventCollectorSender.SendMode.Sequential, // Sequential sending to keep message in order + BatchSizeBytes == 0 && BatchSizeCount == 0 ? 0 : 250, // BatchInterval - Set to 0 to disable + BatchSizeBytes, // BatchSizeBytes - Set to 0 to disable + BatchSizeCount, // BatchSizeCount - Set to 0 to disable + IgnoreSslErrors, // Enable Ssl Error ignore for self singed certs *BOOL* + proxyConfig, // Proxy Config - Set to false to disable + MaxConnectionsPerServer, // MaxConnectionsPerServer + new HttpEventCollectorResendMiddleware(RetriesOnError).Plugin, // Resend Middleware with retry + httpVersion10Hack: UseHttpVersion10Hack + ); + _hecSender.OnError += (e) => { InternalLogger.Error(e, "SplunkHttpEventCollector(Name={0}): Failed to send LogEvents", Name); }; + } + + /// + /// Disposes the initialized HttpEventCollectorSender + /// + protected override void CloseTarget() + { + try + { + _hecSender?.Dispose(); + base.CloseTarget(); + } + finally + { + _hecSender = null; + } + } + + /// + /// Writes the specified log event information. + /// + /// The log event information. + /// logEventInfo + /// SplunkHttpEventCollector SendEventToServer() called before InitializeTarget() + protected override void Write(LogEventInfo logEventInfo) + { + // Sanity check for LogEventInfo + if (logEventInfo == null) + { + throw new ArgumentNullException(nameof(logEventInfo)); + } + + // Make sure we have a properly setup HttpEventCollectorSender + if (_hecSender == null) + { + throw new NLogRuntimeException("SplunkHttpEventCollector SendEventToServer() called before InitializeTarget()"); + } + + // Build MetaData + var index = RenderLogEvent(Index, logEventInfo); + var source = RenderLogEvent(Source, logEventInfo); + var sourceType = RenderLogEvent(SourceType, logEventInfo); + var metaData = GetMetaData(index, source, sourceType); + + // Use NLog's built in tooling to get properties + var properties = GetAllProperties(logEventInfo); + + if (IncludePositionalParameters && logEventInfo.Parameters != null) + { + for (var i = 0; i < logEventInfo.Parameters.Length; ++i) + { + properties[string.Concat("{", i.ToString(), "}")] = logEventInfo.Parameters[i]; + } + } + + // Send the event to splunk + string renderedMessage = RenderLogEvent(Layout, logEventInfo); + _hecSender.Send(logEventInfo.TimeStamp, null, logEventInfo.Level.Name, logEventInfo.Message, renderedMessage, logEventInfo.Exception, properties, metaData); + if (BatchSizeBytes == 0 && BatchSizeCount == 0) + { + _hecSender.FlushSync(); + } + } + + /// + /// Flush any pending log messages asynchronously (in case of asynchronous targets). + /// + /// The asynchronous continuation. + protected override void FlushAsync(AsyncContinuation asyncContinuation) + { + try + { + _hecSender?.FlushSync(); + asyncContinuation(null); + } + catch (Exception ex) + { + asyncContinuation(ex); + } + } + + /// + /// Gets the meta data. + /// + /// Name of the logger. + /// + private HttpEventCollectorEventInfo.Metadata GetMetaData(string index, string source, string sourcetype) + { + var hostName = _hostName ?? (_hostName = GetMachineName()); + if (!_metaData.TryGetValue(source ?? string.Empty, out var metaData)) + { + if (_metaData.Count > 1000) + _metaData.Clear(); // Extreme case that should never happen + metaData = new HttpEventCollectorEventInfo.Metadata(string.IsNullOrEmpty(index) ? null : index, string.IsNullOrEmpty(source) ? null : source, sourcetype, hostName); + _metaData[source ?? string.Empty] = metaData; + } + + return metaData; + } + + /// + /// Gets the machine name + /// + private static string GetMachineName() + { + return TryLookupValue(() => Environment.GetEnvironmentVariable("COMPUTERNAME"), "COMPUTERNAME") + ?? TryLookupValue(() => Environment.GetEnvironmentVariable("HOSTNAME"), "HOSTNAME") + ?? TryLookupValue(() => Environment.MachineName, "MachineName") + ?? TryLookupValue(() => System.Net.Dns.GetHostName(), "DnsHostName"); + } + + /// + /// Tries the lookup value. + /// + /// The lookup function. + /// Type of the lookup. + /// + private static string TryLookupValue(Func lookupFunc, string lookupType) + { + try + { + string lookupValue = lookupFunc()?.Trim(); + return string.IsNullOrEmpty(lookupValue) ? null : lookupValue; + } + catch (Exception ex) + { + NLog.Common.InternalLogger.Warn(ex, "SplunkHttpEventCollector: Failed to lookup {0}", lookupType); + return null; + } + } + } +}