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

Url is missing from appinsights logs for requests if use FunctionStartup #63

Open
dhanore opened this issue Aug 26, 2021 · 9 comments
Open

Comments

@dhanore
Copy link

dhanore commented Aug 26, 2021

  1. Run any number of load on sandbox
  2. Go to appinsights -> Logs
  3. Query for the following -
    requests | where cloud_RoleName contains "test" | order by duration desc | project itemType, cloud_RoleName, url, appName, timestamp, name, cloud_RoleInstance.

What we have observed - If Startup class inherit IWebJobStartup then we are getting URL in appinsights logs for request, but if Startup class inherit FunctionStartup then we are not getting Url in appinsights logs.

We want to use Startup class Inherit FunctionStartup as we have written so much code based upon that in so many services. And we want to use constructor DI which is not possible in IWebJobStartup (as per sample which I have).

URL_Missing_AppInsights

  1. Getting URL in appinsight logs if we use below code in startup class

[assembly: WebJobsStartup(typeof(Startup))]
namespace TestApplicationWebJob.Infrastructure
{
[ExcludeFromCodeCoverage]
internal class Startup : IWebJobsStartup
{
static string appInstanceId = Guid.NewGuid().ToString();
public void Configure(IWebJobsBuilder builder)
{
#pragma warning disable CS0618 // Type or member is obsolete
builder.AddDependencyInjection(ConfigureServices);
builder.AddSwashBuckle(Assembly.GetExecutingAssembly(), opts =>
{

  1. Not getting URL in appinsights logs, if we use below code in startup class

[assembly: FunctionsStartup(typeof(Startup))]
namespace TestApplicationFunc.Infrastructure
{
[ExcludeFromCodeCoverage]
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
RegisterServices(builder.Services);

        builder.AddSwashBuckle(Assembly.GetExecutingAssembly(), opts =>
        {
            opts.SpecVersion = OpenApiSpecVersion.OpenApi3_0;
            opts.Documents = new[]
            {
                new SwaggerDocument
                {
@v-bbalaiagar v-bbalaiagar self-assigned this Aug 30, 2021
@v-bbalaiagar
Copy link

Hi @dhanore, Thank you for your feedback! We will check for the possibilities internally and update you with the findings.

@v-bbalaiagar v-bbalaiagar transferred this issue from Azure/Azure-Functions Sep 2, 2021
@v-bbalaiagar
Copy link

Tagging @fabiocav / @brettsam , for further insights

@dhanore
Copy link
Author

dhanore commented Sep 7, 2021

Hi @v-bbalaiagar

Thank you for picking up this bug.
Hope you got a chance to review this bug. Would you please let me know the findings or fixes which you suggest to solve this bug on our side?

cc- @fabiocav , @brettsam

@dhanore
Copy link
Author

dhanore commented Sep 17, 2021

Hi, Did you get a chance to look into this issue or any plan for upcoming days to give some feedback?.

@dhanore
Copy link
Author

dhanore commented Sep 24, 2021

@v-bbalaiagar
Hi, I cannot see any status update and any comments on this issue. Could you please help me with the status or position of this issue. And I can see this issue has been transferred, could you please help me to identify the location of this issue here?

cc - @fabiocav @brettsam

@dhanore
Copy link
Author

dhanore commented Sep 29, 2021

@v-bbalaiagar @fabiocav @brettsam - Can you please let me know what is the status of this issue and where is this issue has transferred so I can see the status of it and any workaround for this issue as this is a priority task in our board?

@fabiocav
Copy link
Member

@dhanore would you mind sharing the full startup implementations for both cases (or a reduced repro) either here or in a repo?
Thank you!

@dhanore
Copy link
Author

dhanore commented Sep 30, 2021

@fabiocav -

Below is WebJobStartup code - which is not giving Url for request in appInsights and FunctionStartup code which is giving result after that

using AzureFunctions.Extensions.Swashbuckle;
using AzureFunctions.Extensions.Swashbuckle.Settings;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.AspNetCore.Builder;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using Willezone.Azure.WebJobs.Extensions.DependencyInjection;
using TestApplication.Infrastructure;

[assembly: WebJobsStartup(typeof(Startup))]
namespace TestApplication.Infrastructure
{
    [ExcludeFromCodeCoverage]
    internal class Startup : IWebJobsStartup
    {
        public void Configure(IApplicationBuilder app)
        {
            app.UseSwagger();
        }

        public void Configure(IWebJobsBuilder builder)
        {
            builder.AddDependencyInjection(ConfigureServices);

            builder.AddSwashBuckle(Assembly.GetExecutingAssembly(), opts =>
            {
                opts.SpecVersion = OpenApiSpecVersion.OpenApi3_0;
                opts.Documents = new[]
                {
                    new SwaggerDocument
                    {
                        Name = "v1",
                        Title = "Test App",
                        Description = "Test App",
                        Version = "v1"
                    }
                };
                opts.ConfigureSwaggerGen = (c =>
                {
                    c.AddSecurityDefinition(SecuritySchemes.DevtokenSecurityId,
                        SecuritySchemes.DevTokenSecurityScheme);
                    c.AddSecurityDefinition(SecuritySchemes.JwtSecurityId,
                        SecuritySchemes.JwtSecurityScheme);
                    c.OperationFilter<SecurityOperationFilter>();
                    c.CustomOperationIds(apiDesc =>
                    {
                        var info = apiDesc.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo : null;
                        var customAttrInfo = (FunctionNameAttribute)info?.GetCustomAttributes()?
                        .FirstOrDefault(attr => attr is FunctionNameAttribute);
                        return customAttrInfo?.Name;
                    });
                });
                opts.XmlPath = "TestApp.xml";

            });
        }

        readonly HttpClient httpClient = new HttpClient() { Timeout = new TimeSpan(0, 10, 0) };

        private void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IRegistryHelper, RegistryHelper>();

            services.AddSingleton<IConfigHelper>(
                new ConfigHelper(services.BuildServiceProvider().GetRequiredService<IRegistryHelper>()));

            var configHelper =
               services
                   .BuildServiceProvider()
                   .GetRequiredService<IConfigHelper>();

            services.AddMemoryCache();

            services.AddTransient<IJwtHelper, JwtHelper>(s => new JwtHelper());

            services.AddSingleton(new TelemetryClient());

            // Registering services

            services.AddTransient<IFunctionLogger, FunctionLogger>();
            services.AddTransient<IRequestSecurityHelper, RequestSecurityHelper>();
            services.AddTransient<ITestManager, TestManager>();
            services.AddTransient<IExternalServiceHelper, ExternalServiceHelper>();
            services.AddTransient<ITestDatabaseHelper, TestDatabaseHelper>();
            services.AddTransient<IHttpClientManager, HttpClientManager>();


            services.AddHttpClient("TestClient")
            .ConfigurePrimaryHttpMessageHandler(() =>
            {
                return new HttpClientHandler()
                {
                    AllowAutoRedirect = false,
                    UseDefaultCredentials = true
                };
            });

            var logger =
                services
                    .BuildServiceProvider()
                    .GetRequiredService<IFunctionLogger>();

            try
            {
                #region db setup

                var cosmosUnifiedConfigurationDB = new CosmosDBConfiguration
                {
                    ThroughputType = ThroughputType.AutoScale,
                    DatabaseId = Constants.DATABASENAME,
                    ThroughputLevel = ThroughputLevel.DatabaseLevel,
                    RetryOptions = new CosmosDBRetryOptions()
                    { MaxRetryWaitTimeInSeconds = 100, MaxRetryAttempts = 60 },
                    DoNotOverrideThroughputSettings = true
                };

                var containerConfig = new CosmosDBContainerSettings
                {
                    ContainerId = Constants.COLLECTION_NAME,
                    PartitionKeyPath = Constants.PARTITION_KEY
                };

                services.AddTransient<ICosmosDBRepository<TestAzureEntity>>(s =>
                                                                            new CosmosDBRepository<TestAzureEntity>(containerConfig, cosmosUnifiedConfigurationDB));

                // initialize db:
                // the following line is about making sure the DB collection is created 
                // so that it doesn´t run on every new instance of a server 
                logger.LogTrace($"Initializing DB DBId: {Constants.DATABASENAME} Container: {Constants.COLLECTION_NAME} PartitionKey: / id", "RegisterServices", SeverityLevel.Information, Category.Request);
                DBSetupHelper.InitializeDBCollection(services);

                // Please keep it running until bootstrapper V3 code
                // with a bug fix for multiple concurrent initializations.
                // for more details on the subject
                DBSetupHelper.initRepos(services);

                #endregion
            }
            catch (Exception ex)
            {
                // log the exception
                logger.LogException(ex, "Startup.RegisterServices", "Exception thrown in db setup of startup class for service.",
                    Category.Startup, SubCategory.Incoming, null);
            }

            #region setup for service bus

            // Service bus DI
            services.AddSingleton<ITopicClient>(
                t => new TopicClient(configHelper.ServiceBusConnectionString, configHelper.TopicName)
                        );
            services.AddTransient<ISubscriptionSetup, SubscriptionSetup>();
            services.AddTransient<IServiceBusManager, ServiceBusManager>();
            services.AddSingleton<IAsbHelper, AsbHelper>();

            // build subscription setup instance
            var subscriptionSetup =
                    services
                        .BuildServiceProvider()
                            .GetRequiredService<ISubscriptionSetup>();

            // setup subscriptions
            subscriptionSetup.SetupSubscriptionClient();

            #endregion
        }
    }

    internal class ApiKeyScheme : OpenApiSecurityScheme
    {

    }
}

Function Startup which is giving Url of Request in AppInsights

using AzureFunctions.Extensions.Swashbuckle.Settings;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using TestApplication.Infrastructure.Function

[assembly: FunctionsStartup(typeof(Startup))]
namespace TestApplication.Infrastructure.Function
{
    [ExcludeFromCodeCoverage]
    public class Startup : FunctionsStartup
    {
       // static string appInstanceId = Guid.NewGuid().ToString();

        /// <summary>
        /// Configure startup
        /// </summary>
        /// <param name="builder"></param>
        public override void Configure(IFunctionsHostBuilder builder)
        {
            RegisterServices(builder.Services);

            builder.AddSwashBuckle(Assembly.GetExecutingAssembly(), opts =>
            {
                opts.SpecVersion = OpenApiSpecVersion.OpenApi3_0;
                opts.Documents = new[]
                {
                    new SwaggerDocument
                    {
                        Name = "v1",
                        Title = "Test Application",
                        Description = "Test Application",
                        Version = "v1"
                    }
                };
                opts.ConfigureSwaggerGen = (c =>
                {
                    c.AddSecurityDefinition(SecuritySchemes.DevtokenSecurityId,
                        SecuritySchemes.DevTokenSecurityScheme);
                    c.AddSecurityDefinition(SecuritySchemes.JwtSecurityId,
                        SecuritySchemes.JwtSecurityScheme);
                    c.OperationFilter<SecurityOperationFilter>();
                    c.CustomOperationIds(apiDesc =>
                    {
                        var info = apiDesc.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo : null;
                        var customAttrInfo = (FunctionNameAttribute)info?.GetCustomAttributes()?
                        .FirstOrDefault(attr => attr is FunctionNameAttribute);
                        return customAttrInfo?.Name;
                    });
                });
                opts.XmlPath = "TestApplication.xml";

            });
        }

        private void RegisterServices(IServiceCollection services)
        {
            services.AddTransient<IRegistryHelper, RegistryHelper>();

            services.AddSingleton<IConfigHelper>(
                new ConfigHelper(services.BuildServiceProvider().GetRequiredService<IRegistryHelper>()));

            var configHelper =
               services
                   .BuildServiceProvider()
                   .GetRequiredService<IConfigHelper>();

            services.AddMemoryCache();

            // Registering services
            services.AddTransient<IJwtHelper, JwtHelper>(s => new JwtHelper());

            var telemetryConfig = TelemetryConfiguration.CreateDefault();
            telemetryConfig.TelemetryInitializers.Add(new CloudRoleNameInitializer());
            services.AddSingleton(new TelemetryClient(telemetryConfig));

            services.AddTransient<IFunctionLogger, FunctionLogger>();
            services.AddTransient<IRequestSecurityHelper, RequestSecurityHelper>();
            services.AddTransient<ITestManager, TestManager>();
            services.AddTransient<IExternalServiceHelper, ExternalServiceHelper>();
            services.AddTransient<ITestDatabaseHelper, TestDatabaseHelper>();
            services.AddTransient<IHttpClientManager, HttpClientManager>();

            services.AddHttpClient("TestApplication")
            .ConfigurePrimaryHttpMessageHandler(() =>
            {
                return new HttpClientHandler()
                {
                    AllowAutoRedirect = false,
                    UseDefaultCredentials = true
                };
            });

            var logger =
                services
                    .BuildServiceProvider()
                    .GetRequiredService<IFunctionLogger>();

            try
            {
                #region db setup

                var cosmosUnifiedConfigurationDB = new CosmosDBConfiguration
                {
                    ThroughputType = ThroughputType.AutoScale,
                    DatabaseId = Constants.DATABASENAME,
                    ThroughputLevel = ThroughputLevel.DatabaseLevel,
                    RetryOptions = new CosmosDBRetryOptions()
                    { MaxRetryWaitTimeInSeconds = 100, MaxRetryAttempts = 60 },
                    DoNotOverrideThroughputSettings = true
                };

                var containerConfig = new CosmosDBContainerSettings
                {
                    ContainerId = Constants.COLLECTION_NAME,
                    PartitionKeyPath = Constants.PARTITION_KEY
                };


                services.AddTransient<ICosmosDBRepository<BoosterAzureEntity>>(s =>
                                                                            new CosmosDBRepository<BoosterAzureEntity>(containerConfig, cosmosUnifiedConfigurationDB));

                // initialize db:
                // the following line is about making sure the DB collection is created 
                // so that it doesn´t run on every new instance of a server 
                DBSetupHelper.InitializeDBCollection(services);

                // with a bug fix for multiple concurrent initializations.
                // also check Azure function Bootstrapper V3 Documentation.docx 
                // for more details on the subject
                DBSetupHelper.initRepos(services);

                #endregion
            }
            catch (Exception ex)
            {
                // log the exception
                logger.LogException(ex, "Startup.RegisterServices", "Exception thrown in db setup of startup class for service.",
                    Category.Startup, SubCategory.Incoming, null);
            }

            #region setup for service bus

            // Service bus DI
            services.AddSingleton<ITopicClient>(
                t => new TopicClient(configHelper.ServiceBusConnectionString, configHelper.TopicName)
                        );
            services.AddTransient<ISubscriptionSetup, SubscriptionSetup>();
            services.AddTransient<IServiceBusManager, ServiceBusManager>();
            services.AddSingleton<IAsbHelper, AsbHelper>();

            // build subscription setup instance
            var subscriptionSetup =
                    services
                        .BuildServiceProvider()
                            .GetRequiredService<ISubscriptionSetup>();

            // setup subscriptions
            subscriptionSetup.SetupSubscriptionClient();

            #endregion
        }
    }
}

@v-bbalaiagar v-bbalaiagar removed their assignment Sep 30, 2021
@dhanore
Copy link
Author

dhanore commented Oct 4, 2021

@fabiocav, @brettsam - I have shared the code for both the files above (IWebJob and FunctionStartup). I hope this helps you and let us know if you find anything. Please let me know if you need any other assistance from my side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants