Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add telemetry support #8

Merged
merged 2 commits into from
Oct 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Autofac.Extensions.DependencyInjection.AzureFunctions
{
/// <summary>
/// Extension methods to initialize support for AppSettings file for use with the <see cref="IFunctionsHostBuilder"/>.
/// </summary>
public static class AppSettingsExtensions
{
/// <summary>
/// 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.
/// </summary>
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
/// <returns>The IFunctionsHostBuilder.</returns>
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder)
{
var fileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);
string currentDirectory = fileInfo.Directory.Parent.FullName;

var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (string.IsNullOrWhiteSpace(environment))
environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
if (string.IsNullOrWhiteSpace(environment))
environment = "Development"; // Fallback to Development when none is set.

return UseAppSettings(hostBuilder, (builder) =>
{
builder
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
.AddJsonFile("host.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
});
}

/// <summary>
/// Creates an <see cref="IConfiguration"/> instance and configures it as declared by <paramref name="configurationAction"/> action.
/// </summary>
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
/// <param name="configurationAction">Action on a <see cref="IConfigurationBuilder"/> that adds configurations to a .NET Core application.</param>
/// <returns>The IFunctionsHostBuilder.</returns>
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder, Action<IConfigurationBuilder> configurationAction = null)
{
var configurationBuilder = new ConfigurationBuilder() as IConfigurationBuilder;

var descriptor = hostBuilder.Services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration));
if (descriptor?.ImplementationInstance is IConfiguration configRoot)
{
configurationBuilder.AddConfiguration(configRoot);
}

configurationAction?.Invoke(configurationBuilder);
var configuration = configurationBuilder.Build();

hostBuilder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));

return hostBuilder;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

<ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.15.0" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.8" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
using Autofac.Builder;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Autofac.Extensions.DependencyInjection.AzureFunctions
{
/// <summary>
/// Extension methods for use with the <see cref="IFunctionsHostBuilder"/>
/// Extension methods to initialize <see cref="Autofac" /> for use with the <see cref="IFunctionsHostBuilder"/>
/// </summary>
public static class ConfigurationExtensions
{
Expand Down Expand Up @@ -50,97 +44,5 @@ public static IFunctionsHostBuilder UseAutofacServiceProviderFactory(this IFunct

return hostBuilder;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
/// <returns>The IFunctionsHostBuilder.</returns>
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder)
{
var fileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);
string currentDirectory = fileInfo.Directory.Parent.FullName;

var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (string.IsNullOrWhiteSpace(environment))
environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
if (string.IsNullOrWhiteSpace(environment))
environment = "Development"; // Fallback to Development when none is set.

return UseAppSettings(hostBuilder, (builder) =>
{
builder
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
.AddJsonFile("host.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
});
}

/// <summary>
/// Creates an <see cref="IConfiguration"/> instance and configures it as declared by <paramref name="configurationAction"/> action.
/// </summary>
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
/// <param name="configurationAction">Action on a <see cref="IConfigurationBuilder"/> that adds configurations to a .NET Core application.</param>
/// <returns>The IFunctionsHostBuilder.</returns>
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder, Action<IConfigurationBuilder> configurationAction = null)
{
var configurationBuilder = new ConfigurationBuilder() as IConfigurationBuilder;

var descriptor = hostBuilder.Services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration));
if (descriptor?.ImplementationInstance is IConfiguration configRoot)
{
configurationBuilder.AddConfiguration(configRoot);
}

configurationAction?.Invoke(configurationBuilder);
var configuration = configurationBuilder.Build();

hostBuilder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));

return hostBuilder;
}


/// <summary>
/// Adds logging services to the specified HostBuilder
/// </summary>
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
/// <param name="configure">The <see cref="ILoggingBuilder"/> configuration delegate.</param>
/// <returns>The IFunctionsHostBuilder.</returns>
public static IFunctionsHostBuilder UseLogger(this IFunctionsHostBuilder hostBuilder, Action<ILoggingBuilder, IConfiguration> configure)
{
var configuration = hostBuilder.Services.Where(x => x.ServiceType == typeof(IConfiguration)).SingleOrDefault()?.ImplementationInstance as IConfiguration;

hostBuilder.Services.AddLogging((config) => configure?.Invoke(config, configuration));

return hostBuilder;
}



