Logging Enhancements (#1521)

* Recreated Kavita Logging with Serilog instead of Default. This needs to be move out of the appsettings now, to allow auto updater to patch.

* Refactored the code to be completely configured via Code rather than appsettings.json. This is a required step for Auto Updating.

* Added in the ability to send logs directly to the UI only for users on the log route. Stopping implementation as Alerts page will handle the rest of the implementation.

* Fixed up the backup service to not rely on Config from appsettings.json

* Tweaked the Logging levels available

* Moved everything over to File-scoped namespaces

* Moved everything over to File-scoped namespaces

* Code cleanup, removed an old migration and changed so debug logging doesn't print sensitive db data

* Removed dead code
This commit is contained in:
Joseph Milazzo 2022-09-12 19:25:48 -05:00 committed by GitHub
parent 9f715cc35f
commit d1a14f7e68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
212 changed files with 16599 additions and 16834 deletions

View file

@ -15,135 +15,134 @@ using API.Entities.Metadata;
using API.Helpers.Converters;
using AutoMapper;
namespace API.Helpers
namespace API.Helpers;
public class AutoMapperProfiles : Profile
{
public class AutoMapperProfiles : Profile
public AutoMapperProfiles()
{
public AutoMapperProfiles()
{
CreateMap<LibraryDto, Library>();
CreateMap<Volume, VolumeDto>();
CreateMap<MangaFile, MangaFileDto>();
CreateMap<Chapter, ChapterDto>();
CreateMap<Series, SeriesDto>();
CreateMap<CollectionTag, CollectionTagDto>();
CreateMap<Person, PersonDto>();
CreateMap<Genre, GenreTagDto>();
CreateMap<Tag, TagDto>();
CreateMap<AgeRating, AgeRatingDto>();
CreateMap<PublicationStatus, PublicationStatusDto>();
CreateMap<LibraryDto, Library>();
CreateMap<Volume, VolumeDto>();
CreateMap<MangaFile, MangaFileDto>();
CreateMap<Chapter, ChapterDto>();
CreateMap<Series, SeriesDto>();
CreateMap<CollectionTag, CollectionTagDto>();
CreateMap<Person, PersonDto>();
CreateMap<Genre, GenreTagDto>();
CreateMap<Tag, TagDto>();
CreateMap<AgeRating, AgeRatingDto>();
CreateMap<PublicationStatus, PublicationStatusDto>();
CreateMap<SeriesMetadata, SeriesMetadataDto>()
.ForMember(dest => dest.Writers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Writer)))
.ForMember(dest => dest.CoverArtists,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.CoverArtist)))
.ForMember(dest => dest.Characters,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Character)))
.ForMember(dest => dest.Publishers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Publisher)))
.ForMember(dest => dest.Colorists,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Colorist)))
.ForMember(dest => dest.Inkers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Inker)))
.ForMember(dest => dest.Letterers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Letterer)))
.ForMember(dest => dest.Pencillers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Penciller)))
.ForMember(dest => dest.Translators,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Translator)))
.ForMember(dest => dest.Editors,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Editor)));
CreateMap<SeriesMetadata, SeriesMetadataDto>()
.ForMember(dest => dest.Writers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Writer)))
.ForMember(dest => dest.CoverArtists,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.CoverArtist)))
.ForMember(dest => dest.Characters,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Character)))
.ForMember(dest => dest.Publishers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Publisher)))
.ForMember(dest => dest.Colorists,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Colorist)))
.ForMember(dest => dest.Inkers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Inker)))
.ForMember(dest => dest.Letterers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Letterer)))
.ForMember(dest => dest.Pencillers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Penciller)))
.ForMember(dest => dest.Translators,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Translator)))
.ForMember(dest => dest.Editors,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Editor)));
CreateMap<Chapter, ChapterMetadataDto>()
.ForMember(dest => dest.Writers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Writer)))
.ForMember(dest => dest.CoverArtists,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.CoverArtist)))
.ForMember(dest => dest.Colorists,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Colorist)))
.ForMember(dest => dest.Inkers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Inker)))
.ForMember(dest => dest.Letterers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Letterer)))
.ForMember(dest => dest.Pencillers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Penciller)))
.ForMember(dest => dest.Publishers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Publisher)))
.ForMember(dest => dest.Translators,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Translator)))
.ForMember(dest => dest.Characters,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Character)))
.ForMember(dest => dest.Editors,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Editor)));
CreateMap<Chapter, ChapterMetadataDto>()
.ForMember(dest => dest.Writers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Writer)))
.ForMember(dest => dest.CoverArtists,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.CoverArtist)))
.ForMember(dest => dest.Colorists,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Colorist)))
.ForMember(dest => dest.Inkers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Inker)))
.ForMember(dest => dest.Letterers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Letterer)))
.ForMember(dest => dest.Pencillers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Penciller)))
.ForMember(dest => dest.Publishers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Publisher)))
.ForMember(dest => dest.Translators,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Translator)))
.ForMember(dest => dest.Characters,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Character)))
.ForMember(dest => dest.Editors,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Editor)));
// CreateMap<SeriesRelation, RelatedSeriesDto>()
// .ForMember(dest => dest.Adaptations,
// opt =>
// opt.MapFrom(src => src.Where(p => p.Role == PersonRole.Writer)))
// CreateMap<SeriesRelation, RelatedSeriesDto>()
// .ForMember(dest => dest.Adaptations,
// opt =>
// opt.MapFrom(src => src.Where(p => p.Role == PersonRole.Writer)))
CreateMap<AppUser, UserDto>();
CreateMap<SiteTheme, SiteThemeDto>();
CreateMap<AppUserPreferences, UserPreferencesDto>()
.ForMember(dest => dest.Theme,
opt =>
opt.MapFrom(src => src.Theme))
.ForMember(dest => dest.BookReaderThemeName,
opt =>
opt.MapFrom(src => src.BookThemeName))
.ForMember(dest => dest.BookReaderLayoutMode,
opt =>
opt.MapFrom(src => src.BookReaderLayoutMode));
CreateMap<AppUser, UserDto>();
CreateMap<SiteTheme, SiteThemeDto>();
CreateMap<AppUserPreferences, UserPreferencesDto>()
.ForMember(dest => dest.Theme,
opt =>
opt.MapFrom(src => src.Theme))
.ForMember(dest => dest.BookReaderThemeName,
opt =>
opt.MapFrom(src => src.BookThemeName))
.ForMember(dest => dest.BookReaderLayoutMode,
opt =>
opt.MapFrom(src => src.BookReaderLayoutMode));
CreateMap<AppUserBookmark, BookmarkDto>();
CreateMap<AppUserBookmark, BookmarkDto>();
CreateMap<ReadingList, ReadingListDto>();
CreateMap<ReadingListItem, ReadingListItemDto>();
CreateMap<ReadingList, ReadingListDto>();
CreateMap<ReadingListItem, ReadingListItemDto>();
CreateMap<Series, SearchResultDto>()
.ForMember(dest => dest.SeriesId,
opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.LibraryName,
opt => opt.MapFrom(src => src.Library.Name));
CreateMap<Series, SearchResultDto>()
.ForMember(dest => dest.SeriesId,
opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.LibraryName,
opt => opt.MapFrom(src => src.Library.Name));
CreateMap<Library, LibraryDto>()
.ForMember(dest => dest.Folders,
opt =>
opt.MapFrom(src => src.Folders.Select(x => x.Path).ToList()));
CreateMap<Library, LibraryDto>()
.ForMember(dest => dest.Folders,
opt =>
opt.MapFrom(src => src.Folders.Select(x => x.Path).ToList()));
CreateMap<AppUser, MemberDto>()
.AfterMap((ps, pst, context) => context.Mapper.Map(ps.Libraries, pst.Libraries));
CreateMap<AppUser, MemberDto>()
.AfterMap((ps, pst, context) => context.Mapper.Map(ps.Libraries, pst.Libraries));
CreateMap<RegisterDto, AppUser>();
CreateMap<RegisterDto, AppUser>();
CreateMap<IList<ServerSetting>, ServerSettingDto>()
.ConvertUsing<ServerSettingConverter>();
CreateMap<IList<ServerSetting>, ServerSettingDto>()
.ConvertUsing<ServerSettingConverter>();
CreateMap<IEnumerable<ServerSetting>, ServerSettingDto>()
.ConvertUsing<ServerSettingConverter>();
CreateMap<IEnumerable<ServerSetting>, ServerSettingDto>()
.ConvertUsing<ServerSettingConverter>();
}
}
}

