feat: upgrade to .NET 6, refactor everything

This commit is contained in:
spiral
2021-11-26 21:10:56 -05:00
parent d28e99ba43
commit 1918c56937
314 changed files with 27954 additions and 27966 deletions

View File

@@ -2,27 +2,30 @@ using Autofac;
using Microsoft.Extensions.Configuration;
namespace PluralKit.Core
namespace PluralKit.Core;
public class ConfigModule<T>: Module where T : new()
{
public class ConfigModule<T>: Module where T : new()
private readonly string _submodule;
public ConfigModule(string submodule = null)
{
private readonly string _submodule;
_submodule = submodule;
}
public ConfigModule(string submodule = null)
{
_submodule = submodule;
}
protected override void Load(ContainerBuilder builder)
{
// We're assuming IConfiguration is already available somehow - it comes from various places (auto-injected in ASP, etc)
protected override void Load(ContainerBuilder builder)
{
// We're assuming IConfiguration is already available somehow - it comes from various places (auto-injected in ASP, etc)
// Register the CoreConfig and where to find it
builder.Register(c =>
c.Resolve<IConfiguration>().GetSection("PluralKit").Get<CoreConfig>() ?? new CoreConfig())
.SingleInstance();
// Register the CoreConfig and where to find it
builder.Register(c => c.Resolve<IConfiguration>().GetSection("PluralKit").Get<CoreConfig>() ?? new CoreConfig()).SingleInstance();
// Register the submodule config (BotConfig, etc) if specified
if (_submodule != null)
builder.Register(c => c.Resolve<IConfiguration>().GetSection("PluralKit").GetSection(_submodule).Get<T>() ?? new T()).SingleInstance();
}
// Register the submodule config (BotConfig, etc) if specified
if (_submodule != null)
builder.Register(c =>
c.Resolve<IConfiguration>().GetSection("PluralKit").GetSection(_submodule).Get<T>() ?? new T())
.SingleInstance();
}
}

View File

@@ -3,20 +3,19 @@ using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
namespace PluralKit.Core
namespace PluralKit.Core;
public class DataStoreModule: Module
{
public class DataStoreModule: Module
protected override void Load(ContainerBuilder builder)
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<DbConnectionCountHolder>().SingleInstance();
builder.RegisterType<DatabaseMigrator>().SingleInstance();
builder.RegisterType<Database>().As<IDatabase>().SingleInstance();
builder.RegisterType<ModelRepository>().AsSelf().SingleInstance();
builder.RegisterType<DbConnectionCountHolder>().SingleInstance();
builder.RegisterType<DatabaseMigrator>().SingleInstance();
builder.RegisterType<Database>().As<IDatabase>().SingleInstance();
builder.RegisterType<ModelRepository>().AsSelf().SingleInstance();
builder.RegisterType<DispatchService>().AsSelf().SingleInstance();
builder.RegisterType<DispatchService>().AsSelf().SingleInstance();
builder.Populate(new ServiceCollection().AddMemoryCache());
}
builder.Populate(new ServiceCollection().AddMemoryCache());
}
}

View File

