From b8bb7361907ef567b9a486bfafe90785e6ee6c8b Mon Sep 17 00:00:00 2001 From: Zirak Date: Sun, 17 Mar 2024 22:46:05 +0000 Subject: [PATCH] WIP Ensure sql statements end up in ActivitySource There are quite a few ways for an sql statement to wound up being executed, including but not only: 1. Passing it into the `MySQLCommand` constructor 2. Assigning a `MySQLCommand` instance's `CommandText` 3. Using `CommandType.TableDirect` with a table name 4. Using a stored procedure 5. ...with parameters 6. Opening a connection And probably more. The existing bridge into the `ActivitySource` answers some of them, and this commit hopes to bridge some of the gap. Namely: - Scenario 2 (`CommandText` assignment) - Scenario 3 (`TableDirect`) shows the actually executing SQL This commit is currently to be considered in a draft state; it is not ready to be merged. Inline review comments will be added for clarifications and questions. --- MySQL.Data/src/MySQLActivitySource.cs | 4 +-- MySQL.Data/src/MySqlCommand.cs | 2 +- MySQL.Data/tests/MySql.Data.Tests/CmdTests.cs | 12 +++++++ MySQL.Data/tests/MySql.Data.Tests/TestBase.cs | 31 +++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/MySQL.Data/src/MySQLActivitySource.cs b/MySQL.Data/src/MySQLActivitySource.cs index 069a3bc3d..5f0bce2cb 100644 --- a/MySQL.Data/src/MySQLActivitySource.cs +++ b/MySQL.Data/src/MySQLActivitySource.cs @@ -67,7 +67,7 @@ internal static void SetException(Activity activity, Exception ex) } - internal static Activity CommandStart(MySqlCommand command) + internal static Activity CommandStart(MySqlCommand command, string sql) { if (!Active) return null; @@ -81,7 +81,7 @@ internal static Activity CommandStart(MySqlCommand command) activity?.SetTag("db.system", "mysql"); activity?.SetTag("db.name", command.Connection.Database); activity?.SetTag("db.user", command.Connection.Settings.UserID); - activity?.SetTag("db.statement", command.OriginalCommandText); + activity?.SetTag("db.statement", sql); activity?.SetTag("thread.id", Thread.CurrentThread.ManagedThreadId); activity?.SetTag("thread.name", Thread.CurrentThread.Name); if (command.CommandType == CommandType.TableDirect) diff --git a/MySQL.Data/src/MySqlCommand.cs b/MySQL.Data/src/MySqlCommand.cs index c3b5a241b..5e3f02221 100644 --- a/MySQL.Data/src/MySqlCommand.cs +++ b/MySQL.Data/src/MySqlCommand.cs @@ -760,7 +760,7 @@ internal async Task ExecuteReaderAsync(CommandBehavior behavior // Tell whoever is listening that we have started out command #if NET5_0_OR_GREATER - CurrentActivity = MySQLActivitySource.CommandStart(this); + CurrentActivity = MySQLActivitySource.CommandStart(this, sql); #endif try { diff --git a/MySQL.Data/tests/MySql.Data.Tests/CmdTests.cs b/MySQL.Data/tests/MySql.Data.Tests/CmdTests.cs index 3522add9d..ff4b56f16 100644 --- a/MySQL.Data/tests/MySql.Data.Tests/CmdTests.cs +++ b/MySQL.Data/tests/MySql.Data.Tests/CmdTests.cs @@ -477,6 +477,10 @@ public void DontAllowBatching() using (MySqlConnection c = new MySqlConnection(connStr.GetConnectionString(true))) { c.Open(); + + using var _ = AddActivityListener( + (activity) => Assert.AreEqual("SELECT 1", activity.GetTagItem("db.statement")) + ); MySqlCommand cmd = new MySqlCommand("SELECT 1", c); cmd.ExecuteScalar(); } @@ -490,6 +494,13 @@ public void TableCommandType() ExecuteSQL("CREATE TABLE Test1 (id INT, name VARCHAR(20))"); ExecuteSQL("INSERT INTO Test1 VALUES (2, 'B')"); + using var _ = AddActivityListener((activity) => + { + Assert.AreEqual("Test,Test1", activity.GetTagItem("db.sql.table")); + Assert.AreEqual("SELECT * FROM Test,Test1", activity.GetTagItem("db.statement")); + } + ); + MySqlCommand cmd = new MySqlCommand("Test,Test1", Connection); cmd.CommandType = CommandType.TableDirect; using (MySqlDataReader reader = cmd.ExecuteReader()) @@ -949,6 +960,7 @@ public void LastInsertedIdInMultipleStatements() cmd.ExecuteNonQuery(); Assert.AreEqual(2, cmd.LastInsertedId); + using var _ = AddActivityListener((activity) => Assert.AreEqual("SELECT * FROM Test", activity.GetTagItem("db.statement"))); cmd.CommandText = "SELECT * FROM Test"; int id = 1; diff --git a/MySQL.Data/tests/MySql.Data.Tests/TestBase.cs b/MySQL.Data/tests/MySql.Data.Tests/TestBase.cs index 0dcf501fd..db4483389 100644 --- a/MySQL.Data/tests/MySql.Data.Tests/TestBase.cs +++ b/MySQL.Data/tests/MySql.Data.Tests/TestBase.cs @@ -333,6 +333,37 @@ public string GetMySqlServerIp(bool isIpV6 = false) return isIpV6 ? ipv6 : ipv4; } + + // XXX This shouldn't be the final form - it probably shouldn't be placed + // here, the logic is incomplete, etc. Was good for a few hacky tests + public static ActivityListener AddActivityListener(Action activityCheck) + { + var listener = new ActivityListener + { + ShouldListenTo = source => + { + return source.Name == "connector-net"; + }, + ActivityStopped = activity => + { + Assert.AreEqual("mysql", activity.GetTagItem("db.system")); + + if (activityCheck == null) + { + Assert.Fail("Activity raised without a checking function"); + } + else + { + activityCheck(activity); + } + }, + Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData, + }; + + ActivitySource.AddActivityListener(listener); + + return listener; + } #endregion } }