// Copyright Epic Games, Inc. All Rights Reserved.
using System.IO.Pipelines;
using System.Text;
using Microsoft.Extensions.Logging;
namespace HordeServer.Auditing
{
///
/// Message from an audit log
///
public interface IAuditLogMessage
{
///
/// Timestamp for the event
///
public DateTime TimeUtc { get; }
///
/// Severity of the message
///
public LogLevel Level { get; }
///
/// The message payload. Should be an encoded JSON object, with format/properties fields.
///
public string Data { get; }
}
///
/// Channel for a particular entity
///
public interface IAuditLogChannel : ILogger
{
///
/// Finds messages matching certain criteria
///
///
///
///
///
/// Cancellation token for the operation
///
IAsyncEnumerable FindAsync(DateTime? minTime = null, DateTime? maxTime = null, int? index = null, int? count = null, CancellationToken cancellationToken = default);
///
/// Deletes messages between a given time range for a particular object
///
/// Minimum time to remove
/// Maximum time to remove
/// Cancellation token for the operation
/// Async task
Task DeleteAsync(DateTime? minTime = null, DateTime? maxTime = null, CancellationToken cancellationToken = default);
///
/// Flush all writes to this log
///
/// Cancellation token for the operation
Task FlushAsync(CancellationToken cancellationToken = default);
}
///
/// Channel for a particular entity
///
public interface IAuditLogChannel : IAuditLogChannel
{
///
/// Identifier for the subject of this channel
///
TSubject Subject { get; }
}
///
/// Message in an audit log
///
/// Type of entity that the log is for
public interface IAuditLogMessage : IAuditLogMessage
{
///
/// Unique id for the entity
///
public TTargetId Subject { get; }
}
///
/// Interface for a collection of log messages for a particular document type
///
public interface IAuditLog
{
///
/// Get the channel for a particular subject
///
///
///
IAuditLogChannel this[TSubject subject] { get; }
}
///
/// Factory for instantiating audit log instances
///
///
public interface IAuditLogFactory
{
///
/// Create a new audit log instance, with the given database name
///
///
///
///
IAuditLog Create(string collectionName, string subjectProperty);
}
///
/// Extension methods for audit log channels
///
public static class AuditLogExtensions
{
///
/// Retrieve historical information about a specific agent
///
/// Channel to query
/// Writer for Json data
/// Minimum time for records to return
/// Maximum time for records to return
/// Offset of the first result
/// Number of records to return
/// Cancellation token for the operation
/// Information about the requested agent
public static async Task FindAsync(this IAuditLogChannel channel, PipeWriter bodyWriter, DateTime? minTime = null, DateTime? maxTime = null, int index = 0, int count = 50, CancellationToken cancellationToken = default)
{
string prefix = "{\n\t\"entries\":\n\t[";
await bodyWriter.WriteAsync(Encoding.UTF8.GetBytes(prefix), cancellationToken);
string separator = "";
await foreach (IAuditLogMessage message in channel.FindAsync(minTime, maxTime, index, count, cancellationToken))
{
string line = $"{separator}\n\t\t{message.Data}";
await bodyWriter.WriteAsync(Encoding.UTF8.GetBytes(line), cancellationToken);
separator = ",";
}
string suffix = "\n\t]\n}";
await bodyWriter.WriteAsync(Encoding.UTF8.GetBytes(suffix), cancellationToken);
}
}
}