Browse Source

add basic logging ui (#14)

pull/16/head
Jason Dove 6 years ago committed by GitHub
parent
commit
e62074cc26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      ErsatzTV.Application/Logs/LogEntryViewModel.cs
  2. 16
      ErsatzTV.Application/Logs/Mapper.cs
  3. 7
      ErsatzTV.Application/Logs/Queries/GetRecentLogEntries.cs
  4. 21
      ErsatzTV.Application/Logs/Queries/GetRecentLogEntriesHandler.cs
  5. 12
      ErsatzTV.Core/Domain/LogEntry.cs
  6. 2
      ErsatzTV.Core/FileSystemLayout.cs
  7. 11
      ErsatzTV.Core/Interfaces/Repositories/ILogRepository.cs
  8. 12
      ErsatzTV.Infrastructure/Data/Configurations/LogEntryConfiguration.cs
  9. 21
      ErsatzTV.Infrastructure/Data/LogContext.cs
  10. 19
      ErsatzTV.Infrastructure/Data/Repositories/LogRepository.cs
  11. 1
      ErsatzTV/ErsatzTV.csproj
  12. 29
      ErsatzTV/Pages/Logs.razor
  13. 2
      ErsatzTV/Program.cs
  14. 1
      ErsatzTV/Shared/MainLayout.razor
  15. 4
      ErsatzTV/Startup.cs

12
ErsatzTV.Application/Logs/LogEntryViewModel.cs

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
using System;
namespace ErsatzTV.Application.Logs
{
public record LogEntryViewModel(
int Id,
DateTime Timestamp,
string Level,
string Exception,
string RenderedMessage,
string Properties);
}

16
ErsatzTV.Application/Logs/Mapper.cs

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Logs
{
internal static class Mapper
{
internal static LogEntryViewModel ProjectToViewModel(LogEntry logEntry) =>
new(
logEntry.Id,
logEntry.Timestamp,
logEntry.Level,
logEntry.Exception,
logEntry.RenderedMessage,
logEntry.Properties);
}
}

7
ErsatzTV.Application/Logs/Queries/GetRecentLogEntries.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
using System.Collections.Generic;
using MediatR;
namespace ErsatzTV.Application.Logs.Queries
{
public record GetRecentLogEntries : IRequest<List<LogEntryViewModel>>;
}

21
ErsatzTV.Application/Logs/Queries/GetRecentLogEntriesHandler.cs

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using MediatR;
using static ErsatzTV.Application.Logs.Mapper;
namespace ErsatzTV.Application.Logs.Queries
{
public class GetRecentLogEntriesHandler : IRequestHandler<GetRecentLogEntries, List<LogEntryViewModel>>
{
private readonly ILogRepository _logRepository;
public GetRecentLogEntriesHandler(ILogRepository logRepository) => _logRepository = logRepository;
public Task<List<LogEntryViewModel>> Handle(GetRecentLogEntries request, CancellationToken cancellationToken) =>
_logRepository.GetRecentLogEntries().Map(list => list.Map(ProjectToViewModel).ToList());
}
}

12
ErsatzTV.Core/Domain/LogEntry.cs

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
using System;
namespace ErsatzTV.Core.Domain
{
public record LogEntry(
int Id,
DateTime Timestamp,
string Level,
string Exception,
string RenderedMessage,
string Properties);
}

2
ErsatzTV.Core/FileSystemLayout.cs

@ -13,6 +13,8 @@ namespace ErsatzTV.Core @@ -13,6 +13,8 @@ namespace ErsatzTV.Core
public static readonly string DatabasePath = Path.Combine(AppDataFolder, "ersatztv.sqlite3");
public static readonly string LogDatabasePath = Path.Combine(AppDataFolder, "logs.sqlite3");
public static readonly string ImageCacheFolder = Path.Combine(AppDataFolder, "cache", "images");
public static readonly string PlexSecretsPath = Path.Combine(AppDataFolder, "plex-secrets.json");

11
ErsatzTV.Core/Interfaces/Repositories/ILogRepository.cs

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Core.Interfaces.Repositories
{
public interface ILogRepository
{
public Task<List<LogEntry>> GetRecentLogEntries();
}
}

12
ErsatzTV.Infrastructure/Data/Configurations/LogEntryConfiguration.cs

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
using ErsatzTV.Core.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ErsatzTV.Infrastructure.Data.Configurations
{
public class LogEntryConfiguration : IEntityTypeConfiguration<LogEntry>
{
public void Configure(EntityTypeBuilder<LogEntry> builder) =>
builder.ToTable("Logs");
}
}

21
ErsatzTV.Infrastructure/Data/LogContext.cs

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
using ErsatzTV.Core.Domain;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Infrastructure.Data
{
public class LogContext : DbContext
{
public LogContext(DbContextOptions<LogContext> options)
: base(options)
{
}
public DbSet<LogEntry> LogEntries { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfigurationsFromAssembly(typeof(LogContext).Assembly);
}
}
}

19
ErsatzTV.Infrastructure/Data/Repositories/LogRepository.cs

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Infrastructure.Data.Repositories
{
public class LogRepository : ILogRepository
{
private readonly LogContext _logContext;
public LogRepository(LogContext logContext) => _logContext = logContext;
public Task<List<LogEntry>> GetRecentLogEntries() =>
_logContext.LogEntries.OrderByDescending(e => e.Id).Take(100).ToListAsync();
}
}

1
ErsatzTV/ErsatzTV.csproj

@ -25,6 +25,7 @@ @@ -25,6 +25,7 @@
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.SQLite" Version="5.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.0.2" />
</ItemGroup>

29
ErsatzTV/Pages/Logs.razor

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
@page "/system/logs"
@using ErsatzTV.Application.Logs
@using ErsatzTV.Application.Logs.Queries
@inject IMediator Mediator
<MudTable FixedHeader="true" Dense="true" Items="_logEntries">
<HeaderContent>
<MudTh>Timestamp</MudTh>
<MudTh>Level</MudTh>
<MudTh>Message</MudTh>
<MudTh>Properties</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Timestamp">@context.Timestamp</MudTd>
<MudTd DataLabel="Level">@context.Level</MudTd>
<MudTd DataLabel="Message">@context.RenderedMessage</MudTd>
<MudTd DataLabel="Message">@context.Properties</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager/>
</PagerContent>
</MudTable>
@code {
private List<LogEntryViewModel> _logEntries;
protected override async Task OnInitializedAsync() => _logEntries = await Mediator.Send(new GetRecentLogEntries());
}

2
ErsatzTV/Program.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Threading.Tasks;
using ErsatzTV.Core;
using ErsatzTV.Extensions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
@ -25,6 +26,7 @@ namespace ErsatzTV @@ -25,6 +26,7 @@ namespace ErsatzTV
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.Enrich.FromLogContext()
.WriteTo.SQLite(FileSystemLayout.LogDatabasePath, retentionPeriod: TimeSpan.FromDays(1))
.CreateLogger();
try

1
ErsatzTV/Shared/MainLayout.razor

@ -32,6 +32,7 @@ @@ -32,6 +32,7 @@
</MudNavGroup>
<MudNavLink Href="/schedules">Schedules</MudNavLink>
<MudNavLink Href="/playouts">Playouts</MudNavLink>
<MudNavLink Href="/system/logs">Logs</MudNavLink>
</MudNavMenu>
</MudDrawer>
<MudMainContent>

4
ErsatzTV/Startup.cs

@ -108,6 +108,9 @@ namespace ErsatzTV @@ -108,6 +108,9 @@ namespace ErsatzTV
o.MigrationsAssembly("ErsatzTV.Infrastructure");
}));
services.AddDbContext<LogContext>(
options => options.UseSqlite($"Data Source={FileSystemLayout.LogDatabasePath}"));
services.AddMediatR(typeof(GetAllChannels).Assembly);
services.AddRefitClient<IPlexTvApi>()
@ -156,6 +159,7 @@ namespace ErsatzTV @@ -156,6 +159,7 @@ namespace ErsatzTV
services.AddScoped<IConfigElementRepository, ConfigElementRepository>();
services.AddScoped<IProgramScheduleRepository, ProgramScheduleRepository>();
services.AddScoped<IPlayoutRepository, PlayoutRepository>();
services.AddScoped<ILogRepository, LogRepository>();
services.AddScoped<IFFmpegLocator, FFmpegLocator>();
services.AddScoped<ISmartCollectionBuilder, SmartCollectionBuilder>();
services.AddScoped<ILocalMetadataProvider, LocalMetadataProvider>();

Loading…
Cancel
Save