Skip to content

Commit 6d72e66

Browse files
committed
Add XML comments to the LoggingHook
Signed-off-by: Kyle Julian <[email protected]>
1 parent 8b3bbd2 commit 6d72e66

File tree

2 files changed

+176
-209
lines changed

2 files changed

+176
-209
lines changed

src/OpenFeature/Hooks/LoggingHook.cs

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Microsoft.Extensions.Logging;
7+
using OpenFeature.Model;
8+
9+
namespace OpenFeature.Hooks
10+
{
11+
/// <summary>
12+
/// The logging hook is a hook which logs messages during the flag evaluation life-cycle.
13+
/// </summary>
14+
public sealed partial class LoggingHook : Hook
15+
{
16+
private readonly ILogger _logger;
17+
private readonly bool _includeContext;
18+
19+
/// <summary>
20+
/// Initialise a <see cref="LoggingHook"/> with a <paramref name="logger"/> and optional Evaluation Context. <paramref name="includeContext"/> will
21+
/// include properties in the <see cref="HookContext{T}.EvaluationContext"/> to the generated logs.
22+
/// </summary>
23+
public LoggingHook(ILogger logger, bool includeContext = false)
24+
{
25+
this._logger = logger;
26+
this._includeContext = includeContext;
27+
}
28+
29+
/// <inheritdoc/>
30+
public override ValueTask<EvaluationContext> BeforeAsync<T>(HookContext<T> context, IReadOnlyDictionary<string, object>? hints = null, CancellationToken cancellationToken = default)
31+
{
32+
var evaluationContext = this._includeContext ? context.EvaluationContext : null;
33+
34+
var beforeLogContent = new LoggingHookContent(
35+
context.ClientMetadata.Name,
36+
context.ProviderMetadata.Name,
37+
context.FlagKey,
38+
context.DefaultValue?.ToString(),
39+
evaluationContext);
40+
41+
this.HookBeforeStageExecuted(beforeLogContent);
42+
43+
return base.BeforeAsync(context, hints, cancellationToken);
44+
}
45+
46+
/// <inheritdoc/>
47+
public override ValueTask ErrorAsync<T>(HookContext<T> context, Exception error, IReadOnlyDictionary<string, object>? hints = null, CancellationToken cancellationToken = default)
48+
{
49+
var evaluationContext = this._includeContext ? context.EvaluationContext : null;
50+
51+
var beforeLogContent = new LoggingHookContent(
52+
context.ClientMetadata.Name,
53+
context.ProviderMetadata.Name,
54+
context.FlagKey,
55+
context.DefaultValue?.ToString(),
56+
evaluationContext);
57+
58+
this.HookErrorStageExecuted(beforeLogContent);
59+
60+
return base.ErrorAsync(context, error, hints, cancellationToken);
61+
}
62+
63+
/// <inheritdoc/>
64+
public override ValueTask AfterAsync<T>(HookContext<T> context, FlagEvaluationDetails<T> details, IReadOnlyDictionary<string, object>? hints = null, CancellationToken cancellationToken = default)
65+
{
66+
var evaluationContext = this._includeContext ? context.EvaluationContext : null;
67+
68+
var beforeLogContent = new LoggingHookContent(
69+
context.ClientMetadata.Name,
70+
context.ProviderMetadata.Name,
71+
context.FlagKey,
72+
context.DefaultValue?.ToString(),
73+
evaluationContext);
74+
75+
this.HookAfterStageExecuted(beforeLogContent);
76+
77+
return base.AfterAsync(context, details, hints, cancellationToken);
78+
}
79+
80+
[LoggerMessage(
81+
EventId = 0,
82+
Level = LogLevel.Debug,
83+
Message = "Before Flag Evaluation {Content}")]
84+
partial void HookBeforeStageExecuted(LoggingHookContent content);
85+
86+
[LoggerMessage(
87+
EventId = 0,
88+
Level = LogLevel.Error,
89+
Message = "Error during Flag Evaluation {Content}")]
90+
partial void HookErrorStageExecuted(LoggingHookContent content);
91+
92+
[LoggerMessage(
93+
EventId = 0,
94+
Level = LogLevel.Debug,
95+
Message = "After Flag Evaluation {Content}")]
96+
partial void HookAfterStageExecuted(LoggingHookContent content);
97+
98+
/// <summary>
99+
/// Generates a log string with contents provided by the <see cref="LoggingHook"/>.
100+
/// <para>
101+
/// Specification for log contents found at https://github.com/open-feature/spec/blob/d261f68331b94fd8ed10bc72bc0485cfc72a51a8/specification/appendix-a-included-utilities.md#logging-hook
102+
/// </para>
103+
/// </summary>
104+
internal class LoggingHookContent
105+
{
106+
private readonly string _domain;
107+
private readonly string _providerName;
108+
private readonly string _flagKey;
109+
private readonly string _defaultValue;
110+
private readonly EvaluationContext? _evaluationContext;
111+
112+
public LoggingHookContent(string? domain, string? providerName, string flagKey, string? defaultValue, EvaluationContext? evaluationContext = null)
113+
{
114+
this._domain = string.IsNullOrEmpty(domain) ? "missing" : domain!;
115+
this._providerName = string.IsNullOrEmpty(providerName) ? "missing" : providerName!;
116+
this._flagKey = flagKey;
117+
this._defaultValue = string.IsNullOrEmpty(defaultValue) ? "missing" : defaultValue!;
118+
this._evaluationContext = evaluationContext;
119+
}
120+
121+
public override string ToString()
122+
{
123+
var stringBuilder = new StringBuilder();
124+
125+
stringBuilder.Append("Domain:");
126+
stringBuilder.Append(this._domain);
127+
stringBuilder.Append(Environment.NewLine);
128+
129+
stringBuilder.Append("ProviderName:");
130+
stringBuilder.Append(this._providerName);
131+
stringBuilder.Append(Environment.NewLine);
132+
133+
stringBuilder.Append("FlagKey:");
134+
stringBuilder.Append(this._flagKey);
135+
stringBuilder.Append(Environment.NewLine);
136+
137+
stringBuilder.Append("DefaultValue:");
138+
stringBuilder.Append(this._defaultValue);
139+
stringBuilder.Append(Environment.NewLine);
140+
141+
if (this._evaluationContext != null)
142+
{
143+
stringBuilder.Append("Context:");
144+
stringBuilder.Append(Environment.NewLine);
145+
foreach (var kvp in this._evaluationContext.AsDictionary())
146+
{
147+
stringBuilder.Append('\t');
148+
stringBuilder.Append(kvp.Key);
149+
stringBuilder.Append(':');
150+
stringBuilder.Append(GetValueString(kvp.Value) ?? "missing");
151+
stringBuilder.Append(Environment.NewLine);
152+
}
153+
}
154+
155+
return stringBuilder.ToString();
156+
}
157+
158+
static string? GetValueString(Value value)
159+
{
160+
if (value.IsNull)
161+
return string.Empty;
162+
163+
if (value.IsString)
164+
return value.AsString;
165+
166+
if (value.IsBoolean)
167+
return value.AsBoolean.ToString();
168+
169+
if (value.IsDateTime)
170+
return value.AsDateTime?.ToString("O");
171+
172+
return value.ToString();
173+
}
174+
}
175+
}
176+
}

src/OpenFeature/LoggingHook.cs

-209
This file was deleted.

0 commit comments

Comments
 (0)