Skip to content

Commit fee5190

Browse files
authoredOct 6, 2020
Add telemetry support (#8)
* Add telemetry client support, this will allow a function to obtain a TelemetryClient instance through injection and issue Track calls manually. * Some refactoring to improve code readability
1 parent 49ed6d8 commit fee5190

16 files changed

+421
-110
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Configuration;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.DependencyInjection.Extensions;
5+
using System;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Reflection;
9+
10+
namespace Autofac.Extensions.DependencyInjection.AzureFunctions
11+
{
12+
/// <summary>
13+
/// Extension methods to initialize support for AppSettings file for use with the <see cref="IFunctionsHostBuilder"/>.
14+
/// </summary>
15+
public static class AppSettingsExtensions
16+
{
17+
/// <summary>
18+
/// Creates an <see cref="IConfiguration"/> instance and attaches to an `appsettings.json` file, also adding an `appsettings.{environment}.json` on top of it, if available, based on current ASPNETCORE_ENVIRONMENT environment variable.
19+
/// </summary>
20+
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
21+
/// <returns>The IFunctionsHostBuilder.</returns>
22+
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder)
23+
{
24+
var fileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);
25+
string currentDirectory = fileInfo.Directory.Parent.FullName;
26+
27+
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
28+
if (string.IsNullOrWhiteSpace(environment))
29+
environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
30+
if (string.IsNullOrWhiteSpace(environment))
31+
environment = "Development"; // Fallback to Development when none is set.
32+
33+
return UseAppSettings(hostBuilder, (builder) =>
34+
{
35+
builder
36+
.SetBasePath(currentDirectory)
37+
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
38+
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
39+
.AddJsonFile("host.json", optional: true, reloadOnChange: true)
40+
.AddEnvironmentVariables();
41+
});
42+
}
43+
44+
/// <summary>
45+
/// Creates an <see cref="IConfiguration"/> instance and configures it as declared by <paramref name="configurationAction"/> action.
46+
/// </summary>
47+
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
48+
/// <param name="configurationAction">Action on a <see cref="IConfigurationBuilder"/> that adds configurations to a .NET Core application.</param>
49+
/// <returns>The IFunctionsHostBuilder.</returns>
50+
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder, Action<IConfigurationBuilder> configurationAction = null)
51+
{
52+
var configurationBuilder = new ConfigurationBuilder() as IConfigurationBuilder;
53+
54+
var descriptor = hostBuilder.Services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration));
55+
if (descriptor?.ImplementationInstance is IConfiguration configRoot)
56+
{
57+
configurationBuilder.AddConfiguration(configRoot);
58+
}
59+
60+
configurationAction?.Invoke(configurationBuilder);
61+
var configuration = configurationBuilder.Build();
62+
63+
hostBuilder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));
64+
65+
return hostBuilder;
66+
}
67+
}
68+
}

‎Autofac.Extensions.DependencyInjection.AzureFunctions/Autofac.Extensions.DependencyInjection.AzureFunctions.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
<ItemGroup>
3333
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />
34+
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.15.0" />
3435
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.0.0" />
3536
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.8" />
3637
</ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
1-
using Autofac.Builder;
2-
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
1+
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
32
using Microsoft.Azure.WebJobs.Host;
4-
using Microsoft.Extensions.Configuration;
53
using Microsoft.Extensions.DependencyInjection;
64
using Microsoft.Extensions.DependencyInjection.Extensions;
7-
using Microsoft.Extensions.Logging;
85
using System;
9-
using System.IO;
10-
using System.Linq;
11-
using System.Reflection;
126

