Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ActivitySource #1036

Closed
bgrainger opened this issue Sep 18, 2021 · 3 comments · Fixed by #1037
Closed

Add ActivitySource #1036

bgrainger opened this issue Sep 18, 2021 · 3 comments · Fixed by #1037
Milestone

Comments

@bgrainger
Copy link
Member

bgrainger commented Sep 18, 2021

Proposal

ADO.NET libraries should be instrumented with the ActivitySource API.

This document is based on the Instrumenting a library/application with .NET Activity API recommendations from OpenTelemetry.

Basics

Install v5.0.1 or later of the System.Diagnostics.DiagnosticSource NuGet package.

Create a single ActivitySource named after the assembly name of the library being instrumented:

ActivitySource activitySource = new(typeof(ExampleDbConnection).Assembly.GetName();

Activities

The ActivityKind MUST be set to ActivityKind.Client.

The standard activity names are:

  • Open - establishing a connection to a database server
    • The actual implementation may simply be retrieving a connection from a pool; this is still recorded as an Activity.
  • Execute - executing a command
    • OpenTelemetry recommends that this be named <db.operation> <db.name>.<db.sql.table>, <db.operation> <db.name>, or <db.name>. Since those values will already be set as custom tags on the Activity, an ActivityListener that is reporting Activity objects to an OpenTelemetry endpoint should override the span name (with the values of those tags) if desired.
    • Is it not necessary to record ExecuteScalar, ExecuteNonQuery, and ExecuteReader as different activities.
    • The Execute activity ends when the DbDataReader returned by ExecuteReader is closed/disposed. AddEvent will be used to record when the first response is returned by the server. For MySqlConnector, the event name is read-result-set-header, and can occur multiple times during an activity if multiple result sets are read. For Npgsql, the event name is received-first-response.
  • Commit - committing a transaction
  • Rollback - rolling back a transaction
    • NOTE: These are currently represented as their own activities, but could alternatively be events added to a "Transaction" Activity that encompasses the entire transaction. This may change in the future based on developments in the OpenTelemetry database conventions.

Future activity names may include the following. These should not be used until formalized by this specification.

  • Close - closing a database connection
  • Prepare - preparing a statement

Tags

These tags are taken from the OpenTelemetry Semantic Conventions; that document is the authoritative source and should take precedence in case of a conflict with the recommendations here.

Tag Type Description Examples Required
db.system string The DBMS. See OTel spec for allowable values. mysql, postgresql, other_sql yes
db.connection_string string The connection string. Sensitive information should be removed. (In many ADO.NET providers, this is the default, but users can override it with Persist Security Info = true.) Server=dbserver; Database=mydb; User ID=myuser no
db.connection_id string The "connection ID" or "server thread" etc. that uniquely identifies this connection on the DB server. (This is an extension to the standard OpenTelemetry attributes.) 173 no
db.user string User name for accessing the database. myuser no
net.peer.name string Remote hostname or similar. Usually the same as DbConnection.DataSource. dbserver [1]
net.peer.ip string Remote IP address of the peer. 127.0.0.1 [1]
net.peer.port int Remote port number. 1433, 3306 [2]
net.transport string Transport protocol used. ip_tcp, inproc yes
db.name string The name of the database being accessed. For commands that switch the database, this should be set to the target database. Usually the same as DbConnection.Database. customersmain yes
db.statement string The database statement being executed, i.e., DbCommand.CommandText. [3] SELECT * FROM example_table;  yes [4]
db.operation string The name of the operation being executed, e.g. the SQL keyword. [5] SELECT, INSERT no
db.sql.table string The name of the primary table that the operation is acting upon, including the schema name (if applicable). [6] public.userscustomers recommended
thread.id int Current managed thread ID, i.e., Thread.CurrentThread.ManagedThreadId. 42 no
thread.name string Current thread name, i.e., Thread.CurrentThread.Name. Worker Thread no
exception.type string The type of the exception (its fully-qualified class name, if applicable). ExampleLib.ExampleDbException [7]
exception.message string The exception message. Could not connect to remote server [7]
exception.stacktrace string A managed stack trace no
  1. One of net.peer.name or net.peer.ip should be specified.
  2. Required if using a non-default port.
  3. The value may be sanitized to exclude sensitive information. In particular, parameter placeholders SHOULD NOT be replaced with parameter values.
  4. Users may want to opt in to sending this data (due to confidentiality and size).
  5. When setting this to an SQL keyword, it is not recommended to attempt any client-side parsing of db.statement just to get this property, but it should be set if the operation name is provided by the library being instrumented. If the SQL statement has an ambiguous operation, or performs more than one operation, this value may be omitted.
  6. It is not recommended to attempt any client-side parsing of db.statement just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set.
  7. If an error occurs, at least one of exception.type and exception.message is required.

Exceptions

If an exception occurs, the Status should be set to ActivityStatusCode.Error and the exception details should be added as an event named exception, as per https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#record-exception.

Example

public class ExampleDbConnection : DbConnection
{
	static ActivitySource _activitySource = new(typeof(ExampleDbConnection).Assembly.GetName().Name,
		typeof(ExampleDbConnection).Assembly.GetName().Version.ToString());

	public override void Open()
	{
		using var activity = _activitySource.StartActivity("Open", ActivityKind.Client);
		try
		{
			if (activity is { IsAllDataRequested: true })
			{
				activity.SetTag("db.system", "other_sql");
				activity.SetTag("db.connection_string", this.ConnectionString);
				activity.SetTag("db.user", userId);
				activity.SetTag("db.name", this.Database);
				activity.SetTag("net.peer.name", this.DataSource);
				activity.SetTag("net.transport", "ip_tcp");
				activity.SetTag("thread.id", Thread.CurrentThread.ManagedThreadId);
			}
			
			// ... actual Open implementation elided ...
		}
		catch (Exception ex) when (activity is { IsAllDataRequested: true })
		{
			activity.SetStatus(ActivityStatusCode.Error, ex.Message);
			activity.AddEvent(new ActivityEvent("exception", tags: new ActivityTagsCollection
			{
				{ "exception.type", exception.GetType().FullName },
				{ "exception.message", exception.Message },
				{ "exception.stacktrace", exception.ToString() },
			}));
			throw;
		}
	}
}
@yang-xiaodong
Copy link

Hello @bgrainger ,

I have a question, after supporting ActivitySource, can we listen the transaction commit and rollback event in ActivityListener?

Thanks

@bgrainger
Copy link
Member Author

Those events aren't yet formalised in this proposal but would be obvious next candidates for publishing as an Activity.

@bgrainger bgrainger added this to the 2.0 milestone Oct 16, 2021
@bgrainger
Copy link
Member Author

Available in 2.0.0-beta.5.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

2 participants