Serilog import log in a class - .net-core

0 console APP using serilog for logging
using var log = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("myapp.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
Console.WriteLine("Hello, World!");
log.Information("Hello, Serilog!");
Prova p = new Prova();
p.ciao();
I want to use log in a new class Prova. How can i do that without reconfigure serilog?

Related

Get an ILogger instance for App Insights from ServiceCollection in a WebForms .NET Framework application

We want to get an ILogger instance so that it can be passed to other library.
We tried below, but the ILogger instance does not log into Application Insights. It logs into Event Viewer successfully.
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(builder => builder
.AddFilter("Default", LogLevel.Information)
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddEventLog(config => { config.LogName = "Pages"; config.SourceName = "Pages"; })
.AddApplicationInsights(telemetry =>
telemetry.ConnectionString = "my-key",
options => options = new ApplicationInsightsLoggerOptions()));
var serviceProvider = serviceCollection.BuildServiceProvider();
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("my-logger");
logger.LogInformation("Hi");
We have added the necessary packages i.e. Microsoft.Extensions.Logging and Microsoft.Extensions.Logging.ApplicationInsights
Is there no way, we can get an ILogger instance from ServiceCollection for AppInsights?
If you are using visual studio then you can configure the application Insights directly through the connection service option.
Just click on the conneted service file in the solution explorer
After that click on Add a service dependency to configure the application insights
Now a pop will emerge in which select Azure Application Insights then click on next.
Now select an application Insghts from the list or create a new one .
Now just continue to click next / finish until the process is complete. Thus, now you have configured the application insights in your web form and traces will start appearing in the application insights.
Azure portal output :-
I figured out that it was not logging to AppInsights because there was no TelemetryChannel or TelemetryClient properly configured.
Approach 1 - Using Telemetry Channel
using (var channel = new InMemoryChannel()) {
                var serviceCollection = new ServiceCollection();
                serviceCollection.Configure<TelemetryConfiguration>(config => config.TelemetryChannel = channel);
                serviceCollection.AddLogging(builder => builder
                .AddFilter("Default", LogLevel.Information)
                .AddFilter("Microsoft", LogLevel.Warning)
                .AddFilter("System", LogLevel.Warning)
                .AddEventLog(config => { config.LogName = "Pages"; config.SourceName = "Pages"; })
                .AddApplicationInsights(config => config.ConnectionString = "my-key",
                options => { }));
                var serviceProvider = serviceCollection.BuildServiceProvider();
                var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
                var logger = loggerFactory.CreateLogger("my-logger");
                logger.LogInformation("my new try");                 channel.Flush();
                System.Threading.Thread.Sleep(1000);
            }
Approach 2 - Injecting Telemetry Client using the Microsoft.ApplicationInsights.WorkerService package
var serviceCollection = new ServiceCollection();
serviceCollection.AddApplicationInsightsTelemetryWorkerService(options => options.ConnectionString = "my-key");
serviceCollection.AddLogging(builder => builder
.AddFilter<Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider>("", LogLevel.Information)
.AddFilter("Default", LogLevel.Information)
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddEventLog(config => { config.LogName = "Pages"; config.SourceName = "Pages"; }));
var serviceProvider = serviceCollection.BuildServiceProvider();
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var telemetryClient = serviceProvider.GetService<TelemetryClient>();
var logger = loggerFactory.CreateLogger("my-logger");
logger.LogInformation("my new try");                
telemetryClient.Flush();
            System.Threading.Thread.Sleep(1000);
Both approaches will work fine; we used approach 2.

Serilog not working in ASP.NET Core 7 Integration Test with CustomWebApplicationFactory<Program>

I'm trying to use separate configuration file for Serilog in my Integration Tests in my ASP.NET Core 7 Web API.
Here is my Program.cs,
//Read Configuration from appSettings
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
//Initialize Logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.CreateLogger();
Log.Information($"Starting {typeof(Program).Assembly.FullName}");
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();//Uses Serilog instead of default .NET Logger
// Removed code for brevity
var app = builder.Build();
// Removed code for brevity
await app.RunAsync();
Here is my CustomWebApplicationFactory<Program>,
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
var projectDir = Directory.GetCurrentDirectory();
builder.ConfigureAppConfiguration((context, conf) =>
{
conf.AddJsonFile(Path.Combine(projectDir, "appsettings.Test.json"));
});
builder.UseEnvironment("Test");
builder.ConfigureTestServices(async services =>
{
// Removed code for brevity
});
}
}
As seen in the above code, I have my appsettings.Test.json inside the root of my Integration Test project.
When I run the test, it always takes the appsettings.json from my API project. So I tried to change my Program.cs as follows,
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsEnvironment("Test"))
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.Test.json")
.Build();
//Initialize Logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.CreateLogger();
}
else
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
//Initialize Logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.CreateLogger();
}
Log.Information($"Starting {typeof(Program).Assembly.FullName}");
builder.Host.UseSerilog();//Uses Serilog instead of default .NET Logger
// Removed code for brevity
var app = builder.Build();
// Removed code for brevity
await app.RunAsync();
After the above change no logs we recorded during my Integration Test. So I decided to add appsettings.Test.json to my API project and Copy Always to output directory but still no luck.
Previously when I was using Startup.cs, I used to override CreateHost inside CustomWebApplicationFactory<Startup> as shown below,
protected override IHostBuilder CreateHostBuilder()
{
//Read Configuration from appSettings
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.Test.json")
.Build();
//Initialize Logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.CreateLogger();
Log.Information($"Starting Integration Tests.");
var builder = Host.CreateDefaultBuilder()
.UseSerilog()
.ConfigureWebHost(x =>
{
x.UseStartup<Startup>();
x.UseTestServer();
});
return builder;
}
But now after moving to new way in ASP.NET Core 7. There is no Startup.cs and the test was giving error like,
No Application was selected to run/start.
So I removed override IHostBuilder CreateHostBuilder() from CustomWebApplicationFactory<Program>.
Please can anyone assist on how to use separate configuration file for Integration Test in ASP.NET Core 7?
I ran into the same issue, as net7 microsoft docs (and Visual Studio 2022 intellisense/warnings) on changing the appSettings config are not aligned with what Serilog requires.
Microsoft's recommendation to change appSettings config (and then setup Serilog, based on Serilog's recommendations):
var builder = WebApplication.CreateBuilder(args);
var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
builder.Configuration.Sources.Clear();
builder.Configuration.AddJsonFile("siteid.json", optional: false, reloadOnChange: true);
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
builder.Configuration.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true);
builder.Configuration.AddEnvironmentVariables();
if (args != null) builder.Configuration.AddCommandLine(args);
// Configure Serilog
builder.Host.UseSerilog((hostingContext, loggerConfiguration) =>
loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
);
But this results in Serilog not logging anything as you stated.
My solution was to config appSettings using builder.Host.ConfigureAppConfiguration(), as builder.Host is what Serilog seems to use:
#pragma warning disable ASP0013 // Warning suggests switching from using Configure methods to WebApplicationBuilder.Configuration
var builder = WebApplication.CreateBuilder(args);
builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config.Sources.Clear();
config.AddJsonFile("siteid.json", optional: false, reloadOnChange: true);
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
if (args != null) config.AddCommandLine(args);
});
#pragma warning restore ASP0013
// Configure Serilog
builder.Host.UseSerilog((hostingContext, loggerConfiguration) =>
loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
);
This works and the correct appSettings are populated and Serilog logs as expected. However Visual Studio shows a Warning and suggests switching from using Configure methods (ConfigureAppConfiguration) to WebApplicationBuilder.Configuration (builder.Configuration.AddJsonFile), thus the #pragma warning disable ASP0013 lines.
So in your specific case, I can't offer any guidance in regards to your factory, but on updating your Program.cs, I might suggest this as a soution:
#pragma warning disable ASP0013 // Warning suggests switching from using Configure methods to WebApplicationBuilder.Configuration
var builder = WebApplication.CreateBuilder(args);
builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment; // getting enviroment here instead of your `builder.Environment.IsEnvironment("Test")`
config.Sources.Clear(); // If you want to clear existing defaults ???
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); //(e.g. appsettings.Test.json) overrides any values in appsettings.json
//config.AddEnvironmentVariables();
//if (args != null) config.AddCommandLine(args);
});
#pragma warning restore ASP0013
// Configure Serilog
builder.Host.UseSerilog((hostingContext, loggerConfiguration) =>
loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
);

