Skip to content

Commit ee0e983

Browse files
authored
Merge pull request #31 from vtex/vtexsplunklogger/evidence
Implemented VTEX evidence concept Improved ILogger extension class
2 parents 45af956 + 7b51f4c commit ee0e983

File tree

9 files changed

+159
-39
lines changed

9 files changed

+159
-39
lines changed

src/SampleWebAPI/Startup.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public void ConfigureServices(IServiceCollection services)
3737
/// <param name="loggerFactory">Logger factory.</param>
3838
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
3939
{
40+
ILoggerExtensions.SetApplication("SplunkLoggerSampleWebAPI");
41+
4042
loggerFactory.AddDebug();
4143

4244
var splunkConfiguration = new SplunkLoggerConfiguration()

src/SplunkLogger/Configurations/SplunkLoggerConfiguration.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,16 @@ public class SplunkLoggerConfiguration
1010
/// <summary>
1111
/// Gets or sets the hec configuration.
1212
/// </summary>
13-
/// <value>The hec configuration.</value>
1413
public HECConfiguration HecConfiguration { get; set; }
1514

1615
/// <summary>
1716
/// Gets or sets the socket configuration.
1817
/// </summary>
19-
/// <value>The socket configuration.</value>
2018
public SocketConfiguration SocketConfiguration { get; set; }
2119

2220
/// <summary>
2321
/// Gets or sets the threshold.
2422
/// </summary>
25-
/// <value>The threshold.</value>
2623
public LogLevel Threshold { get; set; } = LogLevel.Warning;
2724
}
2825
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.Text;
2+
3+
namespace Vtex
4+
{
5+
static class ByteArrayExtensions
6+
{
7+
internal static string ToHex(this byte[] bytes, bool upperCase = false)
8+
{
9+
StringBuilder result = new StringBuilder(bytes.Length * 2);
10+
11+
for (int i = 0; i < bytes.Length; i++)
12+
result.Append(bytes[i].ToString(upperCase ? "X2" : "x2"));
13+
14+
return result.ToString();
15+
}
16+
}
17+
}

src/VTEXSplunkLogger/ILoggerExtensions.cs

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
using System;
22
using System.Collections.Concurrent;
3+
using System.Diagnostics;
4+
using System.Net.Http;
5+
using System.Net.Http.Headers;
6+
using System.Security.Cryptography;
7+
using System.Text;
38
using Microsoft.Extensions.Logging;
49
using Vtex.SplunkLogger;
510

