« Serilog » : différence entre les versions

De Banane Atomic
Aller à la navigationAller à la recherche
 
 
(15 versions intermédiaires par le même utilisateur non affichées)
Ligne 1 : Ligne 1 :
[[Category:CSharp]]
[[Category:CSharp]]
= Utilisation =
<kode lang='cs'>
public sealed class MyClass
{
    private readonly ILogger logger = Log.Logger.ForContext<MyClass>();
    public void MyMethod()
    {
        logger.Verbose("message");
</kode>
= Sinks =
= Sinks =
Définit où les log doivent être écris.
Définit où les log doivent être écris.
Ligne 5 : Ligne 16 :
== Console ==
== Console ==
<kode lang='cs'>
<kode lang='cs'>
// configuration
const string customTemplate = "{Timestamp:yyyy-MMM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}";
const string customTemplate = "{Timestamp:yyyy-MMM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}";
ILogger logger = new LoggerConfiguration()
Log.Logger = new LoggerConfiguration()
     .WriteTo.Console(outputTemplate: customTemplate)
     .WriteTo.Console(outputTemplate: customTemplate)
     .CreateLogger();
     .CreateLogger();
// make the log available statically
Log.Logger = logger;
// log
Log.Logger.Information("Text {NamedProperty}", value);
</kode>
</kode>


Nuget packages: Serilog, Serilog.Sinks.Console
Nuget packages: {{boxx|Serilog.Sinks.Console}}


== Rolling file ==
== [https://github.com/serilog/serilog-sinks-file (Rolling) File] ==
Le fichier sera nommé {{boxx|file-YYYYMMDD.log}}.
Le fichier sera nommé {{boxx|file-YYYYMMDD.log}}.
<kode lang='cs'>
<kode lang='cs'>
ILogger logger = new LoggerConfiguration()
ILogger logger = new LoggerConfiguration()
     .WriteTo.RollingFile("file.log", retainedFileCountLimit: 3)
     .WriteTo.File(
        "file.log",
        rollOnFileSizeLimit: true,
        fileSizeLimitBytes: 10_000_000,  // 10 MB
        retainedFileCountLimit: 3)       // retainedFileCountLimit, max number of log files to keep
     .CreateLogger();
     .CreateLogger();
// retainedFileCountLimit, max number of log files to keep
</kode>
</kode>
Nuget packages: Serilog.Sinks.RollingFile


== Log4net ==
== Log4net ==
Ligne 52 : Ligne 58 :


= Structured data =
= Structured data =
Par défaut, Serilog gère les types suivant: bool, numbers, string, dates, nullables, IEnumerable, Dictionary (si la clé est type de base)<br>
Par défaut, Serilog gère les types suivant: {{boxx|bool}} {{boxx|numbers}} {{boxx|string}} {{boxx|dates}} {{boxx|nullables}} {{boxx|IEnumerable}} {{boxx|Dictionary}} (si la clé est type de base)<br>
Pour les autres types, {{boxx|ToString}} est utilisé.
Pour les autres types, {{boxx|ToString}} est utilisé.
<kode lang='cs'>
<kode lang='cs'>
Ligne 89 : Ligne 95 :


= Level =
= Level =
Par défaut, les levels Verbose et Debug ne sont logués.
Par défaut, les levels {{boxx|Verbose}} et {{boxx|Debug}} ne sont logués.
<kode lang='cs'>
<kode lang='cs'>
Log.Logger.Verbose("Text");
Log.Logger.Verbose("Text");
Ligne 148 : Ligne 154 :
}
}
</filebox>
</filebox>
= App settings =
NuGet package: Serilog.Extras.AppSettings


= Timing =
= Timing =
NuGet package: Serilog.Extras.Timing
NuGet package: Serilog.Extras.Timing
= ASP.NET web API =
Nuget packages: {{boxx|Serilog.AspNetCore}}
<filebox fn='Program.cs'>
builder.Host.UseSerilog((ctx, services, config) => config.ReadFrom.Configuration(ctx.Configuration));
</filebox>
= [https://mcguirev10.com/2018/02/07/serilog-dependency-injection-easy-ip-logging.html Dependency Injection] =
{{warn | Utiliser plutôt {{boxx|Serilog.Extensions.Logging.File}} avec la DI par défaut fournit par {{boxx|Microsoft.Extensions.Logging}}}}
<filebox fn='Program.cs'>
static void Main(string[] args)
{
    Log.Logger = new LoggerConfiguration()
        .WriteTo.Console(outputTemplate: customTemplate)
        .CreateLogger();
    var services = new ServiceCollection();
    services.AddTransient<IMyClass, MyClass>()
            .AddSingleton(Log.Logger);
    var provider = services.BuildServiceProvider();
    var myObject = provider.GetService<IMyClass>();
    myObject.MyMethod();
</filebox>
<filebox fn='MyClass.cs'>
public class MyClass : IMyClass
{
    private readonly ILogger logger;
    public MyClass(ILogger logger)
    {
        this.logger = logger.ForContext<MyClass>();
    }
    public void MyMethod()
    {
        logger.Debug("message");
</filebox>


= Configuration file =
= Configuration file =
Ligne 182 : Ligne 225 :


== [https://github.com/serilog/serilog-settings-configuration appsettings.json] ==
== [https://github.com/serilog/serilog-settings-configuration appsettings.json] ==
Nuget package: {{boxx|Microsoft.Extensions.Configuration.Json}} {{boxx|Serilog.Settings.Configuration}}
Nuget package: {{boxx|Serilog.Settings.Configuration}}
<kode lang='cs'>
<kode lang='cs'>
var configuration = new ConfigurationBuilder()
var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
     .AddJsonFile("appsettings.json")
     .AddJsonFile("appsettings.json")
    .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
     .Build();
     .Build();


Ligne 193 : Ligne 238 :
</kode>
</kode>


<filebox fn='appsettings.json'>
<filebox fn='appsettings.json' collapsed>
"Serilog": {
  "Using": [ "Serilog.Sinks.File" ],
  "MinimumLevel": "Error",
  "WriteTo": [
    {
      "Name": "File",
      "Args": {
        "path": "logs/API.log",
        "rollOnFileSizeLimit": true,
        "fileSizeLimitBytes": 10000000,
        "retainedFileCountLimit": 3
      }
    }
  ],
  "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
</filebox>
 
<filebox fn='appsettings.json' collapsed>
{
{
   "Serilog": {
   "Serilog": {

Dernière version du 18 octobre 2024 à 14:39

Utilisation

Cs.svg
public sealed class MyClass
{
    private readonly ILogger logger = Log.Logger.ForContext<MyClass>();

    public void MyMethod()
    {
        logger.Verbose("message");

Sinks

Définit où les log doivent être écris.

Console

Cs.svg
const string customTemplate = "{Timestamp:yyyy-MMM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}";
Log.Logger = new LoggerConfiguration()
    .WriteTo.Console(outputTemplate: customTemplate)
    .CreateLogger();

Nuget packages: Serilog.Sinks.Console

(Rolling) File

Le fichier sera nommé file-YYYYMMDD.log.

Cs.svg
ILogger logger = new LoggerConfiguration()
    .WriteTo.File(
        "file.log",
        rollOnFileSizeLimit: true,
        fileSizeLimitBytes: 10_000_000,  // 10 MB
        retainedFileCountLimit: 3)       // retainedFileCountLimit, max number of log files to keep
    .CreateLogger();

Log4net

Cs.svg
// configure log4net
XmlConfigurator.Configure();

Log.Logger = new LoggerConfiguration()
    .WriteTo.Log4Net(supplyContextMessage: true)
    .CreateLogger();
Il n'est pas possible d'utiliser les Repositories.

Application Insights

Nuget package: Serilog.Sinks.ApplicationInsights

Cs.svg
Log.Logger = new LoggerConfiguration()
                .WriteTo.ApplicationInsights(TelemetryConverter.Traces)
                .CreateLogger();

Trace vs Event
Ne pas oublier de configurer Application Insights.

Structured data

Par défaut, Serilog gère les types suivant: bool numbers string dates nullables IEnumerable Dictionary (si la clé est type de base)
Pour les autres types, ToString est utilisé.

Cs.svg
Log.Logger.Information("Text {NamedProperty1} {NamedProperty2}", value1, value2);

Destructured object

Cs.svg
var user = new User
{
    Name = "Joe",
    Age = 35
};

// pour éviter que ToString ne soit utilisé, ajouter un @ devant le nom
Log.Logger.Information("User: {@User}", user);
// User: {"Name": "Joe", "Age": 35, "$type": "User"}

// configurer la déstructuration
ILogger logger = new LoggerConfiguration()
    .Destructure.ByTransforming<User>(u => new { u.Name, u.Age })
    .WriteTo.Console()
    .CreateLogger();

Log.Logger.Information("User: {@User}", user);
// User: {"Name": "Joe", "Age": 35}

Format

Cs.svg
Log.Logger.Information("{StringProperty}", myString);    // "MyString"
Log.Logger.Information("{StringProperty:l}", myString);  // MyString

Log.Logger.Information("{NumberProperty:0.00}", Math.PI);  // 3,14

Level

Par défaut, les levels Verbose et Debug ne sont logués.

Cs.svg
Log.Logger.Verbose("Text");
Log.Logger.Debug("Text");
Log.Logger.Information("Text");
Log.Logger.Warning("Text");
Log.Logger.Error("Text");
Log.Logger.Fatal("Text");

ILogger logger = new LoggerConfiguration()
    .MinimumLevel.Verbose()  // définit le level minimum de log pour tous les sink
    .WriteTo.Console(LogEventLevel.Information)  // restreint le level minimum de log pour le sink Console
    .CreateLogger();

Filter

Cs.svg
Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .Filter.ByIncludingOnly(Matching.FromSource<Program>())  // inclus seulement les log avec un SourceContext = type Program
    .Filter.ByIncludingOnly(Matching.FromSource("MyNamespace"))  // inclus seulement les log avec un SourceContext commençant par MyNamespace
    .CreateLogger();

// SourceContext
Log.Logger.ForContext<Program>().Information("Text");

Event enricher

Cs.svg
ILogger logger = new LoggerConfiguration()
    .Enrich.WithProperty("ApplicationName", "MyApp")
    .WriteTo.Console()
    .CreateLogger();

Log.Logger.Information("{ApplicationName} - Text");  // MyApp - Text

ILogger logger = new LoggerConfiguration()
    .Enrich.With<MyEventEnricher>()
    .WriteTo.Console()
    .CreateLogger();

Log.Logger.Information("{ApplicationName} - {ApplicationVersion} - Text");  // MyApp - 1.0.0.0 - Text
MyEventEnricher.cs
class MyEventEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var applicationAssembly = Assembly.GetEntryAssembly();

        var name = applicationAssembly.GetName().Name;
        var version = applicationAssembly.GetName().Version;

        logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ApplicationName", name));
        logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ApplicationVersion", version));
    }
}

Timing

NuGet package: Serilog.Extras.Timing

ASP.NET web API

Nuget packages: Serilog.AspNetCore

Program.cs
builder.Host.UseSerilog((ctx, services, config) => config.ReadFrom.Configuration(ctx.Configuration));

Dependency Injection

Utiliser plutôt Serilog.Extensions.Logging.File avec la DI par défaut fournit par Microsoft.Extensions.Logging
Program.cs
static void Main(string[] args)
{
    Log.Logger = new LoggerConfiguration()
        .WriteTo.Console(outputTemplate: customTemplate)
        .CreateLogger();

    var services = new ServiceCollection();
    services.AddTransient<IMyClass, MyClass>()
            .AddSingleton(Log.Logger);

    var provider = services.BuildServiceProvider();

    var myObject = provider.GetService<IMyClass>();
    myObject.MyMethod();
MyClass.cs
public class MyClass : IMyClass
{
    private readonly ILogger logger;

    public MyClass(ILogger logger)
    {
        this.logger = logger.ForContext<MyClass>();
    }

    public void MyMethod()
    {
        logger.Debug("message");

Configuration file

App.config

Nuget package: Serilog.Settings.AppSettings

Cs.svg
var logger = new LoggerConfiguration()
    .ReadFrom.AppSettings()
    .CreateLogger();
App.config
<configuration>
    <appSettings>
        <add key="serilog:minimum-level" value="Warning" />

        <add key="serilog:using:Console" value="Serilog.Sinks.Console" />
        <add key="serilog:write-to:Console" />

        <add key="serilog:using:ApplicationInsights" value="Serilog.Sinks.ApplicationInsights" />
        <add key="serilog:write-to:ApplicationInsights.telemetryConverter"
             value="Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights" />

        <add key="serilog:using:Log4Net" value="Serilog.Sinks.Log4Net" />
        <add key="serilog:write-to:Log4Net.supplyContextMessage" value="true" />
    </appSettings>

appsettings.json

Nuget package: Serilog.Settings.Configuration

Cs.svg
var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
    .Build();

var logger = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration)
    .CreateLogger();
appsettings.json
"Serilog": {
  "Using": [ "Serilog.Sinks.File" ],
  "MinimumLevel": "Error",
  "WriteTo": [
    {
      "Name": "File",
      "Args": {
        "path": "logs/API.log",
        "rollOnFileSizeLimit": true,
        "fileSizeLimitBytes": 10000000,
        "retainedFileCountLimit": 3
      }
    }
  ],
  "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
appsettings.json
{
  "Serilog": {
    "Using":  ["Serilog.Sinks.Console"],
    "MinimumLevel": "Debug",
    "WriteTo": [
      { "Name": "Console" },
      { "Name": "File", "Args": { "path": "%TEMP%\\Logs\\serilog-configuration-sample.txt" } }
    ],
    "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"],
    "Destructure": [
      { "Name": "With", "Args": { "policy": "Sample.CustomPolicy, Sample" } },
      { "Name": "ToMaximumDepth", "Args": { "maximumDestructuringDepth": 4 } },
      { "Name": "ToMaximumStringLength", "Args": { "maximumStringLength": 100 } },
      { "Name": "ToMaximumCollectionCount", "Args": { "maximumCollectionCount": 10 } }
    ],
    "Properties": {
        "Application": "Sample"
    }
  }
}

Debug

Cs.svg
// afficher les infos de debug
SelfLog.Enable(Console.WriteLine);