Skip to content

Commit de3a207

Browse files
authored
Fixes ApplicationInsights exception handling (#9)
* Make logging sample clearer * Add exception correlation in Application Insights Minor adjustments to documentation, including PowerShell runner
1 parent c8554fa commit de3a207

28 files changed

+266
-81
lines changed

README.md

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,28 +97,51 @@ To quickly see the application running using pre-built docker images and docker-
9797

9898
### Using OpenTelemetry
9999

100+
1. Clone this repository
100101
1. Open terminal under `ready-to-run\sample`
101102
1. Execute `docker-compose up`
102-
1. Generate load with `watch -n 2 curl --request GET http://localhost:5001/api/enqueue/WebSiteA`
103-
1. View traces in [Jaeger](http://localhost:16686/)
104-
1. View metrics by searching for "Enqueued_Item" in [Prometheus](http://localhost:9090)
105-
1. Build dashboards in [Grafana](http://localhost:3000/) (admin/password1)
103+
1. Generate load in terminal with
104+
105+
```bash
106+
watch -n 2 curl --request GET http://localhost:5001/api/enqueue/WebSiteA
107+
```
108+
109+
for PowerShell use this script:
110+
111+
```Powershell
112+
while (1) {Invoke-WebRequest -Uri http://localhost:5001/api/enqueue/WebSiteA; sleep 2}
113+
```
114+
115+
4. View traces in [Jaeger](http://localhost:16686/)
116+
5. View metrics by searching for "Enqueued_Item" in [Prometheus](http://localhost:9090)
117+
6. Build dashboards in [Grafana](http://localhost:3000/) (admin/password1)
106118

107119
### Using Application Insights SDK
108120

121+
1. Clone this repository
109122
1. Open terminal under `ready-to-run\sample`
110-
2. Add .env file with following content:
123+
1. Create file `ready-to-run\sample\.env` with following content:
111124

112125
```env
113126
USE_APPLICATIONINSIGHTS=true
114127
USE_OPENTELEMETRY=false
115128
AI_INSTRUMENTATIONKEY=<ENTER-APPLICATION-INSIGHTS-INSTRUMENTATION-KEY>
116129
```
117130

118-
3. Execute `docker-compose up`
119-
4. Generate load with `watch -n 2 curl --request GET http://localhost:5001/api/enqueue/WebSiteA`
120-
5. View logs, traces and metrics in Azure Portal Application Insights
131+
4. Execute `docker-compose up`
132+
5. Generate load in terminal with
121133

134+
```bash
135+
watch -n 2 curl --request GET http://localhost:5001/api/enqueue/WebSiteA
136+
```
137+
138+
for PowerShell use this script:
139+
140+
```Powershell
141+
while (1) {Invoke-WebRequest -Uri http://localhost:5001/api/enqueue/WebSiteA; sleep 2}
142+
```
143+
144+
5. View logs, traces and metrics in Azure Portal Application Insights
122145

123146
## Conclusion
124147

media/003-ai-e2e-traces-and-logs.jpg

214 KB
Loading

media/003-ai-e2e-traces.jpg

120 KB
Loading

media/03-ai-custom-tracing.png

32.4 KB
Loading

media/04-ai-span-error-search.png

85.5 KB
Loading

media/04-ai-trace-and-logs.png

214 KB
Loading

media/04-ot-span-error-search.png

92.1 KB
Loading

media/04-ot-span-error.png

77.2 KB
Loading
85.8 KB
Loading

media/05-ai-e2e-with-exception.png

52.8 KB
Loading
86.5 KB
Loading

media/05-ai-metric-error.png

93.4 KB
Loading

prometheus.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,8 @@ scrape_configs:
1515
scrape_interval: 5s
1616
static_configs:
1717
- targets: ['sample.mainapi:9184']
18+
19+
- job_name: 'rabbitmq-processor'
20+
scrape_interval: 5s
21+
static_configs:
22+
- targets: ['sample.rabbitmqprocessor:9185']

ready-to-run/prometheus-grafana/prometheus.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,8 @@ scrape_configs:
1515
scrape_interval: 5s
1616
static_configs:
1717
- targets: ['host.docker.internal:9184']
18+
19+
- job_name: 'rabbitmq-processor'
20+
scrape_interval: 5s
21+
static_configs:
22+
- targets: ['host.docker.internal:9185']

ready-to-run/sample/docker-compose.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,15 @@ services:
3030
- SampleApp__UseOpenTelemetry=${USE_OPENTELEMETRY-true}
3131
- SampleApp__ApplicationInsightsInstrumentationKey=${AI_INSTRUMENTATIONKEY-}
3232
- SampleApp__ApplicationInsightsForOpenTelemetryInstrumentationKey=${AI_INSTRUMENTATIONKEY_OPENTELEMETRY-}
33+
- OpenTelemetry__Prometheus__Url=http://sample.rabbitmqprocessor:9185/metrics/
3334
- OpenTelemetry__Jaeger__AgentHost=${JAEGER_AGENTHOST-jaeger}
3435
depends_on:
3536
- rabbitmq
3637
- sample.timeapi
3738
- prometheus
3839
- jaeger
40+
ports:
41+
- "9185:9185"
3942

4043
sample.timeapi:
4144
image: fbeltrao/dotnetobservabilitysample-timeapi

scenario1.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ To generate load use the following script:
119119
watch -n 1 curl --request GET http://localhost:5002/api/time/dbtime
120120
```
121121

122+
```powershell
123+
while (1) {Invoke-WebRequest -Uri http://localhost:5002/api/time/dbtime; sleep 1}
124+
```
125+
122126
## Where to go next
123127

124128
- [Back to overview](./README.md)

scenario2.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ To generate load use the following script:
116116
watch -n 1 curl --request GET http://localhost:5001/api/dbtime
117117
```
118118

119+
```powershell
120+
while (1) {Invoke-WebRequest -Uri http://localhost:5001/api/dbtime; sleep 1}
121+
```
122+
123+
119124
## Where to go next
120125

121126
- [Back to overview](./README.md)

scenario3.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,18 +243,30 @@ Enqueuing from "WebSiteA" every 2 seconds
243243
watch -n 2 curl --request GET http://localhost:5001/api/enqueue/WebSiteA
244244
```
245245

246+
```powershell
247+
while (1) {Invoke-WebRequest -Uri http://localhost:5001/api/enqueue/WebSiteA; sleep 2}
248+
```
249+
246250
Enqueuing from "WebSiteB" every 10 seconds
247251

248252
```bash
249253
watch -n 10 curl --request GET http://localhost:5001/api/enqueue/WebSiteB
250254
```
251255

256+
```powershell
257+
while (1) {Invoke-WebRequest -Uri http://localhost:5001/api/enqueue/WebSiteB; sleep 10}
258+
```
259+
252260
Enqueuing from "WebSiteC" every 30 seconds
253261

254262
```bash
255263
watch -n 30 curl --request GET http://localhost:5001/api/enqueue/WebSiteC
256264
```
257265

266+
```powershell
267+
while (1) {Invoke-WebRequest -Uri http://localhost:5001/api/enqueue/WebSiteC; sleep 30}
268+
```
269+
258270
## Where to go next
259271

260272
- [Back to overview](./README.md)

src/Sample.Common/CloudRoleTelemetryInitializer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Diagnostics;
23
using System.Reflection;
34
using Microsoft.ApplicationInsights.Channel;
5+
using Microsoft.ApplicationInsights.DataContracts;
46
using Microsoft.ApplicationInsights.Extensibility;
57

68
namespace Sample.Common

src/Sample.Common/SampleServiceCollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ static IServiceCollection AddSampleOpenTelemetry(this IServiceCollection service
161161
// Add start/stop lifetime support
162162
services.AddHostedService<PromotheusExporterHostedService>();
163163

164-
Console.WriteLine("Using OpenTelemetry Prometheus exporter");
164+
Console.WriteLine($"Using OpenTelemetry Prometheus exporter in '{prometheusExporterOptions.Url}'");
165165
}
166166
}
167167

src/Sample.RabbitMQCollector/ActivityEnabledModel.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,16 +145,17 @@ public void BasicPublish(string exchange, string routingKey, bool mandatory, IBa
145145
activity.AddTag(Constants.RoutingKeyTagName, routingKey);
146146

147147
diagnosticSource.StartActivity(activity, null);
148+
}
148149

149-
basicProperties = basicProperties ?? model.CreateBasicProperties();
150-
if (basicProperties.Headers == null)
151-
{
152-
basicProperties.Headers = new Dictionary<string, object>();
153-
}
154-
155-
basicProperties.Headers.Add(TraceParent.HeaderKey, activity.Id);
150+
// Add into the header the current activity identifier
151+
basicProperties = basicProperties ?? model.CreateBasicProperties();
152+
if (basicProperties.Headers == null)
153+
{
154+
basicProperties.Headers = new Dictionary<string, object>();
156155
}
157156

157+
basicProperties.Headers.Add(TraceParent.HeaderKey, Activity.Current.Id);
158+
158159
try
159160
{
160161
model.BasicPublish(exchange, routingKey, mandatory, basicProperties, body);

src/Sample.RabbitMQCollector/Sample.RabbitMQCollector.csproj

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
<ItemGroup>
99
<PackageReference Include="RabbitMQ.Client" Version="5.1.2" />
1010
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.7.0" />
11-
</ItemGroup>
12-
13-
<ItemGroup>
1411
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.12.0" />
1512
<PackageReference Include="OpenTelemetry" Version="0.2.0-alpha.151" />
1613
</ItemGroup>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Runtime.Serialization;
3+
4+
namespace Sample.RabbitMQProcessor
5+
{
6+
[Serializable]
7+
public class InvalidEventNameException : Exception
8+
{
9+
public InvalidEventNameException()
10+
{
11+
}
12+
13+
public InvalidEventNameException(string message) : base(message)
14+
{
15+
}
16+
17+
public InvalidEventNameException(string message, Exception innerException) : base(message, innerException)
18+
{
19+
}
20+
21+
protected InvalidEventNameException(SerializationInfo info, StreamingContext context) : base(info, context)
22+
{
23+
}
24+
}
25+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.ApplicationInsights;
4+
using Microsoft.ApplicationInsights.Metrics;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using OpenTelemetry.Metrics;
7+
using OpenTelemetry.Metrics.Configuration;
8+
using OpenTelemetry.Trace;
9+
using Sample.Common;
10+
11+
namespace Sample.RabbitMQProcessor
12+
{
13+
public class Metrics : IAppMetrics
14+
{
15+
private readonly Metric appInsightsProcessedItemCounter;
16+
private readonly Metric appInsightsProcessedFailedItemCounter;
17+
18+
private Meter meter;
19+
private Counter<long> openTelemetryProcessedItemCounter;
20+
private Counter<long> openTelemetryProcessedFailedItemCounter;
21+
22+
public Metrics(IServiceProvider serviceProvider)
23+
{
24+
var telemetryClient = serviceProvider.GetService<TelemetryClient>();
25+
if (telemetryClient != null)
26+
{
27+
this.appInsightsProcessedItemCounter = telemetryClient.GetMetric(new MetricIdentifier("Sample App", "Processed Item", "Source"));
28+
this.appInsightsProcessedFailedItemCounter = telemetryClient.GetMetric(new MetricIdentifier("Sample App", "Processed Failed Item", "Source"));
29+
}
30+
}
31+
32+
void IAppMetrics.Initialize(MeterFactory meterFactory)
33+
{
34+
this.meter = meterFactory.GetMeter("Sample App");
35+
this.openTelemetryProcessedItemCounter = meter.CreateInt64Counter("Processed Item");
36+
this.openTelemetryProcessedFailedItemCounter = meter.CreateInt64Counter("Processed Failed Item");
37+
38+
}
39+
40+
public void TrackItemProcessed(double metricValue, string source, bool succeeded)
41+
{
42+
appInsightsProcessedItemCounter?.TrackValue(succeeded ? 1 : 0, source);
43+
appInsightsProcessedFailedItemCounter?.TrackValue(succeeded ? 0 : 1, source);
44+
45+
if (meter != null)
46+
{
47+
var context = default(SpanContext);
48+
var labelSet = new Dictionary<string, string>()
49+
{
50+
{ "Source", source },
51+
};
52+
53+
openTelemetryProcessedItemCounter.Add(context, succeeded ? 1 : 0, this.meter.GetLabelSet(labelSet));
54+
openTelemetryProcessedFailedItemCounter.Add(context, succeeded ? 0 : 1, this.meter.GetLabelSet(labelSet));
55+
56+
// Collect is called here explicitly as there is
57+
// no controller implementation yet.
58+
// TODO: There should be no need to cast to MeterSdk.
59+
//(meter as MeterSdk).Collect();
60+
}
61+
}
62+
}
63+
}

src/Sample.RabbitMQProcessor/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public static IHostBuilder CreateHostBuilder(string[] args)
2424
.ConfigureServices((hostContext, services) =>
2525
{
2626
services.AddWorkerSampleTelemetry(hostContext.Configuration);
27+
services.AddSingleton<IAppMetrics, Metrics>();
28+
services.AddSingleton(x => (Metrics)x.GetRequiredService<IAppMetrics>());
2729

2830
services.AddHttpClient();
2931
services.AddHostedService<WebQueueConsumerHostedService>();

0 commit comments

Comments
 (0)