From where is the InstrumentationKey read?
context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
I have put that key in the applicationInsights.config in the section
<InstrumentationKey>94efb022-e651-46a0-b103-5735daa213f1</InstrumentationKey>
but its not taken from there...
var builder = new HostBuilder()
.UseEnvironment("Development")
.ConfigureWebJobs(b =>
{
// Add extensions and other WebJobs services
})
.ConfigureAppConfiguration(b =>
{
// Add configuration sources
})
.ConfigureLogging((context, b) =>
{
// Add Logging Providers
b.AddConsole();
// If this key exists in any config, use it to enable App Insights
string appInsightsKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(appInsightsKey))
{
// This uses the options callback to explicitly set the instrumentation key.
b.AddApplicationInsights(o => o.InstrumentationKey = appInsightsKey);
}
})
.UseConsoleLifetime();
If you want to read it on Azure just set it in the Application Settings in the Portal.
And if you are running it locally, in the appsettings.json file add APPINSIGHTS_INSTRUMENTATIONKEY field there.
{
"AzureWebJobsStorage": "{storage connection string}",
"APPINSIGHTS_INSTRUMENTATIONKEY": "{instrumentation key}"
}
Further more information, refer to this doc :Add Application Insights logging. Hope this could help you.
You need to install below packages:
Microsoft.Azure.WebJobs.Logging.ApplicationInsights (Currently in beta)
Microsoft.Extensions.Logging
Microsoft.Extensions.Logging.Console
and Configure JobHostConfiguration as below:
string instrumentationKey = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
if (!string.IsNullOrEmpty(instrumentationKey))
{
// build up a LoggerFactory with ApplicationInsights and a Console Logger
config.LoggerFactory = new LoggerFactory().AddApplicationInsights(instrumentationKey, null).AddConsole();
config.Tracing.ConsoleLevel = TraceLevel.Off;
}
You can read about configuration application insight with Azure web job here. Hope it helps.
Related
I am using ASP.NET Core to make a web application that also uses SignalR Core to provide real time functionality. I use Azure AD B2C for user management. I have successfully used Microsoft.Identity.Web (https://github.com/AzureAD/microsoft-identity-web) to secure my API endpoints using tokens generated by Azure AD B2C.
I would like to do the same for my SignalR Core hubs. The documentation reads to add the appropriate annotation to your hubs/methods, which I have done. SignalR's client side library adds the access token as a query parameter which must be extracted and added to the context manually in the configuration of your ASP.NET core application, like so.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/hubs/chat")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
However, this seems to be incompatible with the configuration supplied by Microsoft.Identity.Web, here:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"));
How can I make SignalR work with Microsoft.Identity.Web?
That should do it:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(configuration);
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
Func<MessageReceivedContext, Task> existingOnMessageReceivedHandler = options.Events.OnMessageReceived;
options.Events.OnMessageReceived = async context =>
{
await existingOnMessageReceivedHandler(context);
StringValues accessToken = context.Request.Query["access_token"];
PathString path = context.HttpContext.Request.Path;
// If the request is for our hub...
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
{
// Read the token out of the query string
context.Token = accessToken;
}
};
});
Instead of adding a JwtBearer, you can configure the JwtBearerOptions object this way.
Adapted from this documentation: https://github.com/AzureAD/microsoft-identity-web/wiki/customization
You can use Visual Studio to set up the SignalR connection, and then just add this line in startup.cs (VS might add it automatically)
services.AddSignalR().AddAzureSignalR();
This dev sample has SignalR set up, just the connection string is missing, but might give you an idea of what to do. Most of this was done automatically with VS. If you have issues setting it up, please open an issue in the repo. thanks.
I am using Azure App Configuration Store to store configuration. I am using the following code in startup.cs to load my config from Azure.
var builder = new ConfigurationBuilder();
builder.AddAzureAppConfiguration(options =>
{
options.Connect(this.Values.AppConfigConnectionString);
options.Select(keyFilter: KeyFilter.Any, labelFilter: this.Values.Env);
});
var config = builder.Build();
Now this config variable contains my queue names. I need this dynamic so to create and handle it in 4 different environments. Dev / Stage / QA / Prod.
public async Task Run(
[QueueTrigger("%QueueName%", Connection = "StorageConnection")]VoiceHubEvent item)
This isn't working as my local.settings.json file doesn't contain QueueName entry.
Is it possible to make use of config variable in Run() to resolve queuename? By reloading queue trigger function or something?
Thanks,
Kiran.
Is it possible to make use of config variable in Run() to resolve queuename? By reloading queue trigger function or something?
Yes, you can.
Create an extensions method for the IWebJobsBuilder interface to set up a connection to AzureAppConfiguration.
public static IWebJobsBuilder AddAzureConfiguration(this IWebJobsBuilder webJobsBuilder)
{
//-- Get current configuration
var configBuilder = new ConfigurationBuilder();
var descriptor = webJobsBuilder.Services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration));
if (descriptor?.ImplementationInstance is IConfigurationRoot configuration)
configBuilder.AddConfiguration(configuration);
var config = configBuilder.Build();
//-- Add Azure Configuration
configBuilder.AddAzureAppConfiguration(options =>
{
var azureConnectionString = config[TRS.Shared.Constants.CONFIGURATION.KEY_AZURECONFIGURATION_CONNECTIONSTRING];
if (string.IsNullOrWhiteSpace(azureConnectionString)
|| !azureConnectionString.StartsWith("Endpoint=https://"))
throw new InvalidOperationException($"Missing/wrong configuration value for key '{TRS.Shared.Constants.CONFIGURATION.KEY_AZURECONFIGURATION_CONNECTIONSTRING}'.");
options.Connect(azureConnectionString);
});
//build the config again so it has the key vault provider
config = configBuilder.Build();
return webJobsBuilder;
}
Where the azureConnectionString is read from you appsetting.json and should contain the url to the Azure App Configuration.
In startup.cs:
public void Configure(IWebJobsBuilder builder)
{
builder.AddAzureConfiguration();
ConfigureServices(builder.Services)
.BuildServiceProvider(true);
}
For more details, you could refer to this SO thread.
Previously when I've created Azure webjobs in .NET, it has automatically configured logging to a text file on Azure storage on path /azure-webjobs-hosts/output-logs/.txt. However now when I'm working with .NET Core and version 3.0.14 of the Webjobs SDK, it no longer has this behavior by default.
Logging to console and also Application Insights works well with my current setup, however I do want text file logging on Azure Storage as well via the binded ILogger. I've followed official guides on how to configure it, but it all points to storage queues and I'm not sure that is a good approach and how to tweak it.
Configuration code:
static async Task Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
builder.UseEnvironment("development");
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
string instrumentationKey = context.Configuration["ApplicationInsightsKey"];
if (!string.IsNullOrEmpty(instrumentationKey))
{
b.AddApplicationInsightsWebJobs(o => o.InstrumentationKey = instrumentationKey);
}
});
builder.ConfigureServices(s => s.AddSingleton<IConfigurationRoot>(config));
var host = builder.Build();
using (host)
{
var jobHost = host.Services.GetService(typeof(IJobHost)) as JobHost;
await host.StartAsync();
await jobHost.CallAsync("Run");
await host.StopAsync();
}
}
Job code:
[NoAutomaticTrigger]
public async Task Run(ILogger logger)
{
logger.LogInformation("I want this text in console as well as on Azure Storage");
// ..logic
}
If you want to save the logs(via ILogger) to blob storage, you need to publish the webjobs to azure. You can follow the steps below:
1.Add b.AddAzureWebAppDiagnostics(); method(need install the nuget package Microsoft.Extensions.Logging.AzureAppServices) into builder.ConfigureLogging code block:
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
b.AddAzureWebAppDiagnostics();
//other code
});
2.Publish it to azure, then in azure portal -> the related app service -> App Service Logs, configure "Application Logging(blob)", screenshot as below:
3.After running the webjob, nav to the blob storage which is configured in step 2, you can see a new log file is generated, and all the logs(includes logs via ILogger) are in the file.
I do want text file logging on Azure Storage as well via the binded ILogger.
The Serilog.Sinks.AzureBlobStorage can help write logs to Azure Blob Storage, and which would work well both on azure and local.
In Program.cs
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var AzureBlobStorageLogger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
b.AddSerilog(logger: AzureBlobStorageLogger);
});
In appsettings.json
{
"Serilog": {
"Using": [
"Serilog.Sinks.AzureBlobStorage"
],
"WriteTo": [
{
"Name": "AzureBlobStorage",
"Args": {
"connectionString": "{your_connectionString_here}",
"storageContainerName": "azure-webjobs-hosts",
"storageFileName": "output-logs/{yyyy}/{MM}/{dd}/log.txt"
}
}
]
},
//other settings
//...
}
Besides, as #IvanYang mentioned, you can also achieve it by adding Azure diagnostics logger, but this provider only works when the project runs in the Azure environment. It does not write logs to Azure Blob Storage when we run webjob on local.
I'm building .Net Core background service, using ApplicationInsights.WorkerService nuget package. The documentation regarding sampling configuration says to refer to this:
https://learn.microsoft.com/en-us/azure/azure-monitor/app/sampling#configure-sampling-settings
And it shows this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, TelemetryConfiguration configuration)
{
var builder = configuration.DefaultTelemetrySink.TelemetryProcessorChainBuilder;
// For older versions of the Application Insights SDK, use the following line instead:
// var builder = configuration.TelemetryProcessorChainBuilder;
// Using adaptive sampling
builder.UseAdaptiveSampling(maxTelemetryItemsPerSecond:5);
// Alternately, the following configures adaptive sampling with 5 items per second, and also excludes DependencyTelemetry from being subject to sampling.
// builder.UseAdaptiveSampling(maxTelemetryItemsPerSecond:5, excludedTypes: "Dependency");
// If you have other telemetry processors:
builder.Use((next) => new AnotherProcessor(next));
builder.Build();
// ...
}
Now on HostBuilder I don't see any extension methods that would give me the TelemetryConfiguration, source code of the nuget doesn't have it either:
https://github.com/microsoft/ApplicationInsights-aspnetcore/blob/develop/NETCORE/src/Microsoft.ApplicationInsights.WorkerService/ApplicationInsightsExtensions.cs
So how do I get either TelemetryConfiguration or TelemetryProcessorChainBuilder on a HostBuilder? At the moment it looks like this:
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
services.AddApplicationInsightsTelemetryWorkerService();
});
You should use it as below:
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
services.Configure<TelemetryConfiguration>((config)=>
{
var builder = config.DefaultTelemetrySink.TelemetryProcessorChainBuilder;
builder.UseAdaptiveSampling(maxTelemetryItemsPerSecond: 5);
builder.Build();
}
);
// Your other code
});
I'm trying to develop WebJob using SDK 3.0.x, and testing it locally. I've followed the sample in github without any success.
When running it locally everything is going ok, it also see the ProcessQueueMessage function but it doesn't pick the messages from the queue.
Program.cs
static void Main(string[] args)
{
var builder = new HostBuilder();
//builder.UseEnvironment(EnvironmentName.Development);
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
});
builder.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
// If the key exists in settings, use it to enable Application Insights.
string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(instrumentationKey))
{
b.AddApplicationInsights(o => o.InstrumentationKey = instrumentationKey);
}
});
builder.ConfigureServices((context, services) =>
{
//services.AddSingleton<IJobActivator, MyJobActivator>();
services.AddScoped<Functions, Functions>();
services.AddSingleton<IHostService, HostService>();
})
.UseConsoleLifetime();
var host = builder.Build();
using (host)
{
host.Run();
}
}
Functions.cs
public class Functions
{
private readonly IHostService _hostService;
public Functions(IHostService hostService)
{
_hostService = hostService;
}
// This function will get triggered/executed when a new message is written
// on an Azure Queue called queue.
public void ProcessQueueMessage([QueueTrigger("newrequests")] string dd,
//DateTimeOffset expirationTime,
//DateTimeOffset insertionTime,
//DateTimeOffset nextVisibleTime,
//string queueTrigger,
//string id,
//string popReceipt,
//int dequeueCount,
ILogger logger)
{
var newRequestItem = new RequestQueueItem();
logger.LogTrace($"New queue item received...");
//logger.LogInformation($" QueueRef = {id} - DequeueCount = {dequeueCount} - Message Content [Id = {newRequestItem.Id}, RequestDate = {newRequestItem.RequestDate}, Mobile = {newRequestItem.Mobile}, ProviderCode = {newRequestItem.ProviderCode}, ItemIDClass = {newRequestItem.MappingIDClass}]");
// TODO: Read the DatabaseConnectionString from App.config
logger.LogTrace($" Getting DB ConnectionString...");
var connectionString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString;
// TODO: Initiation of provider service instance
logger.LogTrace($" Init IalbayanmtnclientserviceClient service instance...");
var bayanService = new AlbayanMtnWCFService.IalbayanmtnclientserviceClient();
// TODO: sending request to provider service endpoint and wait for response
logger.LogTrace($" Sending request to Service Endpoint...");
var response= bayanService.requestpaymenttransactionAsync("agentcode", "agentpassword", "accountno", int.Parse(newRequestItem.TransactionType), newRequestItem.MappingIDClass, newRequestItem.Mobile, (int)newRequestItem.Id).Result;
logger.LogTrace($"Done processing queue item");
}
}
Here is the screen shot for the output
Appreciate your help
Screen shot for queue messages 'newrequests'
enter image description here
From your snapshot, your webjob runs well on local. It didn't pick message because you don't add message in the newrequests queue.
The function only be triggered after you add the message. Or I will get the same result just like yours.
About the tutorial , your could refer to the official doc:Get started with the Azure WebJobs SDK. And make sure you set the right storage account. The below is my appsettings.json. Make sure the "Copy to output directory" property of the appSettings.json file is set to either Copy if newer or Copy always. Or it will run into exception:Storage account 'Storage' is not configured.
{
"ConnectionStrings": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=mystorage;AccountKey=key;..."
}
}
Hope this could help you, if you still have other questions, please let me know.