-
Notifications
You must be signed in to change notification settings - Fork 658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Link and Event classes #130
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,6 +70,47 @@ | |
ParentSpan = typing.Optional[typing.Union["Span", "SpanContext"]] | ||
|
||
|
||
class Link: | ||
"""A link to a `Span`.""" | ||
|
||
def __init__( | ||
self, context: "SpanContext", attributes: types.Attributes = None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should attributes be typing.Optional? since it defaults to none. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I directly changed the type of |
||
) -> None: | ||
self._context = context | ||
self._attributes = attributes | ||
|
||
@property | ||
def context(self) -> "SpanContext": | ||
return self._context | ||
|
||
@property | ||
def attributes(self) -> types.Attributes: | ||
return self._attributes | ||
|
||
|
||
class Event: | ||
"""A text annotation with a set of attributes.""" | ||
|
||
def __init__( | ||
self, name: str, timestamp: int, attributes: types.Attributes = None | ||
) -> None: | ||
self._name = name | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A general question - do we expect this to be implemented in the API package or the SDK? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd +1 for keeping it in the API. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By keeping it on the API we are providing a reference implementation and also allowing users to extend it if needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Agreed, that's how we do it in Java too, btw. |
||
self._attributes = attributes | ||
self._timestamp = timestamp | ||
|
||
@property | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Making these properties in the API means subclasses can't make them regular attributes. Is that intentional? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I could say it is, subclasses would have to re-implement this property if needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But they could never choose to make these regular attributes. That is, you can always replace attributes with properties in a subclass, but not vice-versa. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, good point with the replacing! But if the derived class defines a read-only property, it would break the base class' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not totally sure I am following this conversation. |
||
def name(self) -> str: | ||
return self._name | ||
|
||
@property | ||
def attributes(self) -> types.Attributes: | ||
return self._attributes | ||
|
||
@property | ||
def timestamp(self) -> int: | ||
return self._timestamp | ||
|
||
|
||
class Span: | ||
"""A span represents a single operation within a trace.""" | ||
|
||
|
@@ -102,34 +143,44 @@ def get_context(self) -> "SpanContext": | |
A :class:`.SpanContext` with a copy of this span's immutable state. | ||
""" | ||
|
||
def set_attribute( | ||
self: "Span", key: str, value: types.AttributeValue | ||
) -> None: | ||
def set_attribute(self, key: str, value: types.AttributeValue) -> None: | ||
"""Sets an Attribute. | ||
|
||
Sets a single Attribute with the key and value passed as arguments. | ||
""" | ||
|
||
def add_event( | ||
self: "Span", name: str, attributes: types.Attributes = None | ||
self, name: str, attributes: types.Attributes = None | ||
) -> None: | ||
"""Adds an Event. | ||
"""Adds an `Event`. | ||
|
||
Adds a single Event with the name and, optionally, attributes passed | ||
Adds a single `Event` with the name and, optionally, attributes passed | ||
as arguments. | ||
""" | ||
|
||
def add_lazy_event(self, event: Event) -> None: | ||
"""Adds an `Event`. | ||
|
||
Adds an `Event` that has previously been created. | ||
""" | ||
|
||
def add_link( | ||
self: "Span", | ||
self, | ||
link_target_context: "SpanContext", | ||
attributes: types.Attributes = None, | ||
) -> None: | ||
"""Adds a Link to another span. | ||
"""Adds a `Link` to another span. | ||
|
||
Adds a single Link from this Span to another Span identified by the | ||
Adds a single `Link` from this Span to another Span identified by the | ||
`SpanContext` passed as argument. | ||
""" | ||
|
||
def add_lazy_link(self, link: "Link") -> None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would almost argue that add_link should just always take a link object. Is it that much harder to do:
instead of
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it seems a bit redundant as of now, though I'd vote for having only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have a strong opinion on any of both options, I just followed the specs: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#add-links, there the two methods are required There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could optionally have only the For historical reference: we added these as in Java this is a usual thing to add there (overload with different parameters, that is). We could drop this for Python if we feel like that, I feel. |
||
"""Adds a `Link` to another span. | ||
|
||
Adds a `Link` that has previously been created. | ||
""" | ||
|
||
def update_name(self, name: str) -> None: | ||
"""Updates the `Span` name. | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these classes are so simple, why did you not keep using
namedtuple
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC the idea is that users can extend these classes (hence the lazy prefix). And personally I'd prefer to keep them as very simple classes (otherwise, I'd totally be up for named tuples ;) )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if those classes are very simple, I think having a proper class allows more flexibility, for instance as mentioned by @carlosalberto, when users want to extend those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namedtuples can be extended, they are full classes. In fact, a common pattern, that's even mentioned in the docs of namedtuple is something like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but IHMO it's still better to have this as simple classes, as it's clearer for users who want to extend it, instead of having to go read the
namedtuple
documentation ;)(If we had this classes as private or SDK specific, I'd be also more open to have them as
namedtuple
s)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Oberon00 what's the benefit of using namedtuples here? If the goal is to avoid boilerplate we might want to use the attrs library. The docs include some reasons not to use namedtuples for data classes. One big reason is that subclasses of namedtuples have a MRO that looks very different from plain old classes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, these reasons sound valid enough. I retract my objections then 😃 I think since the goal of the API is to be a low level building block we should prefer a bit of boilerplate code over an additional dependency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, I think there's a stronger reason for using attrs in the SDK than the API, but we may not want it at all.