How to pass connection string to Application Insights?

I have a .NET Core 3.1 console application and would like to configure it using a connection string specified in appsettings.json.
This is the code for a test app:
static void Main(string[] args)
{
var configurationBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
// To be able to read configuration from .json files
var configuration = configurationBuilder.Build();
// Create the DI container.
IServiceCollection services = new ServiceCollection();
services.AddApplicationInsightsTelemetryWorkerService();
// Build ServiceProvider.
IServiceProvider serviceProvider = services.BuildServiceProvider();
// Obtain TelemetryClient instance from DI, for additional manual tracking or to flush.
var telemetryClient = serviceProvider.GetRequiredService<TelemetryClient>();
telemetryClient.TrackTrace("Hello, world 3!");
// Explicitly call Flush() followed by sleep is required in Console Apps.
// This is to ensure that even if application terminates, telemetry is sent to the back-end.
telemetryClient.Flush();
Task.Delay(5000).Wait();
}
The problem is that it seems like Application Insight is not picking up the connection string. I do not see any Trace messages in Application Insights. If I pass the instrumentation key to AddApplicationInsightsTelemetryWorkerService it works.
This is the content of appsettings.json:
{
"ApplicationInsights": {
"ConnectionString": "<my connection string>"
}
}
What am I missing?
I don't know if the issue is still exist, but you can pass the Connection String straight to the TelemetryConfiguration.
var telemetryConfiguration = TelemetryConfiguration.CreateDefault();
telemetryConfiguration.ConnectionString = Configuration["AzureApplicationInsightsConnectionString"];
Or, you can add it to the servicecollection:
var options = new ApplicationInsightsServiceOptions { ConnectionString = Configuration["AzureApplicationInsightsConnectionString"] };
services.AddApplicationInsightsTelemetry(options: options);

