Azure Function App Dependency Inject Logger not logging - azure

I have a Function App that I’m trying to using DI in a class for the logger so that I don’t have to pass the ILogger to every function. I’ve been doing some researching and found where I’m supposed to use ILogger in the class constructor. I’ve done all of the changes I believe are correct but it’s still not logging anything from that class.
I’ve added the logging to my Startup class, updated the hosts.json file, and used ILogger in the constructor. There are no errors but nothing gets logged. Does anyone see something I’m missing?
Startup.cs
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddLogging();
builder.Services.AddTransient<IDataManager, DataManager>();
}
Host.json
{
"version": "2.0",
"logging": {
"fileLoggingMode": "always",
"logLevel": {
"default": "Debug"
},
"applicationInsights": {
"samplingSettings": {
"isEnabled": false,
"excludedTypes": "Request"
}
}
}
}
DataManager.cs
public class DataManager : IDataManager
{
private DataContext _db;
private readonly ILogger<DataManager> _log;
public DataManager(ILogger<DataManager> log, DataContext dataContext)
{
_db = dataContext;
_log = log;
}
}

I have reproduced in my environment and got expected results as below and the code taken from #nareshnagpal06 's GitHub repository:
Startup.cs:
using Microsoft.Azure.Functions.Extensions.DependencyInjection; // install nuget - "Microsoft.Azure.Functions.Extensions"
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
[assembly: FunctionsStartup(typeof(FunctionApp50.Startup))]
namespace FunctionApp50
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IHelperClass, HelperClass>();
}
}
}
HelperClass.cs:
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace FunctionApp50
{
public class HelperClass : IHelperClass
{
private static ILogger<IHelperClass> _logger;
public HelperClass(ILogger<IHelperClass> logger)
{
_logger = logger;
}
public void Dowork()
{
_logger.LogInformation("Dowork: Execution Started");
/* rest of the functionality below
.....
.....
*/
_logger.LogInformation("Dowork: Execution Completed");
}
}
}
IHelperClass.cs:
using System;
using System.Collections.Generic;
using System.Text;
namespace FunctionApp50
{
public interface IHelperClass
{
void Dowork();
}
}
Function1.cs:
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
namespace FunctionApp50
{
public class Function1 // Ensure class is not static (which comes by default)
{
private IHelperClass _helper;
public Function1(IHelperClass helper)
{
_helper = helper;
}
[FunctionName("Function1")]
public void Run([TimerTrigger("0 */1 * * * *")] TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
// call helper
_helper.Dowork();
}
}
}
host.json:
{
"version": "2.0",
"logging": {
"fileLoggingMode": "always",
"applicationInsights": {
"samplingExcludedTypes": "Request",
"samplingSettings": {
"isEnabled": true
}
},
"logLevel": {
"Function": "Information"
}
}
}
Terminal Output:
File Output:
Firstly go to run, then paste %temp%\LogFiles\Application\Functions.,then click on Host file in the location then click on log file then the output is:

Related

Azure Function - Unable to log message to Application insights from service class

I am facing issues in logging messages from the service class of Azure Function app. There are no issues in logging from the function class. I followed the solution mentioned on Azure Functions - ILogger Logging across classes but cant figure out the issue.
host.json
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
},
"logLevel": {
"FunctionApp.Services.RestService": "Information"
}
}
}
RestService.cs
public class RestService : IRestService
{
private readonly ILogger<IRestService> _logger;
public RestService(ILogger<IRestService> logger)
{
_logger = logger;
}
public async Task<RestResponse> SampleMethod()
{
_logger.LogInformation("************************************TEST THIS WORK************************************","1212121212");
}
}
IRestService.cs
public interface IRestService
{
Task<RestResponse> SampleMethod(string url, string requestBody, string soapAction);
}
I am calling the RestService class from the function app.
Startup.cs
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton<IRestService, RestService>();
}
}
As mentioned earlier, the log messages from the function class appears in App Insight but not from the service class. What am I missing?
I have reproduced in my environment and got expected results as below:
You need to change Host.json file.
host.json:
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingExcludedTypes": "Request",
"samplingSettings": {
"isEnabled": true
}
},
"logLevel": {
"Function": "Information"
}
}
}
Taken Code from (for Class, Interface class, Startup and main function) reference (Link)
I have written similar code as you but changed Host.json code. Try to change hots.json and you will get output as I have got.

How do I log results from an Azure Function App dependency?

