Skip to content

Commit 9780e5a

Browse files
author
mwatson
committed
Core - Fix log serialization issue. Add NLog project
1 parent acfe8a7 commit 9780e5a

26 files changed

+10585
-490
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
// General Information about an assembly is controlled through the following
6+
// set of attributes. Change these attribute values to modify the information
7+
// associated with an assembly.
8+
[assembly: AssemblyConfiguration("")]
9+
[assembly: AssemblyCompany("")]
10+
[assembly: AssemblyProduct("StackifyLib.NLog")]
11+
[assembly: AssemblyTrademark("")]
12+
13+
// Setting ComVisible to false makes the types in this assembly not visible
14+
// to COM components. If you need to access a type in this assembly from
15+
// COM, set the ComVisible attribute to true on that type.
16+
[assembly: ComVisible(false)]
17+
18+
// The following GUID is for the ID of the typelib if this project is exposed to COM
19+
[assembly: Guid("55cc4568-242f-40d7-9bd3-23c4f502c161")]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
5+
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
6+
</PropertyGroup>
7+
8+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
9+
<PropertyGroup Label="Globals">
10+
<ProjectGuid>55cc4568-242f-40d7-9bd3-23c4f502c161</ProjectGuid>
11+
<RootNamespace>StackifyLib.NLog</RootNamespace>
12+
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
13+
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
14+
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
15+
</PropertyGroup>
16+
17+
<PropertyGroup>
18+
<SchemaVersion>2.0</SchemaVersion>
19+
</PropertyGroup>
20+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
21+
</Project>
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Globalization;
5+
using System.Linq;
6+
using System.Reflection;
7+
using NLog;
8+
using NLog.Targets;
9+
using StackifyLib.Internal.Logs;
10+
using StackifyLib.Models;
11+
using StackifyLib.Utils;
12+
13+
namespace StackifyLib.nLog
14+
{
15+
[Target("StackifyTarget")]
16+
public class StackifyTarget : TargetWithLayout
17+
{
18+
private bool _HasContextKeys = false;
19+
public string apiKey { get; set; }
20+
public string uri { get; set; }
21+
public string globalContextKeys { get; set; }
22+
public string mappedContextKeys { get; set; }
23+
//public string callContextKeys { get; set; }
24+
public bool? logMethodNames { get; set; }
25+
public bool? logAllParams { get; set; }
26+
27+
private List<string> _GlobalContextKeys = new List<string>();
28+
private List<string> _MappedContextKeys = new List<string>();
29+
// private List<string> _CallContextKeys = new List<string>();
30+
31+
private LogClient _logClient = null;
32+
33+
protected override void CloseTarget()
34+
{
35+
try
36+
{
37+
Utils.StackifyAPILogger.Log("NLog target closing");
38+
_logClient.Close();
39+
StackifyLib.Internal.Metrics.MetricClient.StopMetricsQueue("NLog CloseTarget");
40+
}
41+
catch (Exception ex)
42+
{
43+
Utils.StackifyAPILogger.Log("NLog target closing error: " + ex.ToString());
44+
}
45+
}
46+
47+
protected override void InitializeTarget()
48+
{
49+
Utils.StackifyAPILogger.Log("NLog InitializeTarget");
50+
51+
_logClient = new LogClient("StackifyLib.net-nlog", apiKey, uri);
52+
if (!String.IsNullOrEmpty(globalContextKeys))
53+
{
54+
_GlobalContextKeys = globalContextKeys.Split(',').Select(s => s.Trim()).ToList();
55+
}
56+
57+
if (!String.IsNullOrEmpty(mappedContextKeys))
58+
{
59+
_MappedContextKeys = mappedContextKeys.Split(',').Select(s => s.Trim()).ToList();
60+
}
61+
62+
//if (!String.IsNullOrEmpty(callContextKeys))
63+
//{
64+
// _CallContextKeys = callContextKeys.Split(',').Select(s => s.Trim()).ToList();
65+
//}
66+
67+
68+
_HasContextKeys = _GlobalContextKeys.Any() || _MappedContextKeys.Any();
69+
// _HasContextKeys = _GlobalContextKeys.Any() || _MappedContextKeys.Any() || _CallContextKeys.Any();
70+
}
71+
72+
protected override void Write(LogEventInfo logEvent)
73+
{
74+
try
75+
{
76+
//make sure the buffer isn't overflowing
77+
//if it is skip since we can't do anything with the message
78+
if (Logger.PrefixEnabled() || _logClient.CanQueue())
79+
{
80+
var logMsg = Translate(logEvent);
81+
if (logMsg != null)
82+
{
83+
_logClient.QueueMessage(logMsg);
84+
}
85+
}
86+
else
87+
{
88+
StackifyAPILogger.Log("Unable to send log because the queue is full");
89+
}
90+
}
91+
catch (Exception ex)
92+
{
93+
StackifyAPILogger.Log(ex.ToString());
94+
}
95+
96+
}
97+
98+
99+
private Dictionary<string, object> GetDiagnosticContextProperties()
100+
{
101+
102+
103+
Dictionary<string, object> properties = new Dictionary<string, object>();
104+
105+
106+
string ndc = NLog.NestedDiagnosticsContext.TopMessage;
107+
108+
if (!String.IsNullOrEmpty(ndc))
109+
{
110+
properties["ndc"] = ndc;
111+
}
112+
113+
114+
if (!_HasContextKeys)
115+
{
116+
return properties;
117+
}
118+
119+
// GlobalDiagnosticsContext
120+
121+
foreach (string gdcKey in _GlobalContextKeys)
122+
{
123+
if (NLog.GlobalDiagnosticsContext.Contains(gdcKey))
124+
{
125+
string gdcValue = NLog.GlobalDiagnosticsContext.Get(gdcKey);
126+
127+
if (gdcValue != null)
128+
{
129+
properties.Add(gdcKey.ToLower(), gdcValue);
130+
}
131+
}
132+
}
133+
// MappedDiagnosticsContext
134+
135+
foreach (string mdcKey in _MappedContextKeys)
136+
{
137+
if (NLog.MappedDiagnosticsContext.Contains(mdcKey))
138+
{
139+
string mdcValue = NLog.MappedDiagnosticsContext.Get(mdcKey);
140+
141+
if (mdcValue != null)
142+
{
143+
properties.Add(mdcKey.ToLower(), mdcValue);
144+
}
145+
}
146+
}
147+
148+
//foreach (string key in _CallContextKeys)
149+
//{
150+
// object value = CallContext.LogicalGetData(key);
151+
152+
// if (value != null)
153+
// {
154+
// properties[key.ToLower()] = value;
155+
// }
156+
//}
157+
158+
return properties;
159+
160+
}
161+
162+
internal LogMsg Translate(LogEventInfo loggingEvent)
163+
{
164+
165+
if (loggingEvent == null)
166+
return null;
167+
168+
//do not log our own messages. This is to prevent any sort of recursion that could happen since calling to send this will cause even more logging to happen
169+
if (loggingEvent.FormattedMessage != null && loggingEvent.FormattedMessage.IndexOf("StackifyLib:", StringComparison.OrdinalIgnoreCase) > -1)
170+
return null;
171+
172+
StackifyLib.Models.LogMsg msg = new LogMsg();
173+
174+
175+
if (loggingEvent.Level != null)
176+
{
177+
msg.Level = loggingEvent.Level.Name;
178+
}
179+
180+
181+
182+
if (loggingEvent.HasStackTrace && loggingEvent.UserStackFrame != null)
183+
{
184+
var frame = loggingEvent.UserStackFrame;
185+
186+
MethodBase method = frame.GetMethod();
187+
if (method != (MethodBase) null && method.DeclaringType != (Type) null)
188+
{
189+
if (method.DeclaringType != (Type) null)
190+
{
191+
msg.SrcMethod = method.DeclaringType.FullName + "." + method.Name;
192+
msg.SrcLine = frame.GetFileLineNumber();
193+
}
194+
}
195+
196+
}
197+
198+
199+
//if it wasn't set above for some reason we will do it this way as a fallback
200+
if (string.IsNullOrEmpty(msg.SrcMethod))
201+
{
202+
msg.SrcMethod = loggingEvent.LoggerName;
203+
204+
if ((logMethodNames ?? false))
205+
{
206+
var frames = StackifyLib.Logger.GetCurrentStackTrace(loggingEvent.LoggerName, 1, true);
207+
208+
if (frames.Any())
209+
{
210+
var first = frames.First();
211+
212+
msg.SrcMethod = first.Method;
213+
msg.SrcLine = first.LineNum;
214+
}
215+
}
216+
}
217+
218+
string formattedMessage;
219+
220+
//Use the layout render to allow custom fields to be logged, but not if it is the default format as it logs a bunch fields we already log
221+
//really no reason to use a layout at all
222+
if (this.Layout != null && this.Layout.ToString() != "'${longdate}|${level:uppercase=true}|${logger}|${message}'") //do not use if it is the default
223+
{
224+
formattedMessage = this.Layout.Render(loggingEvent);
225+
}
226+
else
227+
{
228+
formattedMessage = loggingEvent.FormattedMessage;
229+
}
230+
231+
msg.Msg = (formattedMessage ?? "").Trim();
232+
233+
object debugObject = null;
234+
Dictionary<string, object> args = new Dictionary<string, object>();
235+
236+
if ((loggingEvent.Parameters != null) && (loggingEvent.Parameters.Length > 0))
237+
{
238+
239+
for (int i = 0; i < loggingEvent.Parameters.Length; i++)
240+
{
241+
var item = loggingEvent.Parameters[i];
242+
243+
if (item == null)
244+
{
245+
continue;
246+
}
247+
else if (item is Exception)
248+
{
249+
if (loggingEvent.Exception == null)
250+
{
251+
loggingEvent.Exception = (Exception)item;
252+
}
253+
}
254+
else if (item.ToString() == msg.Msg)
255+
{
256+
//ignore it.
257+
}
258+
else if (logAllParams ?? true)
259+
{
260+
args["arg" + i] = loggingEvent.Parameters[i];
261+
debugObject = item;
262+
}
263+
else
264+
{
265+
debugObject = item;
266+
}
267+
}
268+
269+
if ((logAllParams ?? true) && args != null && args.Count > 1)
270+
{
271+
debugObject = args;
272+
}
273+
}
274+
275+
276+
StackifyError error = null;
277+
278+
if (loggingEvent.Exception != null && loggingEvent.Exception is StackifyError)
279+
{
280+
error = (StackifyError) loggingEvent.Exception;
281+
}
282+
else if (loggingEvent.Exception != null)
283+
{
284+
error = StackifyError.New((Exception)loggingEvent.Exception);
285+
}
286+
287+
var diags = GetDiagnosticContextProperties();
288+
if (diags != null && diags.ContainsKey("transid"))
289+
{
290+
msg.TransID = diags["transid"].ToString();
291+
diags.Remove("transid");
292+
}
293+
294+
295+
if (debugObject != null)
296+
{
297+
msg.data = StackifyLib.Utils.HelperFunctions.SerializeDebugData(debugObject, true, diags);
298+
}
299+
else
300+
{
301+
msg.data = StackifyLib.Utils.HelperFunctions.SerializeDebugData(null, false, diags);
302+
}
303+
304+
305+
if (msg.Msg != null && error != null)
306+
{
307+
msg.Msg += "\r\n" + error.ToString();
308+
}
309+
else if (msg.Msg == null && error != null)
310+
{
311+
msg.Msg = error.ToString();
312+
}
313+
314+
if (error == null && (loggingEvent.Level == LogLevel.Error || loggingEvent.Level == LogLevel.Fatal))
315+
{
316+
StringException stringException = new StringException(msg.Msg);
317+
318+
stringException.TraceFrames = StackifyLib.Logger.GetCurrentStackTrace(loggingEvent.LoggerName);
319+
320+
if (!loggingEvent.HasStackTrace || loggingEvent.UserStackFrame == null)
321+
{
322+
if (stringException.TraceFrames.Any())
323+
{
324+
var first = stringException.TraceFrames.First();
325+
326+
msg.SrcMethod = first.Method;
327+
msg.SrcLine = first.LineNum;
328+
}
329+
}
330+
331+
//Make error out of log message
332+
error = StackifyError.New(stringException);
333+
}
334+
335+
if (error != null && !StackifyError.IgnoreError(error) && _logClient.ErrorShouldBeSent(error))
336+
{
337+
error.SetAdditionalMessage(formattedMessage);
338+
msg.Ex = error;
339+
}
340+
else if (error != null && msg.Msg != null)
341+
{
342+
msg.Msg += " #errorgoverned";
343+
}
344+
345+
346+
return msg;
347+
}
348+
349+
350+
351+
}
352+
}

0 commit comments

Comments
 (0)