11using Npgsql . Internal ;
22using System ;
3+ using System . Data ;
34using System . Diagnostics ;
45using System . Net ;
56using System . Net . Sockets ;
@@ -20,19 +21,58 @@ static NpgsqlActivitySource()
2021
2122 internal static bool IsEnabled => Source . HasListeners ( ) ;
2223
23- internal static Activity ? CommandStart ( NpgsqlConnector connector , string sql )
24+ internal static Activity ? CommandStart ( NpgsqlConnector connector , string commandText , CommandType commandType )
2425 {
2526 var settings = connector . Settings ;
26- var activity = Source . StartActivity ( settings . Database ! , ActivityKind . Client ) ;
27+
28+ var dbName = settings . Database ?? connector . InferredUserName ;
29+ string ? dbOperation = null ;
30+ string ? dbSqlTable = null ;
31+ string activityName ;
32+ switch ( commandType )
33+ {
34+ case CommandType . StoredProcedure :
35+ dbOperation = NpgsqlCommand . EnableStoredProcedureCompatMode ? "SELECT" : "CALL" ;
36+ // In this case our activity name follows the concept of the CommandType.TableDirect case
37+ // ("<db.operation> <db.name>.<db.sql.table>") but replaces db.sql.table with the procedure name
38+ // which seems to match the spec's intent without being explicitly specified that way (it suggests
39+ // using the procedure name but doesn't mention using db.operation or db.name in that case).
40+ activityName = $ "{ dbOperation } { dbName } .{ commandText } ";
41+ break ;
42+ case CommandType . TableDirect :
43+ dbOperation = "SELECT" ;
44+ // The OpenTelemetry spec actually asks to include the database name into db.sql.table
45+ // but then again mixes the concept of database and schema.
46+ // As I interpret it, it actually wants db.sql.table to include the schema name and not the
47+ // database name if the concept of schemas exists in the database system.
48+ // This also makes sense in the context of the activity name which otherwise would include the
49+ // database name twice.
50+ dbSqlTable = commandText ;
51+ activityName = $ "{ dbOperation } { dbName } .{ dbSqlTable } ";
52+ break ;
53+ case CommandType . Text :
54+ activityName = dbName ;
55+ break ;
56+ default :
57+ throw new ArgumentOutOfRangeException ( nameof ( commandType ) , commandType , null ) ;
58+ }
59+
60+ var activity = Source . StartActivity ( activityName , ActivityKind . Client ) ;
2761 if ( activity is not { IsAllDataRequested : true } )
2862 return activity ;
2963
3064 activity . SetTag ( "db.system" , "postgresql" ) ;
3165 activity . SetTag ( "db.connection_string" , connector . UserFacingConnectionString ) ;
32- activity . SetTag ( "db.user" , settings . Username ) ;
33- activity . SetTag ( "db.name" , settings . Database ) ;
34- activity . SetTag ( "db.statement" , sql ) ;
66+ activity . SetTag ( "db.user" , connector . InferredUserName ) ;
67+ // We trace the actual (maybe inferred) database name we're connected to, even if it
68+ // wasn't specified in the connection string
69+ activity . SetTag ( "db.name" , dbName ) ;
70+ activity . SetTag ( "db.statement" , commandText ) ;
3571 activity . SetTag ( "db.connection_id" , connector . Id ) ;
72+ if ( dbOperation != null )
73+ activity . SetTag ( "db.operation" , dbOperation ) ;
74+ if ( dbSqlTable != null )
75+ activity . SetTag ( "db.sql.table" , dbSqlTable ) ;
3676
3777 var endPoint = connector . ConnectedEndPoint ;
3878 Debug . Assert ( endPoint is not null ) ;
0 commit comments