I have an Azure Function App project with the following files:
Startup.cs: Registers a dependency
[assembly: FunctionsStartup(typeof(MyLoggingFunction.Startup))]
namespace MyLoggingFunction
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddScoped<MyService>();
}
}
}
MyService.cs: Writes a log message
namespace MyLoggingFunction
{
public class MyService
{
private readonly ILogger<MyService> logger;
public MyService(ILogger<MyService> logger)
{
this.logger = logger;
}
public void Go()
{
this.logger.LogInformation("MyService.Go");
}
}
}
MyFunction.cs: The actual function; uses MyService
namespace MyLoggingFunction
{
public class MyFunction
{
private readonly MyService myService;
public MyFunction(MyService myService)
{
this.myService = myService;
}
[FunctionName("MyFunction")]
public IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
this.myService.Go();
log.LogInformation("All done");
return new OkObjectResult("Done.");
}
}
}
Here is the output visible in Azure after the function runs successfully. Note that the log message from the injected dependency is missing:
How do I get log messages from the dependency to show up as part of the built-in logging?
Add logging level entry to host.json :
{
"version": "2.0",
"logging": {
"logLevel": {
"default": "Information"
}
}
}
You must whitelist the classes and/or namespaces from which you want to allow logging. An example hosts.json file:
{
"logging": {
"logLevel": {
"MyLoggingFunction": "Information"
}
},
"version": "2.0"
}
This is documented as an issue on the azure-function-host GitHub repository.

HttpClient gets disposed w/Azure Functions Dependency Injection

When you add HttpClient using DI in Azure Functions it seems that anything that has a dependency on it must be a singleton, or the HttpClient will get disposed once the lifetime of the dependent class ends.
I'm adding HttpClient w/default settings:
builder.Services.AddHttpClient();
Here's the error I see in the logs when I attempt to re-run the function:
Cannot access a disposed object.
Object name: 'System.Net.Http.HttpClient'.
Can anyone confirm? If so, is this expected behavior?
I am not sure if you had checked the official doc but here's how you should inject the http client factory into your code:
//registering
using System;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Logging;
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton((s) => {
return new MyService();
});
builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
}
}
}
//azure function
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace MyNamespace
{
public class HttpTrigger
{
private readonly IMyService _service;
private readonly HttpClient _client;
public HttpTrigger(IMyService service, IHttpClientFactory httpClientFactory)
{
_service = service;
_client = httpClientFactory.CreateClient();
}
[FunctionName("GetPosts")]
public async Task<IActionResult> Get(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "posts")] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var res = await _client.GetAsync("https://microsoft.com");
await _service.AddResponse(res);
return new OkResult();
}
}
}
https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection

The configuration file 'config.json' was not found and is not optional

I use Azure Functions v2
I try to create Startup file:
[assembly: FunctionsStartup(typeof(AzureAppDomainRegistration.Startup))]
namespace AzureAppDomainRegistration
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var config = new ConfigurationBuilder()
.AddJsonFile("config.json")
.Build();
var connString = config["ConnectionStrings:DataContext"];
builder.Services.AddDbContext<DataContext>(options => options
.UseLazyLoadingProxies()
.UseSqlServer(connString));
builder.Services.AddTransient<IActionsRegistrationInfo, EfActionsRegistrationInfo>();
}
}
}
File "config.json" exists and has option Always to copy:
<None Update="config.json">
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
But when I start Azure Function (locally or publish to Azure) I get an error:
Microsoft.Extensions.Configuration.FileExtensions: The configuration
file 'config.json' was not found and is not optional.
what is wrong?
Before .AddJsonFile() method, add this line of code: .SetBasePath(Environment.CurrentDirectory). Then in your project, right click "config.json" -> select properties -> then set "copy to output directory" to "copy always".
And note that I'm using IWebJobsStartup instead of FunctionsStartup.
Here is the code works for me:
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
//using WebJobsStartup instead of FunctionsStartup
[assembly: WebJobsStartup(typeof(FunctionApp20.Startup))]
namespace FunctionApp20
{
public static class Function1
{
[FunctionName("Function1")]
public static void Run([BlobTrigger("samples-workitems/{name}", Connection = "AzureWebJobsStorage")]Stream myBlob, string name, ILogger log)
{
log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
}
}
//using IWebJobsStartup instead of FunctionsStartup
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var config = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("config.json",true,true)
.Build();
}
}
}

How to use dependency inject for TelemetryConfiguration in Azure Function