View file

@ -1,29 +1,28 @@
using System.Collections.Generic;
using Hangfire;
namespace API.Helpers.Converters
{
public static class CronConverter
{
public static readonly IEnumerable<string> Options = new []
{
"disabled",
"daily",
"weekly",
};
public static string ConvertToCronNotation(string source)
{
var destination = string.Empty;
destination = source.ToLower() switch
{
"daily" => Cron.Daily(),
"weekly" => Cron.Weekly(),
"disabled" => Cron.Never(),
"" => Cron.Never(),
_ => destination
};
namespace API.Helpers.Converters;
return destination;
}
public static class CronConverter
{
public static readonly IEnumerable<string> Options = new []
{
"disabled",
"daily",
"weekly",
};
public static string ConvertToCronNotation(string source)
{
var destination = string.Empty;
destination = source.ToLower() switch
{
"daily" => Cron.Daily(),
"weekly" => Cron.Weekly(),
"disabled" => Cron.Never(),
"" => Cron.Never(),
_ => destination
};
return destination;
}
}

View file

@ -4,69 +4,68 @@ using API.Entities;
using API.Entities.Enums;
using AutoMapper;
namespace API.Helpers.Converters
{
public class ServerSettingConverter : ITypeConverter<IEnumerable<ServerSetting>, ServerSettingDto>
{
public ServerSettingDto Convert(IEnumerable<ServerSetting> source, ServerSettingDto destination, ResolutionContext context)
{
destination ??= new ServerSettingDto();
foreach (var row in source)
{
switch (row.Key)
{
case ServerSettingKey.CacheDirectory:
destination.CacheDirectory = row.Value;
break;
case ServerSettingKey.TaskScan:
destination.TaskScan = row.Value;
break;
case ServerSettingKey.LoggingLevel:
destination.LoggingLevel = row.Value;
break;
case ServerSettingKey.TaskBackup:
destination.TaskBackup = row.Value;
break;
case ServerSettingKey.Port:
destination.Port = int.Parse(row.Value);
break;
case ServerSettingKey.AllowStatCollection:
destination.AllowStatCollection = bool.Parse(row.Value);
break;
case ServerSettingKey.EnableOpds:
destination.EnableOpds = bool.Parse(row.Value);
break;
case ServerSettingKey.BaseUrl:
destination.BaseUrl = row.Value;
break;
case ServerSettingKey.BookmarkDirectory:
destination.BookmarksDirectory = row.Value;
break;
case ServerSettingKey.EmailServiceUrl:
destination.EmailServiceUrl = row.Value;
break;
case ServerSettingKey.InstallVersion:
destination.InstallVersion = row.Value;
break;
case ServerSettingKey.ConvertBookmarkToWebP:
destination.ConvertBookmarkToWebP = bool.Parse(row.Value);
break;
case ServerSettingKey.EnableSwaggerUi:
destination.EnableSwaggerUi = bool.Parse(row.Value);
break;
case ServerSettingKey.TotalBackups:
destination.TotalBackups = int.Parse(row.Value);
break;
case ServerSettingKey.InstallId:
destination.InstallId = row.Value;
break;
case ServerSettingKey.EnableFolderWatching:
destination.EnableFolderWatching = bool.Parse(row.Value);
break;
}
}
namespace API.Helpers.Converters;
return destination;
public class ServerSettingConverter : ITypeConverter<IEnumerable<ServerSetting>, ServerSettingDto>
{
public ServerSettingDto Convert(IEnumerable<ServerSetting> source, ServerSettingDto destination, ResolutionContext context)
{
destination ??= new ServerSettingDto();
foreach (var row in source)
{
switch (row.Key)
{
case ServerSettingKey.CacheDirectory:
destination.CacheDirectory = row.Value;
break;
case ServerSettingKey.TaskScan:
destination.TaskScan = row.Value;
break;
case ServerSettingKey.LoggingLevel:
destination.LoggingLevel = row.Value;
break;
case ServerSettingKey.TaskBackup:
destination.TaskBackup = row.Value;
break;
case ServerSettingKey.Port:
destination.Port = int.Parse(row.Value);
break;
case ServerSettingKey.AllowStatCollection:
destination.AllowStatCollection = bool.Parse(row.Value);
break;
case ServerSettingKey.EnableOpds:
destination.EnableOpds = bool.Parse(row.Value);
break;
case ServerSettingKey.BaseUrl:
destination.BaseUrl = row.Value;
break;
case ServerSettingKey.BookmarkDirectory:
destination.BookmarksDirectory = row.Value;
break;
case ServerSettingKey.EmailServiceUrl:
destination.EmailServiceUrl = row.Value;
break;
case ServerSettingKey.InstallVersion:
destination.InstallVersion = row.Value;
break;
case ServerSettingKey.ConvertBookmarkToWebP:
destination.ConvertBookmarkToWebP = bool.Parse(row.Value);
break;
case ServerSettingKey.EnableSwaggerUi:
destination.EnableSwaggerUi = bool.Parse(row.Value);
break;
case ServerSettingKey.TotalBackups:
destination.TotalBackups = int.Parse(row.Value);
break;
case ServerSettingKey.InstallId:
destination.InstallId = row.Value;
break;
case ServerSettingKey.EnableFolderWatching:
destination.EnableFolderWatching = bool.Parse(row.Value);
break;
}
}
return destination;
}
}

View file

@ -4,30 +4,29 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace API.Helpers
namespace API.Helpers;
public class PagedList<T> : List<T>
{
public class PagedList<T> : List<T>
public PagedList(IEnumerable<T> items, int count, int pageNumber, int pageSize)
{
public PagedList(IEnumerable<T> items, int count, int pageNumber, int pageSize)
{
CurrentPage = pageNumber;
TotalPages = (int) Math.Ceiling(count / (double) pageSize);
PageSize = pageSize;
TotalCount = count;
AddRange(items);
}
public int CurrentPage { get; set; }
public int TotalPages { get; set; }
public int PageSize { get; set; }
public int TotalCount { get; set; }
public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
{
// NOTE: OrderBy warning being thrown here even if query has the orderby statement
var count = await source.CountAsync();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
CurrentPage = pageNumber;
TotalPages = (int) Math.Ceiling(count / (double) pageSize);
PageSize = pageSize;
TotalCount = count;
AddRange(items);
}
}
public int CurrentPage { get; set; }
public int TotalPages { get; set; }
public int PageSize { get; set; }
public int TotalCount { get; set; }
public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
{
// NOTE: OrderBy warning being thrown here even if query has the orderby statement
var count = await source.CountAsync();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
}

View file

@ -1,18 +1,17 @@
namespace API.Helpers
{
public class PaginationHeader
{
public PaginationHeader(int currentPage, int itemsPerPage, int totalItems, int totalPages)
{
CurrentPage = currentPage;
ItemsPerPage = itemsPerPage;
TotalItems = totalItems;
TotalPages = totalPages;
}
namespace API.Helpers;
public int CurrentPage { get; set; }
public int ItemsPerPage { get; set; }
public int TotalItems { get; set; }
public int TotalPages { get; set; }
public class PaginationHeader
{
public PaginationHeader(int currentPage, int itemsPerPage, int totalItems, int totalPages)
{
CurrentPage = currentPage;
ItemsPerPage = itemsPerPage;
TotalItems = totalItems;
TotalPages = totalPages;
}
}
public int CurrentPage { get; set; }
public int ItemsPerPage { get; set; }
public int TotalItems { get; set; }
public int TotalPages { get; set; }
}

View file

@ -5,27 +5,26 @@ using System.Data.Common;
using API.DTOs;
using Microsoft.EntityFrameworkCore;
namespace API.Helpers
namespace API.Helpers;
public static class SqlHelper
{
public static class SqlHelper
public static List<T> RawSqlQuery<T>(DbContext context, string query, Func<DbDataReader, T> map)
{
public static List<T> RawSqlQuery<T>(DbContext context, string query, Func<DbDataReader, T> map)
using var command = context.Database.GetDbConnection().CreateCommand();
command.CommandText = query;
command.CommandType = CommandType.Text;
context.Database.OpenConnection();
using var result = command.ExecuteReader();
var entities = new List<T>();
while (result.Read())
{
using var command = context.Database.GetDbConnection().CreateCommand();
command.CommandText = query;
command.CommandType = CommandType.Text;
context.Database.OpenConnection();
using var result = command.ExecuteReader();
var entities = new List<T>();
while (result.Read())
{
entities.Add(map(result));
}
return entities;
entities.Add(map(result));
}
return entities;
}
}

View file

@ -1,18 +1,17 @@
namespace API.Helpers
{
public class UserParams
{
private const int MaxPageSize = int.MaxValue;
public int PageNumber { get; init; } = 1;
private readonly int _pageSize = MaxPageSize;
namespace API.Helpers;
/// <summary>
/// If set to 0, will set as MaxInt
/// </summary>
public int PageSize
{
get => _pageSize;
init => _pageSize = (value == 0) ? MaxPageSize : value;
}
public class UserParams
{
private const int MaxPageSize = int.MaxValue;
public int PageNumber { get; init; } = 1;
private readonly int _pageSize = MaxPageSize;
/// <summary>
/// If set to 0, will set as MaxInt
/// </summary>
public int PageSize
{
get => _pageSize;
init => _pageSize = (value == 0) ? MaxPageSize : value;
}
}