diff --git a/src/ApplicationInsights.Kubernetes/Extensions/ApplicationInsightsExtensions.cs b/src/ApplicationInsights.Kubernetes/Extensions/ApplicationInsightsExtensions.cs
index 5880b14..94ae6da 100644
--- a/src/ApplicationInsights.Kubernetes/Extensions/ApplicationInsightsExtensions.cs
+++ b/src/ApplicationInsights.Kubernetes/Extensions/ApplicationInsightsExtensions.cs
@@ -18,14 +18,18 @@ public static partial class ApplicationInsightsExtensions
/// Enables Application Insights for Kubernetes on the Default TelemetryConfiguration in the dependency injection container with custom options.
///
/// Collection of service descriptors.
- /// Action to customize the configuration of Application Insights for Kubernetes.
/// Sets the diagnostics log levels for the enricher.
+ /// If true, the Application Insights enricher library will not use any background (hosted) services,
+ /// and Kubernetes information will not be fetched automatically. Hosted services are not allowed in some environments, e.g. Azure Function.
+ /// For more information see https://github.com/Azure/azure-functions-host/issues/5447#issuecomment-575368316
+ /// Action to customize the configuration of Application Insights for Kubernetes.
/// Provides a custom implementation to check whether it is inside kubernetes cluster or not.
/// The service collection for chaining the next operation.
public static IServiceCollection AddApplicationInsightsKubernetesEnricher(
this IServiceCollection services,
- Action? applyOptions = default,
LogLevel? diagnosticLogLevel = LogLevel.None,
+ bool disableBackgroundService = false,
+ Action? applyOptions = default,
IClusterEnvironmentCheck? clusterCheck = default)
{
diagnosticLogLevel ??= LogLevel.None; // Default to None.
@@ -37,7 +41,7 @@ public static IServiceCollection AddApplicationInsightsKubernetesEnricher(
if (!KubernetesTelemetryInitializerExists(services))
{
- services.ConfigureKubernetesTelemetryInitializer(applyOptions, clusterCheck);
+ services.ConfigureKubernetesTelemetryInitializer(applyOptions, clusterCheck, disableBackgroundService);
}
return services;
}
@@ -48,7 +52,7 @@ public static IServiceCollection AddApplicationInsightsKubernetesEnricher(
public static void StartApplicationInsightsKubernetesEnricher(this IServiceProvider serviceProvider)
{
IK8sInfoBootstrap? k8sInfoBootstrap = serviceProvider.GetService();
- if(k8sInfoBootstrap is null)
+ if (k8sInfoBootstrap is null)
{
_logger.LogInformation("No service registered by type {0}. Either not running in a Kubernetes cluster or `{1}()` wasn't called on the service collection.", nameof(IK8sInfoBootstrap), nameof(AddApplicationInsightsKubernetesEnricher));
return;
@@ -69,9 +73,10 @@ private static bool KubernetesTelemetryInitializerExists(IServiceCollection serv
internal static void ConfigureKubernetesTelemetryInitializer(
this IServiceCollection services,
Action? overwriteOptions,
- IClusterEnvironmentCheck? clusterCheck)
+ IClusterEnvironmentCheck? clusterCheck,
+ bool skipRegisterBackendService = false)
{
- IKubernetesServiceCollectionBuilder kubernetesServiceCollectionBuilder = new KubernetesServiceCollectionBuilder(overwriteOptions, clusterCheck);
+ IKubernetesServiceCollectionBuilder kubernetesServiceCollectionBuilder = new KubernetesServiceCollectionBuilder(overwriteOptions, clusterCheck, skipRegisterBackendService);
_ = kubernetesServiceCollectionBuilder.RegisterServices(services);
}
}
diff --git a/src/ApplicationInsights.Kubernetes/Extensions/KubernetesServiceCollectionBuilder.cs b/src/ApplicationInsights.Kubernetes/Extensions/KubernetesServiceCollectionBuilder.cs
index 44686e9..35964e8 100644
--- a/src/ApplicationInsights.Kubernetes/Extensions/KubernetesServiceCollectionBuilder.cs
+++ b/src/ApplicationInsights.Kubernetes/Extensions/KubernetesServiceCollectionBuilder.cs
@@ -17,6 +17,7 @@ namespace Microsoft.Extensions.DependencyInjection;
internal class KubernetesServiceCollectionBuilder : IKubernetesServiceCollectionBuilder
{
private readonly IClusterEnvironmentCheck _clusterCheck;
+ private readonly bool _skipRegisterBackendService;
private readonly Action? _customizeOptions;
private readonly ApplicationInsightsKubernetesDiagnosticSource _logger = ApplicationInsightsKubernetesDiagnosticSource.Instance;
@@ -29,10 +30,12 @@ internal class KubernetesServiceCollectionBuilder : IKubernetesServiceCollection
///
public KubernetesServiceCollectionBuilder(
Action? customizeOptions,
- IClusterEnvironmentCheck? clusterCheck)
+ IClusterEnvironmentCheck? clusterCheck,
+ bool skipRegisterBackendService)
{
_customizeOptions = customizeOptions;
_clusterCheck = clusterCheck ?? new ClusterEnvironmentCheck();
+ _skipRegisterBackendService = skipRegisterBackendService;
}
///
@@ -142,7 +145,13 @@ protected virtual void RegisterK8sEnvironmentFactory(IServiceCollection serviceC
serviceCollection.TryAddScoped();
serviceCollection.TryAddSingleton(_ => K8sEnvironmentHolder.Instance);
+ _logger.LogTrace("Registering bootstrap and hosted service.");
serviceCollection.TryAddSingleton();
- serviceCollection.AddHostedService();
+ if (!_skipRegisterBackendService)
+ {
+ _logger.LogInformation("Skip registering {0} by user configuration.", nameof(K8sInfoBackgroundService));
+ serviceCollection.AddHostedService();
+ }
+ _logger.LogTrace("Registered bootstrap and hosted service.");
}
}
diff --git a/src/ApplicationInsights.Kubernetes/IK8sInfoBootstrap.cs b/src/ApplicationInsights.Kubernetes/IK8sInfoBootstrap.cs
index e1c69bb..1d2ab56 100644
--- a/src/ApplicationInsights.Kubernetes/IK8sInfoBootstrap.cs
+++ b/src/ApplicationInsights.Kubernetes/IK8sInfoBootstrap.cs
@@ -8,7 +8,7 @@ namespace Microsoft.ApplicationInsights.Kubernetes;
/// The intention is for the client to have a handle to start getting Kubernetes info to be consumed by the .
/// Remark: This is supposed to only be used in Console Application. Do NOT use this in ASP.NET or Worker, where the hosted service exists.
///
-internal interface IK8sInfoBootstrap
+public interface IK8sInfoBootstrap
{
///
/// Bootstrap the fetch of Kubernetes information.
diff --git a/tests/UnitTests/AppInsightsKubernetesOptionsTests.cs b/tests/UnitTests/AppInsightsKubernetesOptionsTests.cs
index bd93781..04035d4 100644
--- a/tests/UnitTests/AppInsightsKubernetesOptionsTests.cs
+++ b/tests/UnitTests/AppInsightsKubernetesOptionsTests.cs
@@ -18,7 +18,7 @@ public void ShouldHaveDefaultOptions()
clusterCheck.Setup(c => c.IsInCluster).Returns(true);
- KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: default, clusterCheck.Object);
+ KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: default, clusterCheck.Object, skipRegisterBackendService: false);
IServiceCollection services = new ServiceCollection();
IConfiguration configuration = (new ConfigurationBuilder()).Build();
@@ -44,7 +44,7 @@ public void ShouldTakeOptionFromIConfiguration()
clusterCheck.Setup(c => c.IsInCluster).Returns(true);
- KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: default, clusterCheck.Object);
+ KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: default, clusterCheck.Object, skipRegisterBackendService: false);
IServiceCollection services = new ServiceCollection();
IConfiguration configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary()
@@ -71,7 +71,7 @@ public void ShouldTakeDelegateOverwriteForSettings()
KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: opt =>
{
opt.InitializationTimeout = TimeSpan.FromSeconds(10); // The user settings through code will take precedence.
- }, clusterCheck.Object);
+ }, clusterCheck.Object, skipRegisterBackendService: false);
IServiceCollection services = new ServiceCollection();
IConfiguration configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary()
@@ -101,7 +101,7 @@ public void ShouldAllowSetTelemetryKeyProcessorByCode()
KubernetesServiceCollectionBuilder builder = new KubernetesServiceCollectionBuilder(customizeOptions: opt =>
{
opt.TelemetryKeyProcessor = keyTransformer;
- }, clusterCheck.Object);
+ }, clusterCheck.Object, skipRegisterBackendService: false);
IServiceCollection services = new ServiceCollection();
IConfiguration configuration = new ConfigurationBuilder().Build();
diff --git a/tests/UnitTests/ApplicationInsightsExtensionsTests.cs b/tests/UnitTests/ApplicationInsightsExtensionsTests.cs
index 0e65e2e..3c80212 100644
--- a/tests/UnitTests/ApplicationInsightsExtensionsTests.cs
+++ b/tests/UnitTests/ApplicationInsightsExtensionsTests.cs
@@ -1,6 +1,9 @@
using System;
+using System.Linq;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Moq;
using Xunit;
namespace Microsoft.ApplicationInsights.Kubernetes.Tests;
@@ -29,7 +32,7 @@ public void ShouldAllowOverwriteOptions()
IServiceCollection collection = new ServiceCollection();
// If there's compile error, check if the signature of AddApplicationInsightsKubernetesEnricher was changed.
- collection = collection.AddApplicationInsightsKubernetesEnricher(opt => opt.InitializationTimeout = TimeSpan.FromMinutes(15));
+ collection = collection.AddApplicationInsightsKubernetesEnricher(applyOptions: opt => opt.InitializationTimeout = TimeSpan.FromMinutes(15));
Assert.NotNull(collection);
}
@@ -52,9 +55,35 @@ public void ShouldAllowOverwritingOptionsAndDiagnosticLoggingLevel()
// If there's compile error, check if the signature of AddApplicationInsightsKubernetesEnricher was changed.
collection = collection.AddApplicationInsightsKubernetesEnricher(
- opt => opt.InitializationTimeout = TimeSpan.FromMinutes(15),
- diagnosticLogLevel: LogLevel.Trace);
+ diagnosticLogLevel: LogLevel.Trace,
+ applyOptions: opt => opt.InitializationTimeout = TimeSpan.FromMinutes(15));
Assert.NotNull(collection);
}
+
+ [Theory]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ public void ShouldNotRegisterHostedServiceWhenSet(bool disableBackgroundService, bool expectServiceRegistered)
+ {
+ IServiceCollection collection = new ServiceCollection();
+
+ Mock clusterCheck = new();
+ clusterCheck.Setup(c => c.IsInCluster).Returns(true);
+
+ // If there's compile error, check if the signature of AddApplicationInsightsKubernetesEnricher was changed.
+ collection = collection.AddApplicationInsightsKubernetesEnricher(disableBackgroundService: disableBackgroundService, clusterCheck: clusterCheck.Object);
+
+ Assert.NotNull(collection);
+ bool registered = collection.Any(serviceDescriptor => serviceDescriptor.ServiceType == typeof(IHostedService));
+
+ if (expectServiceRegistered)
+ {
+ Assert.True(registered);
+ }
+ else
+ {
+ Assert.False(registered);
+ }
+ }
}
diff --git a/tests/UnitTests/KubernetesEnablementTests.cs b/tests/UnitTests/KubernetesEnablementTests.cs
index bf7569f..b0a21db 100644
--- a/tests/UnitTests/KubernetesEnablementTests.cs
+++ b/tests/UnitTests/KubernetesEnablementTests.cs
@@ -21,7 +21,7 @@ public void ServicesRegistered()
clusterCheckMock.Setup(c => c.IsInCluster).Returns(true);
- KubernetesServiceCollectionBuilder target = new KubernetesServiceCollectionBuilder(customizeOptions: null, clusterCheckMock.Object);
+ KubernetesServiceCollectionBuilder target = new KubernetesServiceCollectionBuilder(customizeOptions: null, clusterCheckMock.Object, skipRegisterBackendService: false);
target.RegisterServices(services);
Assert.NotNull(services.FirstOrDefault(sd => sd.ImplementationType == typeof(KubernetesTelemetryInitializer)));