Skip to content

Commit d9998ef

Browse files
authored
[SYNPY-1606] File Upload/Download OpenTelemetry Transfer Data (#1204)
* File Upload/Download OpenTelemetry Transfer Data
1 parent 6a4e858 commit d9998ef

File tree

15 files changed

+891
-178
lines changed

15 files changed

+891
-178
lines changed

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
## Here are the environment variables you can set for the Synapse Python client to configure OpenTelemetry tracing and metrics:
2+
# You can copy this file to `.env` and fill in the values as needed.
3+
# OTEL_SERVICE_NAME=my-service-using-synapse-python-client
4+
# OTEL_EXPORTER_OTLP_ENDPOINT=http://fill-me-in
5+
# OTEL_SERVICE_INSTANCE_ID=local_development_testing
6+
# OTEL_EXPORTER_OTLP_HEADERS=# Authorization

.github/workflows/build.yml

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -123,40 +123,6 @@ jobs:
123123
else
124124
echo "synapse_pat_available=true" >> $GITHUB_OUTPUT;
125125
fi
126-
- name: OpenTelemtry pre-check
127-
id: otel-check
128-
if: ${{ steps.secret-check.outputs.secrets_available == 'true' && steps.secret-check.outputs.synapse_pat_available == 'true' }}
129-
shell: bash
130-
run: |
131-
# Leave disabled during normal integration test runs - Enable when we want to
132-
# collect the data.
133-
# echo "run_opentelemetry=true" >> $GITHUB_OUTPUT;
134-
echo "run_opentelemetry=false" >> $GITHUB_OUTPUT;
135-
136-
# AWS CLI is pre-installed on github hosted runners - Commented out for GH runs
137-
# curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
138-
# unzip awscliv2.zip
139-
# sudo ./aws/install
140-
# curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" -o "session-manager-plugin.deb"
141-
# sudo dpkg -i session-manager-plugin.deb
142-
# - name: Create AWS Config
143-
# if: ${{ steps.otel-check.outputs.run_opentelemetry == 'true' }}
144-
# shell: bash
145-
# run: |
146-
# touch test.awsConfig
147-
# printf "[default]\nregion = us-east-1\ncredential_process = \"tests/integration/synapse_creds.sh\" \"https://sc.sageit.org\" \"${{ secrets.synapse_personal_access_token }}\"\n" >> test.awsConfig
148-
# chmod +x tests/integration/synapse_creds.sh
149-
# If you are exporting data using `otlp` you can start a port forwading session
150-
# - name: SSM Port Forward Start
151-
# if: ${{ steps.otel-check.outputs.run_opentelemetry == 'true' }}
152-
# shell: bash
153-
# env:
154-
# AWS_CONFIG_FILE: "test.awsConfig"
155-
# run: |
156-
# # Start a port-forwarding session in a non-interactive way. AWS will clean-up
157-
# # stale sessions after 20 minutes of inactivity
158-
# aws ssm start-session --target i-0ffcdecd1edf375ee --document-name AWS-StartPortForwardingSession --parameters "portNumber"=["4318"],"localPortNumber"=["4318"] & disown
159-
# sleep 15
160126
161127
# run integration tests iff the decryption keys for the test configuration are available.
162128
# they will not be available in pull requests from forks.
@@ -194,10 +160,12 @@ jobs:
194160
export EXTERNAL_S3_BUCKET_NAME="${{secrets.EXTERNAL_S3_BUCKET_NAME}}"
195161
export EXTERNAL_S3_BUCKET_AWS_ACCESS_KEY_ID="${{secrets.EXTERNAL_S3_BUCKET_AWS_ACCESS_KEY_ID}}"
196162
export EXTERNAL_S3_BUCKET_AWS_SECRET_ACCESS_KEY="${{secrets.EXTERNAL_S3_BUCKET_AWS_SECRET_ACCESS_KEY}}"
197-
if [ ${{ steps.otel-check.outputs.run_opentelemetry }} == "true" ]; then
198-
# Set to 'file' to enable OpenTelemetry export to file
199-
export SYNAPSE_OTEL_INTEGRATION_TEST_EXPORTER="file"
200-
fi
163+
164+
# Set env vars for OTEL
165+
export OTEL_EXPORTER_OTLP_ENDPOINT="${{ vars.OTEL_EXPORTER_OTLP_ENDPOINT }}"
166+
export OTEL_SERVICE_INSTANCE_ID="${{ vars.OTEL_SERVICE_INSTANCE_ID }}"
167+
export SYNAPSE_INTEGRATION_TEST_OTEL_ENABLED="${{ vars.SYNAPSE_INTEGRATION_TEST_OTEL_ENABLED }}"
168+
export OTEL_EXPORTER_OTLP_HEADERS="${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}"
201169
202170
# Setup ignore patterns based on Python version
203171
IGNORE_FLAGS="--ignore=tests/integration/synapseclient/test_command_line_client.py"
@@ -217,13 +185,6 @@ jobs:
217185
218186
# Execute the CLI tests in a non-dist way because they were causing some test instability when being run concurrently
219187
pytest -sv --reruns 3 --cov-append --cov=. --cov-report xml tests/integration/synapseclient/test_command_line_client.py
220-
- name: Upload otel spans
221-
uses: actions/upload-artifact@v4
222-
if: always()
223-
with:
224-
name: otel_spans_integration_testing_${{ matrix.os }}
225-
path: tests/integration/otel_spans_integration_testing_*.ndjson
226-
if-no-files-found: ignore
227188
- name: Upload coverage report
228189
id: upload_coverage_report
229190
uses: actions/upload-artifact@v4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ coverage.xml
3232

3333
.ipynb_checkpoints
3434
*.ipynb
35+
.env

README.md

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ The purpose of synapseutils is to create a space filled with convenience functio
273273

274274
OpenTelemetry (OTEL)
275275
--------------------------------
276-
[OpenTelemetry](https://opentelemetry.io/) helps support the analysis of traces and spans which can provide insights into latency, errors, and other performance metrics. The synapseclient is ready to provide traces should you want them. The Synapse Python client supports OTLP Exports and can be configured via environment variables as defined [here](https://opentelemetry-python.readthedocs.io/en/stable/exporter/otlp/otlp.html).
276+
[OpenTelemetry](https://opentelemetry.io/) helps support the analysis of traces and spans which can provide insights into latency, errors, and other performance metrics. The synapseclient is ready to provide traces should you want them. The Synapse Python client supports OTLP Exports and can be configured via environment variables as defined [here](https://opentelemetry.io/docs/specs/otel/protocol/exporter/).
277277

278278
Read more about OpenTelemetry in Python [here](https://opentelemetry.io/docs/instrumentation/python/)
279279

@@ -290,47 +290,87 @@ docker run --name jaeger \
290290
jaegertracing/all-in-one:latest
291291
```
292292
Explanation of ports:
293-
* `4318` HTTP
294-
* `16686` Jaeger UI
293+
* `4318` HTTP port for OTLP data collection
294+
* `16686` Jaeger UI for visualizing traces
295295

296296
Once the docker container is running you can access the Jaeger UI via: `http://localhost:16686`
297297

298-
#### Example
299-
By default the OTEL exporter sends trace data to `http://localhost:4318/v1/traces`, however you may override this by setting the `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` environment variable.
298+
#### Environment Variable Configuration
300299

301-
```
300+
By default, the OTEL exporter sends trace data to `http://localhost:4318/v1/traces`. You can customize the behavior through environment variables:
301+
302+
* `OTEL_SERVICE_NAME`: Defines a unique identifier for your application or service in telemetry data (defaults to 'synapseclient'). Set this to a descriptive name that represents your specific implementation, making it easier to filter and analyze traces in your monitoring system.
303+
* `OTEL_EXPORTER_OTLP_ENDPOINT`: Specifies the destination URL for sending telemetry data (defaults to 'http://localhost:4318'). Configure this to direct data to your preferred OpenTelemetry collector or monitoring service.
304+
* `OTEL_DEBUG_CONSOLE`: Controls local visibility of telemetry data. Set to 'true' to output trace information to the console, which is useful for development and troubleshooting without an external collector.
305+
* `OTEL_SERVICE_INSTANCE_ID`: Distinguishes between multiple instances of the same service (e.g., 'prod', 'development', 'local'). This helps identify which specific deployment or environment generated particular traces.
306+
* `OTEL_EXPORTER_OTLP_HEADERS`: Configures authentication and metadata for telemetry exports. Use this to add API keys, authentication tokens, or custom metadata when sending traces to secured collectors or third-party monitoring services.
307+
308+
309+
#### Enabling OpenTelemetry in your code
310+
To enable OpenTelemetry with the Synapse Python client, simply call the
311+
`enable_open_telemetry()` method on the Synapse class. Additionally you can access an
312+
instance of the OpenTelemetry tracer via the `get_tracer()` call. This will allow you
313+
to create new spans for your code.
314+
315+
```python
302316
import synapseclient
303-
from opentelemetry import trace
304-
from opentelemetry.sdk.trace import TracerProvider
305-
from opentelemetry.sdk.trace.export import BatchSpanProcessor
306-
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
307-
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
308-
309-
trace.set_tracer_provider(
310-
TracerProvider(
311-
resource=Resource(attributes={SERVICE_NAME: "my_own_code_above_synapse_client"})
312-
)
313-
)
314-
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
315-
tracer = trace.get_tracer("my_tracer")
316-
synapseclient.Synapse.enable_open_telemetry(True)
317317

318-
@tracer.start_as_current_span("my_span_name")
319-
def main():
318+
# Enable OpenTelemetry with default settings
319+
synapseclient.Synapse.enable_open_telemetry()
320+
tracer = synapseclient.Synapse.get_tracer()
321+
322+
# Then create and use the Synapse client as usual
323+
with tracer.start_as_current_span("my_function_span"):
320324
syn = synapseclient.Synapse()
321-
syn.login()
322-
my_entity = syn.get("syn52569429")
323-
print(my_entity)
325+
syn.login(authToken='auth_token')
326+
```
324327

325-
main()
328+
#### Advanced Configuration
329+
330+
You can pass additional resource attributes to `enable_open_telemetry()`:
331+
332+
```python
333+
import synapseclient
334+
335+
# Enable with custom resource attributes
336+
synapseclient.Synapse.enable_open_telemetry(
337+
resource_attributes={
338+
"deployment.environment": "development",
339+
"service.version": "1.2.3", # Overrides the `OTEL_SERVICE_NAME` environment variable
340+
"service.instance.id": "4.5.6", # Overrides the `OTEL_SERVICE_INSTANCE_ID` environment variable
341+
"custom.attribute": "value"
342+
}
343+
)
326344
```
327345

346+
When OpenTelemetry is enabled in the Synapse client, the following happens automatically:
347+
348+
1. Instrumentation is set up for:
349+
- **Threading** (via `ThreadingInstrumentor`): Ensures proper context propagation across threads, which is essential for maintaining trace continuity in multi-threaded applications
350+
- **HTTP libraries**:
351+
- `requests` (via `RequestsInstrumentor`): Captures all HTTP requests made using the requests library, including methods, URLs, status codes, and timing information
352+
- `httpx` (via `HTTPXClientInstrumentor`): Tracks both synchronous and asynchronous HTTP requests made with the httpx library
353+
- `urllib` (via `URLLibInstrumentor`): Monitors lower-level HTTP operations made directly with Python's standard library
354+
- Each instrumented HTTP library includes custom hooks that extract Synapse entity IDs from URLs when possible and add them as span attributes
355+
356+
2. Traces are configured to collect spans across your application:
357+
- Spans automatically capture operation duration, status, and errors.
358+
- An attribute propagation mechanism ensures that certain attributes (like `synapse.transfer.direction` and `synapse.operation.category`) are properly passed to child spans for uploads/downloads.
359+
- Trace data is exported via OTLP (OpenTelemetry Protocol).
360+
361+
3. Resource information is automatically added to your traces, including:
362+
- Python version
363+
- OS type
364+
- Synapse client version
365+
- Service name (defaults to "synapseclient" but can be customized via environment variables)
366+
- Service instance ID
328367

368+
Note that once enabled, OpenTelemetry cannot be disabled in the same process - you would need to restart your Python interpreter to disable it.
329369

330370

331371
License and Copyright
332372
---------------------
333373

334-
© Copyright 2013-23 Sage Bionetworks
374+
© Copyright 2013-25 Sage Bionetworks
335375

336376
This software is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).

0 commit comments

Comments
 (0)