-
Notifications
You must be signed in to change notification settings - Fork 874
Expand file tree
/
Copy pathNpgsqlActivitySource.cs
More file actions
120 lines (106 loc) · 4.83 KB
/
NpgsqlActivitySource.cs
File metadata and controls
120 lines (106 loc) · 4.83 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
using Npgsql.Internal;
using System;
using System.Data;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
namespace Npgsql;
static class NpgsqlActivitySource
{
static readonly ActivitySource Source = new("Npgsql", "0.1.0");
internal static bool IsEnabled => Source.HasListeners();
internal static Activity? CommandStart(NpgsqlConnector connector, string commandText, CommandType commandType)
{
var settings = connector.Settings;
var dbName = settings.Database ?? connector.InferredUserName;
string? dbOperation = null;
string? dbSqlTable = null;
string activityName;
switch (commandType)
{
case CommandType.StoredProcedure:
dbOperation = NpgsqlCommand.EnableStoredProcedureCompatMode ? "SELECT" : "CALL";
// In this case our activity name follows the concept of the CommandType.TableDirect case
// ("<db.operation> <db.name>.<db.sql.table>") but replaces db.sql.table with the procedure name
// which seems to match the spec's intent without being explicitly specified that way (it suggests
// using the procedure name but doesn't mention using db.operation or db.name in that case).
activityName = $"{dbOperation} {dbName}.{commandText}";
break;
case CommandType.TableDirect:
dbOperation = "SELECT";
// The OpenTelemetry spec actually asks to include the database name into db.sql.table
// but then again mixes the concept of database and schema.
// As I interpret it, it actually wants db.sql.table to include the schema name and not the
// database name if the concept of schemas exists in the database system.
// This also makes sense in the context of the activity name which otherwise would include the
// database name twice.
dbSqlTable = commandText;
activityName = $"{dbOperation} {dbName}.{dbSqlTable}";
break;
case CommandType.Text:
activityName = dbName;
break;
default:
throw new ArgumentOutOfRangeException(nameof(commandType), commandType, null);
}
var activity = Source.StartActivity(activityName, ActivityKind.Client);
if (activity is not { IsAllDataRequested: true })
return activity;
activity.SetTag("db.system", "postgresql");
activity.SetTag("db.connection_string", connector.UserFacingConnectionString);
activity.SetTag("db.user", connector.InferredUserName);
// We trace the actual (maybe inferred) database name we're connected to, even if it
// wasn't specified in the connection string
activity.SetTag("db.name", dbName);
activity.SetTag("db.statement", commandText);
activity.SetTag("db.connection_id", connector.Id);
if (dbOperation != null)
activity.SetTag("db.operation", dbOperation);
if (dbSqlTable != null)
activity.SetTag("db.sql.table", dbSqlTable);
var endPoint = connector.ConnectedEndPoint;
Debug.Assert(endPoint is not null);
switch (endPoint)
{
case IPEndPoint ipEndPoint:
activity.SetTag("net.transport", "ip_tcp");
activity.SetTag("net.peer.ip", ipEndPoint.Address.ToString());
if (ipEndPoint.Port != 5432)
activity.SetTag("net.peer.port", ipEndPoint.Port);
activity.SetTag("net.peer.name", settings.Host);
break;
case UnixDomainSocketEndPoint:
activity.SetTag("net.transport", "unix");
activity.SetTag("net.peer.name", settings.Host);
break;
default:
throw new ArgumentOutOfRangeException("Invalid endpoint type: " + endPoint.GetType());
}
return activity;
}
internal static void ReceivedFirstResponse(Activity activity)
{
var activityEvent = new ActivityEvent("received-first-response");
activity.AddEvent(activityEvent);
}
internal static void CommandStop(Activity activity)
{
activity.SetTag("otel.status_code", "OK");
activity.Dispose();
}
internal static void SetException(Activity activity, Exception ex, bool escaped = true)
{
var tags = new ActivityTagsCollection
{
{ "exception.type", ex.GetType().FullName },
{ "exception.message", ex.Message },
{ "exception.stacktrace", ex.ToString() },
{ "exception.escaped", escaped }
};
var activityEvent = new ActivityEvent("exception", tags: tags);
activity.AddEvent(activityEvent);
activity.SetTag("otel.status_code", "ERROR");
activity.SetTag("otel.status_description", ex is PostgresException pgEx ? pgEx.SqlState : ex.Message);
activity.Dispose();
}
}