# Java Agent: Custom span in multithread context

**URL:** <https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608>\
**Category:** APM\
**Created:** [April 26, 2019, 9:33am UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608 "2019-04-26T09:33:14Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![suikast42](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/suikast42/32/29865_2.png) [@suikast42](https://discuss.elastic.co/u/suikast42)\
**Post date:** [April 26, 2019, 9:33am UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/1 "2019-04-26T09:33:14Z")

</div>

I try to catpure spans of therad cals.

For eaxample.

Thread A creates Therad B. Thread A cretes the inital span and Thread B should be listed in transaction of Thread B.

If I start the Span in Thread B with the TransactionId of Thread A then I get the folowing error:

The traceparent header has to be at least 55 chars long, but was 'bdcdf7d968cf8f7a'

What kind of ID should the ElasticApm.startTransactionWithRemoteParent(this::getID) provide ?

---

<div class="post-metadata">

**Author:** ![felixbarny](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/felixbarny/32/27341_2.png) [@felixbarny](https://discuss.elastic.co/u/felixbarny)\
**Post date:** [April 26, 2019, 9:51am UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/2 "2019-04-26T09:51:23Z")

</div>

Could you explain your scenario in a bit more detail? Typically, you don't create a transaction in the same service when there is already one active. Have you tried starting a child span on thread B via [`Span#startSpan`](https://www.elastic.co/guide/en/apm/agent/java/current/public-api.html#api-span-start-span-with-type)?

Does the span on thread A end before the span on thread B is started?

---

<div class="post-metadata">

**Author:** ![suikast42](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/suikast42/32/29865_2.png) [@suikast42](https://discuss.elastic.co/u/suikast42)\
**Post date:** [April 26, 2019, 10:30am UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/3 "2019-04-26T10:30:41Z")

</div>

No Thread A is not stopped unless Thread B is finished and Thread A end the inital Transaction if Thread B is finished it's work.

If I use ElasticApm.currentTransaction() in Thread B then I get a NoopTransaction. Beacause Thread B is not assoiated with a Transaction. That is expected as mentioned in the docu.

And If I use the transaction id of Thread A in Thread B to create a child span then I get the error.

My Example code in Thread B. The question is what kind of ID should getId deliver. Actualy it is the id od the inital transaction

```
final Transaction transaction = ElasticApm.startTransactionWithRemoteParent(this::getID)

        Span span = transaction
                .startSpan("WorkerJob", getName(), "run")
                .setName(getName())
        
        try (final Scope scope = transaction.activate()) {
            span.injectTraceHeaders((name, value) -> System.err.println("Trace headers: " + name +" -> "+value));
            Thread.sleep(random(10, 1000));
        } catch (Exception e) {
            span.captureException(e);
        } finally {
            span.end();
        }
```

---

<div class="post-metadata">

**Author:** ![felixbarny](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/felixbarny/32/27341_2.png) [@felixbarny](https://discuss.elastic.co/u/felixbarny)\
**Post date:** [April 26, 2019, 12:23pm UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/4 "2019-04-26T12:23:37Z")

</div>

Instead of creating another transaction, I'd suggest to transfer the transaction to thread B and to start a span from it like that:

```auto
final Transaction transaction = ElasticApm.startTransaction();
final Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        try (Scope scope = transaction.activate()) {
            final Span span = ElasticApm.currentSpan()
                .startSpan("WorkerJob", "name", "run")
                .setName("name");
            try (Scope spanScope = span.activate()) {
                // do stuff
            } catch (Exception e) {
                span.captureException(e);
                throw e;
            } finally {
                span.end();
            }
        }
    }
});
thread.run();
thread.join();
transaction.end();

```

---

<div class="post-metadata">

**Author:** ![suikast42](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/suikast42/32/29865_2.png) [@suikast42](https://discuss.elastic.co/u/suikast42)\
**Post date:** [April 26, 2019, 12:52pm UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/5 "2019-04-26T12:52:20Z")

</div>

Thanks for fast reply. Of course this approach works fine.

But the approach with transmit a span id is more lightweight I assume.

In my inital transaction I capture the trace ID with  
`transaction.injectTraceHeaders((name, value) -> traceHeader = value)`  
and propagate this id to Thread B. And that's works as exppected. But annoying thing here is that I need a further Transaction in Thread B which shows in the APM UI.

The unnamed Transactions are not nedded. How can I avoid that ?

 ![2019_04_26_14_49_42_Worker3](https://us1.discourse-cdn.com/elastic/original/3X/5/e/5ea52e318d8e25f9a4be5623e4f0df68a501f9c7.png)

---

<div class="post-metadata">

**Author:** ![felixbarny](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/felixbarny/32/27341_2.png) [@felixbarny](https://discuss.elastic.co/u/felixbarny)\
**Post date:** [April 26, 2019, 1:00pm UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/6 "2019-04-26T13:00:52Z")

</div>

> [@suikast42](#):
>
> But the approach with transmit a span id is more lightweight I assume.

Why do you think so?

> [@suikast42](#):
>
> transaction.injectTraceHeaders((name, value) -\> traceHeader = value)

Currently, there is only one header but we will likely add more so you would override the traceHeader variable.

What you could do is to make a map containing the headers like that:

```auto
Map<String, String> headers = new HashMap<>();
transaction.injectTraceHeaders(headers::put);
...
Transaction transaction = ElasticApm.startTransactionWithRemoteParent(headers::get)

```

Just don't create spans but only use the transactions to avoid the unnamed transactions.

But I'm still not sure why you want multiple transactions. Can't you create a Transaction for `Worker3` and only create Spans afterwards?

---

<div class="post-metadata">

**Author:** ![suikast42](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/suikast42/32/29865_2.png) [@suikast42](https://discuss.elastic.co/u/suikast42)\
**Post date:** [April 26, 2019, 1:36pm UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/7 "2019-04-26T13:36:37Z")

</div>

> [@felixbarny](#):
>
> Why do you think so?

I have a JavaEE 8 app running in Wildfly 16. I want track the EJB boundary and the JMS onMessage Invocations with the real SQL statements. I track the real SQL with wrapping the underlying JDBC driver. For that purpose I think that's more lightweight to ship a TransactionID to the JDBC driver instead of the tranaction object.

> [@felixbarny](#):
>
> Currently, there is only one header but we will likely add more so you would override the traceHeader variable.

I got it. I see as well there's currently one header.

> [@felixbarny](#):
>
> Just don't create spans but only use the transactions to avoid the unnamed transactions.

Yes you are right. But there is a litte but big diffefence between transaction and span that I can see. Transaction doesn't report the stack.

---

<div class="post-metadata">

**Author:** ![sabareesh1](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/sabareesh1/32/42619_2.png) [@sabareesh1](https://discuss.elastic.co/u/sabareesh1)\
**Post date:** [April 26, 2019, 11:36pm UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/8 "2019-04-26T23:36:40Z")

</div>

Glad we are using Spring Sleuth. This looks messy

---

<div class="post-metadata">

**Author:** ![Eyal\_Koren](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/eyal_koren/32/36830_2.png) [@Eyal\_Koren](https://discuss.elastic.co/u/Eyal_Koren)\
**Post date:** [April 28, 2019, 7:39am UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/9 "2019-04-28T07:39:30Z")

</div>

> [@suikast42](#):
>
> I want track the EJB boundary and the JMS onMessage Invocations with the real SQL statements.

If you can provide a description of your app architecture, this could assist us understand what you are trying to achieve. Is it a message bean you are using? Are you monitoring both sides of it? Which one contains the sample code you enclosed? Which one of is doing JDBC calls?

> I track the real SQL with wrapping the underlying JDBC driver. For that purpose I think that's more lightweight to ship a TransactionID to the JDBC driver instead of the tranaction object.

Can you further describe how you are wrapping the driver and for which purpose? Which JDBC driver are you using? Also, not sure about the performance hit you are worried about with transferring the Transaction object reference.

> But there is a litte but big diffefence between transaction and span that I can see. Transaction doesn't report the stack.

Because our data model is not designed for this kind of usage (multiple transactions creating during a single request handling in the same service). This will also make your main service summary page in the APM UI less usable, as you would get an entry for each of your spans in the transaction list. If possible, @felixbarny's suggestion to activate the Transaction object on the worker thread seems most likely to provide the optimal solution.

---

<div class="post-metadata">

**Author:** ![suikast42](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/suikast42/32/29865_2.png) [@suikast42](https://discuss.elastic.co/u/suikast42)\
**Post date:** [April 29, 2019, 6:55am UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/10 "2019-04-29T06:55:45Z")

</div>

> [@Eyal\_Koren](#):
>
> If you can provide a description of your app architecture, this could assist us understand what you are trying to achieve. Is it a message bean you are using? Are you monitoring both sides of it? Which one contains the sample code you enclosed? Which one of is doing JDBC calls?

The architecture relies on the standard JavaEE architecture. With some lagacy hooks of course 😉 We don't use Mdbs. We register manualy message listeners on jms ( for acivatating dan deactivating the listeners during runtime ). So JDBC calls can come from serval places . REST cal, Remote EJB, Timed actions, Event driven actions and so on.

> [@Eyal\_Koren](#):
>
> Can you further describe how you are wrapping the driver and for which purpose? Which JDBC driver are you using? Also, not sure about the performance hit you are worried about with transferring the Transaction object reference.

I don't like the output of hibernate prepared statements. I can't use this queries for make performance analysis on the database. For that purpose I use [DriverSpy](https://github.com/arthurblake/log4jdbc/blob/master/src-jdbc4/net/sf/log4jdbc/DriverSpy.java) and [JSQLParser](https://github.com/JSQLParser/JSqlParser).

> [@Eyal\_Koren](#):
>
> Because our data model is not designed for this kind of usage

Ok this part was my misunderstanding of spans and transactions.

---

<div class="post-metadata">

**Author:** ![Eyal\_Koren](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/eyal_koren/32/36830_2.png) [@Eyal\_Koren](https://discuss.elastic.co/u/Eyal_Koren)\
**Post date:** [April 29, 2019, 7:34am UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/11 "2019-04-29T07:34:21Z")

</div>

The [injectTraceHeaders](https://www.elastic.co/guide/en/apm/agent/java/current/public-api.html#api-transaction-inject-trace-headers) and [startTransactionWithRemoteParent](https://www.elastic.co/guide/en/apm/agent/java/current/public-api.html#api-start-transaction-with-remote-parent-header) APIs you referred to are designed to enable manual distributed tracing, so they may be useful to assist you with the JMS and EJB monitoring.

In addition, here is an overview [our data model](https://www.elastic.co/guide/en/apm/get-started/current/apm-data-model.html).

I hope this helps,  
Eyal.

---

<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:** [May 20, 2019, 3:34am UTC](https://discuss.elastic.co/t/java-agent-custom-span-in-multithread-context/178608/12 "2019-05-20T03:34:27Z")

</div>

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