Leviasan.Sanlog.EntityFrameworkCore
1.1.3
dotnet add package Leviasan.Sanlog.EntityFrameworkCore --version 1.1.3
NuGet\Install-Package Leviasan.Sanlog.EntityFrameworkCore -Version 1.1.3
<PackageReference Include="Leviasan.Sanlog.EntityFrameworkCore" Version="1.1.3" />
paket add Leviasan.Sanlog.EntityFrameworkCore --version 1.1.3
#r "nuget: Leviasan.Sanlog.EntityFrameworkCore, 1.1.3"
// Install Leviasan.Sanlog.EntityFrameworkCore as a Cake Addin #addin nuget:?package=Leviasan.Sanlog.EntityFrameworkCore&version=1.1.3 // Install Leviasan.Sanlog.EntityFrameworkCore as a Cake Tool #tool nuget:?package=Leviasan.Sanlog.EntityFrameworkCore&version=1.1.3
Leviasan.Sanlog.EntityFrameworkCore
Represents the logger for the NET8.0+ apps. Provides API for using EntityFrameworkCore context as storage for saving log entries from different applications in one database scheme separated by application identifier.
Get started
Add a logger to pipeline
There are 2 extension methods to add a logger to service collection. The first approach writes an event to the database after it occurred.
public static ILoggingBuilder AddSanlogService(this ILoggingBuilder builder, ...)
This approach uses BackgroundService
to write an event to the queue, that writes one to the database in another thread.
public static ILoggingBuilder AddHostedSanlogService(this ILoggingBuilder builder, ...)
Migration and implementation of the IDesignTimeDbContextFactory<SanlogDbContext>
Install an additional NuGet package Microsoft.EntityFrameworkCore.Tools
.
To create a migration need to invoke Add-Migration InitialDatabase -Context SanlogDbContext
. To apply need to invoke Update-Database -Context SanlogDbContext
.
Simple implementation of the IDesignTimeDbContextFactory<SanlogDbContext>
for a trivial app that uses Microsoft.EntityFrameworkCore.SqlServer
as log entry storage.
internal sealed class SanlogDbContextDesignTimeDbContextFactory : IDesignTimeDbContextFactory<SanlogDbContext>
{
public SanlogDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<SanlogDbContext>();
// Here need Use* method of your database provider
var connectionString = "Server=(localdb)\\mssqllocaldb;Database=sanlogdb;Trusted_Connection=True";
optionsBuilder.UseSqlServer(connectionString, serverOptions =>
{
// [Required] Set migration assembly
serverOptions.MigrationsAssembly(typeof(SanlogDbContextDesignTimeDbContextFactory).Assembly.GetName().Name);
});
return new SanlogDbContext(optionsBuilder.Options);
}
}
Design-time service for an application that used many environments.
To create a migration need to invoke Add-Migration InitialDatabase -Args "appsettings.json LoggerConnection" -Context SanlogDbContext
. To apply need to invoke Update-Database -Args "appsettings.json LoggerConnection" -Context SanlogDbContext
.
internal sealed class SanlogDbContextDesignTimeDbContextFactory : IDesignTimeDbContextFactory<SanlogDbContext>
{
public SanlogDbContext CreateDbContext(string[] args)
{
if (args.Length != 2)
throw new InvalidOperationException("Invalid arguments count");
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.SetBasePath(AppDomain.CurrentDomain.BaseDirectory);
configurationBuilder.AddJsonFile(args[0]);
var configuration = configurationBuilder.Build()!;
var optionsBuilder = new DbContextOptionsBuilder<SanlogDbContext>();
var connectionString = configuration.GetConnectionString(args[1]);
optionsBuilder.UseSqlServer(connectionString, serverOptions => // Here need Use* method of your database provider
{
// [Required] Set migration assembly
serverOptions.MigrationsAssembly(typeof(SanlogDbContextDesignTimeDbContextFactory).Assembly.GetName().Name);
});
return new SanlogDbContext(optionsBuilder.Options);
}
}
Registers your application
You must connect to your database provider and invoke the insert
command to the LogApps
table. For example for MSSQL: insert into dbo.LogApps values ('e6bcc7df-e201-4d0b-02a3-08dbd09ffc89', 'ConsoleApp', 'Development')
.
Logging in a trivial app
This first example shows the basics, but it is only suitable for a trivial console app. In the next section, you see how to improve the code by considering scale, performance, configuration, and typical programming patterns.
Example for database provider: Microsoft.EntityFrameworkCore.SqlServer
.
internal sealed partial class Program
{
private static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddSanlogService(
contextConfigure =>
{
// Configure your database provider
var connectionString = "Server=(localdb)\\mssqllocaldb;Database=sanlogdb;Trusted_Connection=True";
contextConfigure.UseSqlServer(connectionString);
},
loggingConfigure =>
{
// [Required] Here need to insert your specific application identifier
loggingConfigure.AppId = Guid.Parse("e6bcc7df-e201-4d0b-02a3-08dbd09ffc89");
}));
ILogger logger = factory.CreateLogger(nameof(Program));
LogInvokedMethod(logger, null, nameof(Program), nameof(Main));
Console.WriteLine("Finished");
}
[LoggerMessage(Level = LogLevel.Information, Message = "ClassName: {ClassName}. Method: {MethodName}")]
static partial void LogInvokedMethod(ILogger logger, Exception? exception, string className, string methodName);
}
The preceding example:
- Creates an
ILoggerFactory
. TheILoggerFactory
stores all the configuration and determines where log messages are sent. - Creates an
ILogger
with a category named "Program". The category is a string associated with each message logged by theILogger
object. It's used to group log messages from the same class (or category) together while searching or filtering logs. - Calls
LogInvokedMethod
to log a message at the Information level. The log level indicates the logged event's severity and filters out less important log messages. The log entry also includes a message template "ClassName: {ClassName}. Method: {MethodName}" and a key-value pairs ClassName = Program and MethodName = Main. The key name (or placeholder) comes from the word inside the curly braces in the template and the value comes from the remaining method argument. Logging compile-time source generation is usually a better alternative toILogger
extension methods likeLogInformation
. Logging source generation offers better performance, and stronger typing, and avoids spreading string constants throughout your methods. The tradeoff is that using this technique requires a bit more code.
Integration with hosts and dependency injection
If your application uses Dependency Injection (DI) or a host such as ASP.NET's WebApplication or Generic Host then you should use ILoggerFactory
and ILogger
objects from the DI container rather than creating them directly. Host builders initialize the default configuration, then add a configured ILoggerFactory
object to the host's DI container when the host is built. Before the host is built you can adjust the logging configuration via HostApplicationBuilder.Logging
, WebApplicationBuilder.Logging
, or similar APIs on other hosts. Hosts also apply logging configuration from default configuration sources such as appsettings.json
and environment variables. For more information, see Configuration in .NET.
internal sealed class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
/* ... Another configuration ... */
builder.AddHostedSanlogService(contextConfigure =>
{
// Configure your database provider
var connectionString = builder.Configuration.GetConnectionString("LoggerConnection");
contextConfigure.UseSqlServer(connectionString);
});
var app = builder.Build();
/* ... Configuring pipeline ... */
app.Run();
}
}
Configure logging
Logging configuration is set in code or via external sources, such as config files and environment variables. Using external configuration is beneficial when possible because it can be changed without rebuilding the application. However, some tasks, such as setting logging providers, can only be configured from code.
For apps that use a host, logging configuration is commonly provided by the "Logging" section of appsettings.{Environment}.json
files. For apps that don't use a host, external configuration sources are set up explicitly or configured in code instead.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
},
"SanlogLoggerProvider": {
"AppId": "e6bcc7df-e201-4d0b-02a3-08dbd09ffc89", // [Required] Here need to insert your specific application identifier
"IncludeScopes": true,
"LogLevel": { // [Optional] Override default log level configuration
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
}
}
}
}
Attention!
ODP.NET
does supportGuid
s.Guid
s can be inserted into aRAW(16)
column which is big enough to hold anyGuid
value. But caution needs to be taken in order to handleGuid
s appropriately. This is due to the fact that as the .NETGuid
structure flips the byte values in reverse order for the integer-based parts of theGuid
values whenGuid(byte[ ])
constructor is used and when theToByteArray()
method on theGuid
struct is invoked. More information: https://docs.oracle.com/en/database/oracle/oracle-database/23/odpnt/featGUID.html.- If using
EFCore.NamingConventions
you must configureDbContextOptionsBuilder
using invokeUse*CaseNamingConvention
in both places with the same method. It is the design time service that implementsIDesignTimeDbContextFactory<SanlogDbContext>
and the second place is while configuring the Sanlog logger.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- Leviasan.Sanlog.Abstractions (>= 1.1.3)
- Microsoft.EntityFrameworkCore (>= 8.0.6)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.1.3 | 115 | 6/25/2024 |