Skip to content

Commit b4d12db

Browse files
authored
Merge b38bb3b into 98c6ca7
2 parents 98c6ca7 + b38bb3b commit b4d12db

9 files changed

+139
-59
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ The Application Insights integration for Azure Functions `v3` and `v4` suffers f
77
- Exceptions are logged twice for the HTTP binding
88
- Exceptions are logged three times for the Service Bus binding
99

10-
The next issue has no impact on the cost of Application Insights but is related to the development experience. `TelemetryConfiguration` is not registered in the Inversion Of Control container when the Application Insights connection string is not set. Emitting a custom metric requires to inject the `TelemetryConfiguration`. Running locally without having configured the Application Insights connection string will then result in an exception.
10+
The next issue has no impact on the cost of Application Insights but is related to the development experience. `TelemetryConfiguration` is not registered in the Inversion Of Control container when the Application Insights connection string is not set. Emitting a custom metric requires injecting the `TelemetryConfiguration`. Running locally without having configured the Application Insights connection string will then result in an exception.
1111

12-
The last issue is not related to Application Insights but also negatively affect developers' productivity. The custom Console logger provider used by the Azure Functions runtime does not include the stack trace when displaying an exception (for the HTTP binding at least).
12+
The last issue is not related to Application Insights but also negatively affects developers' productivity. The custom Console logger provider used by the Azure Functions runtime does not include the stack trace when displaying an exception (for the HTTP binding at least).
1313

1414
If you are not familiar with some of the more advanced features of Application Insights, I suggest you go through the below references before reading the rest of this document:
1515

@@ -41,8 +41,8 @@ dotnet add package AzureFunctions.Better.ApplicationInsights
4141

4242
For the most basic integration, you need to provide:
4343

44-
- `{ApplicationName}` used to set Application Insights' _Cloud role name_
45-
- `{TypeFromEntryAssembly}` typically would be `typeof(Startup)`. I read the [Assembly Informational Version][assembly-informational-version] of the entry assembly to set Application Insights' _Application version_ (I use _unknown_ as a fallback)
44+
- `{ApplicationName}` used to set Application Insights' _Cloud role name_ (optional). When not provided, the default behaviour is preserved (the _Cloud role name_ will be set to the Function App's name)
45+
- `{TypeFromEntryAssembly}` typically would be `typeof(Startup)`. When `{ApplicationName}` is provided, I read the [Assembly Informational Version][assembly-informational-version] of the entry assembly to set Application Insights' _Application version_ (I use _unknown_ as a fallback). When `{ApplicationName}` is not provided, _Application version_ will not be present on the telemetry items
4646

4747
In your `Startup` `class` add the below snippet:
4848

@@ -94,7 +94,7 @@ This is done by calling `AddCustomConsoleLogging`. You will then consistently ge
9494

9595
### Registering telemetry initializers
9696

97-
:memo: The built-in integration supports telemetry initializers. The custom integration supports registering telemetry initializers in the same way than the built-in integration does.
97+
:memo: The built-in integration supports telemetry initializers. The custom integration supports registering telemetry initializers in the same way as the built-in integration does.
9898

9999
Telemetry initializers can either be registered using `TImplementation`:
100100

