-
-
Notifications
You must be signed in to change notification settings - Fork 158
/
Copy pathSchemaGenerationTracer.cs
153 lines (119 loc) · 5.25 KB
/
SchemaGenerationTracer.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
151
152
153
using JsonApiDotNetCore.Resources.Annotations;
using JsonApiDotNetCore.Serialization.Objects;
using Microsoft.Extensions.Logging;
namespace JsonApiDotNetCore.OpenApi.Swashbuckle;
/// <summary>
/// Enables to log recursive component schema generation at trace level.
/// </summary>
internal sealed partial class SchemaGenerationTracer
{
private readonly ILoggerFactory _loggerFactory;
public SchemaGenerationTracer(ILoggerFactory loggerFactory)
{
ArgumentNullException.ThrowIfNull(loggerFactory);
_loggerFactory = loggerFactory;
}
public ISchemaGenerationTraceScope TraceStart(object generator)
{
ArgumentNullException.ThrowIfNull(generator);
return InnerTraceStart(generator, () => "(none)");
}
public ISchemaGenerationTraceScope TraceStart(object generator, Type schemaType)
{
ArgumentNullException.ThrowIfNull(generator);
ArgumentNullException.ThrowIfNull(schemaType);
return InnerTraceStart(generator, () => GetSchemaTypeName(schemaType));
}
public ISchemaGenerationTraceScope TraceStart(object generator, AtomicOperationCode operationCode)
{
ArgumentNullException.ThrowIfNull(generator);
return InnerTraceStart(generator, () => $"{nameof(AtomicOperationCode)}.{operationCode}");
}
public ISchemaGenerationTraceScope TraceStart(object generator, RelationshipAttribute relationship)
{
ArgumentNullException.ThrowIfNull(generator);
ArgumentNullException.ThrowIfNull(relationship);
return InnerTraceStart(generator,
() => $"{GetSchemaTypeName(relationship.GetType())}({GetSchemaTypeName(relationship.LeftType.ClrType)}.{relationship.Property.Name})");
}
public ISchemaGenerationTraceScope TraceStart(object generator, Type schemaOpenType, RelationshipAttribute relationship)
{
ArgumentNullException.ThrowIfNull(generator);
ArgumentNullException.ThrowIfNull(schemaOpenType);
ArgumentNullException.ThrowIfNull(relationship);
return InnerTraceStart(generator,
() =>
$"{GetSchemaTypeName(schemaOpenType)} with {GetSchemaTypeName(relationship.GetType())}({GetSchemaTypeName(relationship.LeftType.ClrType)}.{relationship.Property.Name})");
}
private ISchemaGenerationTraceScope InnerTraceStart(object generator, Func<string> getSchemaTypeName)
{
ILogger logger = _loggerFactory.CreateLogger(generator.GetType());
if (logger.IsEnabled(LogLevel.Trace))
{
string schemaTypeName = getSchemaTypeName();
return new SchemaGenerationTraceScope(logger, schemaTypeName);
}
return DisabledSchemaGenerationTraceScope.Instance;
}
private static string GetSchemaTypeName(Type type)
{
if (type.IsConstructedGenericType)
{
string typeArguments = string.Join(',', type.GetGenericArguments().Select(GetSchemaTypeName));
int arityIndex = type.Name.IndexOf('`');
return $"{type.Name[..arityIndex]}<{typeArguments}>";
}
return type.Name;
}
private sealed partial class SchemaGenerationTraceScope : ISchemaGenerationTraceScope
{
private static readonly AsyncLocal<int> RecursionDepthAsyncLocal = new();
private readonly ILogger _logger;
private readonly string _schemaTypeName;
private string? _schemaId;
public SchemaGenerationTraceScope(ILogger logger, string schemaTypeName)
{
ArgumentNullException.ThrowIfNull(logger);
ArgumentNullException.ThrowIfNull(schemaTypeName);
_logger = logger;
_schemaTypeName = schemaTypeName;
RecursionDepthAsyncLocal.Value++;
LogStarted(RecursionDepthAsyncLocal.Value, _schemaTypeName);
}
public void TraceSucceeded(string schemaId)
{
_schemaId = schemaId;
}
public void Dispose()
{
if (_schemaId != null)
{
LogSucceeded(RecursionDepthAsyncLocal.Value, _schemaTypeName, _schemaId);
}
else
{
LogFailed(RecursionDepthAsyncLocal.Value, _schemaTypeName);
}
RecursionDepthAsyncLocal.Value--;
}
[LoggerMessage(Level = LogLevel.Trace, SkipEnabledCheck = true, Message = "({Depth:D2}) Started for {SchemaTypeName}.")]
private partial void LogStarted(int depth, string schemaTypeName);
[LoggerMessage(Level = LogLevel.Trace, SkipEnabledCheck = true, Message = "({Depth:D2}) Generated '{SchemaId}' from {SchemaTypeName}.")]
private partial void LogSucceeded(int depth, string schemaTypeName, string schemaId);
[LoggerMessage(Level = LogLevel.Trace, SkipEnabledCheck = true, Message = "({Depth:D2}) Failed for {SchemaTypeName}.")]
private partial void LogFailed(int depth, string schemaTypeName);
}
private sealed class DisabledSchemaGenerationTraceScope : ISchemaGenerationTraceScope
{
public static DisabledSchemaGenerationTraceScope Instance { get; } = new();
private DisabledSchemaGenerationTraceScope()
{
}
public void TraceSucceeded(string schemaId)
{
}
public void Dispose()
{
}
}
}