-
Notifications
You must be signed in to change notification settings - Fork 407
Expand file tree
/
Copy pathDatabaseSemanticConventionHelper.cs
More file actions
186 lines (162 loc) · 6.08 KB
/
Copy pathDatabaseSemanticConventionHelper.cs
File metadata and controls
186 lines (162 loc) · 6.08 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
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
#if INCLUDE_SQL_QUERY_PARSER
using System.Diagnostics;
#endif
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Configuration;
#if INCLUDE_SQL_QUERY_PARSER
using OpenTelemetry.Instrumentation;
using OpenTelemetry.Trace;
#endif
namespace OpenTelemetry.Internal;
/// <summary>
/// Helper class for Database Semantic Conventions.
/// </summary>
/// <remarks>
/// Due to a breaking change in the semantic convention, affected instrumentation libraries
/// must inspect an environment variable to determine which attributes to emit.
/// This is expected to be removed when the instrumentation libraries reach Stable.
/// <see href="https://github.com/open-telemetry/semantic-conventions/blob/v1.28.0/docs/database/database-spans.md"/>.
/// </remarks>
internal static class DatabaseSemanticConventionHelper
{
internal const string SemanticConventionOptInKeyName = "OTEL_SEMCONV_STABILITY_OPT_IN";
internal static readonly char[] Separator = [',', ' '];
[Flags]
internal enum DatabaseSemanticConvention
{
/// <summary>
/// Instructs an instrumentation library to emit the old experimental Database attributes.
/// </summary>
Old = 0x1,
/// <summary>
/// Instructs an instrumentation library to emit the new, v1.28.0 Database attributes.
/// </summary>
New = 0x2,
/// <summary>
/// Instructs an instrumentation library to emit both the old and new attributes.
/// </summary>
Dupe = Old | New,
}
public static DatabaseSemanticConvention GetSemanticConventionOptIn(IConfiguration configuration)
{
if (TryGetConfiguredValues(configuration, out var values))
{
if (values.Contains("database/dup"))
{
return DatabaseSemanticConvention.Dupe;
}
else if (values.Contains("database"))
{
return DatabaseSemanticConvention.New;
}
}
return DatabaseSemanticConvention.Old;
}
#if INCLUDE_SQL_QUERY_PARSER
public static void ApplyConventionsForQueryText(
Activity activity,
string? commandText,
bool emitOldAttributes,
bool emitNewAttributes,
bool sanitizeQuery = true,
bool useBackslashEscapes = false)
{
var queryText = commandText ?? string.Empty;
var querySummary = string.Empty;
if (sanitizeQuery)
{
var sqlStatementInfo = SqlProcessor.GetSanitizedSql(commandText, useBackslashEscapes);
queryText = sqlStatementInfo.SanitizedSql;
querySummary = sqlStatementInfo.DbQuerySummary;
}
if (emitOldAttributes)
{
activity.SetTag(SemanticConventions.AttributeDbStatement, queryText);
}
if (emitNewAttributes)
{
activity.SetTag(SemanticConventions.AttributeDbQueryText, queryText);
if (!string.IsNullOrEmpty(querySummary))
{
activity.SetTag(SemanticConventions.AttributeDbQuerySummary, querySummary);
activity.DisplayName = querySummary;
}
}
}
public static void AddTagsForSamplingAndUpdateActivityNameForQueryText(
ref TagList tagList,
string? commandText,
ref string activityName,
bool sanitizeQuery = true)
{
var queryText = commandText ?? string.Empty;
var querySummary = string.Empty;
if (sanitizeQuery)
{
var sqlStatementInfo = SqlProcessor.GetSanitizedSql(commandText);
queryText = sqlStatementInfo.SanitizedSql;
querySummary = sqlStatementInfo.DbQuerySummary;
}
tagList.Add(SemanticConventions.AttributeDbQueryText, queryText);
if (!string.IsNullOrEmpty(querySummary))
{
tagList.Add(SemanticConventions.AttributeDbQuerySummary, querySummary);
activityName = querySummary;
}
}
public static void ApplyConventionsForStoredProcedure(
Activity activity,
string? commandText,
bool emitOldAttributes,
bool emitNewAttributes)
{
if (emitOldAttributes)
{
activity.SetTag(SemanticConventions.AttributeDbStatement, commandText);
}
if (emitNewAttributes)
{
activity.SetTag(SemanticConventions.AttributeDbOperationName, "EXECUTE");
activity.SetTag(SemanticConventions.AttributeDbStoredProcedureName, commandText);
var dbQuerySummary = $"EXECUTE {commandText}";
activity.SetTag(SemanticConventions.AttributeDbQuerySummary, dbQuerySummary);
activity.DisplayName = dbQuerySummary;
}
}
public static void AddTagsForSamplingAndUpdateActivityNameForStoredProcedure(
ref TagList tagsList,
string? commandText,
ref string activityName)
{
tagsList.Add(SemanticConventions.AttributeDbOperationName, "EXECUTE");
tagsList.Add(SemanticConventions.AttributeDbStoredProcedureName, commandText);
var dbQuerySummary = $"EXECUTE {commandText}";
tagsList.Add(SemanticConventions.AttributeDbQuerySummary, dbQuerySummary);
activityName = dbQuerySummary;
}
#endif
private static bool TryGetConfiguredValues(IConfiguration configuration, [NotNullWhen(true)] out HashSet<string>? values)
{
try
{
var stringValue = configuration[SemanticConventionOptInKeyName];
if (string.IsNullOrWhiteSpace(stringValue))
{
values = null;
return false;
}
#pragma warning disable IDE0370 // Suppression is unnecessary
var stringValues = stringValue!.Split(separator: Separator, options: StringSplitOptions.RemoveEmptyEntries);
#pragma warning restore IDE0370 // Suppression is unnecessary
values = new HashSet<string>(stringValues, StringComparer.OrdinalIgnoreCase);
return true;
}
catch
{
values = null;
return false;
}
}
}