MySQL.Data/src/MySQLActivitySource.cs (90 lines of code) (raw):
// Copyright © 2023, 2025, Oracle and/or its affiliates.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 2.0, as
// published by the Free Software Foundation.
//
// This program is designed to work with certain software (including
// but not limited to OpenSSL) that is licensed under separate terms, as
// designated in a particular file or component or in included license
// documentation. The authors of MySQL hereby grant you an additional
// permission to link the program and your derivative works with the
// separately licensed software that they have either included with
// the program or referenced in the documentation.
//
// Without limiting anything contained in the foregoing, this file,
// which is part of MySQL Connector/NET, is also subject to the
// Universal FOSS Exception, version 1.0, a copy of which can be found at
// http://oss.oracle.com/licenses/universal-foss-exception.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License, version 2.0, for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
using System;
using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using MySql.Data.MySqlClient;
namespace MySql.Data
{
#if NET5_0_OR_GREATER
static class MySQLActivitySource
{
static readonly ActivitySource Source;
static MySQLActivitySource()
{
var assembly = typeof(MySQLActivitySource).Assembly;
var version = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version ?? "0.0.0";
Source = new("connector-net", version);
}
private static bool Active => Source.HasListeners();
internal static Activity OpenConnection(MySqlConnectionStringBuilder settings)
{
return InternalOpenConnection(settings, "Connection");
}
internal static Activity OpenPooledConnection(MySqlConnectionStringBuilder settings)
{
return InternalOpenConnection(settings, "Connection (pooled)");
}
private static Activity InternalOpenConnection(MySqlConnectionStringBuilder settings, string name)
{
if (!Active) return null;
var activity = Source.StartActivity(name, ActivityKind.Client);
activity?.SetTag("net.transport", settings.ConnectionProtocol);
activity?.SetTag("db.connection_string", settings.GetConnectionString(false));
if (settings.ConnectionProtocol == MySqlConnectionProtocol.Tcp)
activity?.SetTag("net.peer.port", settings.Port);
return activity;
}
internal static void CloseConnection(Activity activity)
{
activity?.SetTag("otel.status_code", "OK");
activity?.Dispose();
}
internal static void SetException(Activity activity, Exception ex)
{
var tags = new ActivityTagsCollection
{
{ "exception.type", ex.GetType().FullName },
{ "exception.message", ex.Message },
{ "exception.stacktrace", ex.ToString() },
};
var activityEvent = new ActivityEvent("exception", tags: tags);
activity?.AddEvent(activityEvent);
activity?.SetTag("otel.status_code", "ERROR");
activity?.SetTag("otel.status_description", ex is MySqlException ? (ex as MySqlException).SqlState : ex.Message);
activity?.Dispose();
}
internal static Activity CommandStart(MySqlCommand command)
{
if (!Active) return null;
var settings = command.Connection.Settings;
var activity = Activity.Current != null ? Source.StartActivity("SQL Statement", ActivityKind.Client, Activity.Current.Context) : Source.StartActivity("SQL Statement", ActivityKind.Client);
if (Activity.Current != null)
{
// passing through this attribute will propagate the context into the server
string query_attr = $"00-{Activity.Current.Context.TraceId}-{Activity.Current.Context.SpanId}-00";
command.Attributes.SetAttribute("traceparent", query_attr);
}
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("thread.id", Thread.CurrentThread.ManagedThreadId);
activity?.SetTag("thread.name", Thread.CurrentThread.Name);
if (command.CommandType == CommandType.TableDirect)
activity?.SetTag("db.sql.table", command.CommandText);
return activity;
}
internal static void ReceivedFirstResponse(Activity activity)
{
var activityEvent = new ActivityEvent("first-packet-received");
activity.AddEvent(activityEvent);
}
internal static void CommandStop(Activity activity)
{
activity?.SetTag("otel.status_code", "OK");
activity?.Dispose();
}
}
#endif
}