@@ -12,13 +17,51 @@ public static class ILoggerExtensions
1217
{
1318
static readonly EventId emptyEventId = new EventId();
1419
static readonly ConcurrentDictionary<ILogger, MetricManager> metricManagers = new ConcurrentDictionary<ILogger, MetricManager>();
20+
static readonly HttpClient evidenceHttpClient = CreateEvidenceHttpClient();
21+
22+
static string Application = "";
23+
24+
static HttpClient CreateEvidenceHttpClient()
25+
{
26+
HttpClient httpClient = new HttpClient
27+
{
28+
BaseAddress = new Uri("http://evidence.vtex.com"), // This is a internal VTEX route. If you aren't from VTEX you will not be able to use it.
29+
MaxResponseContentBufferSize = 2147483647,
30+
Timeout = TimeSpan.FromSeconds(10)
31+
};
32+
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
33+
return httpClient;
34+
}
1535

1636
static void KpiReady(object sender, VTEXKpiEntry kpiEntry)
1737
{
1838
if(sender is ILogger)
1939
((ILogger)sender).Log(LogLevel.Critical, emptyEventId, kpiEntry, null, null);
2040
}
2141

42+
static string GenerateHash(string evidenceData)
43+
{
44+
var bytes = Encoding.UTF8.GetBytes(evidenceData);
45+
byte[] md5Bytes = new byte[] { };
46+
using (var md5Instance = MD5.Create())
47+
{
48+
md5Bytes = md5Instance.ComputeHash(bytes);
49+
}
50+
return md5Bytes.ToHex();
51+
}
52+
53+
54+
/// <summary>
55+
/// Method used to define application name at project startup.
56+
/// </summary>
57+
public static void SetApplication(string application)
58+
{
59+
if (string.IsNullOrWhiteSpace(application))
60+
throw new ArgumentNullException(nameof(application));
61+
62+
Application = application;
63+
}
64+
2265
/// <summary>
2366
/// Log to Splunk.
2467
/// </summary>
@@ -31,10 +74,41 @@ static void KpiReady(object sender, VTEXKpiEntry kpiEntry)
3174
/// <param name="extraParameters">Extra parameters.</param>
3275
public static void DefineVTEXLog(this ILogger logger, LogLevel logLevel, string workflowType, string workflowInstance, string account = "", Exception exception = null, params Tuple<string, string>[] extraParameters)
3376
{
77+
if (string.IsNullOrWhiteSpace(Application))
78+
throw new NullReferenceException("You must call `ILoggerExtensions.SetApplication` method before call to save log.");
79+
3480
string formattedMessage = string.Empty;
81+
string evidenceHash = "";
82+
string evidenceText = "";
83+
84+
if (exception != null)
85+
{
86+
evidenceText = string.Format(
87+
"Account: {1}{0}WorkflowType: {2}{0}WorkflowInstance: {3}{0}ExceptionType: {4}{0}ExceptionMessage: {5}{0}ExceptionBaseStack:{6}{0}ExceptionStack: {7}",
88+
Environment.NewLine,
89+
account,
90+
workflowType,
91+
workflowInstance,
92+
exception.GetType().Name,
93+
exception.GetBaseException().Message,
94+
exception.GetBaseException().StackTrace,
95+
exception.StackTrace);
96+
evidenceHash = GenerateHash(evidenceText);
97+
var putPath = $"/api/evidence?application={Application}&hash={evidenceHash}";
98+
evidenceHttpClient.PutAsync(putPath, new StringContent(evidenceText, Encoding.UTF8, "text/plain"))
99+
.ContinueWith(task => {
100+
if (task.IsCompletedSuccessfully)
101+
Debug.WriteLine("VTEX Evidence Status: Sucess");
102+
else if (task.IsCanceled)
103+
Debug.WriteLine("VTEX Evidence Status: Canceled");
104+
else
105+
Debug.WriteLine("VTEX Evidence Status: Error " + task.Exception != null ? task.Exception.ToString() : "");
106+
});;
107+
}
108+
35109
logger.Log(logLevel,
36110
emptyEventId,
37-
new VTEXLogEntry(workflowType, workflowInstance, account, exception, extraParameters),
111+
new VTEXLogEntry(Application, workflowType, workflowInstance, account, evidenceHash, extraParameters),
38112
exception, (VTEXLogEntry arg1, Exception arg2) =>
39113
{
40114
if (string.IsNullOrWhiteSpace(formattedMessage))
@@ -44,7 +118,8 @@ public static void DefineVTEXLog(this ILogger logger, LogLevel logLevel, string
44118
if (exception != null)
45119
exceptionSegment = $"Exception type: {exception.GetType().FullName}. Exception message: {exception.Message}";
46120
var accountSegment = !string.IsNullOrWhiteSpace(account) ? account : "-";
47-
formattedMessage = string.Format($"[{logLevel}] {eventSegment} {accountSegment}. {exceptionSegment}");
121+
var evidenceSegment = !string.IsNullOrWhiteSpace(evidenceText) ? $"Evidence hash: {evidenceHash}. Evidence text: {evidenceText}" : "";
122+
formattedMessage = string.Format($"[{logLevel}] {eventSegment} {accountSegment}.{Environment.NewLine}{exceptionSegment}.{Environment.NewLine}{evidenceSegment}");
48123
}
49124
return formattedMessage;
50125
});
@@ -60,7 +135,10 @@ public static void DefineVTEXLog(this ILogger logger, LogLevel logLevel, string
60135
/// <param name="extraParameters">Extra parameters.</param>
61136
public static void DefineVTEXKpi(this ILogger logger, string kpiName, float kpiValue, string account = "", params Tuple<string, string>[] extraParameters)
62137
{
63-
metricManagers.GetOrAdd(logger, new MetricManager(logger, KpiReady)).RegisterKpi(kpiName, kpiValue, account, extraParameters);
138+
if (string.IsNullOrWhiteSpace(Application))
139+
throw new NullReferenceException("You must call `ILoggerExtensions.SetApplication` method before call to save log.");
140+
141+
metricManagers.GetOrAdd(logger, new MetricManager(logger, KpiReady)).RegisterKpi(Application, kpiName, kpiValue, account, extraParameters);
64142
}
65143
}
66144
}

src/VTEXSplunkLogger/MetricManager.cs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,21 @@ internal MetricManager(ILogger logger, EventHandler<VTEXKpiEntry> kpiReady)
6464

6565
#region [ Internal Methods ]
6666

67-
internal void RegisterKpi(string kpiName, float kpiValue, string account = "", params Tuple<string, string>[] extraParameters)
67+
internal void RegisterKpi(string application, string kpiName, float kpiValue, string account = "", params Tuple<string, string>[] extraParameters)
6868
{
69+
if (string.IsNullOrWhiteSpace(application))
70+
throw new ArgumentNullException(nameof(application));
6971

72+
if (string.IsNullOrWhiteSpace(kpiName))
73+
throw new ArgumentNullException(nameof(kpiName));
74+
7075
var extraFields = new Dictionary<string, string>();
7176

7277
if (extraParameters != null && extraParameters.Length > 0)
7378
extraParameters.ToList().ForEach(tuple => extraFields.Add(tuple.Item1, tuple.Item2));
7479

80+
extraFields.Add("application", application);
81+
7582
if (!string.IsNullOrWhiteSpace(account))
7683
extraFields.Add("account", account);
7784

@@ -148,28 +155,34 @@ VTEXKpiEntry GenerateEntry(string key, Tuple<ulong, float> valueTuple, float max
148155
string metricName = string.Empty;
149156
Dictionary<string, string> extraFields = null;
150157
RetreiveKeyItems(key, out metricName, out extraFields);
151-
152-
VTEXKpiEntry entry = new VTEXKpiEntry(metricName)
153-
{
154-
Count = valueTuple.Item1,
155-
Name = metricName,
156-
Sum = valueTuple.Item2,
157-
Max = maxValue,
158-
Min = minValue
159-
};
158+
var account = "";
159+
var application = "";
160160

161161
if (extraFields != null && extraFields.Count > 0)
162162
{
163+
if (extraFields.ContainsKey("application"))
164+
{
165+
application = extraFields["application"];
166+
extraFields.Remove("application");
167+
}
168+
163169
if (extraFields.ContainsKey("account"))
164170
{
165-
entry.Account = extraFields["account"];
171+
account = extraFields["account"];
166172
extraFields.Remove("account");
167173
}
168174
}
169175

170-
entry.ExtraParameters = extraFields;
171-
172-
return entry;
176+
return new VTEXKpiEntry(application, metricName)
177+
{
178+
Count = valueTuple.Item1,
179+
Name = metricName,
180+
Sum = valueTuple.Item2,
181+
Max = maxValue,
182+
Min = minValue,
183+
Account = account,
184+
ExtraParameters = extraFields
185+
};
173186
}
174187

175188
void RetreiveKeyItems(string key, out string metricName, out Dictionary<string, string> customFields)

src/VTEXSplunkLogger/VTEXKpiEntry.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ namespace Vtex.SplunkLogger
77
/// </summary>
88
public class VTEXKpiEntry
99
{
10+
/// <summary>
11+
/// Application name.
12+
/// </summary>
13+
public string Application { get; private set; }
14+
1015
/// <summary>
1116
/// For which account this kpi happened.
1217
/// </summary>
@@ -47,9 +52,10 @@ public class VTEXKpiEntry
4752
/// </summary>
4853
public float Value { get; internal set; }
4954

50-
internal VTEXKpiEntry(string name)
55+
internal VTEXKpiEntry(string application, string name)
5156
{
5257
ExtraParameters = new Dictionary<string, string>();
58+
Application = application;
5359
Name = name;
5460
}
5561

src/VTEXSplunkLogger/VTEXLogEntry.cs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ namespace Vtex.SplunkLogger
88
/// </summary>
99
public class VTEXLogEntry
1010
{
11+
/// <summary>
12+
/// Application name.
13+
/// </summary>
14+
public string Application { get; private set; }
15+
1116
/// <summary>
1217
/// Workflow type is a VTEX concept almost similar as `{EventId.Name}` concept.
1318
/// </summary>
@@ -24,42 +29,41 @@ public class VTEXLogEntry
2429
public string Account { get; private set; }
2530

2631
/// <summary>
27-
/// Exception to be logged.
32+
/// Extra parameters that will be represented as `{Key}="{Value}"` entries at Splunk text message.
2833
/// </summary>
29-
public Exception Exception { get; private set; }
34+
public List<Tuple<string, string>> ExtraParameters { get; private set; }
3035

3136
/// <summary>
32-
/// Extra parameters that will be represented as `{Key}="{Value}"` entries at Splunk text message.
37+
/// Evidence information.
3338
/// </summary>
34-
public List<Tuple<string, string>> ExtraParameters { get; private set; }
39+
public string Evidence { get; private set; }
3540

3641
/// <summary>
3742
/// Initializes a new instance of the <see cref="T:VTEX.SampleWebAPI.Logging.VTEXLogEntry"/> class.
3843
/// </summary>
44+
/// <param name="application">Application name.</param>
3945
/// <param name="workflowType">Workflow type.</param>
4046
/// <param name="workflowInstance">Workflow instance.</param>
4147
/// <param name="account">Account.</param>
42-
/// <param name="exception">Exception.</param>
48+
/// <param name="evidence">Evidence text.</param>
4349
/// <param name="extraParameters">Extra parameters.</param>
44-
public VTEXLogEntry(string workflowType, string workflowInstance, string account = "", Exception exception = null, params Tuple<string, string>[] extraParameters)
50+
public VTEXLogEntry(string application, string workflowType, string workflowInstance, string account = "", string evidence = "", params Tuple<string, string>[] extraParameters)
4551
{
52+
if (string.IsNullOrWhiteSpace(application))
53+
throw new ArgumentNullException(nameof(application));
54+
4655
if (string.IsNullOrWhiteSpace(workflowType))
4756
throw new ArgumentNullException(nameof(workflowType));
4857

4958
if (string.IsNullOrWhiteSpace(workflowInstance))
5059
throw new ArgumentNullException(nameof(workflowInstance));
5160

5261
ExtraParameters = new List<Tuple<string, string>>(extraParameters);
62+
Application = application;
5363
WorkflowType = workflowType;
5464
WorkflowInstance = workflowInstance;
5565
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-
}
66+
Evidence = evidence;
6367
}
6468
}
6569
}

src/VTEXSplunkLogger/VTEXSplunkLogger.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp2.0</TargetFramework>
5-
<ReleaseVersion>1.1.0</ReleaseVersion>
6-
<Version>1.1.0</Version>
5+
<ReleaseVersion>1.2.0</ReleaseVersion>
6+
<Version>1.2.0</Version>
77
<Description>VTEX SplunkLogger library</Description>
88
<RootNamespace>Vtex.SplunkLogger</RootNamespace>
99
<Authors>Fábio Caldas</Authors>

src/VTEXSplunkLogger/VTEXSplunkLoggerFormatter.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ public string Format<T>(LogLevel logLevel, EventId eventId, T state, Exception e
5353
string account = entry.Account;
5454
if (string.IsNullOrWhiteSpace(entry.Account))
5555
account = "-";
56-
log = string.Format($"{DateTime.UtcNow.ToString(DateTimeFormat)} VTEXLog,splunkmanager,{host},{GetVTEXEventLevel(logLevel)},{GetVTEXLogType(logLevel)},\"{entry.WorkflowType}\",\"{entry.WorkflowInstance}\",{account},{appVersion} {extraData}");
56+
string evidence = string.Empty;
57+
if (!string.IsNullOrWhiteSpace(entry.Evidence))
58+
evidence = $"evidence={entry.Evidence} ";
59+
log = string.Format($"{DateTime.UtcNow.ToString(DateTimeFormat)} VTEXLog,{entry.Application},{host},{GetVTEXEventLevel(logLevel)},{GetVTEXLogType(logLevel)},\"{entry.WorkflowType}\",\"{entry.WorkflowInstance}\",{account},{appVersion} {evidence}{extraData}");
5760
}
58-
if(state is VTEXKpiEntry)
61+
else if(state is VTEXKpiEntry)
5962
{
6063
var entry = state as VTEXKpiEntry;
6164

@@ -66,7 +69,7 @@ public string Format<T>(LogLevel logLevel, EventId eventId, T state, Exception e
6669
if (string.IsNullOrWhiteSpace(entry.Account))
6770
account = "-";
6871

69-
log = string.Format($"{DateTime.UtcNow.RemoveSecondMiliSecond().ToString(DateTimeFormat)} VTEXKpi,splunkmanager,{host},{GetVTEXEventLevel(logLevel)},\"{entry.Name}\",{entry.Sum},{entry.Count},{entry.Min},{entry.Max},{account},{appVersion} {extraData}");
72+
log = string.Format($"{DateTime.UtcNow.RemoveSecondMiliSecond().ToString(DateTimeFormat)} VTEXKpi,{entry.Application},{host},{GetVTEXEventLevel(logLevel)},\"{entry.Name}\",{entry.Sum},{entry.Count},{entry.Min},{entry.Max},{account},{appVersion} {extraData}");
7073
}
7174
else
7275
{

0 commit comments

Comments
 (0)