I try to use Dependency injection in Azure Functions for TelemetryConfiguration. In my function I will have it resolved when I inject TelemetryConfiguration in the functions constructor. I suppose I don't really understand how I will do with TelemetryConfiguration in StartUp, thats why I get an exception. How will I add the TelemetryConfiguration I already configured.
I have did an easy example here what I'm doing so far.
[assembly: FunctionsStartup(typeof(StartUp))]
public class StartUp : FunctionsStartup
{
private string OmsModule { get; } = "OMS.VA";
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.Configure<TelemetryConfiguration>(
(o) =>
{
o.InstrumentationKey = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
o.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
});
}
}
public class StopPlaceUpdateTimerTrigger
{
private TelemetryClient _telemetryClient;
private string _azureWebJobsStorage;
public StopPlaceUpdateTimerTrigger(TelemetryConfiguration telemetryConfiguration)
{
_telemetryClient = new TelemetryClient(telemetryConfiguration);
}
[FunctionName("StopPlaceLoader")]
public async Task StopPlaceLoaderMain([TimerTrigger("%CRON_EXPRESSION%", RunOnStartup = true)]TimerInfo myTimerInfo, ILogger log, ExecutionContext context)
{
SetConfig(context);
var cloudTable = await GetCloudTableAsync();
if (cloudTable == null)
{
//Do nothing
}
//Do nothing
}
private async Task<CloudTable> GetCloudTableAsync()
{
var storageAccount = CloudStorageAccount.Parse(_azureWebJobsStorage);
var tableClient = storageAccount.CreateCloudTableClient();
var table = tableClient.GetTableReference(nameof(StopPlaceLoaderCacheRecord));
if (!await table.ExistsAsync())
{
await table.CreateIfNotExistsAsync();
}
return table;
}
private void SetConfig(ExecutionContext context)
{
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true)
.AddEnvironmentVariables()
.Build();
_azureWebJobsStorage = config["AzureWebJobsStorage"];
}
}
//local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol...",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"EnableMSDeployAppOffline": "True",
"CRON_EXPRESSION": "0 */5 22-3 * * *",
"APPINSIGHTS_INSTRUMENTATIONKEY": "..."
}
}
I get the following Exception;
Microsoft.Extensions.DependencyInjection.Abstractions: Unable to resolve service for type 'Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration' while attempting to activate 'OMS.VA.RealTime.StopPlaceLoader.StopPlaceUpdateTimerTrigger'.
Update:
We can change this line of code var newConfig = TelemetryConfiguration.Active; to var newConfig = TelemetryConfiguration.CreateDefault(); , since TelemetryConfiguration.Active is deprecated.
Please use the code below for TelemetryConfiguration DI, I test it with a blob trigger function and works well:
using System.IO;
using System.Linq;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
[assembly: WebJobsStartup(typeof(FunctionApp17.MyStartup))]
namespace FunctionApp17
{
public class MyStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var configDescriptor = builder.Services.SingleOrDefault(tc => tc.ServiceType == typeof(TelemetryConfiguration));
if (configDescriptor?.ImplementationFactory != null)
{
var implFactory = configDescriptor.ImplementationFactory;
builder.Services.Remove(configDescriptor);
builder.Services.AddSingleton(provider =>
{
if (implFactory.Invoke(provider) is TelemetryConfiguration config)
{
var newConfig = TelemetryConfiguration.Active;
newConfig.ApplicationIdProvider = config.ApplicationIdProvider;
newConfig.InstrumentationKey = config.InstrumentationKey;
return newConfig;
}
return null;
});
}
}
}
public class Function1
{
private TelemetryClient _telemetryClient;
public Function1(TelemetryConfiguration telemetryConfiguration)
{
_telemetryClient = new TelemetryClient(telemetryConfiguration);
}
[FunctionName("Function1")]
public void Run([BlobTrigger("samples-workitems/{name}", Connection = "AzureWebJobsStorage")]Stream myBlob, string name, ILogger log)
{
log.LogInformation($"!!!!!!!!!! C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
_telemetryClient.TrackTrace("this is a test message from DI of telemetry client !!!!!!!!!!!!!!");
}
}
}
the test result as below, I can see the logs in the application insights in azure portal:
And one more thing, I see you try to use ITelemetry Initializer in your code. You can follow this GitHub issue for your ITelemetry Initializer or Itelemetry Processor
If you use builder.Services.Configure<TelemetryConfiguration>() to configure, you are using Options pattern in ASP.NET Core.
To access the option, you need to do as following:
public StopPlaceUpdateTimerTrigger(IOptionsMonitor<TelemetryConfiguration> telemetryConfiguration)
{
_telemetryClient = new TelemetryClient(telemetryConfiguration.CurrentValue);
}
If you just want to directly use TelemetryConfiguration object, you need to add it in service collection:
builder.Services.AddSingleton<TelemetryConfiguration >(sp =>
{
var telemetryConfiguration = new TelemetryConfiguration();
telemetryConfiguration.InstrumentationKey = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
telemetryConfiguration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
return telemetryConfiguration;
}
Then you can :
public StopPlaceUpdateTimerTrigger(TelemetryConfiguration telemetryConfiguration)
{
_telemetryClient = new TelemetryClient(telemetryConfiguration);
}
Hope it helps.

Resources