# Get CurrentSpan in .Net APM Agent

**URL:** <https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741>\
**Category:** APM\
**Tags:** dotnet\
**Created:** [December 13, 2019, 3:27am UTC](https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741 "2019-12-13T03:27:34Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![rainbowkid11](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/rainbowkid11/32/45796_2.png) [@rainbowkid11](https://discuss.elastic.co/u/rainbowkid11)\
**Post date:** [December 13, 2019, 3:27am UTC](https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741/1 "2019-12-13T03:27:34Z")

</div>

I was looking to add labels to the CurrentSpan in an [Asp.Net](http://Asp.Net) Core application. The basic is idea I had was to use an EF Core Interceptor to add the labels to the CurrentSpan. However, when I access the CurrentSpan it is null. Any Suggestions on how I can access it from here? Or maybe it's just not available because it's closed already. The transaction is not null, but I would like to attach the information it to the span that it is associated with the DB query.

Thanks!

```auto
public class ApmEFInterceptor : DbCommandInterceptor
    {
        private static readonly int _dbCommandNameLength = 50;

        public override DbDataReader ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader result)
        {
            SetSpanName(command.CommandText);
            return base.ReaderExecuted(command, eventData, result);
        }

        public override Task<DbDataReader> ReaderExecutedAsync(DbCommand command, CommandExecutedEventData eventData, DbDataReader result,
            CancellationToken cancellationToken = new CancellationToken())
        {
            SetSpanName(command.CommandText);
            return base.ReaderExecutedAsync(command, eventData, result, cancellationToken);
        }

        public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
        {
            SetRowsAffected(result);
            SetSpanName(command.CommandText);
            return base.NonQueryExecuted(command, eventData, result);
        }

        public override Task<int> NonQueryExecutedAsync(DbCommand command, CommandExecutedEventData eventData, int result,
            CancellationToken cancellationToken = new CancellationToken())
        {
            SetRowsAffected(result);
            SetSpanName(command.CommandText);
            return base.NonQueryExecutedAsync(command, eventData, result, cancellationToken);
        }

        public override InterceptionResult DataReaderDisposing(DbCommand command, DataReaderDisposingEventData eventData,
            InterceptionResult result)
        {
            SetRowsRead(eventData.ReadCount);
            return base.DataReaderDisposing(command, eventData, result);
        }

        private static void SetRowsAffected(int rowsAffected)
        {
            Agent.Tracer.CurrentSpan?.Labels?.Add("rows_affected", rowsAffected.ToString());
        }

        private static void SetRowsRead(int rowsRead)
        {
            Agent.Tracer.CurrentSpan?.Labels?.Add("rows_read", rowsRead.ToString());
        }
        private static void SetSpanName(string command)
        {
            var currentSpan = Agent.Tracer.CurrentSpan;
            Console.WriteLine($"Is null {currentSpan == null}");
            if (currentSpan == null)
            {
                return;
            }

            var length = Math.Min(command?.Length ?? _dbCommandNameLength, _dbCommandNameLength);
            currentSpan.Name = command?.Substring(0, length);
        }
    }

```

---

<div class="post-metadata">

**Author:** ![Sergey\_Kleyman](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/sergey_kleyman/32/39333_2.png) [@Sergey\_Kleyman](https://discuss.elastic.co/u/Sergey_Kleyman)\
**Post date:** [December 13, 2019, 6:38am UTC](https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741/2 "2019-12-13T06:38:26Z")

</div>

Hi @rainbowkid11 - welcome back!

It's interesting - I didn't know that they added `DbCommandInterceptor` to EF Core as well. .NET Agent already [uses `DbCommandInterceptor` to automatically create spans for EF6](https://github.com/elastic/apm-agent-dotnet/blob/adebbaf865ae2680fea43f13162f36b86b21e39d/src/Elastic.Apm.EntityFramework6/Ef6Interceptor.cs). By looking at that code you can see that what is missing in your code is starting and ending the span for the corresponding DB operation. I don't think you need to use `Agent.Tracer.CurrentSpan` the way you are trying to use it - you only need `CurrentSpan` to make the new span a child of `CurrentSpan` as Agent does [here](https://github.com/elastic/apm-agent-dotnet/blob/1.2.0/src/Elastic.Apm/Model/DbSpanCommon.cs#L11). In addition you need some context object to carry the new span you create in DB operation starting callback (i.e., `...Executing()`) so you can call `End()` on that span in DB operation ended callback (i.e., `...Executed()`). Agent's `Ef6Interceptor` uses `DbCommandInterceptionContext<T>.SetUserState()/.FindUserState()` for that purpose. I see that [EF Core's `IDbCommandInterceptor`](https://github.com/aspnet/EntityFrameworkCore/blob/release/3.0/src/EFCore.Relational/Diagnostics/IDbCommandInterceptor.cs) doesn't receive `DbCommandInterceptionContext<T>` in its callbacks - maybe `CommandEventData` parameter can be used to carry span between `...Executing` and `...Executed` callbacks.

---

<div class="post-metadata">

**Author:** ![Sergey\_Kleyman](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/sergey_kleyman/32/39333_2.png) [@Sergey\_Kleyman](https://discuss.elastic.co/u/Sergey_Kleyman)\
**Post date:** [December 13, 2019, 6:44am UTC](https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741/3 "2019-12-13T06:44:27Z")

</div>

Of course a better long term solution for your use case would be to implement this feature - [Agent's public API should allow user to register filters similar to those of node.js agent](https://github.com/elastic/apm-agent-dotnet/issues/361). We welcome community contributions if you would like to take on it 🙂

---

<div class="post-metadata">

**Author:** ![rainbowkid11](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/rainbowkid11/32/45796_2.png) [@rainbowkid11](https://discuss.elastic.co/u/rainbowkid11)\
**Post date:** [December 13, 2019, 4:04pm UTC](https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741/4 "2019-12-13T16:04:49Z")

</div>

@Sergey_Kleyman Thanks for info! What I really wanted was to hook into the span created by the .Net agent for the database request so I can manipulate the name (to display on Kibana) and also add a couple of labels. A better idea as you mentioned is the filter feature. If I have time I may give it a try.

Thanks for the amazing work!

---

<div class="post-metadata">

**Author:** ![rainbowkid11](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/rainbowkid11/32/45796_2.png) [@rainbowkid11](https://discuss.elastic.co/u/rainbowkid11)\
**Post date:** [December 13, 2019, 4:38pm UTC](https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741/5 "2019-12-13T16:38:30Z")

</div>

For now this worked! Not so elegant but it seems to work.

```auto
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Elastic.Apm;
using Elastic.Apm.Api;
using Microsoft.EntityFrameworkCore.Diagnostics;

namespace Shared.Apm
{
    public class ApmEFInterceptor : DbCommandInterceptor
    {
        private static readonly int _dbCommandNameLength = 100;
        private ISpan _span;

        

        public override Task<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
            CancellationToken cancellationToken = new CancellationToken())
        {
            SetSpan();
            return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
        }
        public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
        {
            SetSpan();
            return base.ReaderExecuting(command, eventData, result);
        }

        public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
        {
            SetSpan();
            return base.NonQueryExecuting(command, eventData, result);
        }
        public override Task<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result,
            CancellationToken cancellationToken = new CancellationToken())
        {
            SetSpan();
            return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);
        }

        public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
        {
            SetRowsAffected(result);
            return base.NonQueryExecuted(command, eventData, result);
        }

        public override Task<int> NonQueryExecutedAsync(DbCommand command, CommandExecutedEventData eventData, int result,
            CancellationToken cancellationToken = new CancellationToken())
        {
            SetRowsAffected(result);
            return base.NonQueryExecutedAsync(command, eventData, result, cancellationToken);
        }

        public override InterceptionResult DataReaderDisposing(DbCommand command, DataReaderDisposingEventData eventData,
            InterceptionResult result)
        {
            SetRowsRead(eventData.ReadCount);
            return base.DataReaderDisposing(command, eventData, result);
        }

        private void SetRowsAffected(int rowsAffected)
        {
            _span?.Labels?.Add("rows_affected", rowsAffected.ToString());
            if (_span != null)
            {
                _span.Name = $"DML Database Call - {rowsAffected} Rows Affected";
            }
        }

        private void SetRowsRead(int rowsRead)
        {
            // database reports 1 more than actual read
            rowsRead--;
            _span?.Labels?.Add("rows_read", rowsRead.ToString());
            if (_span != null)
            {
                _span.Name = $"Select Database Call - {rowsRead} Row(s) Read";
            }
        }
        private void SetSpanName(string command)
        {
            if (_span == null)
            {
                return;
            }

            var length = Math.Min(command?.Length ?? _dbCommandNameLength, _dbCommandNameLength);
            _span.Name = command?.Substring(0, length);
        }

        private void SetSpan()
        {
            _span = Agent.Tracer.CurrentSpan;
        }
    }
}

```

---

<div class="post-metadata">

**Author:** ![Sergey\_Kleyman](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/sergey_kleyman/32/39333_2.png) [@Sergey\_Kleyman](https://discuss.elastic.co/u/Sergey_Kleyman)\
**Post date:** [December 14, 2019, 12:33pm UTC](https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741/6 "2019-12-14T12:33:54Z")

</div>

I'm glad it works for you although I would be very careful because your code implicitly relies  
on the order in which EF Core notifies `DbCommandInterceptor`-s and `DiagnosticSource` listeners (currently Agent uses `DiagnosticSource` listener to automatically create DB spans for EF Core). If any of these EF Core and .NET Agent implementation details change your code might break. What you can do to at least detect if something went wrong and that you possibly changing a wrong span is to check if `Agent.Tracer.CurrentSpan?.Type` is `Elastic.Apm.Api.ApiConstants.TypeDb` (it's a public constant for `"db"`).

---

<div class="post-metadata">

**Author:** ![Sergey\_Kleyman](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/sergey_kleyman/32/39333_2.png) [@Sergey\_Kleyman](https://discuss.elastic.co/u/Sergey_Kleyman)\
**Post date:** [December 14, 2019, 12:37pm UTC](https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741/7 "2019-12-14T12:37:46Z")

</div>

> [@rainbowkid11](#):
>
> ... What I really wanted was to hook into the span created by the .Net agent for the database request so I can manipulate the name (to display on Kibana) and also add a couple of labels.

Yes, that seems to be the main use case for [the filter feature](https://github.com/elastic/apm-agent-dotnet/issues/361) - to manipulate event objects before they undergo final processing and are sent to APM Server.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [January 4, 2020, 8:37am UTC](https://discuss.elastic.co/t/get-currentspan-in-net-apm-agent/211741/8 "2020-01-04T08:37:51Z")

</div>

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.