137
namespace Autofac.Extensions.DependencyInjection.AzureFunctions
148
{
159
/// <summary>
16-
/// Extension methods for use with the <see cref="IFunctionsHostBuilder"/>
10+
/// Extension methods to initialize <see cref="Autofac" /> for use with the <see cref="IFunctionsHostBuilder"/>
1711
/// </summary>
1812
public static class ConfigurationExtensions
1913
{
@@ -50,97 +44,5 @@ public static IFunctionsHostBuilder UseAutofacServiceProviderFactory(this IFunct
5044

5145
return hostBuilder;
5246
}
53-
54-
/// <summary>
55-
/// Creates an <see cref="IConfiguration"/> instance and attaches to an `appsettings.json` file, also adding an `appsettings.{environment}.json` on top of it, if available, based on current ASPNETCORE_ENVIRONMENT environment variable.
56-
/// </summary>
57-
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
58-
/// <returns>The IFunctionsHostBuilder.</returns>
59-
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder)
60-
{
61-
var fileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);
62-
string currentDirectory = fileInfo.Directory.Parent.FullName;
63-
64-
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
65-
if (string.IsNullOrWhiteSpace(environment))
66-
environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
67-
if (string.IsNullOrWhiteSpace(environment))
68-
environment = "Development"; // Fallback to Development when none is set.
69-
70-
return UseAppSettings(hostBuilder, (builder) =>
71-
{
72-
builder
73-
.SetBasePath(currentDirectory)
74-
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
75-
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
76-
.AddJsonFile("host.json", optional: true, reloadOnChange: true)
77-
.AddEnvironmentVariables();
78-
});
79-
}
80-
81-
/// <summary>
82-
/// Creates an <see cref="IConfiguration"/> instance and configures it as declared by <paramref name="configurationAction"/> action.
83-
/// </summary>
84-
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
85-
/// <param name="configurationAction">Action on a <see cref="IConfigurationBuilder"/> that adds configurations to a .NET Core application.</param>
86-
/// <returns>The IFunctionsHostBuilder.</returns>
87-
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder, Action<IConfigurationBuilder> configurationAction = null)
88-
{
89-
var configurationBuilder = new ConfigurationBuilder() as IConfigurationBuilder;
90-
91-
var descriptor = hostBuilder.Services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration));
92-
if (descriptor?.ImplementationInstance is IConfiguration configRoot)
93-
{
94-
configurationBuilder.AddConfiguration(configRoot);
95-
}
96-
97-
configurationAction?.Invoke(configurationBuilder);
98-
var configuration = configurationBuilder.Build();
99-
100-
hostBuilder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));
101-
102-
return hostBuilder;
103-
}
104-
105-
106-
/// <summary>
107-
/// Adds logging services to the specified HostBuilder
108-
/// </summary>
109-
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
110-
/// <param name="configure">The <see cref="ILoggingBuilder"/> configuration delegate.</param>
111-
/// <returns>The IFunctionsHostBuilder.</returns>
112-
public static IFunctionsHostBuilder UseLogger(this IFunctionsHostBuilder hostBuilder, Action<ILoggingBuilder, IConfiguration> configure)
113-
{
114-
var configuration = hostBuilder.Services.Where(x => x.ServiceType == typeof(IConfiguration)).SingleOrDefault()?.ImplementationInstance as IConfiguration;
115-
116-
hostBuilder.Services.AddLogging((config) => configure?.Invoke(config, configuration));
117-
118-
return hostBuilder;
119-
}
120-
121-
122-
123-
/// <summary>
124-
/// Share one instance of the component within the context of a single
125-
/// azure function trigger request.
126-
/// </summary>
127-
/// <typeparam name="TLimit">Registration limit type.</typeparam>
128-
/// <typeparam name="TActivatorData">Activator data type.</typeparam>
129-
/// <typeparam name="TStyle">Registration style.</typeparam>
130-
/// <param name="registration">The registration to configure.</param>
131-
/// <param name="lifetimeScopeTags">Additional tags applied for matching lifetime scopes.</param>
132-
/// <returns>A registration builder allowing further configuration of the component.</returns>
133-
/// <exception cref="System.ArgumentNullException">
134-
/// Thrown if <paramref name="registration" /> is <see langword="null" />.
135-
/// </exception>
136-
public static IRegistrationBuilder<TLimit, TActivatorData, TStyle> InstancePerTriggerRequest<TLimit, TActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TStyle> registration, params object[] lifetimeScopeTags)
137-
{
138-
if (registration == null)
139-
throw new ArgumentNullException(nameof(registration));
140-
141-
var tags = new[] { Scopes.RootLifetimeScopeTag }.Concat(lifetimeScopeTags).ToArray();
142-
return registration.InstancePerMatchingLifetimeScope(tags);
143-
}
144-
14547
}
14648
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Configuration;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Logging;
5+
using System;
6+
using System.Linq;
7+
8+
namespace Autofac.Extensions.DependencyInjection.AzureFunctions
9+
{
10+
/// <summary>
11+
/// Extension methods to initialize support <see cref="ILogger" /> for use with the <see cref="IFunctionsHostBuilder"/>.
12+
/// </summary>
13+
public static class LoggerExtensions
14+
{
15+
/// <summary>
16+
/// Adds logging services to the specified HostBuilder
17+
/// </summary>
18+
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
19+
/// <param name="configure">The <see cref="ILoggingBuilder"/> configuration delegate.</param>
20+
/// <returns>The IFunctionsHostBuilder.</returns>
21+
public static IFunctionsHostBuilder UseLogger(this IFunctionsHostBuilder hostBuilder, Action<ILoggingBuilder, IConfiguration> configure)
22+
{
23+
var configuration = hostBuilder.Services.Where(x => x.ServiceType == typeof(IConfiguration)).SingleOrDefault()?.ImplementationInstance as IConfiguration;
24+
25+
hostBuilder.Services.AddLogging((config) => configure?.Invoke(config, configuration));
26+
27+
return hostBuilder;
28+
}
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
1-
using Microsoft.Extensions.Logging;
1+
using Microsoft.ApplicationInsights;
2+
using Microsoft.ApplicationInsights.Extensibility;
3+
using Microsoft.Extensions.Logging;
24

35
namespace Autofac.Extensions.DependencyInjection.AzureFunctions
46
{
57
internal class LoggerModule : Module
68
{
79
public const string functionNameParam = "functionName";
810
public const string loggerFactoryParam = "loggerFactory";
11+
public const string telemetryParam = "telemetry";
912

1013
protected override void Load(ContainerBuilder builder)
1114
{
15+
builder
16+
.Register((ctx, p) =>
17+
{
18+
var factory = p.Named<ILoggerFactory>(loggerFactoryParam);
19+
20+
return factory;
21+
})
22+
.AsSelf()
23+
.SingleInstance();
24+
1225
builder
1326
.Register((ctx, p) =>
1427
{
@@ -20,16 +33,26 @@ protected override void Load(ContainerBuilder builder)
2033
.AsSelf()
2134
.InstancePerTriggerRequest();
2235

36+
2337
builder
2438
.Register((ctx, p) =>
2539
{
26-
var factory = p.Named<ILoggerFactory>(loggerFactoryParam) ?? ctx.Resolve<ILoggerFactory>();
40+
var client = p.Named<TelemetryClient>(telemetryParam);
2741

28-
return factory;
42+
return client;
2943
})
3044
.AsSelf()
31-
.SingleInstance();
45+
.InstancePerTriggerRequest();
46+
47+
builder
48+
.Register((ctx, p) =>
49+
{
50+
var config = p.Named<TelemetryConfiguration>(telemetryParam);
3251

52+
return config;
53+
})
54+
.AsSelf()
55+
.InstancePerTriggerRequest();
3356
}
3457
}
3558
}

‎Autofac.Extensions.DependencyInjection.AzureFunctions/ScopedJobActivator.cs

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Microsoft.Azure.WebJobs.Host;
1+
using Microsoft.ApplicationInsights;
2+
using Microsoft.ApplicationInsights.Extensibility;
3+
using Microsoft.Azure.WebJobs.Host;
24
using Microsoft.Azure.WebJobs.Host.Executors;
35
using Microsoft.Extensions.DependencyInjection;
46
using Microsoft.Extensions.Logging;
@@ -43,6 +45,22 @@ public T CreateInstance<T>(IFunctionInstanceEx functionInstance)
4345
new NamedParameter(LoggerModule.functionNameParam, functionName)
4446
);
4547

48+
var telemetryClient = functionInstance.InstanceServices.GetService<TelemetryClient>();
49+
if (telemetryClient != null)
50+
{
51+
scope.Resolve<TelemetryClient>(
52+
new NamedParameter(LoggerModule.telemetryParam, telemetryClient)
53+
);
54+
}
55+
var telemetryConfiguration = functionInstance.InstanceServices.GetService<TelemetryConfiguration>();
56+
if (telemetryConfiguration != null)
57+
{
58+
scope.Resolve<TelemetryConfiguration>(
59+
new NamedParameter(LoggerModule.telemetryParam, telemetryConfiguration)
60+
);
61+
}
62+
63+
4664
return CreateInstance<T>(scope);
4765
}
4866

Original file line numberDiff line numberDiff line change
@@ -1,13 +1,40 @@
1-
namespace Autofac.Extensions.DependencyInjection.AzureFunctions
1+
using Autofac.Builder;
2+
using System;
3+
using System.Linq;
4+
5+
namespace Autofac.Extensions.DependencyInjection.AzureFunctions
26
{
37
/// <summary>
4-
/// A set of commonly used constants.
8+
/// A set of commonly used constants and extensions.
59
/// </summary>
610
public static class Scopes
711
{
812
/// <summary>
913
/// Represents the scope of a function trigger request.
1014
/// </summary>
1115
public const string RootLifetimeScopeTag = "AutofacFunctionsScope";
16+
17+
/// <summary>
18+
/// Share one instance of the component within the context of a single
19+
/// azure function trigger request.
20+
/// </summary>
21+
/// <typeparam name="TLimit">Registration limit type.</typeparam>
22+
/// <typeparam name="TActivatorData">Activator data type.</typeparam>
23+
/// <typeparam name="TStyle">Registration style.</typeparam>
24+
/// <param name="registration">The registration to configure.</param>
25+
/// <param name="lifetimeScopeTags">Additional tags applied for matching lifetime scopes.</param>
26+
/// <returns>A registration builder allowing further configuration of the component.</returns>
27+
/// <exception cref="System.ArgumentNullException">
28+
/// Thrown if <paramref name="registration" /> is <see langword="null" />.
29+
/// </exception>
30+
public static IRegistrationBuilder<TLimit, TActivatorData, TStyle> InstancePerTriggerRequest<TLimit, TActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TStyle> registration, params object[] lifetimeScopeTags)
31+
{
32+
if (registration == null)
33+
throw new ArgumentNullException(nameof(registration));
34+
35+
var tags = new[] { Scopes.RootLifetimeScopeTag }.Concat(lifetimeScopeTags).ToArray();
36+
return registration.InstancePerMatchingLifetimeScope(tags);
37+
}
38+
1239
}
1340
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<configuration>
3+
</configuration>

‎SampleAutofacFunction/Functions/Function1.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public void Dispose()
2525
}
2626

2727
[FunctionName(nameof(Function1))]
28-
public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")]string myQueueItem)
28+
public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")] string myQueueItem)
2929
{
3030
await Task.Delay(2000);
3131
_logger.LogInformation($"C# Queue trigger function processed: {myQueueItem}");

0 commit comments

Comments
 (0)