-
Notifications
You must be signed in to change notification settings - Fork 285
/
ApplicationInsightsAppender.cs
235 lines (203 loc) · 8.44 KB
/
ApplicationInsightsAppender.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// -----------------------------------------------------------------------
// <copyright file="ApplicationInsightsAppender.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.
// All rights reserved. 2013
// </copyright>
// -----------------------------------------------------------------------
namespace Microsoft.ApplicationInsights.Log4NetAppender
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using log4net.Appender;
using log4net.Core;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility.Implementation;
using Microsoft.ApplicationInsights.Implementation;
/// <summary>
/// Log4Net Appender that routes all logging output to the Application Insights logging framework.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
Justification = "Releasing the resources on the close method")]
public sealed class ApplicationInsightsAppender : AppenderSkeleton
{
private TelemetryClient telemetryClient;
/// <summary>
/// Gets or sets The Application Insights instrumentationKey for your application.
/// </summary>
/// <remarks>
/// This is normally pushed from when Appender is being initialized.
/// </remarks>
public string InstrumentationKey { get; set; }
internal TelemetryClient TelemetryClient
{
get { return this.telemetryClient; }
}
/// <summary>
/// Gets a value indicating whether layout is required. The <see cref="ApplicationInsightsAppender"/> requires a layout.
/// This Appender converts the LoggingEvent it receives into a text string and requires the layout format string to do so.
/// </summary>
protected override bool RequiresLayout
{
get { return true; }
}
/// <summary>
/// Initializes the Appender and perform instrumentationKey validation.
/// </summary>
public override void ActivateOptions()
{
base.ActivateOptions();
#pragma warning disable CS0618 // Type or member is obsolete
this.telemetryClient = new TelemetryClient();
#pragma warning restore CS0618 // Type or member is obsolete
if (!string.IsNullOrEmpty(this.InstrumentationKey))
{
this.telemetryClient.Context.InstrumentationKey = this.InstrumentationKey;
}
this.telemetryClient.Context.GetInternalContext().SdkVersion = SdkVersionUtils.GetSdkVersion("log4net:");
}
/// <summary>
/// Flushes any buffered log data.
/// </summary>
/// <param name="millisecondsTimeout">The maximum time to wait for logging events to be flushed.</param>
/// <returns>True if all logging events were flushed successfully, else false.</returns>
public override bool Flush(int millisecondsTimeout)
{
this.telemetryClient.Flush();
return true;
}
/// <summary>
/// Append LoggingEvent Application Insights logging framework.
/// </summary>
/// <param name="loggingEvent">Events to be logged.</param>
protected override void Append(LoggingEvent loggingEvent)
{
if (loggingEvent == null)
{
throw new ArgumentNullException(nameof(loggingEvent));
}
if (loggingEvent.ExceptionObject != null)
{
this.SendException(loggingEvent);
}
else
{
this.SendTrace(loggingEvent);
}
}
private static void AddLoggingEventProperty(string key, string value, IDictionary<string, string> metaData)
{
if (value != null && !metaData.ContainsKey(key))
{
metaData.Add(key, value);
}
}
private static void BuildCustomProperties(LoggingEvent loggingEvent, ITelemetry trace)
{
trace.Timestamp = loggingEvent.TimeStamp;
IDictionary<string, string> metaData;
if (trace is ExceptionTelemetry)
{
metaData = ((ExceptionTelemetry)trace).Properties;
}
else
{
metaData = ((TraceTelemetry)trace).Properties;
}
AddLoggingEventProperty("LoggerName", loggingEvent.LoggerName, metaData);
AddLoggingEventProperty("ThreadName", loggingEvent.ThreadName, metaData);
var locationInformation = loggingEvent.LocationInformation;
if (locationInformation != null)
{
AddLoggingEventProperty("ClassName", locationInformation.ClassName, metaData);
AddLoggingEventProperty("FileName", locationInformation.FileName, metaData);
AddLoggingEventProperty("MethodName", locationInformation.MethodName, metaData);
AddLoggingEventProperty("LineNumber", locationInformation.LineNumber, metaData);
}
AddLoggingEventProperty("Domain", loggingEvent.Domain, metaData);
AddLoggingEventProperty("Identity", loggingEvent.Identity, metaData);
var properties = loggingEvent.GetProperties();
if (properties != null)
{
foreach (string key in properties.GetKeys())
{
if (!string.IsNullOrEmpty(key) && !key.StartsWith("log4net", StringComparison.OrdinalIgnoreCase))
{
object value = properties[key];
if (value != null)
{
AddLoggingEventProperty(key, value.ToString(), metaData);
}
}
}
}
}
private static SeverityLevel? GetSeverityLevel(Level logginEventLevel)
{
if (logginEventLevel == null)
{
return null;
}
if (logginEventLevel.Value < Level.Info.Value)
{
return SeverityLevel.Verbose;
}
if (logginEventLevel.Value < Level.Warn.Value)
{
return SeverityLevel.Information;
}
if (logginEventLevel.Value < Level.Error.Value)
{
return SeverityLevel.Warning;
}
if (logginEventLevel.Value < Level.Severe.Value)
{
return SeverityLevel.Error;
}
return SeverityLevel.Critical;
}
private void SendException(LoggingEvent loggingEvent)
{
try
{
var exceptionTelemetry = new ExceptionTelemetry(loggingEvent.ExceptionObject)
{
SeverityLevel = GetSeverityLevel(loggingEvent.Level),
Message = $"{loggingEvent.ExceptionObject.GetType().ToString()}: {loggingEvent.ExceptionObject.Message}",
};
if (loggingEvent.RenderedMessage != null)
{
var message = this.RenderLoggingEvent(loggingEvent);
if (!string.IsNullOrEmpty(message))
{
exceptionTelemetry.Properties.Add("Message", message);
}
}
BuildCustomProperties(loggingEvent, exceptionTelemetry);
this.telemetryClient.Track(exceptionTelemetry);
}
catch (ArgumentNullException exception)
{
throw new LogException(exception.Message, exception);
}
}
private void SendTrace(LoggingEvent loggingEvent)
{
try
{
string message = loggingEvent.RenderedMessage != null ? this.RenderLoggingEvent(loggingEvent) : "Log4Net Trace";
var trace = new TraceTelemetry(message)
{
SeverityLevel = GetSeverityLevel(loggingEvent.Level),
};
BuildCustomProperties(loggingEvent, trace);
this.telemetryClient.Track(trace);
}
catch (ArgumentNullException exception)
{
throw new LogException(exception.Message, exception);
}
}
}
}