@@ -1,8 +1,9 @@
using System;
using System.Globalization;
using Autofac;
using Microsoft.Extensions.Logging;
using NodaTime;
using Serilog;
@@ -11,128 +12,129 @@ using Serilog.Formatting.Compact;
using Serilog.Sinks.Elasticsearch;
using Serilog.Sinks.SystemConsole.Themes;
namespace PluralKit.Core
using ILogger = Serilog.ILogger;
namespace PluralKit.Core;
public class LoggingModule: Module
{
public class LoggingModule: Module
private readonly string _component;
private readonly Action<LoggerConfiguration> _fn;
public LoggingModule(string component, Action<LoggerConfiguration> fn = null, LoggerConfiguration cfg = null)
{
private readonly string _component;
private readonly Action<LoggerConfiguration> _fn;
private LoggerConfiguration _cfg { get; init; }
_component = component;
_fn = fn ?? (_ => { });
_cfg = cfg ?? new LoggerConfiguration();
}
public LoggingModule(string component, Action<LoggerConfiguration> fn = null, LoggerConfiguration cfg = null)
{
_component = component;
_fn = fn ?? (_ => { });
_cfg = cfg ?? new LoggerConfiguration();
}
private LoggerConfiguration _cfg { get; }
protected override void Load(ContainerBuilder builder)
{
builder
.Register(c => InitLogger(c.Resolve<CoreConfig>()))
.AsSelf()
.SingleInstance()
// AutoActivate ensures logging is enabled as early as possible in the API startup flow
// since we set the Log.Logger global >.>
.AutoActivate();
protected override void Load(ContainerBuilder builder)
{
builder
.Register(c => InitLogger(c.Resolve<CoreConfig>()))
.AsSelf()
.SingleInstance()
// AutoActivate ensures logging is enabled as early as possible in the API startup flow
// since we set the Log.Logger global >.>
.AutoActivate();
builder.Register(c => new Microsoft.Extensions.Logging.LoggerFactory().AddSerilog(c.Resolve<ILogger>()))
.As<Microsoft.Extensions.Logging.ILoggerFactory>()
.SingleInstance();
}
builder.Register(c => new LoggerFactory().AddSerilog(c.Resolve<ILogger>()))
.As<ILoggerFactory>()
.SingleInstance();
}
private ILogger InitLogger(CoreConfig config)
{
var consoleTemplate = "[{Timestamp:HH:mm:ss.fff}] {Level:u3} {Message:lj}{NewLine}{Exception}";
var outputTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss.ffffff}] {Level:u3} {Message:lj}{NewLine}{Exception}";
private ILogger InitLogger(CoreConfig config)
{
var consoleTemplate = "[{Timestamp:HH:mm:ss.fff}] {Level:u3} {Message:lj}{NewLine}{Exception}";
var outputTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss.ffffff}] {Level:u3} {Message:lj}{NewLine}{Exception}";
var logCfg = _cfg
.Enrich.FromLogContext()
.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
.Enrich.WithProperty("Component", _component)
.MinimumLevel.Is(config.ConsoleLogLevel)
var logCfg = _cfg
.Enrich.FromLogContext()
.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
.Enrich.WithProperty("Component", _component)
.MinimumLevel.Is(config.ConsoleLogLevel)
// Don't want App.Metrics/D#+ spam
.MinimumLevel.Override("App.Metrics", LogEventLevel.Information)
// Don't want App.Metrics/D#+ spam
.MinimumLevel.Override("App.Metrics", LogEventLevel.Information)
// nor ASP.NET spam
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
// nor ASP.NET spam
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
// Actual formatting for these is handled in ScalarFormatting
.Destructure.AsScalar<SystemId>()
.Destructure.AsScalar<MemberId>()
.Destructure.AsScalar<GroupId>()
.Destructure.AsScalar<SwitchId>()
.Destructure.ByTransforming<ProxyTag>(t => new { t.Prefix, t.Suffix })
.Destructure.With<PatchObjectDestructuring>()
.WriteTo.Async(a =>
{
// Both the same output, except one is raw compact JSON and one is plain text.
// Output simultaneously. May remove the JSON formatter later, keeping it just in cast.
// Flush interval is 50ms (down from 10s) to make "tail -f" easier. May be too low?
a.File(
(config.LogDir ?? "logs") + $"/pluralkit.{_component}.log",
outputTemplate: outputTemplate,
retainedFileCountLimit: 10,
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: null,
flushToDiskInterval: TimeSpan.FromMilliseconds(50),
restrictedToMinimumLevel: config.FileLogLevel,
formatProvider: new UTCTimestampFormatProvider(),
buffered: true);
a.File(
new RenderedCompactJsonFormatter(new ScalarFormatting.JsonValue()),
(config.LogDir ?? "logs") + $"/pluralkit.{_component}.json",
rollingInterval: RollingInterval.Day,
flushToDiskInterval: TimeSpan.FromMilliseconds(50),
restrictedToMinimumLevel: config.FileLogLevel,
buffered: true);
})
.WriteTo.Async(a =>
a.Console(
theme: AnsiConsoleTheme.Code,
outputTemplate: consoleTemplate,
restrictedToMinimumLevel: config.ConsoleLogLevel));
if (config.ElasticUrl != null)
// Actual formatting for these is handled in ScalarFormatting
.Destructure.AsScalar<SystemId>()
.Destructure.AsScalar<MemberId>()
.Destructure.AsScalar<GroupId>()
.Destructure.AsScalar<SwitchId>()
.Destructure.ByTransforming<ProxyTag>(t => new { t.Prefix, t.Suffix })
.Destructure.With<PatchObjectDestructuring>()
.WriteTo.Async(a =>
{
var elasticConfig = new ElasticsearchSinkOptions(new Uri(config.ElasticUrl))
{
AutoRegisterTemplate = true,
AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
MinimumLogEventLevel = config.ElasticLogLevel,
IndexFormat = "pluralkit-logs-{0:yyyy.MM.dd}",
CustomFormatter = new ScalarFormatting.Elasticsearch(),
};
// Both the same output, except one is raw compact JSON and one is plain text.
// Output simultaneously. May remove the JSON formatter later, keeping it just in cast.
// Flush interval is 50ms (down from 10s) to make "tail -f" easier. May be too low?
a.File(
(config.LogDir ?? "logs") + $"/pluralkit.{_component}.log",
outputTemplate: outputTemplate,
retainedFileCountLimit: 10,
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: null,
flushToDiskInterval: TimeSpan.FromMilliseconds(50),
restrictedToMinimumLevel: config.FileLogLevel,
formatProvider: new UTCTimestampFormatProvider(),
buffered: true);
logCfg.WriteTo.Elasticsearch(elasticConfig);
}
a.File(
new RenderedCompactJsonFormatter(new ScalarFormatting.JsonValue()),
(config.LogDir ?? "logs") + $"/pluralkit.{_component}.json",
rollingInterval: RollingInterval.Day,
flushToDiskInterval: TimeSpan.FromMilliseconds(50),
restrictedToMinimumLevel: config.FileLogLevel,
buffered: true);
})
.WriteTo.Async(a =>
a.Console(
theme: AnsiConsoleTheme.Code,
outputTemplate: consoleTemplate,
restrictedToMinimumLevel: config.ConsoleLogLevel));
_fn.Invoke(logCfg);
return Log.Logger = logCfg.CreateLogger();
}
}
// Serilog why is this necessary for such a simple thing >.>
public class UTCTimestampFormatProvider: IFormatProvider
{
public object GetFormat(Type formatType) => new UTCTimestampFormatter();
}
public class UTCTimestampFormatter: ICustomFormatter
{
public string Format(string format, object arg, IFormatProvider formatProvider)
if (config.ElasticUrl != null)
{
// Convert offset to UTC and then print
// FormatProvider defaults to locale-specific stuff so we force-default to invariant culture
// If we pass the given formatProvider it'll conveniently ignore it, for some reason >.>
if (arg is DateTimeOffset dto)
return dto.ToUniversalTime().ToString(format, CultureInfo.InvariantCulture);
if (arg is IFormattable f)
return f.ToString(format, CultureInfo.InvariantCulture);
return arg.ToString();
var elasticConfig = new ElasticsearchSinkOptions(new Uri(config.ElasticUrl))
{
AutoRegisterTemplate = true,
AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
MinimumLogEventLevel = config.ElasticLogLevel,
IndexFormat = "pluralkit-logs-{0:yyyy.MM.dd}",
CustomFormatter = new ScalarFormatting.Elasticsearch()
};
logCfg.WriteTo.Elasticsearch(elasticConfig);
}
_fn.Invoke(logCfg);
return Log.Logger = logCfg.CreateLogger();
}
}
// Serilog why is this necessary for such a simple thing >.>
public class UTCTimestampFormatProvider: IFormatProvider
{
public object GetFormat(Type formatType) => new UTCTimestampFormatter();
}
public class UTCTimestampFormatter: ICustomFormatter
{
public string Format(string format, object arg, IFormatProvider formatProvider)
{
// Convert offset to UTC and then print
// FormatProvider defaults to locale-specific stuff so we force-default to invariant culture
// If we pass the given formatProvider it'll conveniently ignore it, for some reason >.>
if (arg is DateTimeOffset dto)
return dto.ToUniversalTime().ToString(format, CultureInfo.InvariantCulture);
if (arg is IFormattable f)
return f.ToString(format, CultureInfo.InvariantCulture);
return arg.ToString();
}
}

