We are trying to consolidate our logging data in Splunk and then limit/reduce access to the Sitecore servers. I've not seen much from the community on how to set this up.
Are there any examples available demonstrating how to do this?
Sitecore Stack Exchange is a question and answer site for developers and end users of the Sitecore CMS and multichannel marketing software. It only takes a minute to sign up.
Sign up to join this communityThe following must be setup in advance to make logging to Splunk possible.
Note: The following example could use some TLC but gets the job done.
First step is to write some code that taps into the logging mechanism in Sitecore (log4net).
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using log4net.Appender;
using log4net.spi;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Scms.Foundation.Logging
{
public class SplunkAppender : AppenderSkeleton
{
private readonly HttpClient _httpClient;
private Uri _baseUrl;
public SplunkAppender()
{
var httpClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
_httpClient = new HttpClient(httpClientHandler);
}
public string Url { get; set; }
public string Token { get; set; }
public override void ActivateOptions()
{
base.ActivateOptions();
_baseUrl = new Uri(Url);
_httpClient.DefaultRequestHeaders.ExpectContinue = false;
if (string.IsNullOrWhiteSpace(Token))
return;
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Splunk", Token);
}
protected override void Append(LoggingEvent loggingEvent)
{
Task.Run(async () =>
{
try
{
var logEntry = new SplunkLogEntry
{
Event =
{
Level = loggingEvent.Level.Name,
Message = loggingEvent.RenderedMessage,
Time = loggingEvent.TimeStamp.ToString("HH:mm:ss"),
Date = loggingEvent.TimeStamp.ToString("yyyy-MM-dd"),
Username = loggingEvent.UserName
},
Source = loggingEvent.LoggerName
};
var serializedContent = JsonConvert.SerializeObject(logEntry, new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
var stringContent = new StringContent(serializedContent, Encoding.UTF8, "application/json");
var responseMessage = await _httpClient.PostAsync(_baseUrl, stringContent);
}
catch (Exception ex)
{
this.ErrorHandler.Error("Unable to send logging event to remote host: " + Url, ex);
}
});
}
}
public class SplunkLogEntry
{
public string Index { get; set; } = "sitecore";
[JsonProperty("sourcetype")]
public string SourceType { get; set; } = "sitecore:log4net";
public string Source { get; set; } = nameof(SplunkAppender).ToLower();
public string Host { get; set; } = Environment.MachineName.ToLower();
public SplunkEvent Event { get; set; } = new SplunkEvent();
}
public class SplunkEvent
{
public string Level { get; set; }
public string Message { get; set; }
public string Time { get; set; }
public string Date { get; set; }
public string Username { get; set; }
}
}
Second step is to patch in the new appender.
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<log4net>
<appender name="SplunkAppender" type="Scms.Foundation.Logging.SplunkAppender, Scms.Foundation">
<url value="https://your-splunk-instance:8088/services/collector/raw" />
<token value="your-guid-from-splunk" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n"/>
</layout>
</appender>
<root>
<appender-ref ref="SplunkAppender" />
</root>
</log4net>
</sitecore>
</configuration>
Finally, log into Splunk and watch all your hard work pay off.