/// <summary>
/// Share one instance of the component within the context of a single
/// azure function trigger request.
/// </summary>
/// <typeparam name="TLimit">Registration limit type.</typeparam>
/// <typeparam name="TActivatorData">Activator data type.</typeparam>
/// <typeparam name="TStyle">Registration style.</typeparam>
/// <param name="registration">The registration to configure.</param>
/// <param name="lifetimeScopeTags">Additional tags applied for matching lifetime scopes.</param>
/// <returns>A registration builder allowing further configuration of the component.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="registration" /> is <see langword="null" />.
/// </exception>
public static IRegistrationBuilder<TLimit, TActivatorData, TStyle> InstancePerTriggerRequest<TLimit, TActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TStyle> registration, params object[] lifetimeScopeTags)
{
if (registration == null)
throw new ArgumentNullException(nameof(registration));

var tags = new[] { Scopes.RootLifetimeScopeTag }.Concat(lifetimeScopeTags).ToArray();
return registration.InstancePerMatchingLifetimeScope(tags);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;

namespace Autofac.Extensions.DependencyInjection.AzureFunctions
{
/// <summary>
/// Extension methods to initialize support <see cref="ILogger" /> for use with the <see cref="IFunctionsHostBuilder"/>.
/// </summary>
public static class LoggerExtensions
{
/// <summary>
/// Adds logging services to the specified HostBuilder
/// </summary>
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
/// <param name="configure">The <see cref="ILoggingBuilder"/> configuration delegate.</param>
/// <returns>The IFunctionsHostBuilder.</returns>
public static IFunctionsHostBuilder UseLogger(this IFunctionsHostBuilder hostBuilder, Action<ILoggingBuilder, IConfiguration> configure)
{
var configuration = hostBuilder.Services.Where(x => x.ServiceType == typeof(IConfiguration)).SingleOrDefault()?.ImplementationInstance as IConfiguration;

hostBuilder.Services.AddLogging((config) => configure?.Invoke(config, configuration));

return hostBuilder;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
using Microsoft.Extensions.Logging;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.Logging;

namespace Autofac.Extensions.DependencyInjection.AzureFunctions
{
internal class LoggerModule : Module
{
public const string functionNameParam = "functionName";
public const string loggerFactoryParam = "loggerFactory";
public const string telemetryParam = "telemetry";

protected override void Load(ContainerBuilder builder)
{
builder
.Register((ctx, p) =>
{
var factory = p.Named<ILoggerFactory>(loggerFactoryParam);

return factory;
})
.AsSelf()
.SingleInstance();

builder
.Register((ctx, p) =>
{
Expand All @@ -20,16 +33,26 @@ protected override void Load(ContainerBuilder builder)
.AsSelf()
.InstancePerTriggerRequest();


builder
.Register((ctx, p) =>
{
var factory = p.Named<ILoggerFactory>(loggerFactoryParam) ?? ctx.Resolve<ILoggerFactory>();
var client = p.Named<TelemetryClient>(telemetryParam);

return factory;
return client;
})
.AsSelf()
.SingleInstance();
.InstancePerTriggerRequest();

builder
.Register((ctx, p) =>
{
var config = p.Named<TelemetryConfiguration>(telemetryParam);

return config;
})
.AsSelf()
.InstancePerTriggerRequest();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Microsoft.Azure.WebJobs.Host;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -43,6 +45,22 @@ public T CreateInstance<T>(IFunctionInstanceEx functionInstance)
new NamedParameter(LoggerModule.functionNameParam, functionName)
);

var telemetryClient = functionInstance.InstanceServices.GetService<TelemetryClient>();
if (telemetryClient != null)
{
scope.Resolve<TelemetryClient>(
new NamedParameter(LoggerModule.telemetryParam, telemetryClient)
);
}
var telemetryConfiguration = functionInstance.InstanceServices.GetService<TelemetryConfiguration>();
if (telemetryConfiguration != null)
{
scope.Resolve<TelemetryConfiguration>(
new NamedParameter(LoggerModule.telemetryParam, telemetryConfiguration)
);
}


return CreateInstance<T>(scope);
}

Expand Down
31 changes: 29 additions & 2 deletions Autofac.Extensions.DependencyInjection.AzureFunctions/Scopes.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,40 @@
namespace Autofac.Extensions.DependencyInjection.AzureFunctions
using Autofac.Builder;
using System;
using System.Linq;

namespace Autofac.Extensions.DependencyInjection.AzureFunctions
{
/// <summary>
/// A set of commonly used constants.
/// A set of commonly used constants and extensions.
/// </summary>
public static class Scopes
{
/// <summary>
/// Represents the scope of a function trigger request.
/// </summary>
public const string RootLifetimeScopeTag = "AutofacFunctionsScope";

/// <summary>
/// Share one instance of the component within the context of a single
/// azure function trigger request.
/// </summary>
/// <typeparam name="TLimit">Registration limit type.</typeparam>
/// <typeparam name="TActivatorData">Activator data type.</typeparam>
/// <typeparam name="TStyle">Registration style.</typeparam>
/// <param name="registration">The registration to configure.</param>
/// <param name="lifetimeScopeTags">Additional tags applied for matching lifetime scopes.</param>
/// <returns>A registration builder allowing further configuration of the component.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="registration" /> is <see langword="null" />.
/// </exception>
public static IRegistrationBuilder<TLimit, TActivatorData, TStyle> InstancePerTriggerRequest<TLimit, TActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TStyle> registration, params object[] lifetimeScopeTags)
{
if (registration == null)
throw new ArgumentNullException(nameof(registration));

var tags = new[] { Scopes.RootLifetimeScopeTag }.Concat(lifetimeScopeTags).ToArray();
return registration.InstancePerMatchingLifetimeScope(tags);
}

}
}
3 changes: 3 additions & 0 deletions SampleAutofacFunction/ApplicationInsights.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>
2 changes: 1 addition & 1 deletion SampleAutofacFunction/Functions/Function1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void Dispose()
}

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