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.
Related
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:
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.
I am injecting ILogger into my Azure functions, and in my host.json I have the following:
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"maxTelemetryItemsPerSecond": 20
}
}
}
}
I then log as below:
public class EnrollmentFunctions
{
private readonly ILogger<AttestationFunctions> logger;
public EnrollmentFunctions(ILogger<AttestationFunctions> log)
{
logger = log;
}
public async Task<IActionResult> Enroll(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "enroll")] HttpRequest req)
{
logger.LogInformation("Requesting attestation of device name {deviceName}.", req.Query["deviceName"]);
}
}
However no logs are logged as below - appreciate pointers as to what I am doing wrong and where I would see my logs please.
I've followed the below work around to achieve the desired results as described:
I've used generalized logging in my function project using Visual studio and included both ILogger and TelemetryClient in a class Genlog.cs
Genlog.cs
public class Genlog
{
public Genlog(ILogger<Genlog> logger, TelemetryClient telemetry)
{
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
Telemetry = telemetry ?? throw new ArgumentNullException(nameof(telemetry));
}
public void testlog(string mylog)
{
Logger.LogInformation("TestCustomInformationLog:" + mylog);
Telemetry.TrackTrace("TestCustomTraceLog: " + mylog, Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Information);
Telemetry.TrackException(new Exception(""));
}
public ILogger<Genlog> Logger { get; }
public TelemetryClient Telemetry { get; }
}
Startup.cs
#Adding configuring service of Genlog in startup.cs
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddScoped<Genlog>();
}
Function.cs
public class Function1
{
private readonly Genlog genlog;
public Function1(Genlog genlog)
{
this.genlog = genlog ?? throw new ArgumentNullException(nameof(genlog));
}
[FunctionName("Function1")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
# Using Both Ilogger & Telemetry logger in same class.
genlog.testlog("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
genlog.testlog(responseMessage);
return new OkObjectResult(responseMessage);
}
}
Result
Published to Azure to check & retrieve logs:
I have an azure function, it logs the information without any issues.
namespace aspnetcore_azurefun_blob
{
[StorageAccount("AzureWebJobsStorage")]
public class FileTrigger
{
#region Property
private readonly IFileProcessor fileProcessor;
#endregion
#region Constructor
public FileTrigger(IFileProcessor fileProcessor)
{
this.fileProcessor = fileProcessor;
}
#endregion
[FunctionName("FileTrigger")]
public void ProcessFilesFromSamplesContainer([BlobTrigger("samples-workitems/{name}")]Stream myBlob, string name, ILogger log, ExecutionContext context)
{
log.LogInformation("Function: ProcessFilesFromSamplesContainer is called");
log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
var result = fileProcessor.ProcessAsync(myBlob, name);
log.LogInformation($"Function Completed Successfully on {DateTime.Now.ToLongDateString()} # {DateTime.Now.ToShortTimeString()}.\n.");
}
However, I also have the business logic implemented using DI and below is the excerpt of the implementation.
ServiceBase.cs
namespace BusinessService.Services.Common
{
public abstract class ServiceBase<T>
{
public static AppDbContext AppDbContext;
public static ILogger<T> Logger { get; set; }
public static AppConfigurator Configurator { get; set; }
public ServiceBase(AppDbContext appDbContext, ILogger<T> logger, IOptions<AppConfigurator> configurator)
{
AppDbContext = appDbContext ?? throw new ArgumentNullException(nameof(appDbContext));
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
Configurator = configurator.Value ?? throw new ArgumentNullException(nameof(configurator));
}
}
}
FileProcessingService.cs
namespace BusinessService.Services
{
public interface IFileProcessingService
{
void Process(Stream myBlob, string name);
}
public class FileProcessingService : ServiceBase<FileProcessingService>, IFileProcessingService
{
#region Constructor
public FileProcessingService(AppDbContext appDbContext, ILogger<FileProcessingService> logger, IOptions<AppConfigurator> configurator)
: base(appDbContext, logger, configurator) { }
#endregion
#region Public Methods
public void Process(Stream myBlob, string name)
{
AppDbContext.FileRecords.Add(new FileRecords
{
FileName = name,
IsCompleted = DefaultValues.IsCompleted
});
AppDbContext.SaveChanges();
Logger.LogInformation("Reading configuration from the configuration settings file: {Configurator.AzureSQLServerConfigurator.ConnnectionString}");
Logger.LogInformation("Database is updated..!");
}
#endregion
}
}
Line#34 and #35 doesn't Log anything
Logger.LogInformation("Reading configuration from the configuration settings file: {Configurator.AzureSQLServerConfigurator.ConnnectionString}");
Logger.LogInformation("Database is updated..!");
DependencyRegistrar.cs
namespace CrossCutting.DependencyInjection
{
public static class DependencyRegistrar
{
public static void Intialize(this IServiceCollection services)
{
// Initialize App Settings from Configurator Settings Json file
services.AddOptions<AppConfigurator>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection("AppConfigurator").Bind(settings);
})
.Validate((c) =>
{
return !new[] { c.AzureSQLServerConfigurator.ConnnectionString }.Any(s => String.IsNullOrWhiteSpace(s));
});
}
}
}
What am I missing so that FileProcessingService.cs will log the information ?
I have checked your code in our end I could get the logging information. In your code i have noticed in your code you are using the Logger instead of logger.
Because in your Region Constructor you are using ILogger<FileProcessingService> logger from here you have to call the logger to push your logging information into Application Insights/Function execution panel (Output console window)
#region Constructor
public FileProcessingService(AppDbContext appDbContext, ILogger<FileProcessingService> logger, IOptions<AppConfigurator> configurator)
: base(appDbContext, logger, configurator) { }
#endregion
#region Public Methods
public void Process(Stream myBlob, string name)
{
AppDbContext.FileRecords.Add(new FileRecords
{
FileName = name,
IsCompleted = DefaultValues.IsCompleted
});
AppDbContext.SaveChanges();
# changed Logger into logger
logger.LogInformation("Reading configuration from the configuration settings file: {Configurator.AzureSQLServerConfigurator.ConnnectionString}");
logger.LogInformation("Database is updated..!");
}
#endregion
Still if not able to push the logs you can add your namespace in your host.json file to avoid missing the logging
{
"version": "2.0",
"logging": {
"logLevel": {
// Here you can use your Project namespace like BusinessService.Services
"<namespace>": "Information"
}
}
}
I have the following Startup class in my Azure Function v2 project:
[assembly: FunctionsStartup(typeof(AzureAppDomainRegistration.Startup))]
namespace AzureAppDomainRegistration
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var connString = System.Configuration.ConfigurationManager.AppSettings["ConnectionStrings:DataContext"];
//var connString = config["ConnectionStrings:DataContext"];
builder.Services.AddDbContext<DataContext>(options => options
.UseLazyLoadingProxies()
.UseSqlServer(connString));
builder.Services.AddTransient<IActionsRegistrationInfo, EfActionsRegistrationInfo>();
}
}
}
and Function:
public class Function100_CheckEmail
{
readonly IActionsRegistrationInfo _actionsRegistrationInfo;
public Function100_CheckEmail(IActionsRegistrationInfo actionsRegistrationInfo)
{
_actionsRegistrationInfo = actionsRegistrationInfo;
}
[FunctionName("Function100_CheckEmail")]
//public static IActionResult Run(
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
[Queue("email-message-admin-confirmation", Connection = "StorageConnectionString")]CloudQueue outputQueue,
ExecutionContext context,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
but when this function is being executed, I get the following errors on Azure Portal:
without DI it works fine. What is wrong?
.NET Core 2.2
ADDED:
I tried to remote debugger and I see, that Configure method of Startup file has exceptions (logged by App Insights) with ArgumentNullException, but no details. What can be it?
So in order to get a Environment Variable in Azure functions, you need to use
var connStr = Environment.GetEnvironmentVariable("ConnectionStrings:SQLConnectionString", EnvironmentVariableTarget.Process);
Also check if you have the value in local.settings.json which looks something like this
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "<connection string>",
"AzureWebJobsDashboard": "<connection string>"
},
"Host": {
"LocalHttpPort": 7071,
"CORS": "*"
},
"ConnectionStrings": {
"SQLConnectionString": "Value"
}
}
Refereces :
MSFT Docs
A good article