Issue with .netcore 2.1 logging to Azure Blobs - azure

I have a .netcore 2.1 api. I am trying to do some logging in Azure Blobs.
I have been using Microsoft.Extensions.Logging.AzureAppServices(version 2.2.0) in .netcore 2.2 api's with no issue and it logs information to the Azure blobs beautifully.
But when I try the same code with Microsoft.Extensions.Logging.AzureAppServices (version 2.1.1) in .netcore 2.1 it fails to log the information (even though I get the desired out from the API endpoint).
Code that I have tried,in program.cs
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Main() method.");
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.AddAzureWebAppDiagnostics();
})
.ConfigureServices(serviceCollection => serviceCollection
.Configure<AzureFileLoggerOptions>(options =>
{
options.FileName = "azure-diagnostics-";
options.FileSizeLimit = 50 * 1024;
options.RetainedFileCountLimit = 5;
}).Configure<AzureBlobLoggerOptions>(options =>
{
options.BlobName = "Log.txt";
}))
.UseStartup<Startup>();
}
And in azure I have added the settings as in the image below.
Now to test it I had added some logging in the starup.cs and program.cs and controller.cs.
Logs from Startup.cs is being printed into the output blobs, but the other cs files are unable to log the information into the blob.
Anybody has any idea what I am doing wrong?

To configure provider settings, use AzureFileLoggerOptions and AzureBlobLoggerOptions, as shown in the following example:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(logging => logging.AddAzureWebAppDiagnostics())
.ConfigureServices(serviceCollection => serviceCollection
.Configure<AzureFileLoggerOptions>(options =>
{
options.FileName = "azure-diagnostics-";
options.FileSizeLimit = 50 * 1024;
options.RetainedFileCountLimit = 5;
}).Configure<AzureBlobLoggerOptions>(options =>
{
options.BlobName = "log.txt";
}))
.UseStartup<Startup>();
or Alternatively like below:
public void ConfigureServices(IServiceCollection services)
{
//...
services.Configure<AzureFileLoggerOptions>(Configuration.GetSection("AzureLogging"));
}
Appsettings.json
"AzureLogging": {
"FileName" : "azure-diagnostics-",
"FileSizeLimit": 50024,
"RetainedFileCountLimit": 5
}
When you deploy to an App Service app, the application honors the settings in the App Service logs section of the App Service page of the Azure portal. When the following settings are updated, the changes take effect immediately without requiring a restart or redeployment of the app.
Application Logging (Filesystem)
Application Logging (Blob)
Please ensure to have setting like above.Hope it helps.

Related

AspNetCoreRateLimit on Azure

I am struggling with getting the package AspNetCoreRateLimit version 4.0.2 to work when hosting a Blazor WebAsssembly project in Azure.
The solution has been developed in Visual studio 2022 and is made up of 5 individual projects, where one of them is a standard API project. It is based on the net6.0 framework. I have a startup.cs configuration and a RateLimitingMiddleware class used to setup the rate limits and the configuration.
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddRateLimiting();
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(365);
});
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
}, ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddAutoMapper(typeof(Startup));
....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRateLimiting();
if (env.IsDevelopment())
{
app.UseWebAssemblyDebugging();
app.UseSwagger();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseHsts();
...
}
RateLimitingMiddleware.cs
internal static class RateLimitingMiddleware
{
internal static IServiceCollection AddRateLimiting(this IServiceCollection services)
{
// Used to store rate limit counters and ip rules
services.AddOptions();
services.AddMemoryCache();
services.Configure<ClientRateLimitOptions>(options =>
{
options.EnableEndpointRateLimiting = true;
//options.RealIpHeader = "X-Real-IP";
options.ClientIdHeader = "authorization";
//options.EndpointWhitelist = new List<string> { "get:/_framework/*", "get:/_content/*", "*:/lib/*", "*:/css/*", "*:/js/", "*:/appsettings.json", "*:/images/" };
options.GeneralRules = new List<RateLimitRule>
{
new RateLimitRule
{
Endpoint="*:/api/*",
Period = "15m",
Limit=30
},
new RateLimitRule
{
Endpoint="*:/api/*",
Period = "12h",
Limit=20000
},
new RateLimitRule
{
Endpoint="*:/api/*",
Period = "7d",
Limit=1000000
}
};
});
services.AddInMemoryRateLimiting();
// Inject Counter and Store Rules
services.AddSingleton<IClientPolicyStore, MemoryCacheClientPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
//services.AddSingleton<IRateLimitConfiguration, CustomRateLimitConfiguration>();
//
// Return the services
return services;
}
internal static IApplicationBuilder UseRateLimiting(this IApplicationBuilder app)
{
var ipPolicyStore = app.ApplicationServices.GetRequiredService<IIpPolicyStore>();
ipPolicyStore.SeedAsync().GetAwaiter().GetResult();
var clientPolicyStore = app.ApplicationServices.GetRequiredService<IClientPolicyStore>();
clientPolicyStore.SeedAsync().GetAwaiter().GetResult();
app.UseClientRateLimiting();
app.UseIpRateLimiting();
return app;
}
}
With the above configuration the api calls are being limited when testing localhost and using Postman. However when the api is uploaded to our Azure environment the rules are not being implemented correctly.
As stated in the rule defined in the middelware I would like to use the authorization token to limit the number of count the number of requests but cannot get it to work.
I hope that someone has dealt with the same issue and can see where I am going wrong?
Thanks,
Problem solved. Wrong ratelimitheader. Should be CLIENT-IP.

