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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ dist/
/tmp/
*.egg-info
.pytest_cache/
/.project
/.pydevproject
/.README.md.html
/.settings
124 changes: 123 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ This SDK enables Dynatrace customers to extend request level visibility into Pyt
* [Outgoing web requests](#outgoing-web-requests)
* [Trace in-process asynchronous execution](#trace-in-process-asynchronous-execution)
* [Custom request attributes](#custom-request-attributes)
* [Custom services](#custom-services)
* [Messaging](#messaging)
* [Outgoing Messages](#outgoing-messaging)
* [Incoming Messages](#incoming-messaging)
- [Troubleshooting](#troubleshooting)
* [Installation issues](#installation-issues)
* [Post-installation issues](#post-installation-issues)
Expand Down Expand Up @@ -54,6 +58,7 @@ Dynatrace OneAgent version (it is the same as
|:----------------------|:---------------------|:-----------------|
|1.0 |1.1.0 |≥1.141 |
|1.1 |1.3.1 |≥1.151 |
|1.2 |1.4.1 |≥1.161 |

<a name="#using-the-oneagent-sdk-for-python-in-your-application"></a>
## Using the OneAgent SDK for Python in your application
Expand Down Expand Up @@ -207,6 +212,8 @@ A more detailed specification of the features can be found in [Dynatrace OneAgen
|Outgoing web requests |≥1.1.0 |
|Custom request attributes |≥1.1.0 |
|In-process linking |≥1.1.0 |
|Messaging |≥1.2.0 |
|Custom services |≥1.2.0 |

<a name="remote-calls"></a>
### Remote calls
Expand Down Expand Up @@ -387,7 +394,7 @@ The provided in-process link must not be serialized and can only be used inside
tracing where the asynchronous execution takes place:

```python
with sdk.trace_in_process_link(in_process_link):
with sdk.trace_in_process_link(in_process_link):
# Do the asynchronous job
:
```
Expand All @@ -411,6 +418,121 @@ Check out the documentation at:
* [`add_custom_request_attribute`](https://dynatrace.github.io/OneAgent-SDK-for-Python/docs/sdkref.html#oneagent.sdk.SDK.add_custom_request_attribute)


<a name="custom-services"></a>
### Custom services
You can use the SDK to trace custom service methods. A custom service method is a meaningful part
of your code that you want to trace but that does not fit any other tracer. An example could be
the callback of a periodic timer.

```python
with sdk.trace_custom_service('onTimer', 'CleanupTask'):
# Do the cleanup task
:
```

Check out the documentation at:
* [`trace_custom_service`](https://dynatrace.github.io/OneAgent-SDK-for-Python/docs/sdkref.html#oneagent.sdk.SDK.trace_custom_service)


<a name="messaging"></a>
### Messaging

You can use the SDK to trace messages sent or received via a messaging system. When tracing messages,
we distinguish between:

* sending a message
* waiting for and receiving a message
* processing a received message

<a name="outgoing-messaging"></a>
#### Outgoing Messages

All messaging related tracers need a messaging system info object which you have to create prior
to the respective messaging tracer, which is an outgoing message tracer in the example below.

```python
msi_handle = sdk.create_messaging_system_info(
'myMessagingSystem', 'requestQueue', MessagingDestinationType.QUEUE,
ChannelType.TCP_IP, '10.11.12.13')

with msi_handle:
with sdk.trace_outgoing_message(msi_handle) as tracer:
# Get and set the Dynatrace tag.
tag = tracer.outgoing_dynatrace_string_tag
message_to_send.add_header_field(oneagent.sdk.DYNATRACE_MESSAGE_PROPERTY_NAME, tag)

# Send the message.
the_queue.send(message_to_send)

# Optionally set message and/or correlation IDs
tracer.set_vendor_message_id(message_to_send.get_message_id())
tracer.set_correlation_id(message_to_send.get_correlation_id())
```

<a name="incoming-messaging"></a>
#### Incoming Messages

On the incoming side, we need to differentiate between the blocking receiving part and processing
the received message. Therefore two different tracers are being used:

* IncomingMessageReceiveTracer
* IncomingMessageProcessTracer

```python
msi_handle = sdk.create_messaging_system_info(
'myMessagingSystem', 'requestQueue', MessagingDestinationType.QUEUE,
ChannelType.TCP_IP, '10.11.12.13')

with msi_handle:
# Create the receive tracer for incoming messages.
with sdk.trace_incoming_message_receive(msi_handle):
# This is a blocking call, which will return as soon as a message is available.
Message query_message = the_queue.receive()

# Get the Dynatrace tag from the message.
tag = query_message.get_header_field(oneagent.sdk.DYNATRACE_MESSAGE_PROPERTY_NAME)

# Create the tracer for processing incoming messages.
tracer = sdk.trace_incoming_message_process(msi_handle, str_tag=tag)
tracer.set_vendor_message_id(query_message.get_vendor_id())
tracer.set_correlation_id(query_message.get_correlation_id())

with tracer:
# Now let's handle the message ...
print('handle incoming message')
```

In case of non-blocking receive (e. g. using an event handler), there is no need to use an
IncomingMessageReceiveTracer - just trace processing of the message by using the IncomingMessageProcessTracer:

```python
msi_handle = sdk.create_messaging_system_info(
'myMessagingSystem', 'requestQueue', MessagingDestinationType.QUEUE,
ChannelType.TCP_IP, '10.11.12.13')

def on_message_received(message):
# Get the Dynatrace tag from the message.
tag = message.get_header_field(oneagent.sdk.DYNATRACE_MESSAGE_PROPERTY_NAME)

# Create the tracer for processing incoming messages.
tracer = sdk.trace_incoming_message_process(msi_handle, str_tag=tag)
tracer.set_vendor_message_id(message.get_vendor_id())
tracer.set_correlation_id(message.get_correlation_id())

with tracer:
# Now let's handle the message ...
print('handle incoming message')
```

See the documentation for more information:

* [`create_messaging_system_info`](https://dynatrace.github.io/OneAgent-SDK-for-Python/docs/sdkref.html#oneagent.sdk.SDK.create_messaging_system_info)
* [`trace_outgoing_message`](https://dynatrace.github.io/OneAgent-SDK-for-Python/docs/sdkref.html#oneagent.sdk.tracers.trace_outgoing_message)
* [`trace_incoming_message_receive`](https://dynatrace.github.io/OneAgent-SDK-for-Python/docs/sdkref.html#oneagent.sdk.tracers.trace_incoming_message_receive)
* [`trace_incoming_message_process`](https://dynatrace.github.io/OneAgent-SDK-for-Python/docs/sdkref.html#oneagent.sdk.tracers.trace_incoming_message_process)
* [General information on tagging](https://dynatrace.github.io/OneAgent-SDK-for-Python/docs/tagging.html)


<a name="troubleshooting"></a>
## Troubleshooting

Expand Down
3 changes: 3 additions & 0 deletions docs/tagging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ The following classes are incoming-taggable:
:meth:`oneagent.sdk.SDK.trace_incoming_remote_call`
- :class:`IncomingWebRequestTracer` /
:meth:`oneagent.sdk.SDK.trace_incoming_web_request`
- :class:`IncomingMessageProcessTracer` /
:meth:`oneagent.sdk.SDK.trace_incoming_message_process`

The following classes are :class:`OutgoingTaggable`:

- :class:`OutgoingRemoteCallTracer`
- :class:`OutgoingWebRequestTracer`
- :class:`OutgoingMessageTracer`

You first use either :attr:`OutgoingTaggable.outgoing_dynatrace_string_tag` or
:attr:`OutgoingTaggable.outgoing_dynatrace_byte_tag` to retrieve a string or
Expand Down
57 changes: 57 additions & 0 deletions samples/basic-sdk-sample/basic_sdk_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import oneagent # SDK initialization functions
import oneagent.sdk as onesdk # All other SDK functions.

from oneagent.common import MessagingDestinationType


try: # Python 2 compatibility.
input = raw_input #pylint:disable=redefined-builtin
except NameError:
Expand Down Expand Up @@ -166,6 +169,9 @@ def mock_incoming_web_request():
# This call will trigger the diagnostic callback.
sdk.add_custom_request_attribute('another key', None)

# This call simulates incoming messages.
mock_process_incoming_message()

def _process_my_outgoing_request(_tag):
pass

Expand All @@ -190,6 +196,53 @@ def mock_outgoing_web_request():
tracer.add_response_headers({'Content-Length': '1234'})
tracer.set_status_code(200) # OK

def mock_process_incoming_message():
sdk = getsdk()

# Create the messaging system info object.
msi_handle = sdk.create_messaging_system_info(
'MyPythonSenderVendor', 'MyPythonDestination', MessagingDestinationType.QUEUE,
onesdk.Channel(onesdk.ChannelType.UNIX_DOMAIN_SOCKET, 'MyPythonChannelEndpoint'))

with msi_handle:
# Create the receive tracer for incoming messages.
with sdk.trace_incoming_message_receive(msi_handle):
print('here we wait for incoming messages ...')

# Create the tracer for processing incoming messages.
tracer = sdk.trace_incoming_message_process(msi_handle)

# Now we can set the vendor message and correlation IDs. It's possible to set them
# either before the tracer is started or afterwards. But they have to be set before
# the tracer ends.
tracer.set_vendor_message_id('message_id')
with tracer:
print('handle incoming message')
tracer.set_correlation_id('correlation_id')

def mock_outgoing_message():
sdk = getsdk()

# Create the messaging system info object.
msi_handle = sdk.create_messaging_system_info(
'MyPythonReceiverVendor', 'MyPythonDestination', MessagingDestinationType.TOPIC,
onesdk.Channel(onesdk.ChannelType.TCP_IP, '10.11.12.13:1415'))

with msi_handle:
# Create the outgoing message tracer;
with sdk.trace_outgoing_message(msi_handle) as tracer:
# Set the message and correlation IDs.
tracer.set_vendor_message_id('msgId')
tracer.set_correlation_id('corrId')

print('handle outgoing message')

def mock_custom_service():
sdk = getsdk()

with sdk.trace_custom_service('my_fancy_transaction', 'MyFancyService'):
print('do some fancy stuff')

def _diag_callback(text):
print(text)

Expand Down Expand Up @@ -237,6 +290,10 @@ def main():

mock_outgoing_web_request()

mock_outgoing_message()

mock_custom_service()

# We use trace_incoming_remote_call here, because it is one of the few
# calls that create a new path if none is running yet.
with sdk.trace_incoming_remote_call('main', 'main', 'main'):
Expand Down
2 changes: 1 addition & 1 deletion src/oneagent/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@

# See https://www.python.org/dev/peps/pep-0440/ "Version Identification and
# Dependency Specification"
__version__ = '1.1.0'
__version__ = '1.2.0'

logger = logging.getLogger('py_sdk')
logger.setLevel(logging.CRITICAL + 1) # Disabled by default
Expand Down
68 changes: 68 additions & 0 deletions src/oneagent/_impl/native/sdkctypesiface.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,74 @@ def __init__(self, libname):
None,
public=False), CCString)

self._init_messaging()

self._init_custom_service()


def _init_custom_service(self):
initfn = self._initfn

initfn(
'customservicetracer_create_p',
(CCStringPInArg, CCStringPInArg),
handle_t).__doc__ = \
'(service_method, service_name) -> tracer'


def _init_messaging(self):
initfn = self._initfn

initfn(
'messagingsysteminfo_create_p',
(CCStringPInArg, CCStringPInArg, ctypes.c_int32, ctypes.c_int32, CCStringPInArg),
handle_t).__doc__ = \
'(vendor_name, destination_name, destination_type, \
channel_type, channel_endpoint) -> tracer'

initfn(
'messagingsysteminfo_delete',
(handle_t,),
None)

initfn(
'outgoingmessagetracer_create',
(handle_t,),
handle_t)

initfn(
'outgoingmessagetracer_set_vendor_message_id_p',
(handle_t, CCStringPInArg),
None).__doc__ = \
'(tracer_handle, vendor_message_id)'

initfn(
'outgoingmessagetracer_set_correlation_id_p',
(handle_t, CCStringPInArg),
None).__doc__ = '(tracer_handle, correlation_id)'

initfn(
'incomingmessagereceivetracer_create',
(handle_t,),
handle_t)

initfn(
'incomingmessageprocesstracer_create',
(handle_t,),
handle_t)

initfn(
'incomingmessageprocesstracer_set_vendor_message_id_p',
(handle_t, CCStringPInArg),
None).__doc__ = \
'(tracer_handle, vendor_message_id)'

initfn(
'incomingmessageprocesstracer_set_correlation_id_p',
(handle_t, CCStringPInArg),
None).__doc__ = '(tracer_handle, correlation_id)'


def initialize(self):
result = self._initialize()

Expand Down
39 changes: 37 additions & 2 deletions src/oneagent/_impl/native/sdknulliface.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,45 @@ def customrequestattribute_add_strings(self, keys, values, count):
def customrequestattribute_add_string(self, key, value):
pass

#pylint:enable=invalid-name

def trace_in_process_link(self, link_bytes):
pass

def create_in_process_link(self):
pass

# Messaging API

#pylint:disable=too-many-arguments
def messagingsysteminfo_create(self, vendor_name, destination_name, destination_type,
channel_type, channel_endpoint):
return NULL_HANDLE

def messagingsysteminfo_delete(self, handle):
pass

def outgoingmessagetracer_create(self, handle):
return NULL_HANDLE

def outgoingmessagetracer_set_vendor_message_id(self, handle, vendor_message_id):
pass

def outgoingmessagetracer_set_correlation_id(self, handle, correlation_id):
pass

def incomingmessagereceivetracer_create(self, handle):
return NULL_HANDLE

def incomingmessageprocesstracer_create(self, handle):
return NULL_HANDLE

def incomingmessageprocesstracer_set_vendor_message_id(self, handle, message_id):
pass

def incomingmessageprocesstracer_set_correlation_id(self, handle, correlation_id):
pass

#pylint:enable=too-many-arguments
#pylint:enable=invalid-name

def customservicetracer_create(self, service_method, service_name):
return NULL_HANDLE
Loading