2525import com .google .cloud .ServiceOptions ;
2626import com .google .cloud .datastore .execution .AggregationQueryExecutor ;
2727import com .google .cloud .datastore .spi .v1 .DatastoreRpc ;
28- import com .google .cloud .datastore .telemetry .TraceUtil .Context ;
2928import com .google .common .base .MoreObjects ;
3029import com .google .common .base .Preconditions ;
30+ import com .google .common .base .Throwables ;
3131import com .google .common .collect .AbstractIterator ;
3232import com .google .common .collect .ImmutableList ;
3333import com .google .common .collect .ImmutableMap ;
4242import io .opencensus .common .Scope ;
4343import io .opencensus .trace .Span ;
4444import io .opencensus .trace .Status ;
45+ import io .opentelemetry .api .common .Attributes ;
46+ import io .opentelemetry .api .trace .SpanBuilder ;
47+ import io .opentelemetry .api .trace .SpanKind ;
48+ import io .opentelemetry .api .trace .StatusCode ;
49+ import io .opentelemetry .context .Context ;
4550import java .util .ArrayList ;
4651import java .util .Arrays ;
4752import java .util .Collections ;
5358import java .util .Optional ;
5459import java .util .Set ;
5560import java .util .concurrent .Callable ;
56- import javax .annotation .Nonnull ;
61+ import javax .annotation .Nullable ;
5762
5863final class DatastoreImpl extends BaseService <DatastoreOptions > implements Datastore {
5964
@@ -106,15 +111,18 @@ static class ReadWriteTransactionCallable<T> implements Callable<T> {
106111 private volatile TransactionOptions options ;
107112 private volatile Transaction transaction ;
108113
114+ private final com .google .cloud .datastore .telemetry .TraceUtil .SpanContext parentSpanContext ;
115+
109116 ReadWriteTransactionCallable (
110117 Datastore datastore ,
111118 TransactionCallable <T > callable ,
112119 TransactionOptions options ,
113- @ Nonnull Context parentTraceContext ) {
120+ @ Nullable com . google . cloud . datastore . telemetry . TraceUtil . SpanContext parentSpanContext ) {
114121 this .datastore = datastore ;
115122 this .callable = callable ;
116123 this .options = options ;
117124 this .transaction = null ;
125+ this .parentSpanContext = parentSpanContext ;
118126 }
119127
120128 Datastore getDatastore () {
@@ -135,26 +143,57 @@ void setPrevTransactionId(ByteString transactionId) {
135143 options = options .toBuilder ().setReadWrite (readWrite ).build ();
136144 }
137145
146+ private io .opentelemetry .api .trace .Span startSpanWithParentContext (
147+ String spanName ,
148+ com .google .cloud .datastore .telemetry .TraceUtil .SpanContext parentSpanContext ) {
149+ com .google .cloud .datastore .telemetry .TraceUtil otelTraceUtil =
150+ datastore .getOptions ().getTraceUtil ();
151+ SpanBuilder spanBuilder =
152+ otelTraceUtil
153+ .getTracer ()
154+ .spanBuilder (com .google .cloud .datastore .telemetry .TraceUtil .SPAN_NAME_TRANSACTION_RUN )
155+ .setSpanKind (SpanKind .PRODUCER )
156+ .setParent (
157+ Context .current ()
158+ .with (
159+ io .opentelemetry .api .trace .Span .wrap (
160+ parentSpanContext .getSpanContext ())));
161+ return spanBuilder .startSpan ();
162+ }
163+
138164 @ Override
139165 public T call () throws DatastoreException {
140- com .google .cloud .datastore .telemetry .TraceUtil traceUtil =
141- datastore .getOptions ().getTraceUtil ();
142- com .google .cloud .datastore .telemetry .TraceUtil .Span span =
143- traceUtil .startSpan (
166+ // TODO Instead of using OTel Spans directly, TraceUtil.Span should be used here. However,
167+ // the same code in startSpanInternal doesn't work when EnabledTraceUtil.StartSpan is called
168+ // probably because of some thread-local caching that is getting lost. This needs more
169+ // debugging. The code below works and is idiomatic but could be prettier and more consistent
170+ // with the use of TraceUtil-provided framework.
171+ io .opentelemetry .api .trace .Span span =
172+ startSpanWithParentContext (
144173 com .google .cloud .datastore .telemetry .TraceUtil .SPAN_NAME_TRANSACTION_RUN ,
145- datastore . getOptions (). getTraceUtil (). getCurrentContext () );
146- try (com . google . cloud . datastore . telemetry . TraceUtil .Scope ignored = span .makeCurrent ()) {
174+ parentSpanContext );
175+ try (io . opentelemetry . context .Scope ignored = span .makeCurrent ()) {
147176 transaction = datastore .newTransaction (options );
148177 T value = callable .run (transaction );
149178 transaction .commit ();
150179 return value ;
151180 } catch (Exception ex ) {
152181 transaction .rollback ();
182+ span .setStatus (StatusCode .ERROR , ex .getMessage ());
183+ span .recordException (
184+ ex ,
185+ Attributes .builder ()
186+ .put ("exception.message" , ex .getMessage ())
187+ .put ("exception.type" , ex .getClass ().getName ())
188+ .put ("exception.stacktrace" , Throwables .getStackTraceAsString (ex ))
189+ .build ());
190+ span .end ();
153191 throw DatastoreException .propagateUserException (ex );
154192 } finally {
155193 if (transaction .isActive ()) {
156194 transaction .rollback ();
157195 }
196+ span .end ();
158197 if (options != null
159198 && options .getModeCase ().equals (TransactionOptions .ModeCase .READ_WRITE )) {
160199 setPrevTransactionId (transaction .getTransactionId ());
@@ -165,42 +204,30 @@ public T call() throws DatastoreException {
165204
166205 @ Override
167206 public <T > T runInTransaction (final TransactionCallable <T > callable ) {
168- com .google .cloud .datastore .telemetry .TraceUtil .Span span =
169- otelTraceUtil .startSpan (
170- com .google .cloud .datastore .telemetry .TraceUtil .SPAN_NAME_TRANSACTION_RUN );
171- try (com .google .cloud .datastore .telemetry .TraceUtil .Scope ignored = span .makeCurrent ()) {
207+ try {
172208 return RetryHelper .runWithRetries (
173209 new ReadWriteTransactionCallable <T >(
174- this , callable , null , otelTraceUtil .getCurrentContext ()),
210+ this , callable , null , otelTraceUtil .getCurrentSpanContext ()),
175211 retrySettings ,
176212 TRANSACTION_EXCEPTION_HANDLER ,
177213 getOptions ().getClock ());
178214 } catch (RetryHelperException e ) {
179- span .end (e );
180215 throw DatastoreException .translateAndThrow (e );
181- } finally {
182- span .end ();
183216 }
184217 }
185218
186219 @ Override
187220 public <T > T runInTransaction (
188221 final TransactionCallable <T > callable , TransactionOptions transactionOptions ) {
189- com .google .cloud .datastore .telemetry .TraceUtil .Span span =
190- otelTraceUtil .startSpan (
191- com .google .cloud .datastore .telemetry .TraceUtil .SPAN_NAME_TRANSACTION_RUN );
192- try (com .google .cloud .datastore .telemetry .TraceUtil .Scope ignored = span .makeCurrent ()) {
222+ try {
193223 return RetryHelper .runWithRetries (
194224 new ReadWriteTransactionCallable <T >(
195- this , callable , transactionOptions , otelTraceUtil .getCurrentContext ()),
225+ this , callable , transactionOptions , otelTraceUtil .getCurrentSpanContext ()),
196226 retrySettings ,
197227 TRANSACTION_EXCEPTION_HANDLER ,
198228 getOptions ().getClock ());
199229 } catch (RetryHelperException e ) {
200- span .end (e );
201230 throw DatastoreException .translateAndThrow (e );
202- } finally {
203- span .end ();
204231 }
205232 }
206233
@@ -258,11 +285,14 @@ public AggregationResults runAggregation(
258285
259286 com .google .datastore .v1 .RunQueryResponse runQuery (
260287 final com .google .datastore .v1 .RunQueryRequest requestPb ) {
261- com .google .cloud .datastore .telemetry .TraceUtil .Span span =
262- otelTraceUtil .startSpan (com .google .cloud .datastore .telemetry .TraceUtil .SPAN_NAME_RUN_QUERY );
263288 ReadOptions readOptions = requestPb .getReadOptions ();
264- span .setAttribute (
265- "isTransactional" , readOptions .hasTransaction () || readOptions .hasNewTransaction ());
289+ boolean isTransactional = readOptions .hasTransaction () || readOptions .hasNewTransaction ();
290+ String spanName =
291+ (isTransactional
292+ ? com .google .cloud .datastore .telemetry .TraceUtil .SPAN_NAME_TRANSACTION_RUN_QUERY
293+ : com .google .cloud .datastore .telemetry .TraceUtil .SPAN_NAME_RUN_QUERY );
294+ com .google .cloud .datastore .telemetry .TraceUtil .Span span = otelTraceUtil .startSpan (spanName );
295+ span .setAttribute ("isTransactional" , isTransactional );
266296 span .setAttribute ("readConsistency" , readOptions .getReadConsistency ().toString ());
267297
268298 try (com .google .cloud .datastore .telemetry .TraceUtil .Scope ignored = span .makeCurrent ()) {
@@ -275,7 +305,7 @@ com.google.datastore.v1.RunQueryResponse runQuery(
275305 : TRANSACTION_OPERATION_EXCEPTION_HANDLER ,
276306 getOptions ().getClock ());
277307 span .addEvent (
278- com . google . cloud . datastore . telemetry . TraceUtil . SPAN_NAME_RUN_QUERY + ": Completed" ,
308+ spanName + ": Completed" ,
279309 new ImmutableMap .Builder <String , Object >()
280310 .put ("Received" , response .getBatch ().getEntityResultsCount ())
281311 .put ("More results" , response .getBatch ().getMoreResults ().toString ())
@@ -689,7 +719,7 @@ com.google.datastore.v1.BeginTransactionResponse beginTransaction(
689719 com .google .cloud .datastore .telemetry .TraceUtil .Span span =
690720 otelTraceUtil .startSpan (
691721 com .google .cloud .datastore .telemetry .TraceUtil .SPAN_NAME_BEGIN_TRANSACTION ,
692- otelTraceUtil .getCurrentContext ());
722+ otelTraceUtil .getCurrentSpanContext ());
693723 try (com .google .cloud .datastore .telemetry .TraceUtil .Scope scope = span .makeCurrent ()) {
694724 return RetryHelper .runWithRetries (
695725 () -> datastoreRpc .beginTransaction (requestPb ),
0 commit comments