Azure App Service Add Custom Domain .Net Core

I am following a sample from https://github.com/davidebbo/AzureWebsitesSamples
private static ResourceManagementClient _resourceGroupClient;
private static WebSiteManagementClient _websiteClient;
private static AzureEnvironment _environment;
private static DnsManagementClient _dnsClient;
static string _ClientId = Startup.StaticConfig.GetValue<string>("Azure:ClientId");
static string _ClientKey = Startup.StaticConfig.GetValue<string>("Azure:ClientSecret");
static string _TenantId = Startup.StaticConfig.GetValue<string>("Azure:TenantId");
static string _SubscriptionId = Startup.StaticConfig.GetValue<string>("Azure:SubscriptionId");
static string _ResourceGroupName = Startup.StaticConfig.GetValue<string>("Azure:ResourceGroupName");
static string _AppName = Startup.StaticConfig.GetValue<string>("Azure:AppName");
public static string ResourceGroupName { get => _ResourceGroupName; set => _ResourceGroupName = value; }
public static async Task MainAsync()
{
// Set Environment - Choose between Azure public cloud, china cloud and US govt. cloud
_environment = AzureEnvironment.PublicEnvironments[EnvironmentName.AzureCloud];
// Get the credentials
TokenCloudCredentials cloudCreds = await GetCredsFromServicePrincipal();
var tokenCreds = new TokenCredentials(cloudCreds.Token);
//var loggingHandler = new LoggingHandler(new HttpClientHandler());
// Create our own HttpClient so we can do logging
var httpClient = new HttpClient();
// Use the creds to create the clients we need
_resourceGroupClient = new ResourceManagementClient(_environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager), tokenCreds );
_resourceGroupClient.SubscriptionId = cloudCreds.SubscriptionId;
_websiteClient = new WebSiteManagementClient(_environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager), tokenCreds);
_websiteClient.SubscriptionId = cloudCreds.SubscriptionId;
_dnsClient = new DnsManagementClient(tokenCreds);
AddCustomDomainToSite("mycustomdomain.com");
}
private static async Task<TokenCloudCredentials> GetCredsFromServicePrincipal()
{
// Quick check to make sure we're not running with the default app.config
if (_SubscriptionId[0] == '[')
{
throw new Exception("You need to enter your appSettings in app.config to run this sample");
}
var authority = String.Format("{0}{1}", _environment.Endpoints[AzureEnvironment.Endpoint.ActiveDirectory], _TenantId);
var authContext = new AuthenticationContext(authority);
var credential = new ClientCredential(_ClientId, _ClientKey);
var authResult = await authContext.AcquireTokenAsync(_environment.Endpoints[AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId], credential);
return new TokenCloudCredentials(_SubscriptionId, authResult.AccessToken);
}
static void AddCustomDomainToSite(string sDomainName)
{
Domain domain = new Domain();
_websiteClient.Domains.CreateOrUpdateAsync(_ResourceGroupName, "mycustomdomain.com", domain);
}
I am trying to add mycustomdomain.com to my Azure app service. When I execute the code _websiteClient.Domains.CreateOrUpdateAsync(_ResourceGroupName, "mycustomdomain.com", domain);, nothing happens. I do not get any errors, and I do not see the custom domain listed under Custom Domains in my app service.
I have already verified ownership of the domain, and I can add it to my app service via the portal, but I am trying to add it through C#. Can someone please help me?
Did you already check on "_websiteClient.WebApps.CreateOrUpdateHostNameBindingAsync(resourcegroup, appservicename, hostname, binding)"?
The binding i give as parameter is this one:
var binding = new HostNameBinding{CustomHostNameDnsRecordType = CustomHostNameDnsRecordType.CName};
For me this is working to add a custom domain to the app service.

Serilog different levels for different sinks

I have the following piece of c# code for writing logs with multiple sinks (console and file), how do i restrict the console to log only (info, warn and error) and file to log everything.
var outputTemplate = "[{Level:u3}] {Message:lj}{NewLine}{Exception}";
// Logger
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.Console(outputTemplate: outputTemplate, theme:SystemConsoleTheme.Literate)
.WriteTo.File($"logs/log-{DateTime.Now:yyyy-MM-dd_HH:mm:ss.fff}.log")
.CreateLogger();
You can set a value for restrictedToMinimumLevel for each sink, to raise the minimum level that it should log.
It's explained in the documentation with examples:
https://github.com/serilog/serilog/wiki/Configuration-Basics#overriding-per-sink
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.File("log.txt")
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information)
.CreateLogger();

Resources