From fe4e2d93f276cbd5d978a4e62121239e03a3d38c Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Mon, 3 Jun 2019 16:56:48 -0700 Subject: [PATCH 01/12] Add initial tracer API --- opentelemetry/__init__.py | 0 opentelemetry/api/__init__.py | 0 opentelemetry/api/trace/__init__.py | 190 ++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 opentelemetry/__init__.py create mode 100644 opentelemetry/api/__init__.py create mode 100644 opentelemetry/api/trace/__init__.py diff --git a/opentelemetry/__init__.py b/opentelemetry/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/opentelemetry/api/__init__.py b/opentelemetry/api/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py new file mode 100644 index 00000000000..590ae03de09 --- /dev/null +++ b/opentelemetry/api/trace/__init__.py @@ -0,0 +1,190 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The OpenTelemetry tracing API describes the classes used to generate +distributed traces. + +The :class:`.Tracer` class controls access to the execution context, and +manages span creation. Each operation in a trace is represented by a +:class:`.Span`, which records the start, end time, and metadata associated with +the operation. + +This module provides abstract (i.e. unimplemented) classes required for +tracing, and a concrete no-op ``BlankSpan`` that allows applications to use the +API package alone without a supporting implementation. + +The tracer supports implicit and explicit context propagation. By default spans +are created as children of the currently active span, and the newly-created +span becomes the new active span:: + + from opentelemetry.sdk.trace import tracer + + # Create a new root span, set it as the current span in context + with tracer.span("parent"): + # Attach a new child and update the current span + with tracer.span("child"): + do_work(): + # Close child span, set parent as current + # Close parent span, set default span as current + +Under explicit context propagation there is no concept of an active span, and +the caller is responsible for managing the span's lifetime:: + + from opentelemetry.sdk.trace import tracer + from your.integration import deserialize_span + + parent = deserialize_span(serialized_span) + # Explicit parent span assignment + with tracer.span("child", parent=parent) as child: + do_work(span=child) + +Applications should generally use a single global tracer, and use either +implicit or explicit context propagation consistently throughout. + +.. versionadded:: 0.1.0 +""" + +from __future__ import annotations + +from typing import Iterator +from contextlib import contextmanager + + +class Tracer(object): + """Handles span creation and in-process context propagation. + + This class provides methods for manipulating the context, creating spans, + and controlling spans' lifecycles. + """ + + def get_current_span(self) -> Span: + """Get the currently active span from the context. + + Returns: + The currently active :class:`.Span`. + """ + raise NotImplementedError + + @contextmanager + def span(self, name: str, parent: Span=None) -> Iterator[None]: + """Context manager for span creation. + + Create a new child of the current span, or create a root span if no + current span exists. Start the span and set it as the current span in + this tracer's context. + + On exiting the context manager stop the span and set its parent as the + current span. + + Example:: + + with tracer.span("one") as parent: + parent.add_event("parent's event") + with tracer.span("two") as child: + child.add_event("child's event") + tracer.get_current_span() # returns child + tracer.get_current_span() # returns parent + tracer.get_current_span() # returns the previously active span + + Args: + name: The name of the span to be created. + parent: This span's parent. + + Raises: + ValueError: if ``name`` is null. + """ + raise NotImplementedError + + def create_span(self, name: str, parent: Span=None) -> Span: + """Create a new span as a child of the currently active span. + + If called with ``parent`` this method won't affect the tracer's + context. + + See ``span`` for a context manager that controls the span's lifetime. + + Args: + name: The name of the span to be created. + parent: This span's parent. + + Raises: + ValueError: if ``name`` is null. + """ + raise NotImplementedError + + +class Span(object): + """A span represents a single operation within a trace. + """ + + def start(self) -> None: + """Set the current time as the span's start time. + + Each span represents a single operation. The span's start time is the + wall time at which the operation started. + + Only the first call to ``start`` should modify the span, and + implementations are free to ignore or raise on further calls. + """ + raise NotImplementedError + + def end(self) -> None: + """Set the current time as the span's end time. + + The span's end time is the wall time at which the operation finished. + + Only the first call to ``end`` should modify the span, and + implementations are free to ignore or raise on further calls. + """ + raise NotImplementedError + + def get_context(self) -> SpanContext: + """Get an immutable snapshot of this span. + + Returns: + A :class:`.SpanContext` with a copy of this span's immutable state. + """ + raise NotImplementedError + + +class SpanContext(object): + """The state of a Span to propagate between processes. + + This class includes the immutable attributes of a :class:`.Span` that must + be propagated to a span's children and across process boundaries. + + Args: + trace_id: The ID of the trace that this span belongs to. + span_id: This span's ID. + options: Global trace options to propagate. + state: Global tracing-system-specific info to propagate. + """ + + def __init__(self, + trace_id: str, + span_id: str, + options: TraceOptions, + state: TraceState) -> SpanContext: + pass + + +# TODO +class TraceOptions(int): + pass + + +# TODO +class TraceState(dict): + pass From 4456ea1bfb339fec755d32c099b4bcdd454e5f6b Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 4 Jun 2019 15:09:31 -0700 Subject: [PATCH 02/12] Fix context manager type hint --- opentelemetry/api/trace/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index 590ae03de09..833cbc507ee 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -78,7 +78,7 @@ def get_current_span(self) -> Span: raise NotImplementedError @contextmanager - def span(self, name: str, parent: Span=None) -> Iterator[None]: + def span(self, name: str, parent: Span=None) -> Iterator[Span]: """Context manager for span creation. Create a new child of the current span, or create a root span if no From 49a9db1c9cba0e99c15fc2c554fa271e05f4363b Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 5 Jun 2019 13:10:56 -0700 Subject: [PATCH 03/12] Update opentelemetry/api/trace/__init__.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Christian Neumüller --- opentelemetry/api/trace/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index 590ae03de09..263eda4fefd 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -100,7 +100,7 @@ def span(self, name: str, parent: Span=None) -> Iterator[None]: Args: name: The name of the span to be created. - parent: This span's parent. + parent: The new span's parent. Raises: ValueError: if ``name`` is null. From 750a8d3226db108d0fe776445d9508a14a882067 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 5 Jun 2019 13:13:46 -0700 Subject: [PATCH 04/12] Add "Yields" to span docstring, remove "Raises" --- opentelemetry/api/trace/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index d75163ec197..4e52c1d6822 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -100,10 +100,10 @@ def span(self, name: str, parent: Span=None) -> Iterator[Span]: Args: name: The name of the span to be created. - parent: The new span's parent. + parent: This span's parent. - Raises: - ValueError: if ``name`` is null. + Yields: + The newly-created span. """ raise NotImplementedError From dd8fab2683bd4ce44a3cbf7baeabd84db5266d11 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 5 Jun 2019 13:17:06 -0700 Subject: [PATCH 05/12] Change get_context docstring --- opentelemetry/api/trace/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index 4e52c1d6822..d1366b15bf1 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -151,7 +151,10 @@ def end(self) -> None: raise NotImplementedError def get_context(self) -> SpanContext: - """Get an immutable snapshot of this span. + """Get the span's SpanContext. + + Get an immutable, serializable identifier for this span that can be + used to create new child spans. Returns: A :class:`.SpanContext` with a copy of this span's immutable state. From e89b8ea119bf55e2914c8d116244d46ffe014b5f Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 5 Jun 2019 13:22:31 -0700 Subject: [PATCH 06/12] Sort imports --- opentelemetry/api/trace/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index d1366b15bf1..51308d6b7e2 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -58,8 +58,8 @@ from __future__ import annotations -from typing import Iterator from contextlib import contextmanager +from typing import Iterator class Tracer(object): From eb32db0617aff68412c7fdfbdb2774578a1cb1a6 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 5 Jun 2019 13:26:53 -0700 Subject: [PATCH 07/12] Remove "global" from span context docstring --- opentelemetry/api/trace/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index 51308d6b7e2..536a34bcbf4 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -171,8 +171,8 @@ class SpanContext(object): Args: trace_id: The ID of the trace that this span belongs to. span_id: This span's ID. - options: Global trace options to propagate. - state: Global tracing-system-specific info to propagate. + options: Trace options to propagate. + state: Tracing-system-specific info to propagate. """ def __init__(self, From 3ca5e78fe4745bc9639761a26514d3678ca1c959 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 5 Jun 2019 16:26:29 -0700 Subject: [PATCH 08/12] Split up span creation managers --- opentelemetry/api/trace/__init__.py | 96 +++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index 536a34bcbf4..a0c7a95a17b 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -25,30 +25,35 @@ tracing, and a concrete no-op ``BlankSpan`` that allows applications to use the API package alone without a supporting implementation. -The tracer supports implicit and explicit context propagation. By default spans -are created as children of the currently active span, and the newly-created -span becomes the new active span:: +The tracer supports creating spans that are "attached" or "detached" from the +context. By default, new spans are "attached" to the context in that they are +created as children of the currently active span, and the newly-created span +becomes the new active span:: from opentelemetry.sdk.trace import tracer # Create a new root span, set it as the current span in context - with tracer.span("parent"): + with tracer.start_span("parent"): # Attach a new child and update the current span - with tracer.span("child"): + with tracer.start_span("child"): do_work(): # Close child span, set parent as current # Close parent span, set default span as current -Under explicit context propagation there is no concept of an active span, and -the caller is responsible for managing the span's lifetime:: +When creating a span that's "detached" from the context the active span doesn't +change, and the caller is responsible for managing the span's lifetime:: from opentelemetry.sdk.trace import tracer - from your.integration import deserialize_span - parent = deserialize_span(serialized_span) # Explicit parent span assignment - with tracer.span("child", parent=parent) as child: + span = tracer.create_span("child", parent=parent) as child: + + # The caller is responsible for starting and ending the span + span.start() + try: do_work(span=child) + finally: + span.end() Applications should generally use a single global tracer, and use either implicit or explicit context propagation consistently throughout. @@ -72,13 +77,18 @@ class Tracer(object): def get_current_span(self) -> Span: """Get the currently active span from the context. + If there is no current span, return a placeholder span with an invalid + context. + Returns: - The currently active :class:`.Span`. + The currently active :class:`.Span`, or a placeholder span with an + invalid :class:`.SpanContext`. """ raise NotImplementedError + @contextmanager - def span(self, name: str, parent: Span=None) -> Iterator[Span]: + def start_span(self, name: str, parent: Span) -> Iterator[Span]: """Context manager for span creation. Create a new child of the current span, or create a root span if no @@ -98,36 +108,74 @@ def span(self, name: str, parent: Span=None) -> Iterator[Span]: tracer.get_current_span() # returns parent tracer.get_current_span() # returns the previously active span + This is a convenience method for creating spans attached to the + tracer's context. Applications that need more control over the span + lifetime should use :meth:`create_span` instead. For example:: + + with tracer.start_span(name) as span: + do_work() + + is equivalent to:: + + span = tracer.create_span(name, parent=tracer.get_current_span()) + with tracer.use_span(span): + do_work() + Args: name: The name of the span to be created. - parent: This span's parent. + parent: The span's parent. Yields: The newly-created span. """ raise NotImplementedError - def create_span(self, name: str, parent: Span=None) -> Span: - """Create a new span as a child of the currently active span. + def create_span(self, name: str, parent: Span) -> Span: + """Creates a new child span of the given parent. - If called with ``parent`` this method won't affect the tracer's - context. + Creating the span does not start it, and should not affect the tracer's + context. To start the span and update the tracer's context to make it + the currently active span, see :meth:`use_span`. + + Applications that need to explicitly set spans' parents or create spans + detached from the tracer's context should use this method. - See ``span`` for a context manager that controls the span's lifetime. + with tracer.start_span(name) as span: + do_work() + + This is equivalent to:: + + span = tracer.create_span(name, parent=tracer.get_current_span()) + with tracer.use_span(span): + do_work() Args: name: The name of the span to be created. - parent: This span's parent. + parent: The span's parent. + + Returns: + The newly-created span. + """ + raise NotImplementedError - Raises: - ValueError: if ``name`` is null. + @contextmanager + def use_span(self, span: Span) -> Iterator[None]: + """Context manager for controlling a span's lifetime. + + Start the given span and set it as the current span in this tracer's + context. + + On exiting the context manager stop the span and set its parent as the + current span. + + Args: + span: The span to start and make current. """ raise NotImplementedError class Span(object): - """A span represents a single operation within a trace. - """ + """A span represents a single operation within a trace.""" def start(self) -> None: """Set the current time as the span's start time. @@ -179,7 +227,7 @@ def __init__(self, trace_id: str, span_id: str, options: TraceOptions, - state: TraceState) -> SpanContext: + state: TraceState) -> None: pass From c3da103945f67200bad6abfc1b6a39f928e818b0 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Wed, 5 Jun 2019 17:14:27 -0700 Subject: [PATCH 09/12] Get declarative --- opentelemetry/api/trace/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index a0c7a95a17b..19029a9741c 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -75,7 +75,7 @@ class Tracer(object): """ def get_current_span(self) -> Span: - """Get the currently active span from the context. + """Gets the currently active span from the context. If there is no current span, return a placeholder span with an invalid context. @@ -178,7 +178,7 @@ class Span(object): """A span represents a single operation within a trace.""" def start(self) -> None: - """Set the current time as the span's start time. + """Sets the current time as the span's start time. Each span represents a single operation. The span's start time is the wall time at which the operation started. @@ -189,7 +189,7 @@ def start(self) -> None: raise NotImplementedError def end(self) -> None: - """Set the current time as the span's end time. + """Sets the current time as the span's end time. The span's end time is the wall time at which the operation finished. @@ -199,7 +199,7 @@ def end(self) -> None: raise NotImplementedError def get_context(self) -> SpanContext: - """Get the span's SpanContext. + """Gets the span's SpanContext. Get an immutable, serializable identifier for this span that can be used to create new child spans. From 946f57df6dddd43cc6d3efb1d2fcc2eb20186590 Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Thu, 6 Jun 2019 22:04:24 -0700 Subject: [PATCH 10/12] Update tracer method names in docstring --- opentelemetry/api/trace/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index 19029a9741c..70dd645d4a9 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -100,9 +100,9 @@ def start_span(self, name: str, parent: Span) -> Iterator[Span]: Example:: - with tracer.span("one") as parent: + with tracer.start_span("one") as parent: parent.add_event("parent's event") - with tracer.span("two") as child: + with tracer.start_span("two") as child: child.add_event("child's event") tracer.get_current_span() # returns child tracer.get_current_span() # returns parent From dd72455f75b0068bdf87e7415c4e35e4b53ef28d Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 18 Jun 2019 19:19:18 -0700 Subject: [PATCH 11/12] Remove references to SDK from API comments --- opentelemetry/api/trace/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index 70dd645d4a9..00a3ac2c45f 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -30,7 +30,8 @@ created as children of the currently active span, and the newly-created span becomes the new active span:: - from opentelemetry.sdk.trace import tracer + # TODO (#15): which module holds the global tracer? + from opentelemetry.api.trace import tracer # Create a new root span, set it as the current span in context with tracer.start_span("parent"): @@ -43,7 +44,7 @@ When creating a span that's "detached" from the context the active span doesn't change, and the caller is responsible for managing the span's lifetime:: - from opentelemetry.sdk.trace import tracer + from opentelemetry.api.trace import tracer # Explicit parent span assignment span = tracer.create_span("child", parent=parent) as child: From 02d810569155e2af0433aecf210d09e59251932b Mon Sep 17 00:00:00 2001 From: Chris Kleinknecht Date: Tue, 18 Jun 2019 19:25:15 -0700 Subject: [PATCH 12/12] s/raise/pass/ --- opentelemetry/api/trace/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/opentelemetry/api/trace/__init__.py b/opentelemetry/api/trace/__init__.py index 00a3ac2c45f..6e4fc80f428 100644 --- a/opentelemetry/api/trace/__init__.py +++ b/opentelemetry/api/trace/__init__.py @@ -85,7 +85,7 @@ def get_current_span(self) -> Span: The currently active :class:`.Span`, or a placeholder span with an invalid :class:`.SpanContext`. """ - raise NotImplementedError + pass @contextmanager @@ -129,7 +129,7 @@ def start_span(self, name: str, parent: Span) -> Iterator[Span]: Yields: The newly-created span. """ - raise NotImplementedError + pass def create_span(self, name: str, parent: Span) -> Span: """Creates a new child span of the given parent. @@ -157,7 +157,7 @@ def create_span(self, name: str, parent: Span) -> Span: Returns: The newly-created span. """ - raise NotImplementedError + pass @contextmanager def use_span(self, span: Span) -> Iterator[None]: @@ -172,7 +172,7 @@ def use_span(self, span: Span) -> Iterator[None]: Args: span: The span to start and make current. """ - raise NotImplementedError + pass class Span(object): @@ -187,7 +187,7 @@ def start(self) -> None: Only the first call to ``start`` should modify the span, and implementations are free to ignore or raise on further calls. """ - raise NotImplementedError + pass def end(self) -> None: """Sets the current time as the span's end time. @@ -197,7 +197,7 @@ def end(self) -> None: Only the first call to ``end`` should modify the span, and implementations are free to ignore or raise on further calls. """ - raise NotImplementedError + pass def get_context(self) -> SpanContext: """Gets the span's SpanContext. @@ -208,7 +208,7 @@ def get_context(self) -> SpanContext: Returns: A :class:`.SpanContext` with a copy of this span's immutable state. """ - raise NotImplementedError + pass class SpanContext(object):