How to retrieve Azure App Configuration Service settings while in Program.cs

I am using Asp.NET Core 5 (v5.0.6) web API deployed as an Azure App Service and I am migrating from using appsettings.json in the API to integrating with Azure App Configuration service. I have the Azure App Configuration Service set up but the issue I am having is how to access values in my Azure App Configuration service for retrieving a connection string for database access while I am still in Program.cs.
Note that in the CreateHostBuilder() method I am examining the context.HostingEnvironment.IsDevelopment() environment variable and if it is "IsDevelopment", implying a local DEV, I am reading an Azure App Configuration Service connection string via User Secrets but if it is not a local DEV, than I rely on the Managed Identity and just pass in the endpoint value from appsettings.json.
The only values I want to get that are not in Azure App Configuration Service is the local DEV Azure App Configuration Service Connection string (from User Secrets) and the Azure App Configuration Service endpoint from Appsettings.json. All other settings should come from Azure App Configuration Service.
The problem I am trying to solve is how to access the values in Azure App Configuration Service, while still in Program.cs, to retrieve the connection string for access to the Azure SQL database I am using for logging.
In the code below, when I link the Azure App Configuration in the CreateHostBuilderMethod and call build, I expected the values in Azure App Configuration Service to then be available via the static Configuration property. However when I try to retrieve the connection string value, it is always null.
How can I correctly retrieve the values for properties in Azure App Configuration Service to use them in Program.cs?
Here is my Program.cs file;
public class Program
{
public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
.AddUserSecrets<Startup>()
.Build();
public static void Main(string[] args)
{
var host = CreateHostBuilder(args)
.Build();
var connectionString = Configuration["CoreApi:Settings:LoggingDb"]; //<-- This returns null
const string tableName = "ApiLogs";
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Filter.ByExcluding(Matching.FromSource("Microsoft.EntityFrameworkCore.Query"))
.WriteTo.MSSqlServer(
connectionString: connectionString,
sinkOptions: new MSSqlServerSinkOptions { TableName = tableName })
.CreateLogger();
// TODO Enable to debug any startup Serilog issues. Make sure to comment out for PROD
// Serilog.Debugging.SelfLog.Enable(msg =>
// {
// Debug.Print(msg);
// Debugger.Break();
// });
//var host = CreateHostBuilder(args)
// .Build();
try
{
Log.Information("Starting ConfirmDelivery API");
host.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "ConfirmDelivery API Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var settings = config.Build();
if (context.HostingEnvironment.IsDevelopment())
{
var connectionString = settings.GetConnectionString("AzureAppConfiguration");
config.AddAzureAppConfiguration(connectionString);
}
else
{
var endpoint = settings["AppConfigEndpoint"];
var credentials = new ManagedIdentityCredential();
config.AddAzureAppConfiguration(options =>
{
options.Connect(new Uri(endpoint), credentials);
});
}
}).ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Try the code below to get a config value:
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureAppConfiguration;
namespace myapp
{
class Program
{
static void Main(string[] args)
{
var configItemName = "";
var appConfigConnectionString = "";
var builder = new ConfigurationBuilder();
builder.AddAzureAppConfiguration(appConfigConnectionString);
var config = builder.Build();
Console.WriteLine(config[configItemName]);
}
}
}
Result :

Where in Azure Portal are the logger.Log statements?

I have a .NET Core WebAPI app. The app is deployed on Azure as an App Service.
In the code, I have enabled Application Insights like so
public static IWebHost BuildWebHost(string[] args) =>
WebHost
.CreateDefaultBuilder(args)
.UseApplicationInsights()
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")).SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Error);
logging.AddApplicationInsights(" xxxxx-xxxx-xxxx-xxxx--xxxxxxxxxxx").SetMinimumLevel(LogLevel.Trace);
})
.Build();
In the constructor of a controller and inside an method of a controller I have these logging statements.
_logger.LogInformation("ApiController, information");
_logger.LogWarning("ApiController, warning");
_logger.LogCritical("ApiController, critical");
_logger.LogWarning("ApiController, error");
_logger.LogDebug("ApiController, debug");
In the Azure Portal I have Application Insights for my App Service enabled. Here a picture from the portal.
App Insights in Azure Portal
But where do I see the logging statements in the Azure portal?
When I go to Application Insights -> Logs and I query by
search *
I can see the requests made to the API but not the logging statements.
Application Insights Log
Where are the log statements?
First, it is not good practice to configure the log level in code. You can easily configure the log level in the appsettings.json file. So in Program.cs -> public static IWebHost BuildWebHost method, change the code to below:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseApplicationInsights()
.UseStartup<Startup>()
.Build();
Then in appsettings.json(also right click the file -> properties -> set "copy to output directory" to "Copy if newer"):
{
"Logging": {
"IncludeScopes": false,
"ApplicationInsights": {
"LogLevel": {
"Default": "Trace"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
},
"ApplicationInsights": {
"InstrumentationKey": "the key"
}
}
In Controller, like ValuesController:
public class ValuesController : Controller
{
private readonly ILogger _logger;
public ValuesController(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<ValuesController>();
}
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
_logger.LogInformation("ApiController, information");
_logger.LogWarning("ApiController, warning");
_logger.LogCritical("ApiController, critical");
_logger.LogWarning("ApiController, error");
_logger.LogDebug("ApiController, debug");
return new string[] { "value1", "value2" };
}
}
Run the project, and wait for a few minutes(application insights would always take 3 to 5 minutes or more to display the data). Then nave to azure portal -> application insights logs, remember that all the logs written by ILogger are stored in "traces" table. Just write the query like "traces" and specify a proper time range, you should see all the logs like below:

How to use Application settings instead of screts.json when deployed to Azure

I use the secrets.json file to store credentials for my Blazor .Net Core 3.0 Web App and use IConfiguration to get the values like this
public Startup(IConfiguration configuration)
{
Configuration = configuration;
var username = Configuration["Account:Username"];
var password = Configuration["Account:Password"];
new Account(username, password);
}
public IConfiguration Configuration { get; }
Which works fine on my machine in Development, but when I switch to Release in my app Properties > Debug > Environment variables > ASPNETCORE_ENVIRONMENT, the secrets.json doesn't get loaded. Same when I upload the app to Azure, where I have set up the variables as Application settings, it doesn't work. What am I missing here? How can I set up the Web App in Production on Azure, so that I can use the credentials?
edit:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});

