Checkpoint1#1
Conversation
| // Basic foundation for OpenTracing basictracer-compatible carrier writers. | ||
| class BasicCarrierWriter : public CarrierWriter { | ||
| public: | ||
| virtual void Set(const std::string& key, const std::string& value) const = 0; |
There was a problem hiding this comment.
Should Set return an error-code that allows a user to bail out of the writer if the key-value pair can't be written? I found the lack of such a return code inconvenient when writing the CarrierWriter for Nginx's instrumentation, since setting a header value can fail and you have to otherwise manage another error code separately or use exceptions to break out of the writer.
There was a problem hiding this comment.
Yes, sounds like a good idea to return an error here. I'll let you pick the best style :)
| // | ||
| // Returns true on success. | ||
| virtual bool Inject(const SpanContext& sc, CarrierFormat format, | ||
| const CarrierWriter& writer) = 0; |
There was a problem hiding this comment.
Should Inject have a more descriptive return code? Perhaps something similar to this?
There was a problem hiding this comment.
I don't like returning values using reference variables like that, but I think it should be permitted to return some useful information. I just wish C++ had a better style for error types, but I'm fine if there's a std::string pointer for returning error values (same goes for the question about CarrierWriter::Set).
There was a problem hiding this comment.
How about something like Expected<void, std::string> from this library? It also mirrors what's being proposed for a future C++ standard.
There was a problem hiding this comment.
That looks appropriate. I generally support moving in the direction of the C++ language, so yes.
| class StartTimestamp : public StartSpanOption { | ||
| public: | ||
| StartTimestamp(SystemTime system_when, SteadyTime steady_when) | ||
| : system_when_(system_when), steady_when_(steady_when) {} |
There was a problem hiding this comment.
Should we also allow you to construct the StartTimestamp from a single point in time that then uses that time point to set both the system and steady timestamps?
There was a problem hiding this comment.
That sounds nice. Want to create such a type?
There was a problem hiding this comment.
Not sure we need a new type. I was thinking more of just an additional constructor that allows you to pass in a singe time_since_epoch of std::chrono::duration. I updated with what I have in mind.
| // may ignore the tag, but shall not panic. | ||
| // | ||
| // SetTag may be called prior to Finish. | ||
| virtual void SetTag(const std::string& key, const Value& value) = 0; |
There was a problem hiding this comment.
Do we want a more restrictive type for a tag's value?
There was a problem hiding this comment.
I'm not sure. On the one hand, I think it's useful to allow anything goes here, but probably difficult for people to agree on the semantics / interpretation of nested structure other than scalar values. I'd suggest leaving our options open and getting this around for review.
There was a problem hiding this comment.
I.e., restrict values to scalars for now. I think OT needs to put some work into structured data representation but here and now is not the time to hash it out.
| // | ||
| // With the exception of calls to context() (which are always allowed), | ||
| // Finish() must be the last call made to any span instance, and to do | ||
| // otherwise leads to undefined behavior. |
There was a problem hiding this comment.
Following the discussion here, should we allow Span's methods to be called after finish?
There was a problem hiding this comment.
I think it's fine for certain operations to have undefined behavior after finish, but not OK to crash. I believe based on other OT discussions that the SpanContext should be accessible after Finish(), especially, to allow starting a FollowsFrom span after the parent completes.
There was a problem hiding this comment.
How about the contract is that SetTag, SetOperation, Log (when added), etc leave the span in a valid but unspecified state, and calling Finish more than once does nothing. That way they'd be safe to use from a multithreaded context without forcing a user to do additional synchronization.
| // | ||
| // Returns a `SpanContext` that is `non-null` on success. | ||
| virtual std::unique_ptr<SpanContext> Extract(CarrierFormat format, | ||
| const CarrierReader& reader) = 0; |
There was a problem hiding this comment.
Any reason Inject and Extract aren't const?
There was a problem hiding this comment.
Not that I can remember. Let's say they should be and see if there was a reason. :)
| options.start_steady_timestamp = SteadyClock::now(); | ||
| for (const auto& option : option_list) option.get().Apply(options); | ||
| return StartSpanWithOptions(operation_name, options); | ||
| } |
There was a problem hiding this comment.
Should something like StartSpan be made noexcept and specify that a nullptr is returned upon an error?
Not sure the best way to handle something like a std::bad_alloc error that might occur. You could either do nothing (with noexcept the program would terminate) or catch it and return a nullptr.
There was a problem hiding this comment.
I was at Google for a hundred years and (because of their style guide) have very little experience programming with C++ exceptions. I'm not sure I have an opinion, so I recommend doing what you think is best.
| }; | ||
|
|
||
| // Carrier format values. | ||
| enum class CarrierFormat { |
There was a problem hiding this comment.
Is this the right type to use to specify the carrier format? It's not clear to me how something like LightStep's custom binary format or other custom carriers would fit into this.
Sets up an OpenTracing interface based off of LightStep's tracer. A few modifications from the original LightStep code I made include:
StartSpanOptionsandFinishSpanOptionsclasses and changed theApplyfunctions to take these for their arguments instead of aSpan.Other than that, I tried to stay close to the original implementation. I image there will be some other changes we want to make before proposing it as an interface. Here are some things I would consider:
I also wrote some other more specific suggests inline.