View File

@@ -2,31 +2,30 @@ using App.Metrics;
using Autofac;
namespace PluralKit.Core
namespace PluralKit.Core;
public class MetricsModule: Module
{
public class MetricsModule: Module
private readonly string _onlyContext;
public MetricsModule(string onlyContext = null)
{
private readonly string _onlyContext;
_onlyContext = onlyContext;
}
public MetricsModule(string onlyContext = null)
{
_onlyContext = onlyContext;
}
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => InitMetrics(c.Resolve<CoreConfig>()))
.AsSelf().As<IMetrics>().SingleInstance();
}
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => InitMetrics(c.Resolve<CoreConfig>()))
.AsSelf().As<IMetrics>().SingleInstance();
}
private IMetricsRoot InitMetrics(CoreConfig config)
{
var builder = AppMetrics.CreateDefaultBuilder();
if (config.InfluxUrl != null && config.InfluxDb != null)
builder.Report.ToInfluxDb(config.InfluxUrl, config.InfluxDb);
if (_onlyContext != null)
builder.Filter.ByIncludingOnlyContext(_onlyContext);
return builder.Build();
}
private IMetricsRoot InitMetrics(CoreConfig config)
{
var builder = AppMetrics.CreateDefaultBuilder();
if (config.InfluxUrl != null && config.InfluxDb != null)
builder.Report.ToInfluxDb(config.InfluxUrl, config.InfluxDb);
if (_onlyContext != null)
builder.Filter.ByIncludingOnlyContext(_onlyContext);
return builder.Build();
}
}