How do I need to log to see my logs in App Service logs

I have enabled logging to Application logging to both FileSystem and Blob. I log messages using ILogger<T>.LogInformation() and Trace.WriteLine(). None of those are visible in blob. I also cannot find them on filesystem. I also can't see them when I enable log streaming.
Do I need to configure something in my ASP.NET Core application?
The [Microsoft.Extensions.Logging.AzureAppServices][1] provider package writes logs to text files in an Azure App Service app's file system and to blob storage in an Azure Storage account.
The provider package isn't included in the shared framework. To use the provider, add the provider package to the project.
To configure provider settings, use AzureFileLoggerOptions and AzureBlobLoggerOptions, as shown in the following example:
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
var todoRepository = host.Services.GetRequiredService<ITodoRepository>();
todoRepository.Add(new Core.Model.TodoItem() { Name = "Feed the dog" });
todoRepository.Add(new Core.Model.TodoItem() { Name = "Walk the dog" });
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Seeded the database.");
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging => logging.AddAzureWebAppDiagnostics())
.ConfigureServices(serviceCollection => serviceCollection
.Configure<AzureFileLoggerOptions>(options =>
{
options.FileName = "azure-diagnostics-";
options.FileSizeLimit = 50 * 1024;
options.RetainedFileCountLimit = 5;
}).Configure<AzureBlobLoggerOptions>(options =>
{
options.BlobName = "log.txt";
})
)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
You can refer below link for additional reference:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1#azure-app-service-provider
Hope it helps.
Here is a simple way(the referred article is here):
I tested it with .NET core 2.2 mvc project.
The necessary nuget package:
Microsoft.Extensions.Logging.AzureAppServices, version 2.2.0
1.In Startup.cs -> ConfigureServices method, add this line of code:
services.AddSingleton<ILoggerFactory>(new LoggerFactory());
In Startup.cs -> Configure method, change it looks like below:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//add the following code here
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddAzureWebAppDiagnostics(
new AzureAppServicesDiagnosticsSettings
{
OutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss zzz} [{Level}] {RequestId}-{SourceContext}: {Message}{NewLine}{Exception}"
}
);
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
then add following code in the HomeController.cs:
private readonly ILogger _logger;
public HomeController(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<HomeController>();
}
public IActionResult Index()
{
_logger.LogInformation("This is a Log information!!!");
_logger.LogError("This is a Logger error!!!");
return View();
}
2.After publish to azure, in azure portal -> the web app -> App service logs, set the blob storage. The screenshot as below:
3.Run the web, and check the logs in the specified blob storage:

Resources