In an Azure Function project is there a way to get a reference to ILogger inside the Configure() method of Startup.cs?
(I need to log some initialization steps that happen during the configuration hook)
public class StartUp : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
//get reference to ILogger Here
}
}
You can make use of the LoggerFactory to create an Instance of Ilogger in your startup. Here's an working example for you.
public class Startup : FunctionsStartup
{
private ILoggerFactory _loggerFactory;
public override void Configure(IFunctionsHostBuilder builder)
{
var config = new ConfigurationBuilder()
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
builder.Services.AddLogging();
ConfigureServices(builder);
}
public void ConfigureServices(IFunctionsHostBuilder builder)
{
_loggerFactory = new LoggerFactory();
var logger = _loggerFactory.CreateLogger("Startup");
logger.LogInformation("Got Here in Startup");
//Do something with builder
}
}
Related
My testing project has grown to include many AppHost classes and having to update them all when the project changes is duplicating work so I would prefer to use modular startup on them like I do with main project.
In main project I define modular startup like so:
WebHost.CreateDefaultBuilder(args)
.UseModularStartup<Startup>()
.Build();
But in my testing project I create the AppHost like this:
var appHost = new MyCustomAppHost()
.Init()
.Start(BaseUri);
and the apphost is defined like:
public class MyCustomAppHost : AppSelfHostBase
{
public MyCustomAppHost() : base(nameof(LocalProjectAppHost), typeof(MyServices).Assembly) { }
public override void Configure(Container container)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json")
.AddEnvironmentVariables()
.AddUserSecrets(typeof(MyProject.Startup).Assembly);
var configuration = builder.Build();
//config here...
}
}
Is there a way to get modular startup working with AppSelfHostBase? My goal is to be able to specify the modular config types per AppHost like so:
public class Startup : ModularStartup
{
public Startup(IConfiguration configuration)
: base(configuration, typeof(ConfigureRedisTesting), typeof(ConfigureCorsProduction), typeof(... etc){}
}
This way I can mix and match the config files I want for this specific testing apphost and will save me copy pasting all the configs into each apphost and having to maintain them separately.
You can replace the AppSelfHostBase IWebHostBuilder Configuration by overriding ConfigureHost(). Your Startup class will also need to what the existing AppSelfHostBase.Startup does, so a custom AppHost like this should work with ServiceStack's Modular Startup feature:
public class AppHost : AppSelfHostBase {
public AppHost() : base(nameof(AppHost), typeof(MyServices).Assembly) { }
public class Startup : ModularStartup {
public Startup(IConfiguration configuration) : base(configuration, serviceTypes){}
public void ConfigureServices(IServiceCollection services) {
HostInstance.Configuration = Configuration;
HostInstance.Configure(services);
}
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env) {
HostInstance.Configure(app, env);
HostInstance.Bind(app);
((AppHost)HostInstance).RealInit();
}
}
public override IWebHostBuilder ConfigureHost(IWebHostBuilder host, string[] urlBases) {
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json")
.AddEnvironmentVariables()
.AddUserSecrets(typeof(Startup).Assembly);
return host.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseWebRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseConfiguration(builder.Build())
.UseUrls(urlBases);
}
public override void Configure(Container container) {}
}
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();
}
}
}
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.
In Asp.Net.Core.v1 in the inherited DbContextClass I loaded connection string from appsettings.json like this:
private IConfigurationRoot _config;
public MainDbContext(IConfigurationRoot config, DbContextOptions options) : base(options)
{
_config = config;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(_config["Data:SQLConnectionString"]);
}
with all the config changes in v2, this is now a run-time error.
How do I load/use the SQL DB connection string in EFCore.v2 from appsettings.*.json ?
Follow the example at:
https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro
in sections
Create the Database Context
and
Register the context with dependency injection
Note: the name "ConnectionStrings" in JSON is significant
do it like this
private IConfigurationRoot _config;
public MainDbContext(IConfigurationRoot config, DbContextOptions options) : base(options)
{
_config = config;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_config["Data:SQLConnectionString"]);
}
simply remove: base.OnConfiguring(optionsBuilder);
I'm trying to use connection string from my json file by doing next steps
Json file
{
"ConnectionStrings": {
"PlatformDatabase": "Server=xxxx\\SQLEXPRESS;Database=xxxx;Trusted_Connection=True;"
}
}
Access to json
var builder = new ConfigurationBuilder()
.SetBasePath(System.AppContext.BaseDirectory)
.AddJsonFile("appsettings.json",
optional: true,
reloadOnChange: true);
Configuration = builder.Build();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("PlatformDatabase"));
Error: ConfigurationBuilder does not contain a definition for
AddJsonFile.
Does anyone have this problem before? I tried searching it but all the solution I found doesn't work now (i suppose with version 2).
EDIT
note. I created .Net Core 2.0 console application
The problem was solved by install the nuget package
Microsoft.Extensions.Configuration.Json
You do not need to explicit add appsettings.json in ASP.NET Core 2.
You just need the followings inside Startup.cs -
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(
Configuration.GetConnectionString("PlatformDatabase")));
...
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
Another Approach
You register IConfiguration as Singleton in DI container, and then inject it to your DBContext constructor. Then get the connection string inside OnConfiguring method.
public class Startup
{
public Startup(IConfiguration config)
{
_config = config;
}
private IConfiguration _config;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_config);
services.AddDbContext<YOUR_DB_Context>(ServiceLifetime.Scoped);
...
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
}
}
YOUR_DB_Context.cs
public class YOUR_DB_Context : IdentityDbContext OR DbContext
{
private IConfiguration _config;
public YOUR_DB_Context(DbContextOptions options, IConfiguration config)
: base(options)
{
_config = config;
}
protected override void OnModelCreating(ModelBuilder builder)
{
...
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(_config["Data:PlatformDatabase"]);
}
}