From 1d6224c9c66b31c9df602b4281c870a9c400767c Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 26 Apr 2021 07:02:26 -0600 Subject: Add endpoint to log client events --- .../ClientEvent/ClientEventLogger.cs | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs (limited to 'MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs') diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs new file mode 100644 index 000000000..c00a38d1b --- /dev/null +++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs @@ -0,0 +1,38 @@ +using System; +using MediaBrowser.Model.ClientLog; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Controller.ClientEvent +{ + /// + public class ClientEventLogger : IClientEventLogger + { + private const string LogString = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level}] [{ClientName}:{ClientVersion}]: UserId: {UserId} DeviceId: {DeviceId}{NewLine}{Message}"; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + public ClientEventLogger(ILogger logger) + { + _logger = logger; + } + + /// + public void Log(ClientLogEvent clientLogEvent) + { + _logger.Log( + LogLevel.Critical, + LogString, + clientLogEvent.Timestamp, + clientLogEvent.Level.ToString(), + clientLogEvent.ClientName, + clientLogEvent.ClientVersion, + clientLogEvent.UserId ?? Guid.Empty, + clientLogEvent.DeviceId, + Environment.NewLine, + clientLogEvent.Message); + } + } +} \ No newline at end of file -- cgit v1.2.3 From a6357f89abb40deaa84ed0ea52010c098e769e62 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Tue, 26 Oct 2021 18:42:17 -0600 Subject: Add ability to upload entire file --- Jellyfin.Api/Controllers/ClientLogController.cs | 19 +++++++++++++++++-- Jellyfin.Server/Program.cs | 4 ++-- .../ClientEvent/ClientEventLogger.cs | 22 ++++++++++++++++++++-- .../ClientEvent/IClientEventLogger.cs | 14 ++++++++++++-- 4 files changed, 51 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs') diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs index b894deb84..9fe3bf731 100644 --- a/Jellyfin.Api/Controllers/ClientLogController.cs +++ b/Jellyfin.Api/Controllers/ClientLogController.cs @@ -1,4 +1,5 @@ -using Jellyfin.Api.Constants; +using System.Threading.Tasks; +using Jellyfin.Api.Constants; using Jellyfin.Api.Models.ClientLogDtos; using MediaBrowser.Controller.ClientEvent; using MediaBrowser.Model.ClientLog; @@ -57,6 +58,20 @@ namespace Jellyfin.Api.Controllers return NoContent(); } + /// + /// Upload a log file. + /// + /// The file. + /// Submission status. + [HttpPost("File")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task LogFile(IFormFile file) + { + await _clientEventLogger.WriteFileAsync(file.FileName, file.OpenReadStream()) + .ConfigureAwait(false); + return NoContent(); + } + private void Log(ClientLogEventDto dto) { _clientEventLogger.Log(new ClientLogEvent( @@ -69,4 +84,4 @@ namespace Jellyfin.Api.Controllers dto.Message)); } } -} \ No newline at end of file +} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 778e53cf6..2f8986da8 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -607,7 +607,7 @@ namespace Jellyfin.Server "ClientName", (clientName, wt) => wt.File( - Path.Combine(appPaths.LogDirectoryPath, clientName + "_.log"), + Path.Combine(appPaths.LogDirectoryPath, "log_" + clientName + "_.log"), rollingInterval: RollingInterval.Day, outputTemplate: "{Message:l}{NewLine}{Exception}", encoding: Encoding.UTF8)) @@ -632,7 +632,7 @@ namespace Jellyfin.Server "ClientName", (clientName, wt) => wt.File( - Path.Combine(appPaths.LogDirectoryPath, clientName + "_.log"), + Path.Combine(appPaths.LogDirectoryPath, "log_" + clientName + "_.log"), rollingInterval: RollingInterval.Day, outputTemplate: "{Message:l}{NewLine}{Exception}", encoding: Encoding.UTF8)) diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs index c00a38d1b..bdc1a7eff 100644 --- a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using System.Threading.Tasks; using MediaBrowser.Model.ClientLog; using Microsoft.Extensions.Logging; @@ -9,14 +11,19 @@ namespace MediaBrowser.Controller.ClientEvent { private const string LogString = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level}] [{ClientName}:{ClientVersion}]: UserId: {UserId} DeviceId: {DeviceId}{NewLine}{Message}"; private readonly ILogger _logger; + private readonly IServerApplicationPaths _applicationPaths; /// /// Initializes a new instance of the class. /// /// Instance of the interface. - public ClientEventLogger(ILogger logger) + /// Instance of the interface. + public ClientEventLogger( + ILogger logger, + IServerApplicationPaths applicationPaths) { _logger = logger; + _applicationPaths = applicationPaths; } /// @@ -34,5 +41,16 @@ namespace MediaBrowser.Controller.ClientEvent Environment.NewLine, clientLogEvent.Message); } + + /// + public async Task WriteFileAsync(string fileName, Stream fileContents) + { + // Force naming convention: upload_YYYYMMDD_$name + fileName = $"upload_{DateTime.UtcNow:yyyyMMdd}_{fileName}"; + var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName); + await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None); + await fileContents.CopyToAsync(fileStream).ConfigureAwait(false); + await fileStream.FlushAsync().ConfigureAwait(false); + } } -} \ No newline at end of file +} diff --git a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs index bf799c7bf..7cd71a60d 100644 --- a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Model.ClientLog; +using System.IO; +using System.Threading.Tasks; +using MediaBrowser.Model.ClientLog; namespace MediaBrowser.Controller.ClientEvent { @@ -12,5 +14,13 @@ namespace MediaBrowser.Controller.ClientEvent /// /// The client log event. void Log(ClientLogEvent clientLogEvent); + + /// + /// Writes a file to the log directory. + /// + /// The file name. + /// The file contents. + /// A representing the asynchronous operation. + Task WriteFileAsync(string fileName, Stream fileContents); } -} \ No newline at end of file +} -- cgit v1.2.3 From c534c450330759f6595c9601e3fe8b12e6987e69 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Wed, 27 Oct 2021 19:20:14 -0600 Subject: Suggestions from review --- Jellyfin.Api/Controllers/ClientLogController.cs | 53 +++++++++++++++++++--- .../ClientEvent/ClientEventLogger.cs | 6 +-- .../ClientEvent/IClientEventLogger.cs | 7 +-- MediaBrowser.Model/ClientLog/ClientLogEvent.cs | 2 +- .../Configuration/ServerConfiguration.cs | 5 ++ 5 files changed, 59 insertions(+), 14 deletions(-) (limited to 'MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs') diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs index 9fe3bf731..aac3f6a73 100644 --- a/Jellyfin.Api/Controllers/ClientLogController.cs +++ b/Jellyfin.Api/Controllers/ClientLogController.cs @@ -1,7 +1,11 @@ -using System.Threading.Tasks; +using System.Net.Mime; +using System.Threading.Tasks; +using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Models.ClientLogDtos; using MediaBrowser.Controller.ClientEvent; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.ClientLog; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -15,15 +19,25 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] public class ClientLogController : BaseJellyfinApiController { + private const int MaxDocumentSize = 1_000_000; private readonly IClientEventLogger _clientEventLogger; + private readonly IAuthorizationContext _authorizationContext; + private readonly IServerConfigurationManager _serverConfigurationManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. - public ClientLogController(IClientEventLogger clientEventLogger) + /// Instance of the interface. + /// Instance of the interface. + public ClientLogController( + IClientEventLogger clientEventLogger, + IAuthorizationContext authorizationContext, + IServerConfigurationManager serverConfigurationManager) { _clientEventLogger = clientEventLogger; + _authorizationContext = authorizationContext; + _serverConfigurationManager = serverConfigurationManager; } /// @@ -36,6 +50,11 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult LogEvent([FromBody] ClientLogEventDto clientLogEventDto) { + if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) + { + return Forbid(); + } + Log(clientLogEventDto); return NoContent(); } @@ -50,6 +69,11 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult LogEvents([FromBody] ClientLogEventDto[] clientLogEventDtos) { + if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) + { + return Forbid(); + } + foreach (var dto in clientLogEventDtos) { Log(dto); @@ -59,15 +83,30 @@ namespace Jellyfin.Api.Controllers } /// - /// Upload a log file. + /// Upload a document. /// - /// The file. /// Submission status. - [HttpPost("File")] + [HttpPost("Document")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task LogFile(IFormFile file) + [AcceptsFile(MediaTypeNames.Text.Plain)] + [RequestSizeLimit(MaxDocumentSize)] + public async Task LogFile() { - await _clientEventLogger.WriteFileAsync(file.FileName, file.OpenReadStream()) + if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) + { + return Forbid(); + } + + if (Request.ContentLength > MaxDocumentSize) + { + // Manually validate to return proper status code. + return StatusCode(StatusCodes.Status413PayloadTooLarge, $"Payload must be less than {MaxDocumentSize:N0} bytes"); + } + + var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request) + .ConfigureAwait(false); + + await _clientEventLogger.WriteDocumentAsync(authorizationInfo, Request.Body) .ConfigureAwait(false); return NoContent(); } diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs index bdc1a7eff..61f7adff3 100644 --- a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Threading.Tasks; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.ClientLog; using Microsoft.Extensions.Logging; @@ -43,10 +44,9 @@ namespace MediaBrowser.Controller.ClientEvent } /// - public async Task WriteFileAsync(string fileName, Stream fileContents) + public async Task WriteDocumentAsync(AuthorizationInfo authorizationInfo, Stream fileContents) { - // Force naming convention: upload_YYYYMMDD_$name - fileName = $"upload_{DateTime.UtcNow:yyyyMMdd}_{fileName}"; + var fileName = $"upload_{authorizationInfo.Client}_{authorizationInfo.Version}_{DateTime.UtcNow:yyyyMMddHHmmss}.log"; var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName); await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None); await fileContents.CopyToAsync(fileStream).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs index 7cd71a60d..ee8e5806b 100644 --- a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs @@ -1,5 +1,6 @@ using System.IO; using System.Threading.Tasks; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.ClientLog; namespace MediaBrowser.Controller.ClientEvent @@ -18,9 +19,9 @@ namespace MediaBrowser.Controller.ClientEvent /// /// Writes a file to the log directory. /// - /// The file name. - /// The file contents. + /// The current authorization info. + /// The file contents to write. /// A representing the asynchronous operation. - Task WriteFileAsync(string fileName, Stream fileContents); + Task WriteDocumentAsync(AuthorizationInfo authorizationInfo, Stream fileContents); } } diff --git a/MediaBrowser.Model/ClientLog/ClientLogEvent.cs b/MediaBrowser.Model/ClientLog/ClientLogEvent.cs index e4ee88145..21087b564 100644 --- a/MediaBrowser.Model/ClientLog/ClientLogEvent.cs +++ b/MediaBrowser.Model/ClientLog/ClientLogEvent.cs @@ -72,4 +72,4 @@ namespace MediaBrowser.Model.ClientLog /// public string Message { get; } } -} \ No newline at end of file +} diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index d1e999666..37dc49d7a 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -459,5 +459,10 @@ namespace MediaBrowser.Model.Configuration /// Gets or sets a value indicating whether older plugins should automatically be deleted from the plugin folder. /// public bool RemoveOldPlugins { get; set; } + + /// + /// Gets or sets a value indicating whether clients should be allowed to upload logs. + /// + public bool AllowClientLogUpload { get; set; } } } -- cgit v1.2.3 From 91204fc9f0e704a61300a7bd54f52d56f02f44b3 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Wed, 27 Oct 2021 19:40:35 -0600 Subject: Fix logfile name if api key is used --- MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs') diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs index 61f7adff3..04d0a3c43 100644 --- a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs @@ -46,7 +46,7 @@ namespace MediaBrowser.Controller.ClientEvent /// public async Task WriteDocumentAsync(AuthorizationInfo authorizationInfo, Stream fileContents) { - var fileName = $"upload_{authorizationInfo.Client}_{authorizationInfo.Version}_{DateTime.UtcNow:yyyyMMddHHmmss}.log"; + var fileName = $"upload_{authorizationInfo.Client}_{(authorizationInfo.IsApiKey ? "apikey" : authorizationInfo.Version)}_{DateTime.UtcNow:yyyyMMddHHmmss}.log"; var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName); await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None); await fileContents.CopyToAsync(fileStream).ConfigureAwait(false); -- cgit v1.2.3 From 0e584f68409e71204f6b9387cde8efe2adb0fbed Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 28 Oct 2021 16:11:14 -0600 Subject: Update documentation; use information from authorization; return generated filename --- Jellyfin.Api/Controllers/ClientLogController.cs | 43 +++++++++++++++------- .../Models/ClientLogDtos/ClientLogEventDto.cs | 24 ------------ .../ClientEvent/ClientEventLogger.cs | 3 +- .../ClientEvent/IClientEventLogger.cs | 4 +- 4 files changed, 33 insertions(+), 41 deletions(-) (limited to 'MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs') diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs index aac3f6a73..f50d56097 100644 --- a/Jellyfin.Api/Controllers/ClientLogController.cs +++ b/Jellyfin.Api/Controllers/ClientLogController.cs @@ -45,17 +45,22 @@ namespace Jellyfin.Api.Controllers /// /// The client log dto. /// Event logged. + /// Event logging disabled. /// Submission status. [HttpPost] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult LogEvent([FromBody] ClientLogEventDto clientLogEventDto) + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task LogEvent([FromBody] ClientLogEventDto clientLogEventDto) { if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) { return Forbid(); } - Log(clientLogEventDto); + var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request) + .ConfigureAwait(false); + + Log(clientLogEventDto, authorizationInfo); return NoContent(); } @@ -64,19 +69,24 @@ namespace Jellyfin.Api.Controllers /// /// The list of client log dtos. /// All events logged. + /// Event logging disabled. /// Submission status. [HttpPost("Bulk")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult LogEvents([FromBody] ClientLogEventDto[] clientLogEventDtos) + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task LogEvents([FromBody] ClientLogEventDto[] clientLogEventDtos) { if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) { return Forbid(); } + var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request) + .ConfigureAwait(false); + foreach (var dto in clientLogEventDtos) { - Log(dto); + Log(dto, authorizationInfo); } return NoContent(); @@ -85,12 +95,17 @@ namespace Jellyfin.Api.Controllers /// /// Upload a document. /// - /// Submission status. + /// Document saved. + /// Event logging disabled. + /// Upload size too large. + /// Created file name. [HttpPost("Document")] - [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)] [AcceptsFile(MediaTypeNames.Text.Plain)] [RequestSizeLimit(MaxDocumentSize)] - public async Task LogFile() + public async Task> LogFile() { if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) { @@ -106,20 +121,20 @@ namespace Jellyfin.Api.Controllers var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request) .ConfigureAwait(false); - await _clientEventLogger.WriteDocumentAsync(authorizationInfo, Request.Body) + var fileName = await _clientEventLogger.WriteDocumentAsync(authorizationInfo, Request.Body) .ConfigureAwait(false); - return NoContent(); + return Ok(fileName); } - private void Log(ClientLogEventDto dto) + private void Log(ClientLogEventDto dto, AuthorizationInfo authorizationInfo) { _clientEventLogger.Log(new ClientLogEvent( dto.Timestamp, dto.Level, - dto.UserId, - dto.ClientName, - dto.ClientVersion, - dto.DeviceId, + authorizationInfo.UserId, + authorizationInfo.Client, + authorizationInfo.Version, + authorizationInfo.DeviceId, dto.Message)); } } diff --git a/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs b/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs index 04d97047a..9bf9be0a4 100644 --- a/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs +++ b/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs @@ -21,30 +21,6 @@ namespace Jellyfin.Api.Models.ClientLogDtos [Required] public LogLevel Level { get; set; } - /// - /// Gets or sets the user id. - /// - public Guid? UserId { get; set; } - - /// - /// Gets or sets the client name. - /// - [Required] - public string ClientName { get; set; } = string.Empty; - - /// - /// Gets or sets the client version. - /// - [Required] - public string ClientVersion { get; set; } = string.Empty; - - /// - /// - /// Gets or sets the device id. - /// - [Required] - public string DeviceId { get; set; } = string.Empty; - /// /// Gets or sets the log message. /// diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs index 04d0a3c43..870070d35 100644 --- a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs @@ -44,13 +44,14 @@ namespace MediaBrowser.Controller.ClientEvent } /// - public async Task WriteDocumentAsync(AuthorizationInfo authorizationInfo, Stream fileContents) + public async Task WriteDocumentAsync(AuthorizationInfo authorizationInfo, Stream fileContents) { var fileName = $"upload_{authorizationInfo.Client}_{(authorizationInfo.IsApiKey ? "apikey" : authorizationInfo.Version)}_{DateTime.UtcNow:yyyyMMddHHmmss}.log"; var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName); await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None); await fileContents.CopyToAsync(fileStream).ConfigureAwait(false); await fileStream.FlushAsync().ConfigureAwait(false); + return fileName; } } } diff --git a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs index ee8e5806b..6fc54faf2 100644 --- a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs @@ -21,7 +21,7 @@ namespace MediaBrowser.Controller.ClientEvent /// /// The current authorization info. /// The file contents to write. - /// A representing the asynchronous operation. - Task WriteDocumentAsync(AuthorizationInfo authorizationInfo, Stream fileContents); + /// The created file name. + Task WriteDocumentAsync(AuthorizationInfo authorizationInfo, Stream fileContents); } } -- cgit v1.2.3 From 17264a6020774ac50b48bf5fe61ca9a3b1ec4d19 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 5 Nov 2021 10:40:45 -0600 Subject: Use client info from claims --- Jellyfin.Api/Controllers/ClientLogController.cs | 58 ++++++++++++---------- .../ClientEvent/ClientEventLogger.cs | 4 +- .../ClientEvent/IClientEventLogger.cs | 8 ++- 3 files changed, 41 insertions(+), 29 deletions(-) (limited to 'MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs') diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs index 7068c9771..95d07c930 100644 --- a/Jellyfin.Api/Controllers/ClientLogController.cs +++ b/Jellyfin.Api/Controllers/ClientLogController.cs @@ -1,11 +1,12 @@ -using System.Net.Mime; +using System; +using System.Net.Mime; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; +using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.ClientLogDtos; using MediaBrowser.Controller.ClientEvent; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.ClientLog; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -21,22 +22,18 @@ namespace Jellyfin.Api.Controllers { private const int MaxDocumentSize = 1_000_000; private readonly IClientEventLogger _clientEventLogger; - private readonly IAuthorizationContext _authorizationContext; private readonly IServerConfigurationManager _serverConfigurationManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. public ClientLogController( IClientEventLogger clientEventLogger, - IAuthorizationContext authorizationContext, IServerConfigurationManager serverConfigurationManager) { _clientEventLogger = clientEventLogger; - _authorizationContext = authorizationContext; _serverConfigurationManager = serverConfigurationManager; } @@ -50,17 +47,15 @@ namespace Jellyfin.Api.Controllers [HttpPost] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public async Task LogEvent([FromBody] ClientLogEventDto clientLogEventDto) + public ActionResult LogEvent([FromBody] ClientLogEventDto clientLogEventDto) { if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) { return Forbid(); } - var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request) - .ConfigureAwait(false); - - Log(clientLogEventDto, authorizationInfo); + var (clientName, clientVersion, userId, deviceId) = GetRequestInformation(); + Log(clientLogEventDto, userId, clientName, clientVersion, deviceId); return NoContent(); } @@ -74,19 +69,17 @@ namespace Jellyfin.Api.Controllers [HttpPost("Bulk")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public async Task LogEvents([FromBody] ClientLogEventDto[] clientLogEventDtos) + public ActionResult LogEvents([FromBody] ClientLogEventDto[] clientLogEventDtos) { if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) { return Forbid(); } - var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request) - .ConfigureAwait(false); - + var (clientName, clientVersion, userId, deviceId) = GetRequestInformation(); foreach (var dto in clientLogEventDtos) { - Log(dto, authorizationInfo); + Log(dto, userId, clientName, clientVersion, deviceId); } return NoContent(); @@ -118,24 +111,39 @@ namespace Jellyfin.Api.Controllers return StatusCode(StatusCodes.Status413PayloadTooLarge, $"Payload must be less than {MaxDocumentSize:N0} bytes"); } - var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request) - .ConfigureAwait(false); - - var fileName = await _clientEventLogger.WriteDocumentAsync(authorizationInfo, Request.Body) + var (clientName, clientVersion, _, _) = GetRequestInformation(); + var fileName = await _clientEventLogger.WriteDocumentAsync(clientName, clientVersion, Request.Body) .ConfigureAwait(false); return Ok(new ClientLogDocumentResponseDto(fileName)); } - private void Log(ClientLogEventDto dto, AuthorizationInfo authorizationInfo) + private void Log( + ClientLogEventDto dto, + Guid userId, + string clientName, + string clientVersion, + string deviceId) { _clientEventLogger.Log(new ClientLogEvent( dto.Timestamp, dto.Level, - authorizationInfo.UserId, - authorizationInfo.Client, - authorizationInfo.Version, - authorizationInfo.DeviceId, + userId, + clientName, + clientVersion, + deviceId, dto.Message)); } + + private (string ClientName, string ClientVersion, Guid UserId, string DeviceId) GetRequestInformation() + { + var clientName = ClaimHelpers.GetClient(HttpContext.User) ?? "unknown-client"; + var clientVersion = ClaimHelpers.GetIsApiKey(HttpContext.User) + ? "apikey" + : ClaimHelpers.GetVersion(HttpContext.User) ?? "unknown-version"; + var userId = ClaimHelpers.GetUserId(HttpContext.User) ?? Guid.Empty; + var deviceId = ClaimHelpers.GetDeviceId(HttpContext.User) ?? "unknown-device-id"; + + return (clientName, clientVersion, userId, deviceId); + } } } diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs index 870070d35..2dff0f931 100644 --- a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs @@ -44,9 +44,9 @@ namespace MediaBrowser.Controller.ClientEvent } /// - public async Task WriteDocumentAsync(AuthorizationInfo authorizationInfo, Stream fileContents) + public async Task WriteDocumentAsync(string clientName, string clientVersion, Stream fileContents) { - var fileName = $"upload_{authorizationInfo.Client}_{(authorizationInfo.IsApiKey ? "apikey" : authorizationInfo.Version)}_{DateTime.UtcNow:yyyyMMddHHmmss}.log"; + var fileName = $"upload_{clientName}_{clientVersion}_{DateTime.UtcNow:yyyyMMddHHmmss}.log"; var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName); await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None); await fileContents.CopyToAsync(fileStream).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs index 6fc54faf2..34968d493 100644 --- a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs @@ -19,9 +19,13 @@ namespace MediaBrowser.Controller.ClientEvent /// /// Writes a file to the log directory. /// - /// The current authorization info. + /// The client name writing the document. + /// The client version writing the document. /// The file contents to write. /// The created file name. - Task WriteDocumentAsync(AuthorizationInfo authorizationInfo, Stream fileContents); + Task WriteDocumentAsync( + string clientName, + string clientVersion, + Stream fileContents); } } -- cgit v1.2.3 From 892b05c5e60b812d02177cef189d5ee7e858e9ab Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sun, 7 Nov 2021 08:20:11 -0700 Subject: Clean up redundant code --- MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs | 2 -- 1 file changed, 2 deletions(-) (limited to 'MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs') diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs index 2dff0f931..31e86f615 100644 --- a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Threading.Tasks; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.ClientLog; using Microsoft.Extensions.Logging; @@ -50,7 +49,6 @@ namespace MediaBrowser.Controller.ClientEvent var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName); await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None); await fileContents.CopyToAsync(fileStream).ConfigureAwait(false); - await fileStream.FlushAsync().ConfigureAwait(false); return fileName; } } -- cgit v1.2.3 From 666e95e27f92eabb15d6ab7ae399bdbb95eeb318 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sun, 7 Nov 2021 11:41:56 -0700 Subject: Add randomization to generated filename --- MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs') diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs index 31e86f615..82b5b4593 100644 --- a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs @@ -45,7 +45,7 @@ namespace MediaBrowser.Controller.ClientEvent /// public async Task WriteDocumentAsync(string clientName, string clientVersion, Stream fileContents) { - var fileName = $"upload_{clientName}_{clientVersion}_{DateTime.UtcNow:yyyyMMddHHmmss}.log"; + var fileName = $"upload_{clientName}_{clientVersion}_{DateTime.UtcNow:yyyyMMddHHmmss}_{Guid.NewGuid():N}.log"; var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName); await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None); await fileContents.CopyToAsync(fileStream).ConfigureAwait(false); -- cgit v1.2.3 From ea355b4262fa0b817930d2bb751c05e6e0ee3022 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sat, 20 Nov 2021 08:47:05 -0700 Subject: Remove ClientLog endpoints --- Jellyfin.Api/Controllers/ClientLogController.cs | 77 ++-------------------- .../Models/ClientLogDtos/ClientLogEventDto.cs | 30 --------- Jellyfin.Server/Jellyfin.Server.csproj | 1 - Jellyfin.Server/Program.cs | 48 ++++---------- .../ClientEvent/ClientEventLogger.cs | 26 +------- .../ClientEvent/IClientEventLogger.cs | 8 --- 6 files changed, 16 insertions(+), 174 deletions(-) delete mode 100644 Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs (limited to 'MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs') diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs index 95d07c930..98fd22430 100644 --- a/Jellyfin.Api/Controllers/ClientLogController.cs +++ b/Jellyfin.Api/Controllers/ClientLogController.cs @@ -1,5 +1,4 @@ -using System; -using System.Net.Mime; +using System.Net.Mime; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; @@ -7,7 +6,6 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.ClientLogDtos; using MediaBrowser.Controller.ClientEvent; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Model.ClientLog; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -37,54 +35,6 @@ namespace Jellyfin.Api.Controllers _serverConfigurationManager = serverConfigurationManager; } - /// - /// Post event from client. - /// - /// The client log dto. - /// Event logged. - /// Event logging disabled. - /// Submission status. - [HttpPost] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - public ActionResult LogEvent([FromBody] ClientLogEventDto clientLogEventDto) - { - if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) - { - return Forbid(); - } - - var (clientName, clientVersion, userId, deviceId) = GetRequestInformation(); - Log(clientLogEventDto, userId, clientName, clientVersion, deviceId); - return NoContent(); - } - - /// - /// Bulk post events from client. - /// - /// The list of client log dtos. - /// All events logged. - /// Event logging disabled. - /// Submission status. - [HttpPost("Bulk")] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - public ActionResult LogEvents([FromBody] ClientLogEventDto[] clientLogEventDtos) - { - if (!_serverConfigurationManager.Configuration.AllowClientLogUpload) - { - return Forbid(); - } - - var (clientName, clientVersion, userId, deviceId) = GetRequestInformation(); - foreach (var dto in clientLogEventDtos) - { - Log(dto, userId, clientName, clientVersion, deviceId); - } - - return NoContent(); - } - /// /// Upload a document. /// @@ -111,39 +61,20 @@ namespace Jellyfin.Api.Controllers return StatusCode(StatusCodes.Status413PayloadTooLarge, $"Payload must be less than {MaxDocumentSize:N0} bytes"); } - var (clientName, clientVersion, _, _) = GetRequestInformation(); + var (clientName, clientVersion) = GetRequestInformation(); var fileName = await _clientEventLogger.WriteDocumentAsync(clientName, clientVersion, Request.Body) .ConfigureAwait(false); return Ok(new ClientLogDocumentResponseDto(fileName)); } - private void Log( - ClientLogEventDto dto, - Guid userId, - string clientName, - string clientVersion, - string deviceId) - { - _clientEventLogger.Log(new ClientLogEvent( - dto.Timestamp, - dto.Level, - userId, - clientName, - clientVersion, - deviceId, - dto.Message)); - } - - private (string ClientName, string ClientVersion, Guid UserId, string DeviceId) GetRequestInformation() + private (string ClientName, string ClientVersion) GetRequestInformation() { var clientName = ClaimHelpers.GetClient(HttpContext.User) ?? "unknown-client"; var clientVersion = ClaimHelpers.GetIsApiKey(HttpContext.User) ? "apikey" : ClaimHelpers.GetVersion(HttpContext.User) ?? "unknown-version"; - var userId = ClaimHelpers.GetUserId(HttpContext.User) ?? Guid.Empty; - var deviceId = ClaimHelpers.GetDeviceId(HttpContext.User) ?? "unknown-device-id"; - return (clientName, clientVersion, userId, deviceId); + return (clientName, clientVersion); } } } diff --git a/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs b/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs deleted file mode 100644 index 9bf9be0a4..000000000 --- a/Jellyfin.Api/Models/ClientLogDtos/ClientLogEventDto.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Api.Models.ClientLogDtos -{ - /// - /// The client log dto. - /// - public class ClientLogEventDto - { - /// - /// Gets or sets the event timestamp. - /// - [Required] - public DateTime Timestamp { get; set; } - - /// - /// Gets or sets the log level. - /// - [Required] - public LogLevel Level { get; set; } - - /// - /// Gets or sets the log message. - /// - [Required] - public string Message { get; set; } = string.Empty; - } -} diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 045ed6a2b..30b6bff9f 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -44,7 +44,6 @@ - diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 6e4c2280b..7f158aebb 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -13,7 +13,6 @@ using Emby.Server.Implementations; using Jellyfin.Server.Implementations; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.ClientEvent; using MediaBrowser.Controller.Extensions; using MediaBrowser.Model.IO; using Microsoft.AspNetCore.Hosting; @@ -26,7 +25,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Serilog; using Serilog.Extensions.Logging; -using Serilog.Filters; using SQLitePCL; using ConfigurationExtensions = MediaBrowser.Controller.Extensions.ConfigurationExtensions; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -598,46 +596,22 @@ namespace Jellyfin.Server { // Serilog.Log is used by SerilogLoggerFactory when no logger is specified Log.Logger = new LoggerConfiguration() - .WriteTo.Logger(lc => - lc.ReadFrom.Configuration(configuration) - .Enrich.FromLogContext() - .Enrich.WithThreadId() - .Filter.ByExcluding(Matching.FromSource())) - .WriteTo.Logger(lc => - lc.WriteTo.Map( - "ClientName", - (clientName, wt) - => wt.File( - Path.Combine(appPaths.LogDirectoryPath, "log_" + clientName + "_.log"), - rollingInterval: RollingInterval.Day, - outputTemplate: "{Message:l}{NewLine}{Exception}", - encoding: Encoding.UTF8)) - .Filter.ByIncludingOnly(Matching.FromSource())) + .ReadFrom.Configuration(configuration) + .Enrich.FromLogContext() + .Enrich.WithThreadId() .CreateLogger(); } catch (Exception ex) { Log.Logger = new LoggerConfiguration() - .WriteTo.Logger(lc => - lc.WriteTo.Async(x => x.File( - Path.Combine(appPaths.LogDirectoryPath, "log_.log"), - rollingInterval: RollingInterval.Day, - outputTemplate: "{Message:l}{NewLine}{Exception}", - encoding: Encoding.UTF8)) - .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}") - .Enrich.FromLogContext() - .Enrich.WithThreadId()) - .WriteTo.Logger(lc => - lc - .WriteTo.Map( - "ClientName", - (clientName, wt) - => wt.File( - Path.Combine(appPaths.LogDirectoryPath, "log_" + clientName + "_.log"), - rollingInterval: RollingInterval.Day, - outputTemplate: "{Message:l}{NewLine}{Exception}", - encoding: Encoding.UTF8)) - .Filter.ByIncludingOnly(Matching.FromSource())) + .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}") + .WriteTo.Async(x => x.File( + Path.Combine(appPaths.LogDirectoryPath, "log_.log"), + rollingInterval: RollingInterval.Day, + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}", + encoding: Encoding.UTF8)) + .Enrich.FromLogContext() + .Enrich.WithThreadId() .CreateLogger(); Log.Logger.Fatal(ex, "Failed to create/read logger configuration"); diff --git a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs index 82b5b4593..dea1c2f32 100644 --- a/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs @@ -1,47 +1,23 @@ using System; using System.IO; using System.Threading.Tasks; -using MediaBrowser.Model.ClientLog; -using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.ClientEvent { /// public class ClientEventLogger : IClientEventLogger { - private const string LogString = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level}] [{ClientName}:{ClientVersion}]: UserId: {UserId} DeviceId: {DeviceId}{NewLine}{Message}"; - private readonly ILogger _logger; private readonly IServerApplicationPaths _applicationPaths; /// /// Initializes a new instance of the class. /// - /// Instance of the interface. /// Instance of the interface. - public ClientEventLogger( - ILogger logger, - IServerApplicationPaths applicationPaths) + public ClientEventLogger(IServerApplicationPaths applicationPaths) { - _logger = logger; _applicationPaths = applicationPaths; } - /// - public void Log(ClientLogEvent clientLogEvent) - { - _logger.Log( - LogLevel.Critical, - LogString, - clientLogEvent.Timestamp, - clientLogEvent.Level.ToString(), - clientLogEvent.ClientName, - clientLogEvent.ClientVersion, - clientLogEvent.UserId ?? Guid.Empty, - clientLogEvent.DeviceId, - Environment.NewLine, - clientLogEvent.Message); - } - /// public async Task WriteDocumentAsync(string clientName, string clientVersion, Stream fileContents) { diff --git a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs index 34968d493..ad8a1bd24 100644 --- a/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs +++ b/MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs @@ -1,7 +1,5 @@ using System.IO; using System.Threading.Tasks; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.ClientLog; namespace MediaBrowser.Controller.ClientEvent { @@ -10,12 +8,6 @@ namespace MediaBrowser.Controller.ClientEvent /// public interface IClientEventLogger { - /// - /// Logs the event from the client. - /// - /// The client log event. - void Log(ClientLogEvent clientLogEvent); - /// /// Writes a file to the log directory. /// -- cgit v1.2.3