Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- **[FEATURE]**: feat(logging): initial implementation of log points [#3](https://github.com/intergral/deep/pull/3) [@Umaaz](https://github.com/Umaaz)
- **[FEATURE]**: feat(plugins): change plugins to allow better customisation [#22](https://github.com/intergral/deep/pull/22) [@Umaaz](https://github.com/Umaaz)
- **[FEATURE]**: feat(metrics): initial implementation of metric points [#21](https://github.com/intergral/deep/pull/21) [@Umaaz](https://github.com/Umaaz)
- **[FEATURE]**: feat(spans): initial implementation of span points [#25](https://github.com/intergral/deep/pull/25) [@Umaaz](https://github.com/Umaaz)
- **[ENHANCEMENT]**: enhancement(trigger): change tracepoint handling to use triggers [#16](https://github.com/intergral/deep/pull/16) [@Umaaz](https://github.com/Umaaz)
- **[BUGFIX]**: feat(api): add api function to register tracepoint directly [#8](https://github.com/intergral/deep/pull/8) [@Umaaz](https://github.com/Umaaz)

Expand All @@ -14,8 +15,8 @@
<!-- Template START
# 0.1.1 (16/06/2023)

- **[CHANGE]**: description [#PRid](https://github.com/intergral/deep/pull/8) [@user](https://github.com/)
- **[FEATURE]**: description [#PRid](https://github.com/intergral/deep/pull/) [@user](https://github.com/)
- **[ENHANCEMENT]**: description [#PRid](https://github.com/intergral/deep/pull/) [@user](https://github.com/)
- **[BUGFIX]**: description [#PRid](https://github.com/intergral/deep/pull/) [@user](https://github.com/)
- **[CHANGE]**: description [#PRid](https://github.com/intergral/deep/pull/PRid) [@user](https://github.com/user)
- **[FEATURE]**: description [#PRid](https://github.com/intergral/deep/pull/PRid) [@user](https://github.com/user)
- **[ENHANCEMENT]**: description [#PRid](https://github.com/intergral/deep/pull/PRid) [@user](https://github.com/user)
- **[BUGFIX]**: description [#PRid](https://github.com/intergral/deep/pull/PRid) [@user](https://github.com/user)
Template END -->
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ it-test:

.PHONY: coverage
coverage:
pytest tests/unit_tests --cov=deep --cov-report term --cov-fail-under=83 --cov-report html --cov-branch
pytest tests/unit_tests --cov=deep --cov-report term --cov-fail-under=84 --cov-report html --cov-branch

.PHONY: lint
lint:
Expand Down
23 changes: 18 additions & 5 deletions examples/simple-app-otel/src/simple-app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
import time

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.trace.export import (
SimpleSpanProcessor,
)

import deep
from deep.api.tracepoint.constants import METHOD_NAME, SPAN, METHOD, FIRE_COUNT, SNAPSHOT, NO_COLLECT
from simple_test import SimpleTest


Expand Down Expand Up @@ -63,16 +66,26 @@ def main():
resource = Resource(attributes={
SERVICE_NAME: "your-service-name"
})

jaeger_exporter = JaegerExporter(
agent_host_name="localhost",
agent_port=6831
)

provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4317/api/traces"))
provider.add_span_processor(processor)
provider.add_span_processor(SimpleSpanProcessor(jaeger_exporter))
# Sets the global default tracer provider
trace.set_tracer_provider(provider)

deep.start({
_deep = deep.start({
'SERVICE_URL': 'localhost:43315',
'SERVICE_SECURE': 'False',
'POLL_TIMER': 10000
})

tracepoint = _deep.register_tracepoint("simple_test.py", -1,
{METHOD_NAME: 'message', SPAN: METHOD, FIRE_COUNT: '-1',
SNAPSHOT: NO_COLLECT})

print("app running")
main()
117 changes: 87 additions & 30 deletions src/deep/api/plugin/otel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@

from typing import Optional

import deep.logging
from deep.api.attributes import BoundedAttributes
from deep.api.plugin import DidNotEnable, SnapshotDecorator, ResourceProvider
from deep.api.plugin.span import SpanProcessor, Span
from deep.api.resource import Resource
from deep.processor.context.action_context import ActionContext

Expand All @@ -30,13 +32,92 @@
raise DidNotEnable("opentelemetry is not installed", e)


class OTelPlugin(ResourceProvider, SnapshotDecorator):
class _OtelSpan(Span):
"""Wrap Otel span in common interface."""

def __init__(self, proxy: _Span):
"""
Create a new wrapper for Otel span.

:param proxy: the underlying otel span
"""
self.proxy = proxy

@property
def name(self):
"""Get the span name."""
return self.proxy.name

@property
def trace_id(self):
"""Get the trace id."""
return self.__format_trace_id(self.proxy.context.trace_id)

@property
def span_id(self):
"""Get the span id."""
return self.__format_span_id(self.proxy.context.span_id)

def add_attribute(self, key: str, value: str):
"""
Add an attribute to the span.

:param key: the attribute key
:param value: the attribute value
"""
self.proxy.set_attribute(key, value)

def close(self):
"""Close the span."""
if not self.proxy.end_time:
try:
self.proxy.end()
except Exception:
deep.logging.exception("failed to close span")

@staticmethod
def __format_span_id(_id):
return format(_id, "016x")

@staticmethod
def __format_trace_id(_id):
return format(_id, "032x")


class OTelPlugin(ResourceProvider, SnapshotDecorator, SpanProcessor):
"""
Deep Otel plugin.

Provide span and trace information to the snapshot.
"""

def create_span(self, name: str) -> Optional['Span']:
"""
Create and return a new span.

:param name: the name of the span to create
:return: the created span
"""
span = trace.get_tracer("deep").start_as_current_span(name, end_on_exit=False, attributes={'dynamic': 'deep'})
if span:
# noinspection PyUnresolvedReferences
# this is a generator contextlib._GeneratorContextManager
current = span.__enter__()
if isinstance(current, _Span):
return _OtelSpan(current)
return None

def current_span(self) -> Optional['Span']:
"""
Get the current span from the underlying provider.

:return: the current span
"""
span = self.__get_span()
if span:
return _OtelSpan(span)
return None

def resource(self) -> Optional[Resource]:
"""
Provide resource.
Expand All @@ -59,42 +140,18 @@ def decorate(self, context: ActionContext) -> Optional[BoundedAttributes]:

:return: the additional attributes to attach
"""
span = OTelPlugin.__get_span()
span = self.current_span()
if span is not None:
return BoundedAttributes(attributes={
"span_name": OTelPlugin.__span_name(span),
"trace_id": OTelPlugin.__trace_id(span),
"span_id": OTelPlugin.__span_id(span)
"span_name": span.name,
"trace_id": span.trace_id,
"span_id": span.span_id
})
return None

@staticmethod
def __span_name(span):
# type: (_Span)-> Optional[str]
return span.name if span.name else None

@staticmethod
def __span_id(span):
# type: (_Span)-> Optional[str]
return (OTelPlugin.__format_span_id(span.context.span_id)) if span else None

@staticmethod
def __trace_id(span):
# type: (_Span)-> Optional[str]
return (OTelPlugin.__format_trace_id(span.context.trace_id)) if span else None

@staticmethod
def __get_span():
# type: () -> Optional[_Span]
def __get_span() -> Optional[_Span]:
span = trace.get_current_span()
if isinstance(span, _Span):
return span
return None

@staticmethod
def __format_span_id(_id):
return format(_id, "016x")

@staticmethod
def __format_trace_id(_id):
return format(_id, "032x")
85 changes: 85 additions & 0 deletions src/deep/api/plugin/span/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copyright (C) 2024 Intergral GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

"""
Definition of span processor.

Span processor gives the ability to generate spans dynamically.
"""

import abc
from typing import Optional

from deep.api.plugin import Plugin


class SpanProcessor(Plugin, abc.ABC):
"""Span processor connects Deep to a span provider."""

@abc.abstractmethod
def create_span(self, name: str) -> Optional['Span']:
"""
Create and return a new span.

:param name: the name of the span to create
:return: the created span
"""
pass

@abc.abstractmethod
def current_span(self) -> Optional['Span']:
"""
Get the current span from the underlying provider.

:return: the current span
"""
pass


class Span:
"""Internal type to wrap spans."""

@property
@abc.abstractmethod
def name(self):
"""Get the span name."""
pass

@property
@abc.abstractmethod
def trace_id(self):
"""Get the trace id."""
pass

@property
@abc.abstractmethod
def span_id(self):
"""Get the span id."""
pass

@abc.abstractmethod
def add_attribute(self, key: str, value: str):
"""
Add an attribute to the span.

:param key: the attribute key
:param value: the attribute value
"""
pass

@abc.abstractmethod
def close(self):
"""Close the span."""
pass
3 changes: 3 additions & 0 deletions src/deep/api/tracepoint/tracepoint_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ def path(self):
@property
def line_no(self):
"""The tracepoint line number."""
# todo need to support missing line number in grpc
if self._line_no < 0:
return 0
return self._line_no

@property
Expand Down
Loading