-
Notifications
You must be signed in to change notification settings - Fork 727
/
FakeLogRecord.cs
150 lines (132 loc) · 5.9 KB
/
FakeLogRecord.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Extensions.Logging.Testing;
/// <summary>
/// A single log record tracked by <see cref="FakeLogger"/>.
/// </summary>
public class FakeLogRecord
{
/// <summary>
/// Initializes a new instance of the <see cref="FakeLogRecord"/> class.
/// </summary>
/// <param name="level">The level used when producing the log record.</param>
/// <param name="id">The ID representing the specific log statement.</param>
/// <param name="state">The opaque state supplied by the caller when creating the log record.</param>
/// <param name="exception">An optional exception associated with the log record.</param>
/// <param name="message">The formatted message text for the record.</param>
/// <param name="scopes">List of active scopes active for this log record.</param>
/// <param name="category">The optional category for this record, which corresponds to the T in <see cref="ILogger{T}"/>.</param>
/// <param name="enabled">Whether the log level was enabled or not when the <see cref="FakeLogger.Log"/> method was called.</param>
/// <param name="timestamp">The time at which the log record was created.</param>
#pragma warning disable S107 // Methods should not have too many parameters
public FakeLogRecord(LogLevel level, EventId id, object? state, Exception? exception, string message, IReadOnlyList<object?> scopes, string? category, bool enabled, DateTimeOffset timestamp)
#pragma warning restore S107 // Methods should not have too many parameters
{
Level = level;
Id = id;
State = state;
Exception = exception;
Message = Throw.IfNull(message);
Scopes = Throw.IfNull(scopes);
Category = category;
LevelEnabled = enabled;
Timestamp = timestamp;
}
/// <summary>
/// Gets the level used when producing the log record.
/// </summary>
public LogLevel Level { get; }
/// <summary>
/// Gets the ID representing the specific log statement.
/// </summary>
public EventId Id { get; }
/// <summary>
/// Gets the opaque state supplied by the caller when creating the log record.
/// </summary>
public object? State { get; }
/// <summary>
/// Gets the opaque state supplied by the caller when creating the log record as a read-only list.
/// </summary>
/// <remarks>
/// When logging using the code generator logging model, the arguments you supply to the logging method are packaged into a single state object which is delivered to the <see cref="ILogger.Log"/>
/// method. This state can be retrieved as a set of name/value pairs encoded in a read-only list.
///
/// The object returned by this property is the same as what <see cref="State"/> returns, except it has been cast to a read-only list.
/// </remarks>
/// <exception cref="InvalidCastException">The state object is not compatible with supported logging model and is not a read-only list.</exception>
public IReadOnlyList<KeyValuePair<string, string?>>? StructuredState => (IReadOnlyList<KeyValuePair<string, string?>>?)State;
/// <summary>
/// Gets the value of a particular key value pair in the record's structured state.
/// </summary>
/// <param name="key">The key to search for in the record's structured state.</param>
/// <returns>
/// The value associated with the key, or <see langword="null"/> if the key was not found. If the structured
/// state contains multiple entries with the same key, the value associated with the first matching key encountered is returned.
/// </returns>
public string? GetStructuredStateValue(string key)
{
if (StructuredState is not null)
{
foreach (var kvp in StructuredState)
{
if (kvp.Key == key)
{
return kvp.Value;
}
}
}
return null;
}
/// <summary>
/// Gets an optional exception associated with the log record.
/// </summary>
public Exception? Exception { get; }
/// <summary>
/// Gets the formatted message text for the record.
/// </summary>
public string Message { get; }
/// <summary>
/// Gets the logging scopes active when the log record was created.
/// </summary>
public IReadOnlyList<object?> Scopes { get; }
/// <summary>
/// Gets the optional category of this record.
/// </summary>
/// <remarks>
/// The category corresponds to the T value in <see cref="ILogger{T}" />.
/// </remarks>
public string? Category { get; }
/// <summary>
/// Gets a value indicating whether the log level was enabled or disabled when this record was collected.
/// </summary>
public bool LevelEnabled { get; }
/// <summary>
/// Gets the time at which the log record was created.
/// </summary>
public DateTimeOffset Timestamp { get; }
/// <summary>
/// Returns a string representing this object.
/// </summary>
/// <returns>A string that helps identity this object.</returns>
public override string ToString() => Formatter(this);
internal static string Formatter(FakeLogRecord record)
{
// these strings are kept to the same length so the output lines up nicely
var level = record.Level switch
{
LogLevel.Debug => "debug",
LogLevel.Information => " info",
LogLevel.Warning => " warn",
LogLevel.Error => "error",
LogLevel.Critical => " crit",
LogLevel.Trace => "trace",
LogLevel.None => " none",
_ => "invld",
};
return $"[{record.Timestamp:mm:ss.fff}, {level}] {record.Message}";
}
}