@@ -140,7 +140,7 @@ The customisation is configured using application settings:
140140
}
141141
```
142142

143-
If you don't want to use the `ApplicationInsights` key, you can provide another value when configuraing the customisation:
143+
If you don't want to use the `ApplicationInsights` key, you can provide another value when configuring the customisation:
144144

145145
```csharp
146146
var appInsightsOptions = new CustomApplicationInsightsOptionsBuilder(

src/AzureFunctionsTelemetry/ApplicationInsights/ApplicationInsightsServiceCollectionExtensions.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public static class ApplicationInsightsServiceCollectionExtensions
1616
{
1717
/// <summary>
1818
/// Extension method taking over the Application Insights' configuration so that we can register our own Telemetry
19-
/// Initializers and Processors. Stamps each telemetry item with the application name and version. Discards as much
20-
/// redundant telemetry as is humanely possible.
19+
/// Initializers and Processors. Stamps each telemetry item with the application name and version (when provided).
20+
/// Discards as much redundant telemetry as is humanely possible.
2121
/// </summary>
2222
/// <param name="services">The <see cref="IServiceCollection"/> holding all your precious types.</param>
2323
/// <param name="config">The <see cref="CustomApplicationInsightsConfig"/> configuring the Application Insights
@@ -45,10 +45,13 @@ public static IServiceCollection AddCustomApplicationInsights(
4545
throw new ArgumentNullException(nameof(services));
4646
}
4747

48-
var applicationVersion = GetAssemblyInformationalVersion(config.TypeFromEntryAssembly);
49-
var applicationDescriptor = new ApplicationDescriptor(config.ApplicationName, applicationVersion);
50-
services.AddSingleton(applicationDescriptor);
51-
services.AddSingleton<ITelemetryInitializer, ApplicationInitializer>();
48+
if (!string.IsNullOrWhiteSpace(config.ApplicationName))
49+
{
50+
var applicationVersion = GetAssemblyInformationalVersion(config.TypeFromEntryAssembly);
51+
var applicationDescriptor = new ApplicationDescriptor(config.ApplicationName, applicationVersion);
52+
services.AddSingleton(applicationDescriptor);
53+
services.AddSingleton<ITelemetryInitializer, ApplicationInitializer>();
54+
}
5255

5356
services.AddOptions<CustomApplicationInsightsOptions>()
5457
.Configure<IConfiguration>((settings, configuration) =>

src/AzureFunctionsTelemetry/ApplicationInsights/CustomApplicationInsightsConfig.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class CustomApplicationInsightsConfig
99
/// <summary>
1010
/// Will be used as the 'Cloud role name'.
1111
/// </summary>
12-
public string ApplicationName { get; }
12+
public string? ApplicationName { get; }
1313
/// <summary>
1414
/// The 'AssemblyInformationalVersion' of the assembly will be used as the 'Application version'. Falls back to
1515
/// 'unknown'.
@@ -24,15 +24,18 @@ public class CustomApplicationInsightsConfig
2424
/// Even though you can call this constructor yourself, we provide
2525
/// <see cref="CustomApplicationInsightsConfigBuilder"/> to make it easier to configure the integration.
2626
/// </summary>
27-
/// <param name="applicationName">Will be used as the 'Cloud role name'.</param>
27+
/// <param name="applicationName">Will be used as the 'Cloud role name'. If not provided, we won't set the
28+
/// 'Cloud role name' and we will not set the 'Application version'.</param>
2829
/// <param name="typeFromEntryAssembly">The 'AssemblyInformationalVersion' of the assembly will be used as the
29-
/// 'Application version'. Falls back to 'unknown'.</param>
30+
/// 'Application version'. Falls back to 'unknown'. If applicationName is not provided, we will not set the
31+
/// 'Application version'. We also use this type to discover Service Bus triggered Functions so that we can apply
32+
/// special handling to them.</param>
3033
/// <param name="configurationSectionName">The configuration section where we'll read the
3134
/// <see cref="CustomApplicationInsightsOptions"/> from.</param>
3235
/// <exception cref="ArgumentOutOfRangeException">The configuration section name should not be empty or consist only
3336
/// of white-space characters.</exception>
3437
public CustomApplicationInsightsConfig(
35-
string applicationName,
38+
string? applicationName,
3639
Type typeFromEntryAssembly,
3740
string configurationSectionName)
3841
{

src/AzureFunctionsTelemetry/ApplicationInsights/CustomApplicationInsightsConfigBuilder.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Gabo.AzureFunctionsTelemetry.ApplicationInsights;
77
/// </summary>
88
public class CustomApplicationInsightsConfigBuilder
99
{
10-
private readonly string _applicationName;
10+
private readonly string? _applicationName;
1111
private readonly Type _typeFromEntryAssembly;
1212
private string _configurationSectionName = "ApplicationInsights";
1313

@@ -18,13 +18,26 @@ public class CustomApplicationInsightsConfigBuilder
1818
/// </summary>
1919
/// <param name="applicationName">Will be used as the 'Cloud role name'.</param>
2020
/// <param name="typeFromEntryAssembly">The 'AssemblyInformationalVersion' of the assembly will be used as the
21-
/// 'Application version'. Falls back to 'unknown'.</param>
21+
/// 'Application version'. Falls back to 'unknown'. We'll also use this type to discover Service Bus triggered
22+
/// Functions so that we can apply special handling to them.</param>
2223
public CustomApplicationInsightsConfigBuilder(string applicationName, Type typeFromEntryAssembly)
2324
{
2425
_applicationName = applicationName;
2526
_typeFromEntryAssembly = typeFromEntryAssembly;
2627
}
2728

29+
/// <summary>
30+
/// <para>Helps you configure Application Insights.</para>
31+
/// <para>By default we'll read the <see cref="CustomApplicationInsightsOptions"/> from the 'ApplicationInsights'
32+
/// configuration section.</para>
33+
/// </summary>
34+
/// <param name="typeFromEntryAssembly">We'll use this type to discover Service Bus triggered Functions so that we
35+
/// can apply special handling to them.</param>
36+
public CustomApplicationInsightsConfigBuilder(Type typeFromEntryAssembly)
37+
{
38+
_typeFromEntryAssembly = typeFromEntryAssembly;
39+
}
40+
2841
/// <summary>
2942
/// Allows you to set the configuration section where we'll read the <see cref="CustomApplicationInsightsOptions"/>
3043
/// from. The default value is 'ApplicationInsights'.

src/AzureFunctionsTelemetry/README.md

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

33
Automatically discards redundant telemetry items such as Functions execution traces and duplicate exceptions. Allows you to register your own telemetry processor(s) that will be invoked on every telemetry item type.
44

5-
A more detailed documentation is available on [GitHub][documentation].
5+
More detailed documentation is available on [GitHub][documentation].
66

77
## Usage
88

@@ -20,10 +20,10 @@ builder.Services
2020

2121
Where
2222

23-
- `{ApplicationName}` is used to set Application Insights' _Cloud role name_
24-
- `{TypeFromEntryAssembly}` typically would be `typeof(Startup)`. I read the [Assembly Informational Version][assembly-informational-version] of the entry assembly to set Application Insights' _Application version_ (I use _unknown_ as a fallback)
23+
- `{ApplicationName}` used to set Application Insights' _Cloud role name_ (optional). When not provided, the default behaviour is preserved (the _Cloud role name_ will be set to the Function App's name)
24+
- `{TypeFromEntryAssembly}` typically would be `typeof(Startup)`. When `{ApplicationName}` is provided, I read the [Assembly Informational Version][assembly-informational-version] of the entry assembly to set Application Insights' _Application version_ (I use _unknown_ as a fallback). When `{ApplicationName}` is not provided, _Application version_ will not be present on the telemetry items
2525

26-
Additionaly you can discard health requests and Service Bus trigger traces using application settings:
26+
Additionally you can discard health requests and Service Bus trigger traces using application settings:
2727

2828
```json
2929
{
@@ -36,7 +36,7 @@ Additionaly you can discard health requests and Service Bus trigger traces using
3636
}
3737
```
3838

39-
You can also add telemetry initializers and telelemetry processors:
39+
You can also add telemetry initializers and telemetry processors:
4040

4141
```csharp
4242
public override void Configure(IFunctionsHostBuilder builder)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Microsoft.ApplicationInsights.Extensibility;
2+
using Microsoft.Extensions.DependencyInjection;
3+
4+
namespace Gabo.AzureFunctionsTelemetryTests.ApplicationInsights;
5+
6+
public class ApplicationInsightsServiceCollectionExtensionsTests
7+
{
8+
[Fact]
9+
public void GivenApplicationNameNotProvided_ThenDontRegisterApplicationInitializer()
10+
{
11+
// Arrange
12+
var serviceCollection = new ServiceCollection();
13+
var config = new CustomApplicationInsightsConfig(applicationName: null, typeof(StringHelper), "Q");
14+
15+
// Act
16+
var updatedServiceCollection = serviceCollection.AddCustomApplicationInsights(config);
17+
18+
// Assert
19+
var registeredTelemetryInitializers = GetTelemetryInitializers(updatedServiceCollection);
20+
registeredTelemetryInitializers.Should().NotContain(typeof(ApplicationInitializer));
21+
}
22+
23+
[Fact]
24+
public void GivenApplicationNameProvided_ThenRegisterApplicationInitializer()
25+
{
26+
// Arrange
27+
var serviceCollection = new ServiceCollection();
28+
var config = new CustomApplicationInsightsConfig("app-name", typeof(StringHelper), "Q");
29+
30+
// Act
31+
var updatedServiceCollection = serviceCollection.AddCustomApplicationInsights(config);
32+
33+
// Assert
34+
var registeredTelemetryInitializers = GetTelemetryInitializers(updatedServiceCollection);
35+
registeredTelemetryInitializers.Should().Contain(typeof(ApplicationInitializer));
36+
}
37+
38+
private static IReadOnlyList<Type> GetTelemetryInitializers(IServiceCollection serviceCollection)
39+
{
40+
return serviceCollection
41+
.Where(s => s.ServiceType == typeof(ITelemetryInitializer) && s.ImplementationType != null)
42+
.Select(s => s.ImplementationType)
43+
.ToList()!;
44+
}
45+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
namespace Gabo.AzureFunctionsTelemetryTests.ApplicationInsights;
2+
3+
public class CustomApplicationInsightsConfigBuilderTests
4+
{
5+
private const string DefaultConfigurationSectionName = "ApplicationInsights";
6+
7+
[Fact]
8+
public void GivenApplicationNameProvided_ThenSetConfig()
9+
{
10+
// Arrange
11+
var builder = new CustomApplicationInsightsConfigBuilder("some-name", typeof(StringHelper));
12+
13+
// Act
14+
var actualConfig = builder.Build();
15+
16+
// Assert
17+
var expectedConfig =
18+
new CustomApplicationInsightsConfig("some-name", typeof(StringHelper), DefaultConfigurationSectionName);
19+
actualConfig.Should().BeEquivalentTo(expectedConfig);
20+
}
21+
22+
[Fact]
23+
public void GivenApplicationNameNotProvided_ThenDontSetApplicationName()
24+
{
25+
// Arrange
26+
var builder = new CustomApplicationInsightsConfigBuilder(typeof(StringHelper));
27+
28+
// Act
29+
var actualConfig = builder.Build();
30+
31+
// Assert
32+
var expectedConfig =
33+
new CustomApplicationInsightsConfig(applicationName: null, typeof(StringHelper),
34+
DefaultConfigurationSectionName);
35+
actualConfig.Should().BeEquivalentTo(expectedConfig);
36+
}
37+
38+
[Theory]
39+
[InlineData("")]
40+
[InlineData(" ")]
41+
public void GivenEmptyOrWhiteSpaceSectionConfigurationName_ThenThrows(string configurationSectionName)
42+
{
43+
// Arrange
44+
var builder = new CustomApplicationInsightsConfigBuilder("some-name", typeof(StringHelper));
45+
46+
// Act & Assert
47+
Assert.ThrowsAny<Exception>(() => builder.WithConfigurationSectionName(configurationSectionName));
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
namespace Gabo.AzureFunctionsTelemetryTests.ApplicationInsights;
22

3-
public class CustomApplicationInsightsOptionsTests
3+
public class CustomApplicationInsightsConfigTests
44
{
55
[Theory]
66
[InlineData("")]
77
[InlineData(" ")]
8-
public void GivenEmptyOrWhiteSpaceConfigurationSectionName_ThenThrows(string functionName)
8+
public void GivenEmptyOrWhiteSpaceConfigurationSectionName_ThenThrows(string configurationSectionName)
99
{
1010
// Act & Assert
1111
Assert.ThrowsAny<Exception>(() =>
1212
new CustomApplicationInsightsConfig(
1313
"some-name",
1414
typeof(StringHelper),
15-
configurationSectionName: functionName));
15+
configurationSectionName));
1616
}
1717
}

tests/AzureFunctionsTelemetryTests/ApplicationInsights/CustomApplicationInsightsOptionsBuilderTests.cs

Lines changed: 0 additions & 33 deletions
This file was deleted.

0 commit comments

Comments
 (0)