Skip to content

Commit d14742a

Browse files
committed
Created new VTEXSplunkLogger library project
#17
1 parent cad90a5 commit d14742a

File tree

7 files changed

+340
-3
lines changed

7 files changed

+340
-3
lines changed

src/SampleWebAPI/Logging/VTEXSplunkEntryExtensions.cs renamed to src/VTEXSplunkLogger/ILoggerExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
using System;
22
using Microsoft.Extensions.Logging;
3-
using VTEX.SampleWebAPI.Logging;
3+
using Vtex.SplunkLogger;
44

5-
namespace VTEX.SampleWebAPI
5+
namespace Vtex
66
{
77
/// <summary>
88
/// This class contains ILogger extension method to simplify the process to record a VTEX log.
99
/// </summary>
10-
public static class VTEXSplunkEntryExtensions
10+
public static class ILoggerExtensions
1111
{
1212
static readonly EventId EmptyEventId = new EventId();
1313

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using Microsoft.Extensions.Logging;
2+
using Splunk.Providers;
3+
using Splunk.Configurations;
4+
using Splunk;
5+
6+
namespace Vtex.SplunkLogger
7+
{
8+
/// <summary>
9+
/// This class contains ILoggerFactory extension method to simplify the process to add a Splunk logger provider.
10+
/// </summary>
11+
public static class LoggerFactoryExtensions
12+
{
13+
/// <summary>
14+
/// Add <see cref="T:Splunk.Providers.SplunkHECRawLoggerProvider"/> as provider to logger factory.
15+
/// </summary>
16+
/// <param name="loggerFactory">Logger factory.</param>
17+
/// <param name="configuration">Configuration.</param>
18+
/// <param name="formatter">Custom text formatter.</param>
19+
public static ILoggerFactory AddHECRawSplunkLogger(this ILoggerFactory loggerFactory, SplunkLoggerConfiguration configuration, ILoggerFormatter formatter)
20+
{
21+
loggerFactory.AddProvider(new SplunkHECRawLoggerProvider(configuration, formatter));
22+
return loggerFactory;
23+
}
24+
25+
/// <summary>
26+
/// Add <see cref="T:Splunk.Providers.SplunkHECJsonLoggerProvider"/> as provider to logger factory.
27+
/// </summary>
28+
/// <param name="loggerFactory">Logger factory.</param>
29+
/// <param name="configuration">Configuration.</param>
30+
/// <param name="formatter">Custom text formatter.</param>
31+
public static ILoggerFactory AddHECJsonSplunkLogger(this ILoggerFactory loggerFactory, SplunkLoggerConfiguration configuration, ILoggerFormatter formatter)
32+
{
33+
loggerFactory.AddProvider(new SplunkHECJsonLoggerProvider(configuration, formatter));
34+
return loggerFactory;
35+
}
36+
37+
/// <summary>
38+
/// Add <see cref="T:Splunk.Providers.SplunkTcpLoggerProvider"/> as provider to logger factory.
39+
/// </summary>
40+
/// <param name="loggerFactory">Logger factory.</param>
41+
/// <param name="configuration">Configuration.</param>
42+
/// <param name="formatter">Custom text formatter.</param>
43+
public static ILoggerFactory AddTcpSplunkLogger(this ILoggerFactory loggerFactory, SplunkLoggerConfiguration configuration, ILoggerFormatter formatter)
44+
{
45+
loggerFactory.AddProvider(new SplunkTcpLoggerProvider(configuration, formatter));
46+
return loggerFactory;
47+
}
48+
49+
/// <summary>
50+
/// Add <see cref="T:Splunk.Providers.SplunkUdpLoggerProvider"/> as provider to logger factory.
51+
/// </summary>
52+
/// <param name="loggerFactory">Logger factory.</param>
53+
/// <param name="configuration">Configuration.</param>
54+
/// <param name="formatter">Custom text formatter.</param>
55+
public static ILoggerFactory AddUdpSplunkLogger(this ILoggerFactory loggerFactory, SplunkLoggerConfiguration configuration, ILoggerFormatter formatter)
56+
{
57+
loggerFactory.AddProvider(new SplunkUdpLoggerProvider(configuration, formatter));
58+
return loggerFactory;
59+
}
60+
}
61+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+

2+
namespace Vtex.SplunkLogger
3+
{
4+
/// <summary>
5+
/// This enumeration represents the log event levels defined at VTEX.
6+
/// </summary>
7+
public enum VTEXEventLevel : int
8+
{
9+
Critical = 3,
10+
Important = 2,
11+
Default = 1,
12+
Debug = 0,
13+
}
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+

2+
namespace Vtex.SplunkLogger
3+
{
4+
/// <summary>
5+
/// This enumeration represents the log types defined at VTEX.
6+
/// </summary>
7+
public enum VTEXLogType
8+
{
9+
Error,
10+
Warning,
11+
Info
12+
}
13+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Vtex.SplunkLogger
5+
{
6+
/// <summary>
7+
/// This class represents a VTEX log entry.
8+
/// </summary>
9+
public class VTEXSplunkEntry
10+
{
11+
/// <summary>
12+
/// Workflow type is a VTEX concept almost similar as `{EventId.Name}` concept.
13+
/// </summary>
14+
public string WorkflowType { get; private set; }
15+
16+
/// <summary>
17+
/// Workflow instace is a VTEX concept almost similar as `{EventId.Id}` concept.
18+
/// </summary>
19+
public string WorkflowInstance { get; private set; }
20+
21+
/// <summary>
22+
/// For which account this log happened.
23+
/// </summary>
24+
public string Account { get; private set; }
25+
26+
/// <summary>
27+
/// Exception to be logged.
28+
/// </summary>
29+
public Exception Exception { get; private set; }
30+
31+
/// <summary>
32+
/// Extra parameters that will be represented as `{Key}="{Value}"` entries at Splunk text message.
33+
/// </summary>
34+
public List<Tuple<string, string>> ExtraParameters { get; private set; }
35+
36+
/// <summary>
37+
/// Initializes a new instance of the <see cref="T:VTEX.SampleWebAPI.Logging.VTEXSplunkEntry"/> class.
38+
/// </summary>
39+
/// <param name="workflowType">Workflow type.</param>
40+
/// <param name="workflowInstance">Workflow instance.</param>
41+
/// <param name="account">Account.</param>
42+
/// <param name="exception">Exception.</param>
43+
/// <param name="extraParameters">Extra parameters.</param>
44+
public VTEXSplunkEntry(string workflowType, string workflowInstance, string account = "", Exception exception = null, params Tuple<string, string>[] extraParameters)
45+
{
46+
if (string.IsNullOrWhiteSpace(workflowType))
47+
throw new ArgumentNullException(nameof(workflowType));
48+
49+
if (string.IsNullOrWhiteSpace(workflowInstance))
50+
throw new ArgumentNullException(nameof(workflowInstance));
51+
52+
ExtraParameters = new List<Tuple<string, string>>(extraParameters);
53+
WorkflowType = workflowType;
54+
WorkflowInstance = workflowInstance;
55+
Account = account;
56+
Exception = exception;
57+
58+
if (exception != null)
59+
{
60+
ExtraParameters.Add(new Tuple<string, string>("exception_type", exception.GetType().FullName));
61+
ExtraParameters.Add(new Tuple<string, string>("exception_message", exception.Message));
62+
}
63+
}
64+
}
65+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp2.0</TargetFramework>
5+
<Description>VTEX SplunkLogger library</Description>
6+
<ReleaseVersion>1.0.1</ReleaseVersion>
7+
<RootNamespace>Vtex.SplunkLogger</RootNamespace>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\SplunkLogger\SplunkLogger.csproj" />
12+
</ItemGroup>
13+
<ItemGroup>
14+
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
15+
</ItemGroup>
16+
</Project>
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
using System;
2+
using System.Linq;
3+
using System.Net.Http;
4+
using System.Threading;
5+
using Microsoft.Extensions.Logging;
6+
using Splunk;
7+
8+
namespace Vtex.SplunkLogger
9+
{
10+
/// <summary>
11+
/// This class contains all methods to format a Log event into a VTEX standard text.
12+
/// </summary>
13+
/// <remarks>
14+
/// At our Splunk environment at VTEX we expect certain standard of text to be processed and
15+
/// we also have field extraction rules to be applied.
16+
/// Thats the reason why we use a custom LoggerFormmater.
17+
/// </remarks>
18+
public class VTEXSplunkLoggerFormatter : ILoggerFormatter
19+
{
20+
const string DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffZ";
21+
22+
readonly string appVersion;
23+
readonly string host;
24+
25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="T:VTEX.SampleWebAPI.Logging.VTEXSplunkLoggerFormatter"/> class.
27+
/// </summary>
28+
public VTEXSplunkLoggerFormatter()
29+
{
30+
appVersion = Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationVersion;
31+
host = GetHost();
32+
}
33+
34+
/// <summary>
35+
/// Format the specified logLevel, eventId, state and exception into log string entry.
36+
/// </summary>
37+
/// <returns>Formatted log string.</returns>
38+
/// <param name="logLevel">Log level.</param>
39+
/// <param name="eventId">Event identifier.</param>
40+
/// <param name="state">Log object state.</param>
41+
/// <param name="exception">Log exception.</param>
42+
/// <typeparam name="T">Log entry.</typeparam>
43+
public string Format<T>(LogLevel logLevel, EventId eventId, T state, Exception exception)
44+
{
45+
string log;
46+
if (state is VTEXSplunkEntry)
47+
{
48+
var splunkEntry = state as VTEXSplunkEntry;
49+
var dateTime = DateTime.UtcNow.ToString(DateTimeFormat);
50+
string extraData = string.Empty;
51+
if (splunkEntry.ExtraParameters != null && splunkEntry.ExtraParameters.Count > 0)
52+
extraData = string.Join(" ", splunkEntry.ExtraParameters.Select(part => { return $"{part.Item1}=\"{part.Item2}\""; }));
53+
string account = splunkEntry.Account;
54+
if (string.IsNullOrWhiteSpace(splunkEntry.Account))
55+
account = "-";
56+
log = string.Format($"{dateTime} VTEXLog,splunkmanager,{host},{GetVTEXEventLevel(logLevel)},{GetVTEXLogType(logLevel)},\"{splunkEntry.WorkflowType}\",\"{splunkEntry.WorkflowInstance}\",{account},{appVersion} {extraData}");
57+
}
58+
else
59+
{
60+
var eventSegment = "";
61+
if (!string.IsNullOrWhiteSpace(eventId.Name))
62+
eventSegment = $"Event '{eventId.Name}' on {eventId.Id}";
63+
var exceptionSegment = "";
64+
if (exception != null)
65+
exceptionSegment = $"Exception type: {exception.GetType().FullName}. Exception message: {exception.Message}";
66+
log = string.Format($"[{logLevel}] {eventSegment} {state.ToString()}. {exceptionSegment}");
67+
}
68+
return log;
69+
}
70+
71+
/// <summary>
72+
/// Formats the specified logLevel, eventId, state and exception into json entry.
73+
/// </summary>
74+
/// <returns>The json.</returns>
75+
/// <param name="logLevel">Log level.</param>
76+
/// <param name="eventId">Event identifier.</param>
77+
/// <param name="state">Log object state.</param>
78+
/// <param name="exception">Log exception.</param>
79+
/// <typeparam name="T">Log entry.</typeparam>
80+
public SplunkJSONEntry FormatJson<T>(LogLevel logLevel, EventId eventId, T state, Exception exception)
81+
{
82+
return new SplunkJSONEntry(Format(logLevel, eventId, state, exception), 0, host, string.Empty, "Log");
83+
}
84+
85+
/// <summary>
86+
/// Method created to get AWS EC2 host Id, or set `dev` as host if AWS internal call fails.
87+
/// </summary>
88+
string GetHost()
89+
{
90+
string ec2Host = string.Empty;
91+
try
92+
{
93+
using (HttpClient httpClient = new HttpClient())
94+
{
95+
TimeSpan timeSpan = new TimeSpan(0, 0, 5);
96+
var cancellationTokenSource = new CancellationTokenSource((int)timeSpan.TotalMilliseconds);
97+
httpClient.Timeout = timeSpan;
98+
httpClient.BaseAddress = new Uri("http://169.254.169.254/latest/meta-data/");
99+
ec2Host = httpClient
100+
.GetAsync("instance-id", cancellationTokenSource.Token)
101+
.Result
102+
.Content
103+
.ReadAsStringAsync()
104+
.Result;
105+
}
106+
}
107+
catch
108+
{
109+
ec2Host = "dev";
110+
}
111+
return ec2Host;
112+
}
113+
114+
/// <summary>
115+
/// Based on LogLevel we define the correspondent VTEX event level.
116+
/// </summary>
117+
/// <returns>VTEX event level.</returns>
118+
string GetVTEXEventLevel(LogLevel logLevel)
119+
{
120+
VTEXEventLevel eventLevel = VTEXEventLevel.Debug;
121+
switch (logLevel)
122+
{
123+
case LogLevel.Critical:
124+
eventLevel = VTEXEventLevel.Critical;
125+
break;
126+
case LogLevel.Warning:
127+
case LogLevel.Error:
128+
eventLevel = VTEXEventLevel.Important;
129+
break;
130+
case LogLevel.Information:
131+
eventLevel = VTEXEventLevel.Default;
132+
break;
133+
case LogLevel.Trace:
134+
case LogLevel.Debug:
135+
case LogLevel.None:
136+
eventLevel = VTEXEventLevel.Debug;
137+
break;
138+
}
139+
return eventLevel.ToString().ToLower();
140+
}
141+
142+
/// <summary>
143+
/// Based on LogLevel we define the correspondent VTEX log type.
144+
/// </summary>
145+
/// <returns>VTEX log type.</returns>
146+
string GetVTEXLogType(LogLevel logLevel)
147+
{
148+
VTEXLogType logType = VTEXLogType.Info;
149+
switch (logLevel)
150+
{
151+
case LogLevel.Critical:
152+
case LogLevel.Error:
153+
logType = VTEXLogType.Error;
154+
break;
155+
case LogLevel.Warning:
156+
logType = VTEXLogType.Warning;
157+
break;
158+
case LogLevel.Debug:
159+
case LogLevel.Information:
160+
case LogLevel.Trace:
161+
case LogLevel.None:
162+
logType = VTEXLogType.Info;
163+
break;
164+
}
165+
return logType.ToString().ToLower();
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)