The function app runs locally but is deployed as a Linux container. The deployed function isn't reporting any issues in the portal and I can see the three functions listed in the Functions blade. However, none are working (one is a simple HTTP ping which is returning a 502 Bad Gateway response).
So no obvious issues in the portal, but inspection of logs reveals this recurring exception:
System.Threading.Tasks.TaskCanceledException at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess
The formatted message is:
Failed to start a new language worker for runtime: dotnet-isolated
My suspicion is that this is something to do with the value of the site setting linuxFxVersion. Mind you, I've tried using DOTNET|6.0 and DOTNET-ISOLATED|6.0. In both cases it doesn't seem to make a difference and anyway when I export the function app's ARM template it has linuxFxVersion set to the URI of the container image prefixed with DOCKER|.
That seems to relate to this specific advice from Microsoft about pinning the host version on Linux. But it still isn't clear to me which value I should us, and anyway, in another place the advice from another Microsoft document states:
To support zip deployment and running from the deployment package on Linux, you also need to update the linuxFxVersion site config setting to DOTNET-ISOLATED|6.0.
Anyway, Here are the details of my config. I've followed every bit of guidance from Microsoft so I hope someone can spot what I've missed...
First two parts of project file:
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>V4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<ImplicitUsings>true</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="3.0.9" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.3.0" OutputItemType="Analyzer" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.5.2" />
</ItemGroup>
host.json:
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
}
}
Main method in Program.cs:
public static async Task Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureAppConfiguration((context, configurationBuilder) =>
{
configurationBuilder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
if (context.HostingEnvironment.IsDevelopment())
{
configurationBuilder.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true);
}
else
{
configurationBuilder.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true);
}
configurationBuilder.AddEnvironmentVariables();
})
.ConfigureServices((context, collection) => ConfigureServices(collection, context.Configuration, context.HostingEnvironment))
.Build()
.RunAsync();
}
The function provisioning pipeline sets the following app settings:
FUNCTIONS_EXTENSION_VERSION: '~4'
FUNCTIONS_WORKER_RUNTIME: 'dotnet-isolated'
As stated above, the latest version of my site config defined during provisioning includes this:
linuxFxVersion: 'DOTNET-ISOLATED|6.0'
The Docker image uses mcr.microsoft.com/azure-functions/dotnet-isolated:4 as a base image for the published app, and mcr.microsoft.com/dotnet/sdk:6.0 to build it.
Please tell me there's something obvious I'm missing. We currently have two function apps, and neither can be deployed as dotnet-isolated. It's driving me mad!
The exception what you saw is not directly from your application, instead it is a general exception without detaills from an Azure Function runtime-process - so it's useless.
The reasons can be anything, for example: forget to await on a task.
public static async Task Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureAppConfiguration(builder =>
{
builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true)
.Build();
})
.ConfigureServices((context, collection) => ConfigureServices(collection, context.Configuration, context.HostingEnvironment))
.Build();
host.RunAsync(); // await host.RunAsync();
}
Or a corrupted appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning",
"Host": "Warning",
"Function": "Information",
"Host.Aggregator": "Information"
}
}
Or a missing configure call during startup.
public static async Task Main()
{
var host = new HostBuilder()
// .ConfigureFunctionsWorkerDefaults()
.ConfigureAppConfiguration(builder =>
{
builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true)
.Build();
}))
.ConfigureServices((context, collection) => ConfigureServices(collection, context.Configuration, context.HostingEnvironment))
.Build();
await host.RunAsync();
}
That runtime-process from Microsoft which launches your application captures all your console logs and trashes them in case of exceptions during its own startup.
Btw, you should also enable Code Analysis which is per default not enabled in .net 6.
After battling with this for 2 days I decide to strip right down to the bare bones. Followed the Microsoft guide from scratch. This led me to identify the following line as the problem:
configurationBuilder.AddEnvironmentVariables();
As soon as I add this line to my Program and push a new image to the function app, everything breaks upon restart. However, there's no indication as to why. The Docker logs exposed by Kudo report this as the final entry:
2022-05-18T14:06:46.295Z INFO - Container [image-name] for site [function-name] initialized successfully and is ready to serve requests.
App Insights traces indicate that an exception has occurred at line 52 of Program.cs, which is my host.Run(); line.
The exception itself is:
System.InvalidOperationException at Microsoft.Azure.WebJobs.Script.Workers.Rpc.RpcFunctionInvocationDispatcherLoadBalancer.GetLanguageWorkerChannel
And there's a message hidden in there saying "Did not find any initialized language workers" which I understand is Azure Function language for "something bad happened during startup but I'm not going to tell you what or why".
So this at least explains why my function wasn't running, and hopefully my experience will save someone else time in the future, but since my app depends on configuration added to the function by a pipeline I still don't have a working app. For that I will ask a new question and link it here...
Update
Here is the follow up question, which I've already answered!
Related
All attempts to expose a .NET 6 web API on port 8888 fail.
I tried these answers and various other articles online, by adding urls in appsettings.json and configuring the port in Program.cs without success.
Update:
The web API starts on the default port and not on the port I want to expose it to. When configuring ports in Program.cs, I receive
System.InvalidOperationException: 'Changing the URL is not supported
because Addresses IsReadOnly.'
app.Run("http://localhost:8888"); // This code results in Exception above
or
System.NotSupportedException: 'Collection was of a fixed size.'
app.Urls.Add("http://localhost:8888"); // The Exception above occurs after running this line of code
Running the code below doesn't change the port, and the API starts on the default port
builder.WebHost.ConfigureKestrel(options => options.ListenLocalhost(8888));
Adding the line builder.WebHost.ConfigureKestrel(options => options.ListenLocalhost(PortNumber));
to your Program.cs file should allow you to change the default port on a .NET 6 web API.
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options => options.ListenLocalhost(8888));
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
The above change should work also when you are debugging, but another way to change this in development as mentioned in the other answer, you can use the launchSettings.json file under the properties folder of the solution, documentation related to this file and how it works can be found here, an example of what you want would be:
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:31312",
"sslPort": 44346
}
},
"profiles": {
"ProfileNameHere": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:8887;http://localhost:8888",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
The profile section will affect kestrel ports in dev environment so that is the one you should be updating to the desired value. You can also create multiple profiles with different ports and configurations.
There are many solutions had been noted in this page, you can check https://andrewlock.net/5-ways-to-set-the-urls-for-an-aspnetcore-app/
For me,
When I use an IDE, I modify the file Properties\launchSettings.json
When I run the app in console, I add the param --urls "http://localhost:5100"
When I deploy it on IIS, well, it's not a question because IIS always ask me to enter the port. There is no default port
Note: If they don't work on your side, check there any mistake. Maybe you can restart your PC to make sure the port are available.
Update:
I have tried to run at port 8888 and everything is working well (I also use .NET 6)
Can you try this
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>).UseKestrel().UseUrls("http://0.0.0.0:8888");
});
}
This works for docker environment also. Make the port as variable and get it from environment as next step
I have the following code in my console application.
// Build configuration
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(ApplicationInfo.DataPath)
.AddJsonFile("appsettings.json", false, false)
.Build();
// Configure Serilog
string logFormat = "[{Timestamp:yyyy-MM-dd hh:mm:ss tt}][{Level:u3}] {Message:lj}{NewLine}{Exception}";
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.WriteTo.Console(LogEventLevel.Verbose, logFormat)
.WriteTo.File(ApplicationInfo.GetDataFileName("log"), LogEventLevel.Verbose, logFormat)
.CreateLogger();
AppHost = Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureServices((context, services) =>
{
services.AddDbContext<TTApplicationDbContext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
});
services.Configure<EmailSettings>(configuration.GetSection("EmailSettings"));
services.Configure<SftpSettings>(configuration.GetSection("FtpSettings"));
})
.Build();
This seems to be working. However, when I add the following section to my appsettings.json file, I do not get any of the verbose logging from within the framework. All I get is the logging that I specifically log in my application.
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information"
}
}
}
As best I can tell, Serilog is ignoring these settings. How can I change that?
Update:
If I change the Default value to Debug in appsettings.json, that does in fact control whether or not my own calls to LogDebug() show up. But I don't understand why none of Microsoft's logging shows up. Wouldn't Microsoft's code use whatever logger is configured?
While configuring a DotNet Core App, I had the same problem. I found that if I added the Nuget Package serilog-extensions-hosting, it will also start outputting the framework messages to the logs.
https://github.com/serilog/serilog-extensions-hosting
I hope this helps
I am using NET5 ASP.NET MVC application. Application is using Serilog.AspNetCore 3.4.0 for logging
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Error"
}
},
"Serilog": {
"Using": [ "Serilog.Sinks.Console"],
"WriteTo": [
{
"Name": "Console"
}
]
}
}
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
})
.UseSerilog((hostingContext, logging) =>
{
logging.ReadFrom.Configuration(hostingContext.Configuration);
});
});
I have also tried
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(hostingContext.Configuration)
.CreateLogger();
logging.AddSerilog();
});
});
Issue
My expectation is no Information log will be shown in Console since default LogLevel is Error.
However, that is not working. In console I see every request is getting logged including Information
Throughout my application I am using Microsoft.Extensions.Logging.ILogger<MyClassName> to log information. All those statements are actually logging Info even if the LogLevel is Error.
Looks like Serilog ignoring LogLevel from Microsoft.Extensions.Logging.
Note that, I can set serilog's restrictedToMinimumLevel property to Error and that stops logging information. However I think serilog should obey the LogLevel from Microsoft.Extension.Logging
Use MinimumLevel property:
"Logging": {
"MinimumLevel": {
"Default": "Error"
}
}
Also it supports overrides for categories:
"Logging": {
"MinimumLevel": {
"Default": "Error",
"Override": {
"System": "Information",
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore": "Debug"
}
}
}
If you check the project's Github page it advises to actually remove the standard "Logging" section in appsettings.json :-(
I think this is bad since it actually breaks the compatibility with Microsoft.Extensions.Logging framework (you can't anymore change the actual log provider (Log4Net, NLog, Serilog) without changing the appsettings.json).
If you use Serilog.Extensions.Logging and AddSerilog() on ILoggingBuilder, you'll get what you're expecting.
However, IHostBuilder.UseSerilog() (provided by Serilog.Extensions.Hosting, via Serilog.AspNetCore) is almost always a better choice, hence that's what all the Serilog docs show.
Although it seems more "integrated" to use the default configuration section this way, what you're getting behind the scenes is actually two different logging frameworks running at the same time, e.g. in the AddSerilog() case (or in the default NLog configuration):
ILogger<T> -->
MEL (routing, configuration, levels, filters) -->
Serilog (routing, configuration, levels, filters)
Logging is supposed to be very predictable/reliable and lightweight. Having two logging frameworks running at the same time erodes this and creates problems where the levels and filters specified for one framework don't match the other. (E.g. Debug and trace logs not printed when using Nlog and ILoggerFactory in .NetCore console app).
In the UseSerilog() case, (and when NLog's ReplaceLoggerFactory option is specified - mentioned in a comment on the SO thread above), you get:
ILogger<T> -->
Serilog (routing, configuration, levels, filters)
You give up support for the default "Logging" configuration section, but in exchange, you get rid of a whole logging framework at runtime, and all of your Serilog (or NLog) configuration just works, with the Microsoft.Extensions.Logging ILogger<T> available to your application code and the framework.
I have been following the tutorial (Bot Framework Integrate Composer Into Skill Project) to integrate composer dialogs with a skill project. This includes composer created dialogs which call a skill. I am running into the exception "Value cannot be null. (Parameter 'uriString')" when calling the skill.
I ran into various other issues when setting this up, as outlined here Previous Issues, which have been resolved though I had to do a work around to get the settings to work. I now need to determine why the configuration settings are not resolving.
To recreate the issue use the "skill-setting-issue" branch of the repo Git Repo
Prerequisites
Install Bot Emulator:Bot Emulator Install Instructions
Open the solution file in visual studio Integrate-Composer-Dialog-Using-Skill.sln
Put a breakpoint on line 79 of DefaultAdapter.cs - this is where the error can be seen
Start debugging the project
Open Bot Emulator
Connect to the bot: http://localhost:3978/api/messages
Type "Greeting"
Bot should respond with "Hello, I have recognized that you said greeting" - This means that the composer dialog integration is working as expected.
Type "Skill"
The breakpoint on line 79 of DefaultAdapter.cs should trigger giving details of the error.
The error seems to be occurring because the settings values within Composer-With-Skill.dialog between line 52 & 57 cannot be resolved.
"botId": "=settings.MicrosoftAppId",
"skillHostEndpoint": "=settings.skillHostEndpoint",
"connectionName": "=settings.connectionName",
"allowInterruptions": true,
"skillEndpoint": "=settings.skill['integrateComposerDialogUsingSkill'].endpointUrl",
"skillAppId": "=settings.skill['integrateComposerDialogUsingSkill'].msAppId",
If I replace the portions which use =settings.... with actual values then the application works (see master branch for code which has that setup).
e.g.
"botId": "=settings.MicrosoftAppId",
"skillHostEndpoint": "http://localhost:3978/api/skills",
"connectionName": "=settings.connectionName",
"allowInterruptions": true,
"skillEndpoint": "http://localhost:3978/api/echo/messages",
"skillAppId": "00000000-0000-0000-0000-000000000000",
Note that botId & connectionName are not used so do not cause runtime errors.
These values should be retrieved from ComposerDialogs\settings\appsettings.json line 67 through 79.
"skill": {
"integrateComposerDialogUsingSkill": {
"endpointUrl": "http://localhost:3978/api/echo/messages",
"msAppId": "00000000-0000-0000-0000-000000000000"
}
},
"defaultLanguage": "en-us",
"languages": [
"en-us"
],
"customFunctions": [],
"skillHostEndpoint": "http://localhost:3978/api/skills"
}
ComposerDialogs\settings\appsettings.json has been configured to be a setting file in the application in Startup.cs line 51 as per the tutorial I am following.
public class Startup
{
public Startup(IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("cognitivemodels.json", optional: true)
.AddJsonFile($"cognitivemodels.{env.EnvironmentName}.json", optional: true)
//from instructions: https://microsoft.github.io/botframework-solutions/skills/handbook/experimental-add-composer/
.AddJsonFile($"ComposerDialogs\\settings\\appsettings.json", optional: true)
.AddEnvironmentVariables();
I'm not sure why the variables in the Composer-With-Skill.dialog file are not being resolved by the settings values in ComposerDialogs\settings\appsettings.json
The error occurs on line 156 of file Microsoft.Bot.Builder.Dialogs.Adaptive.BeginSkill in method BeginDialogAsync
Bot Builder Git - BeginSkill.cs when the code tries to populate the DialogOptions.SkillHostEndpoint value.
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default)
{
if (Disabled != null && Disabled.GetValue(dc.State))
{
return await dc.EndDialogAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
// Update the dialog options with the runtime settings.
DialogOptions.BotId = BotId.GetValue(dc.State);
DialogOptions.SkillHostEndpoint = new Uri(SkillHostEndpoint.GetValue(dc.State));
DialogOptions.ConversationIdFactory = dc.Context.TurnState.Get<SkillConversationIdFactoryBase>() ?? throw new NullReferenceException("Unable to locate SkillConversationIdFactoryBase in HostContext");
DialogOptions.SkillClient = dc.Context.TurnState.Get<BotFrameworkClient>() ?? throw new NullReferenceException("Unable to locate BotFrameworkClient in HostContext");
DialogOptions.ConversationState = dc.Context.TurnState.Get<ConversationState>() ?? throw new NullReferenceException($"Unable to get an instance of {nameof(ConversationState)} from TurnState.");
DialogOptions.ConnectionName = ConnectionName.GetValue(dc.State);
Edit: Putting the value, "skillHostEndpoint": "http://localhost:3978/api/skills", in the main appsettings.json file causes the code to work.
Edit: moving ComposerDialogs/settings/appsettings.json up to the main folder & renaming to appsettings-composer.json & adjusting the startup.cs does not help
Edit: removing .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) does not prevent the app settings from being available in BeginSkill.cs. So, this seems to indicate that configuring the builder in Startup.cs has no impact on the available settings.
Determined that the IConfiguration instance which was configured & created in the constructor of Startup.cs was not being used when dependency injection was happening on IConfiguration. This was causing the new configuration files added not to be available.
Added
services.AddSingleton<IConfiguration>(Configuration);
To ConfigureServices of Startup.cs so that the correct object would be retrieved & the values would be available.
skill-setting-issue branch of the Git Repo has been updated to reflect this.
This Post helped to determine the issue.
In .NET Core 2 Web API app, I could override configuration urls using appsettings.json, but in the official docs they introduced extra file "hosting.json", Why? What's the point of adding complexity?
Below code is fully working using appsettings.json:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) //see Side note below
.AddJsonFile("appsettings.json", optional: true)
.AddCommandLine(args)
.Build();
return WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
}
}
appsettings.json content:
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
},
"urls": "http://*:5005/"
}
Side note:
Commenting .SetBasePath(Directory.GetCurrentDirectory()) will keep VS 2017 debug mode operational (means apply launchSettings.json, and auto launch url) otherwise it won't. I guess its related to the CreateDefaultBuilder implementation.
I think, hosting.json is a configuration file used specifically for asp.net core application hosting. (if you know more about hosting)
WebHostBuilder directly maps its keys with the hosting.json file and it doesn't have the capability to load the config section as we do in normal configuration settings.
According to link attached in your post
Use Configuration to configure the host. In the following example,
host configuration is optionally specified in a hosting.json file. Any
configuration loaded from the hosting.json file may be overridden by
command-line arguments.
If only we explicitly us hosting.json then the WebHostBuilder configurations can be modified using dotnet command.
for example
dotnet run --urls "http://*:8080"
this will override the urls from hostings.json file.
Hopefully, this may give some idea.
PC: hosting.json can be renamed like myappsettings.json it can
have configuration and Web Host Builder configuration.