-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Expand file tree
/
Copy pathKernelFunctionLogMessages.cs
More file actions
230 lines (209 loc) · 10.8 KB
/
KernelFunctionLogMessages.cs
File metadata and controls
230 lines (209 loc) · 10.8 KB
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// Copyright (c) Microsoft. All rights reserved.
#pragma warning disable SYSLIB1006 // Multiple logging methods cannot use the same event id within a class
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using Microsoft.Extensions.Logging;
namespace Microsoft.SemanticKernel;
/// <summary>
/// Extensions for logging <see cref="KernelFunction"/> invocations.
/// This extension uses the <see cref="LoggerMessageAttribute"/> to
/// generate logging code at compile time to achieve optimized code.
/// </summary>
internal static partial class KernelFunctionLogMessages
{
/// <summary>
/// Logs invocation of a <see cref="KernelFunction"/>.
/// </summary>
[LoggerMessage(
EventId = 0,
Level = LogLevel.Information,
Message = "Function {PluginName}-{FunctionName} invoking.")]
public static partial void LogFunctionInvoking(
this ILogger logger,
string? pluginName,
string functionName);
/// <summary>
/// Logs arguments of a <see cref="KernelFunction"/>.
/// The action provides the benefit of caching the template parsing result for better performance.
/// And the public method is a helper to serialize the arguments.
/// </summary>
private static readonly Action<ILogger, string?, string, string, Exception?> s_logFunctionArguments =
LoggerMessage.Define<string?, string, string>(
logLevel: LogLevel.Trace, // Sensitive data, logging as trace, disabled by default
eventId: 0,
"Function {PluginName}-{FunctionName} arguments: {Arguments}");
[RequiresUnreferencedCode("Uses reflection to serialize function arguments, making it incompatible with AOT scenarios.")]
[RequiresDynamicCode("Uses reflection to serialize the function arguments, making it incompatible with AOT scenarios.")]
public static void LogFunctionArguments(this ILogger logger, string? pluginName, string functionName, KernelArguments arguments)
{
LogFunctionArgumentsInternal(logger, pluginName, functionName, arguments);
}
/// <summary>
/// Logs arguments of a <see cref="KernelFunction"/>.
/// </summary>
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "This method is AOT safe.")]
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "This method is AOT safe.")]
public static void LogFunctionArguments(this ILogger logger, string? pluginName, string functionName, KernelArguments arguments, JsonSerializerOptions jsonSerializerOptions)
{
LogFunctionArgumentsInternal(logger, pluginName, functionName, arguments, jsonSerializerOptions);
}
/// <summary>
/// Logs arguments of a <see cref="KernelFunction"/>.
/// </summary>
[RequiresUnreferencedCode("Uses reflection, if no JOSs are supplied, to serialize function arguments, making it incompatible with AOT scenarios.")]
[RequiresDynamicCode("Uses reflection, if no JOSs are supplied, to serialize function arguments, making it incompatible with AOT scenarios.")]
private static void LogFunctionArgumentsInternal(this ILogger logger, string? pluginName, string functionName, KernelArguments arguments, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (logger.IsEnabled(LogLevel.Trace))
{
try
{
string jsonString;
if (jsonSerializerOptions is not null)
{
JsonTypeInfo<KernelArguments> typeInfo = (JsonTypeInfo<KernelArguments>)jsonSerializerOptions.GetTypeInfo(typeof(KernelArguments));
jsonString = JsonSerializer.Serialize(arguments, typeInfo);
}
else
{
jsonString = JsonSerializer.Serialize(arguments);
}
s_logFunctionArguments(logger, pluginName, functionName, jsonString, null);
}
catch (NotSupportedException ex)
{
s_logFunctionArguments(logger, pluginName, functionName, "Failed to serialize arguments to Json", ex);
}
}
}
/// <summary>
/// Logs successful invocation of a <see cref="KernelFunction"/>.
/// </summary>
[LoggerMessage(
EventId = 0,
Level = LogLevel.Information,
Message = "Function {PluginName}-{FunctionName} succeeded.")]
public static partial void LogFunctionInvokedSuccess(this ILogger logger, string? pluginName, string functionName);
/// <summary>
/// Logs result of a <see cref="KernelFunction"/>.
/// The action provides the benefit of caching the template parsing result for better performance.
/// And the public method is a helper to serialize the result.
/// </summary>
private static readonly Action<ILogger, string?, string, string, Exception?> s_logFunctionResultValue =
LoggerMessage.Define<string?, string, string>(
logLevel: LogLevel.Trace, // Sensitive data, logging as trace, disabled by default
eventId: 0,
"Function {PluginName}-{FunctionName} result: {ResultValue}");
[RequiresUnreferencedCode("Uses reflection to serialize function result, making it incompatible with AOT scenarios.")]
[RequiresDynamicCode("Uses reflection to serialize the function result, making it incompatible with AOT scenarios.")]
public static void LogFunctionResultValue(this ILogger logger, string? pluginName, string functionName, FunctionResult? resultValue)
{
LogFunctionResultValueInternal(logger, pluginName, functionName, resultValue);
}
/// <summary>
/// Logs result of a <see cref="KernelFunction"/>.
/// The action provides the benefit of caching the template parsing result for better performance.
/// And the public method is a helper to serialize the result.
/// </summary>
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "This method is AOT safe.")]
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "This method is AOT safe.")]
public static void LogFunctionResultValue(this ILogger logger, string? pluginName, string functionName, FunctionResult? resultValue, JsonSerializerOptions jsonSerializerOptions)
{
LogFunctionResultValueInternal(logger, pluginName, functionName, resultValue, jsonSerializerOptions);
}
[SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "By design. See comment below.")]
[RequiresUnreferencedCode("Uses reflection, if no JOSs are supplied, to serialize function arguments, making it incompatible with AOT scenarios.")]
[RequiresDynamicCode("Uses reflection, if no JOSs are supplied, to serialize function arguments, making it incompatible with AOT scenarios.")]
private static void LogFunctionResultValueInternal(this ILogger logger, string? pluginName, string functionName, FunctionResult? resultValue, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (logger.IsEnabled(LogLevel.Trace))
{
// Attempt to convert the result value to string using the GetValue heuristic
try
{
s_logFunctionResultValue(logger, pluginName, functionName, resultValue?.GetValue<string>() ?? string.Empty, null);
return;
}
catch { }
// Falling back to Json serialization
try
{
string jsonString;
if (jsonSerializerOptions is not null)
{
JsonTypeInfo<object?> typeInfo = (JsonTypeInfo<object?>)jsonSerializerOptions.GetTypeInfo(typeof(object));
jsonString = JsonSerializer.Serialize(resultValue?.Value, typeInfo);
}
else
{
jsonString = JsonSerializer.Serialize(resultValue?.Value);
}
s_logFunctionResultValue(logger, pluginName, functionName, jsonString, null);
}
catch (NotSupportedException ex)
{
// Fall back to ToString() when JSON serialization isn't supported for this type
// (e.g. Microsoft.Extensions.AI.TextContent is not registered in AbstractionsJsonContext)
try
{
var toStringValue = resultValue?.Value?.ToString() ?? string.Empty;
s_logFunctionResultValue(logger, pluginName, functionName, toStringValue, null);
}
catch
{
s_logFunctionResultValue(logger, pluginName, functionName, "Failed to log function result value", ex);
}
}
}
}
/// <summary>
/// Logs <see cref="KernelFunction"/> error.
/// </summary>
[LoggerMessage(
EventId = 0,
Level = LogLevel.Error,
Message = "Function {PluginName}-{FunctionName} failed. Error: {Message}")]
public static partial void LogFunctionError(
this ILogger logger,
string? pluginName,
string functionName,
Exception exception,
string message);
/// <summary>
/// Logs <see cref="KernelFunction"/> complete.
/// </summary>
[LoggerMessage(
EventId = 0,
Level = LogLevel.Information,
Message = "Function {PluginName}-{FunctionName} completed. Duration: {Duration}s")]
public static partial void LogFunctionComplete(
this ILogger logger,
string? pluginName,
string functionName,
double duration);
/// <summary>
/// Logs streaming invocation of a <see cref="KernelFunction"/>.
/// </summary>
[LoggerMessage(
EventId = 0,
Level = LogLevel.Information,
Message = "Function {PluginName}-{FunctionName} streaming.")]
public static partial void LogFunctionStreamingInvoking(
this ILogger logger,
string? pluginName,
string functionName);
/// <summary>
/// Logs <see cref="KernelFunction"/> streaming complete.
/// </summary>
[LoggerMessage(
EventId = 0,
Level = LogLevel.Information,
Message = "Function {PluginName}-{FunctionName} streaming completed. Duration: {Duration}s.")]
public static partial void LogFunctionStreamingComplete(
this ILogger logger,
string? pluginName,
string functionName,
double duration);
}