namespace System.Logging;
using System.Collections.Generic;
using System.Threading;
/// A log entry's state made of a message template and its values (.NET's
/// `FormattedLogValues`): each value by its hole's name, then the
/// template as `{OriginalFormat}`; written, the message.
public readonly sealed class FormattedLogValues : IReadOnlyList<KeyValuePair<string, object?>> {
private readonly LogValuesFormatter? _formatter;
private readonly object?[] _values;
private readonly string _original;
/// How many templates are kept read (.NET's): past it, one is read at each entry.
private const i32 MaxCachedFormatters = 1024;
/// The templates read so far, by their text: each read once, not at each entry.
private static readonly Lock<Dictionary<string, LogValuesFormatter>> _formatters = new Lock<Dictionary<string, LogValuesFormatter>>(new Dictionary<string, LogValuesFormatter>());
public FormattedLogValues(string? format, object?[] values) {
_original = format ?? LogValuesFormatter.NullValue;
_formatter = values.Length > 0 && format != null ? Formatter(format) : null;
_values = move values;
}
/// A state of a template read already (`LoggerMessage.Define`'s).
public FormattedLogValues(LogValuesFormatter formatter, object?[] values) {
_original = formatter.Template;
_formatter = values.Length > 0 ? formatter : null;
_values = move values;
}
/// Template `format` read: the one read before, else read now (and
/// kept, while there's room).
private static LogValuesFormatter Formatter(string format) {
let mut found: LogValuesFormatter? = null;
lock (_formatters, ref let cache) {
found = cache.TryGetValue(format);
if (found == null) {
let made = new LogValuesFormatter(format);
if (cache.Count < MaxCachedFormatters) {
cache[format] = made;
}
found = made;
}
}
return found ?? new LogValuesFormatter(format);
}
public i32 Count => _formatter == null ? 1 : _formatter.ValueNames.Count + 1;
public KeyValuePair<string, object?> this[i32 index] {
get {
if (index == Count - 1) {
return new KeyValuePair<string, object?>(LogValuesFormatter.OriginalFormat, _original);
}
let name = _formatter == null ? "" : _formatter.ValueNames[index];
let value = index < _values.Length ? _values[index] : null;
return new KeyValuePair<string, object?>(name, value);
}
}
public IEnumerator<KeyValuePair<string, object?>> GetEnumerator() {
for (let mut i = 0; i < Count; i += 1) {
yield return this[i];
}
}
public override string ToString() => _formatter == null ? _original : _formatter.Format(_values);
/// What makes an entry's message of its state: the state written.
public static string Message(FormattedLogValues state, Error? error) => state.ToString();
}