Skip to content

Commit e38ed32

Browse files
[Shim] Exception Telemetry with tests + Updated samples (#3016)
* ExceptionTelemetry DataContract * Updated app, exception properties
1 parent 2c615f6 commit e38ed32

File tree

8 files changed

+1307
-234
lines changed

8 files changed

+1307
-234
lines changed

BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/DataContracts/ExceptionTelemetryTest.cs

Lines changed: 461 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
namespace Microsoft.ApplicationInsights
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.ComponentModel;
6+
using System.Diagnostics;
7+
using System.Diagnostics.CodeAnalysis;
8+
using System.Diagnostics.Tracing;
9+
using System.Linq;
10+
using System.Net;
11+
using System.Net.Http;
12+
using System.Reflection;
13+
using System.Text;
14+
using Microsoft.ApplicationInsights.Channel;
15+
using Microsoft.ApplicationInsights.DataContracts;
16+
using Microsoft.ApplicationInsights.Extensibility;
17+
using Microsoft.ApplicationInsights.Extensibility.Implementation;
18+
using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing;
19+
using Microsoft.Extensions.Logging;
20+
using Microsoft.VisualStudio.TestTools.UnitTesting;
21+
using OpenTelemetry;
22+
using OpenTelemetry.Logs;
23+
24+
[TestClass]
25+
public class TelemetryClientTest
26+
{
27+
private List<LogRecord> logItems;
28+
private TelemetryClient telemetryClient;
29+
30+
[TestInitialize]
31+
public void TestInitialize()
32+
{
33+
var configuration = new TelemetryConfiguration();
34+
this.logItems = new List<LogRecord>();
35+
configuration.InstrumentationKey = Guid.NewGuid().ToString();
36+
configuration.ConnectionString = "InstrumentationKey=" + configuration.InstrumentationKey;
37+
configuration.ConfigureOpenTelemetryBuilder(b => b.WithLogging(l => l.AddInMemoryExporter(logItems)));
38+
this.telemetryClient = new TelemetryClient(configuration);
39+
}
40+
41+
#region TrackException
42+
43+
[TestMethod]
44+
public void TrackExceptionSendsExceptionTelemetryWithSpecifiedNameToProvideSimplestWayOfSendingExceptionTelemetry()
45+
{
46+
Exception ex = new Exception("Test exception message");
47+
this.telemetryClient.TrackException(ex);
48+
49+
this.telemetryClient.Flush();
50+
51+
Assert.AreEqual(1, this.logItems.Count);
52+
var logRecord = this.logItems[0];
53+
Assert.IsNotNull(logRecord.Exception);
54+
Assert.AreSame(ex, logRecord.Exception);
55+
Assert.AreEqual(LogLevel.Error, logRecord.LogLevel);
56+
}
57+
58+
[TestMethod]
59+
public void TrackExceptionWillUseRequiredFieldAsTextForTheExceptionNameWhenTheExceptionNameIsEmptyToHideUserErrors()
60+
{
61+
this.telemetryClient.TrackException((Exception)null);
62+
63+
this.telemetryClient.Flush();
64+
65+
Assert.AreEqual(1, this.logItems.Count);
66+
var logRecord = this.logItems[0];
67+
Assert.IsNotNull(logRecord.Exception);
68+
Assert.AreEqual("n/a", logRecord.Exception.Message);
69+
}
70+
71+
[TestMethod]
72+
public void TrackExceptionSendsExceptionTelemetryWithSpecifiedObjectTelemetry()
73+
{
74+
Exception ex = new Exception("Test telemetry exception");
75+
this.telemetryClient.TrackException(new ExceptionTelemetry(ex));
76+
77+
this.telemetryClient.Flush();
78+
79+
Assert.AreEqual(1, this.logItems.Count);
80+
var logRecord = this.logItems[0];
81+
Assert.IsNotNull(logRecord.Exception);
82+
Assert.AreEqual("Test telemetry exception", logRecord.Exception.Message);
83+
}
84+
85+
[TestMethod]
86+
public void TrackExceptionWillUseABlankObjectAsTheExceptionToHideUserErrors()
87+
{
88+
this.telemetryClient.TrackException((ExceptionTelemetry)null);
89+
90+
this.telemetryClient.Flush();
91+
92+
Assert.AreEqual(1, this.logItems.Count);
93+
var logRecord = this.logItems[0];
94+
Assert.IsNotNull(logRecord.Exception);
95+
}
96+
97+
[TestMethod]
98+
public void TrackExceptionUsesErrorLogLevelByDefault()
99+
{
100+
this.telemetryClient.TrackException(new Exception());
101+
102+
this.telemetryClient.Flush();
103+
104+
Assert.AreEqual(1, this.logItems.Count);
105+
Assert.AreEqual(LogLevel.Error, this.logItems[0].LogLevel);
106+
}
107+
108+
[TestMethod]
109+
public void TrackExceptionWithExceptionTelemetryRespectsSeverityLevel()
110+
{
111+
var telemetry = new ExceptionTelemetry(new Exception("Critical error"))
112+
{
113+
SeverityLevel = SeverityLevel.Critical
114+
};
115+
this.telemetryClient.TrackException(telemetry);
116+
117+
this.telemetryClient.Flush();
118+
119+
Assert.AreEqual(1, this.logItems.Count);
120+
Assert.AreEqual(LogLevel.Critical, this.logItems[0].LogLevel);
121+
}
122+
123+
[TestMethod]
124+
public void TrackExceptionWithPropertiesIncludesPropertiesInLogRecord()
125+
{
126+
var properties = new Dictionary<string, string>
127+
{
128+
{ "key1", "value1" },
129+
{ "key2", "value2" }
130+
};
131+
132+
this.telemetryClient.TrackException(new Exception("Test"), properties);
133+
134+
this.telemetryClient.Flush();
135+
136+
Assert.AreEqual(1, this.logItems.Count);
137+
var logRecord = this.logItems[0];
138+
139+
// Properties should be in the log record attributes
140+
var hasKey1 = false;
141+
var hasKey2 = false;
142+
if (logRecord.Attributes != null)
143+
{
144+
foreach (var attr in logRecord.Attributes)
145+
{
146+
if (attr.Key == "key1" && attr.Value?.ToString() == "value1")
147+
hasKey1 = true;
148+
if (attr.Key == "key2" && attr.Value?.ToString() == "value2")
149+
hasKey2 = true;
150+
}
151+
}
152+
153+
Assert.IsTrue(hasKey1, "Property key1 should be in log record");
154+
Assert.IsTrue(hasKey2, "Property key2 should be in log record");
155+
}
156+
157+
[TestMethod]
158+
public void TrackExceptionWithExceptionTelemetryIncludesProperties()
159+
{
160+
var telemetry = new ExceptionTelemetry(new Exception("Test exception"));
161+
telemetry.Properties["customKey"] = "customValue";
162+
163+
this.telemetryClient.TrackException(telemetry);
164+
165+
this.telemetryClient.Flush();
166+
167+
Assert.AreEqual(1, this.logItems.Count);
168+
var logRecord = this.logItems[0];
169+
170+
var hasCustomKey = false;
171+
if (logRecord.Attributes != null)
172+
{
173+
foreach (var attr in logRecord.Attributes)
174+
{
175+
if (attr.Key == "customKey" && attr.Value?.ToString() == "customValue")
176+
{
177+
hasCustomKey = true;
178+
break;
179+
}
180+
}
181+
}
182+
183+
Assert.IsTrue(hasCustomKey, "Custom property should be in log record");
184+
}
185+
186+
[TestMethod]
187+
public void TrackExceptionWithInnerExceptionPreservesInnerException()
188+
{
189+
var innerException = new InvalidOperationException("Inner exception message");
190+
var outerException = new ApplicationException("Outer exception message", innerException);
191+
192+
this.telemetryClient.TrackException(outerException);
193+
194+
this.telemetryClient.Flush();
195+
196+
Assert.AreEqual(1, this.logItems.Count);
197+
var logRecord = this.logItems[0];
198+
Assert.IsNotNull(logRecord.Exception);
199+
Assert.AreEqual("Outer exception message", logRecord.Exception.Message);
200+
201+
// The exception should have inner exception
202+
Assert.IsNotNull(logRecord.Exception.InnerException);
203+
Assert.AreEqual("Inner exception message", logRecord.Exception.InnerException.Message);
204+
}
205+
206+
#endregion
207+
208+
private double ComputeSomethingHeavy()
209+
{
210+
var random = new Random();
211+
double res = 0;
212+
for (int i = 0; i < 10000; i++)
213+
{
214+
res += Math.Sqrt(random.NextDouble());
215+
}
216+
217+
return res;
218+
}
219+
}
220+
}
Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
namespace Microsoft.ApplicationInsights.DataContracts
22
{
33
using System.Collections.Generic;
4-
using System.Linq;
54

65
/// <summary>
76
/// Wrapper class for ExceptionDetails"/> that lets user gets/sets TypeName and Message.
87
/// </summary>
98
public sealed class ExceptionDetailsInfo
109
{
11-
// TODO : fix the constructor to set properties
12-
1310
/// <summary>
1411
/// Constructs the instance of <see cref="ExceptionDetailsInfo"/>.
1512
/// </summary>
@@ -20,29 +17,51 @@ public sealed class ExceptionDetailsInfo
2017
/// <param name="hasFullStack">Indicates that this exception has full stack information.</param>
2118
/// <param name="stack">Exception's stack trace.</param>
2219
/// <param name="parsedStack">Exception's stack.</param>
23-
#pragma warning disable CA1801 // Review unused parameters
2420
public ExceptionDetailsInfo(int id, int outerId, string typeName, string message, bool hasFullStack,
2521
string stack, IEnumerable<StackFrame> parsedStack)
26-
#pragma warning restore CA1801 // Review unused parameters
2722
{
23+
this.Id = id;
24+
this.OuterId = outerId;
25+
this.TypeName = typeName;
26+
this.Message = message;
27+
this.Stack = stack;
28+
this.ParsedStack = parsedStack != null ? new List<StackFrame>(parsedStack) : null;
29+
this.HasFullStack = hasFullStack;
2830
}
2931

3032
/// <summary>
3133
/// Gets or sets type name of the underlying <see cref="System.Exception"/> that this object represents.
3234
/// </summary>
33-
public string TypeName
34-
{
35-
get;
36-
set;
37-
}
35+
public string TypeName { get; set; }
3836

3937
/// <summary>
4038
/// Gets or sets message name of the underlying <see cref="System.Exception"/> that this object represents.
4139
/// </summary>
42-
public string Message
43-
{
44-
get;
45-
set;
46-
}
40+
public string Message { get; set; }
41+
42+
/// <summary>
43+
/// Gets or sets the exception ID.
44+
/// </summary>
45+
internal int Id { get; set; }
46+
47+
/// <summary>
48+
/// Gets or sets the outer exception ID.
49+
/// </summary>
50+
internal int OuterId { get; set; }
51+
52+
/// <summary>
53+
/// Gets or sets the stack trace as a string.
54+
/// </summary>
55+
internal string Stack { get; set; }
56+
57+
/// <summary>
58+
/// Gets or sets the parsed stack frames for the exception.
59+
/// </summary>
60+
internal IList<StackFrame> ParsedStack { get; set; }
61+
62+
/// <summary>
63+
/// Gets or sets a value indicating whether this exception has full stack information.
64+
/// </summary>
65+
internal bool HasFullStack { get; set; }
4766
}
4867
}

0 commit comments

Comments
 (0)