Skip to content
This repository has been archived by the owner on May 23, 2023. It is now read-only.

Remove finishSpanOnClose flag #291

Closed
felixbarny opened this issue Jul 23, 2018 · 8 comments
Closed

Remove finishSpanOnClose flag #291

felixbarny opened this issue Jul 23, 2018 · 8 comments

Comments

@felixbarny
Copy link
Contributor

felixbarny commented Jul 23, 2018

This is a follow up on #267. The underlying issue of that one is that you can't remember the finishSpanOnClose flag when implementing a bridge for ScopeManager#active().

The point of this issue is to remove the finishSpanOnClose flag altogether, as it is only useful in try-with-resources constructs which are a bad practice because you can't can't log exceptions as the span is closed before the catch block. To cite the main README.md:

Note that startActive(true) finishes the span on Scope.close(). Use it carefully because the try-with-resources construct finishes the span before the catch or finally blocks are executed, which makes logging exceptions and setting tags impossible. It is recommended to start the span and activate it later in try-with-resources. This makes the span available in catch and finally blocks.

So we are basically officially admitting that finishSpanOnClose is a bad practice.

You could argue that try-with-resources is valid if you don't want to log exceptions. But what if a user then later decides that they do want to log exceptions? Do they still remember that they can't use try-with-resources + a catch block then? I would most likely forget about that.

The finishSpanOnClose just enables some syntactic sugar after all which turns out to be problematic. As flag essentially advocates a bad practice, I suggest removing it.

Transition:
Of course, we can't just remove the flag which would introduce a breaking change.

Instead, I propose adding the method io.opentracing.Tracer.SpanBuilder#startActive() without arguments, which has the same semantics like calling spanBuilder.startActive(false). Also, deprecate io.opentracing.Tracer.SpanBuilder#startActive(boolean finishSpanOnClose). This method can then be removed on a later release (for example 1.0.0-RC1)

The Scope interface can still extend from Closeable, because logging an exception is still valid to do after the scope is closed.

I think, even after doing this, #267 (Deprecate or remove ScopeManager.active()) is still relevant, but maybe less severe.

@felixbarny
Copy link
Contributor Author

See also this brave issue for more information about why try-with-resources is considered an anti-pattern: openzipkin/brave#676

@codefromthecrypt
Copy link
Contributor

note this antipattern is also well documented in wingtips too. There's significant documentation burden to explain how to overcome it
https://github.com/Nike-Inc/wingtips#warning-about-error-handling-when-using-try-with-resources-to-autoclose-spans

@sjoerdtalsma
Copy link
Contributor

sjoerdtalsma commented Jul 26, 2018

@felixbarny
Copy link
Contributor Author

The problem is that it requires people to actually read (and remember) the documentation because it's counter-intuitive and thus error-prone.

@codefromthecrypt
Copy link
Contributor

@sjoerdtalsma I think what you are doing is clever, but yeah I agree with @felixbarny.

@carlosalberto
Copy link
Collaborator

So trying to get this one moving again - I played a little bit with updating some instrumentation with this change, and it feels it can definitely make things clearer when you need to add logs on unhandled Exceptions (opposed to the case where you didn't care at all about such errors).

This way we could have something like:

Span span = tracer.buildSpan("foo").start();
try (Scope scope = tracer.scopeManager().activate(span)) {
} catch (Exception exc) {
  Tags.ERROR.set(span, true);
  span.log(ImmutableMap.Builder<String, Object>()
                   .put("event", "error")
                   .put("error.object", exc)
                   .build());
} finally {
  span.finish(); // Optional
}

Or

Scope scope = null;
try {
  scope = tracer.buildSpan("foo").startActive();
} catch (Exception exc) {
  Tags.ERROR.set(scope.span(), true);
  scope.span().log(ImmutableMap.Builder<String, Object>()
                   .put("event", "error")
                   .put("error.object", exc)
                   .build());
} finally {
  if (scope != null) {
    scope.close();
    scope.span.finish(); // Optional
  }
}

And the simplest case of simply activating/deactivating without closing it and without reporting errors (when you don't own the Span) would be kept too.

On the other side, I think that, as Felix mentioned, #267 would be less severe - and I'd like to keep that one after removing the finishSpanOnClose flag, to cover up the case when the user has no context to keep the Scope around (with proper/better documentation, of course).

Thoughts? @felixbarny @yurishkuro @sjoerdtalsma @adriancole

PS - if that sounds feasible, I can definitely champion to update the testbed examples once a PR is created for this, and probably adding one more to showcase this case too.

@felixbarny
Copy link
Contributor Author

Sounds good to me

@carlosalberto
Copy link
Collaborator

Hey @felixbarny ! as we merged #301, shall we close this?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants