monocle-apptrace 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of monocle-apptrace might be problematic. Click here for more details.
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/.gitignore +2 -1
- monocle_apptrace-0.3.0/CHANGELOG.md +75 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/Monocle_User_Guide.md +9 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/Monocle_committer_guide.md +31 -1
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/PKG-INFO +19 -2
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/pyproject.toml +32 -3
- monocle_apptrace-0.3.0/src/monocle_apptrace/__init__.py +1 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/__main__.py +19 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/src/monocle_apptrace/exporters/aws/s3_exporter.py +50 -27
- monocle_apptrace-0.3.0/src/monocle_apptrace/exporters/aws/s3_exporter_opendal.py +137 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/src/monocle_apptrace/exporters/azure/blob_exporter.py +30 -12
- monocle_apptrace-0.3.0/src/monocle_apptrace/exporters/azure/blob_exporter_opendal.py +162 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/exporters/base_exporter.py +48 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/exporters/exporter_processor.py +144 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/src/monocle_apptrace/exporters/file_exporter.py +16 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/exporters/monocle_exporters.py +55 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/src/monocle_apptrace/exporters/okahu/okahu_exporter.py +8 -6
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/__init__.py +1 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/common/__init__.py +2 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/common/constants.py +70 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/common/instrumentor.py +362 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/common/span_handler.py +220 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/common/utils.py +356 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/common/wrapper.py +92 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/common/wrapper_method.py +72 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/botocore/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +95 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/botocore/entities/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +65 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +26 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/botocore/methods.py +16 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/flask/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/flask/_helper.py +29 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/flask/methods.py +13 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/haystack/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +127 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +76 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/retrieval.py +61 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/haystack/methods.py +43 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langchain/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +127 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +72 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/retrieval.py +58 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langchain/methods.py +111 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langgraph/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +48 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langgraph/entities/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +56 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +14 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +172 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +47 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +73 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/retrieval.py +57 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +101 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/openai/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/openai/_helper.py +112 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/openai/entities/__init__.py +0 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +71 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +43 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/openai/methods.py +45 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/requests/__init__.py +4 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/requests/_helper.py +31 -0
- monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel/requests/methods.py +12 -0
- monocle_apptrace-0.2.0/CHANGELOG.md +0 -35
- monocle_apptrace-0.2.0/Pipfile.lock +0 -478
- monocle_apptrace-0.2.0/src/monocle_apptrace/constants.py +0 -22
- monocle_apptrace-0.2.0/src/monocle_apptrace/exporters/base_exporter.py +0 -47
- monocle_apptrace-0.2.0/src/monocle_apptrace/exporters/exporter_processor.py +0 -19
- monocle_apptrace-0.2.0/src/monocle_apptrace/exporters/monocle_exporters.py +0 -27
- monocle_apptrace-0.2.0/src/monocle_apptrace/haystack/__init__.py +0 -9
- monocle_apptrace-0.2.0/src/monocle_apptrace/haystack/wrap_node.py +0 -27
- monocle_apptrace-0.2.0/src/monocle_apptrace/haystack/wrap_openai.py +0 -44
- monocle_apptrace-0.2.0/src/monocle_apptrace/haystack/wrap_pipeline.py +0 -63
- monocle_apptrace-0.2.0/src/monocle_apptrace/instrumentor.py +0 -121
- monocle_apptrace-0.2.0/src/monocle_apptrace/langchain/__init__.py +0 -9
- monocle_apptrace-0.2.0/src/monocle_apptrace/llamaindex/__init__.py +0 -16
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/README.md +0 -47
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/entities/README.md +0 -77
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/entities/app_hosting_types.json +0 -29
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/entities/entities.json +0 -49
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/entities/inference_types.json +0 -33
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/entities/model_types.json +0 -41
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/entities/vector_store_types.json +0 -25
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/entities/workflow_types.json +0 -22
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/maps/attributes/inference/langchain_entities.json +0 -35
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/maps/attributes/inference/llamaindex_entities.json +0 -35
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/maps/attributes/retrieval/langchain_entities.json +0 -27
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/maps/attributes/retrieval/llamaindex_entities.json +0 -27
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/maps/haystack_methods.json +0 -25
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/maps/langchain_methods.json +0 -129
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/maps/llamaindex_methods.json +0 -74
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/spans/README.md +0 -121
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/spans/span_example.json +0 -140
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/spans/span_format.json +0 -55
- monocle_apptrace-0.2.0/src/monocle_apptrace/metamodel/spans/span_types.json +0 -16
- monocle_apptrace-0.2.0/src/monocle_apptrace/utils.py +0 -172
- monocle_apptrace-0.2.0/src/monocle_apptrace/wrap_common.py +0 -417
- monocle_apptrace-0.2.0/src/monocle_apptrace/wrapper.py +0 -26
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/CODEOWNERS.md +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/CODE_OF_CONDUCT.md +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/CONTRIBUTING.md +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/COPYRIGHT.template +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/LICENSE +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/MAINTAINER.md +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/Monocle_contributor_guide.md +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/NOTICE +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/README.md +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/SECURITY.md +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/src/monocle_apptrace/README.md +0 -0
- {monocle_apptrace-0.2.0/src/monocle_apptrace → monocle_apptrace-0.3.0/src/monocle_apptrace/instrumentation/metamodel}/__init__.py +0 -0
- {monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/tox.ini +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
## Version 0.3.0 (2024-12-10)
|
|
2
|
+
|
|
3
|
+
- Fixed issue with passing context in async case ([#150](https://github.com/monocle2ai/monocle/pull/150))
|
|
4
|
+
- Added lambda processor ([#148](https://github.com/monocle2ai/monocle/pull/148))
|
|
5
|
+
- Setup package level init scripts to make the monocle import simpler ([#147](https://github.com/monocle2ai/monocle/pull/147))
|
|
6
|
+
- Boto attributes and test cleanup ([#146](https://github.com/monocle2ai/monocle/pull/146))
|
|
7
|
+
- Openai workflow ([#142](https://github.com/monocle2ai/monocle/pull/142))
|
|
8
|
+
- Add input/output for openai embedding ([#141](https://github.com/monocle2ai/monocle/pull/141))
|
|
9
|
+
- Async method and scope fix ([#140](https://github.com/monocle2ai/monocle/pull/140))
|
|
10
|
+
- Bug fix for helper langchain and langgraph ([#137](https://github.com/monocle2ai/monocle/pull/137))
|
|
11
|
+
- Package main script to run any app with monocle instrumentation ([#132](https://github.com/monocle2ai/monocle/pull/132))
|
|
12
|
+
- Add openai api metamodel ([#131](https://github.com/monocle2ai/monocle/pull/131))
|
|
13
|
+
- Support notion of scopes to group traces/snaps into logical constructs ([#130](https://github.com/monocle2ai/monocle/pull/130))
|
|
14
|
+
- Add Llamaindex ReAct agent ([#127](https://github.com/monocle2ai/monocle/pull/127))
|
|
15
|
+
- Langhcain input fix and s3 exporter prefix support ([#126](https://github.com/monocle2ai/monocle/pull/126))
|
|
16
|
+
- Use standard AWS credential envs ([#123](https://github.com/monocle2ai/monocle/pull/123))
|
|
17
|
+
- Check additional attributes for Azure OpenAI model and consolidate common method in utils ([#122](https://github.com/monocle2ai/monocle/pull/122))
|
|
18
|
+
- Bug fix for accessor ([#121](https://github.com/monocle2ai/monocle/pull/121))
|
|
19
|
+
- Bug fix for empty response ([#120](https://github.com/monocle2ai/monocle/pull/120))
|
|
20
|
+
- Bug fix for inference endpoint ([#119](https://github.com/monocle2ai/monocle/pull/119))
|
|
21
|
+
- Opendal exporter for S3 and Blob ([#117](https://github.com/monocle2ai/monocle/pull/117))
|
|
22
|
+
- Handle specific ModuleNotFoundError exceptions gracefully ([#115](https://github.com/monocle2ai/monocle/pull/115))
|
|
23
|
+
- Adding support for console and memory exporter to list of monocle exporters ([#113](https://github.com/monocle2ai/monocle/pull/113))
|
|
24
|
+
- Add trace id propogation for constant trace id and from request ([#111](https://github.com/monocle2ai/monocle/pull/111))
|
|
25
|
+
- Restructure of monoocle code for easy extensibility ([#109](https://github.com/monocle2ai/monocle/pull/109))
|
|
26
|
+
- S3 update filename prefix ([#98](https://github.com/monocle2ai/monocle/pull/98))
|
|
27
|
+
- Update inference span for botocore sagemaker ([#93](https://github.com/monocle2ai/monocle/pull/93))
|
|
28
|
+
- Capturing inference output and token metadata for bedrock ([#82](https://github.com/monocle2ai/monocle/pull/82))
|
|
29
|
+
- Add dev dependency for Mistral AI integration ([#81](https://github.com/monocle2ai/monocle/pull/81))
|
|
30
|
+
- Add VectorStore deployment URL capture support ([#80](https://github.com/monocle2ai/monocle/pull/80))
|
|
31
|
+
- Clean up cloud exporter implementation ([#79](https://github.com/monocle2ai/monocle/pull/79))
|
|
32
|
+
- Capture inference span input/output events attributes ([#77](https://github.com/monocle2ai/monocle/pull/77))
|
|
33
|
+
- Add release automation workflows ([#76](https://github.com/monocle2ai/monocle/pull/76))
|
|
34
|
+
- Fix gaps in Monocle SDK implementation ([#72](https://github.com/monocle2ai/monocle/pull/72))
|
|
35
|
+
- Add kwargs and return value handling in Accessor ([#71](https://github.com/monocle2ai/monocle/pull/71))
|
|
36
|
+
- Update workflow name formatting ([#69](https://github.com/monocle2ai/monocle/pull/69))
|
|
37
|
+
- Implement Haystack metamodel support ([#68](https://github.com/monocle2ai/monocle/pull/68))
|
|
38
|
+
|
|
39
|
+
## Version 0.2.0 (2024-12-05)
|
|
40
|
+
|
|
41
|
+
## 0.2.0 (Oct 22, 2024)
|
|
42
|
+
|
|
43
|
+
- Ndjson format for S3 and Blob exporters ([#61](https://github.com/monocle2ai/monocle/pull/61))
|
|
44
|
+
- Set monocle exporter from env setting ([#60](https://github.com/monocle2ai/monocle/pull/60))
|
|
45
|
+
- Update workflow name and type with new format ([#59](https://github.com/monocle2ai/monocle/pull/59))
|
|
46
|
+
- Updated async and custom output processor testcase for metamodel([#58](https://github.com/monocle2ai/monocle/pull/58))
|
|
47
|
+
- Build okahu exporter and added test cases for okahu exporte ([#56](https://github.com/monocle2ai/monocle/pull/56))
|
|
48
|
+
- Handle exception in span wrappers([#52](https://github.com/monocle2ai/monocle/pull/52))
|
|
49
|
+
- Metamodel entity changes ([#51](https://github.com/monocle2ai/monocle/pull/51)), ([#54](https://github.com/monocle2ai/monocle/pull/54))
|
|
50
|
+
- Error handling for llm_endpoint and tags ([#50](https://github.com/monocle2ai/monocle/pull/50))
|
|
51
|
+
- Context_output for vector store retriever ([#48](https://github.com/monocle2ai/monocle/pull/48))
|
|
52
|
+
- Direct exporter - AWS S3 ([#42](https://github.com/monocle2ai/monocle/pull/42))
|
|
53
|
+
- Direct Exporter - Blob store ([#41](https://github.com/monocle2ai/monocle/pull/41))
|
|
54
|
+
- Initial metamodel definition ([#39](https://github.com/monocle2ai/monocle/pull/39))
|
|
55
|
+
- Improvement in vectorstore traces ([#38](https://github.com/monocle2ai/monocle/pull/38))
|
|
56
|
+
- Update key for session context field in attributes ([#34](https://github.com/monocle2ai/monocle/pull/34))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## 0.1.0 (Aug 27, 2024)
|
|
60
|
+
|
|
61
|
+
- Fixed LlamaIndex tracing bugs ([#32](https://github.com/monocle2ai/monocle/pull/32))
|
|
62
|
+
- Added support to add AWS cloud infra attributes ([#29](https://github.com/monocle2ai/monocle/pull/29))
|
|
63
|
+
- Added support to add Azure cloud infra attributes ([#23](https://github.com/monocle2ai/monocle/pull/23))
|
|
64
|
+
- Added support for adding provider name in LLM span in traces ([#22](https://github.com/monocle2ai/monocle/pull/22))
|
|
65
|
+
- Added a default file span exporter ([#21](https://github.com/monocle2ai/monocle/pull/21))
|
|
66
|
+
- Moved input and output context and prompts from attributes to events ([#15](https://github.com/monocle2ai/monocle/pull/15))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
## 0.0.1 (Jul 17, 2024)
|
|
74
|
+
|
|
75
|
+
- First monocle release
|
|
@@ -40,6 +40,15 @@ You need to import monocle package and invoke the API ``setup_monocle_telemetry(
|
|
|
40
40
|
|
|
41
41
|
### Using Monocle's out of box support of genAI technology components
|
|
42
42
|
Monocle community has done the hard work of figuring out what to trace and how to extract relevant details from multiple genAI technology components. For example, if you have a python app coded using LlamaIndex and using models hostsed in OpenAI, Monocle can seamlessly trace your app. All you need to do enable Monocle tracing.
|
|
43
|
+
|
|
44
|
+
### Using Monocle's Support for Adding Custom Attributes
|
|
45
|
+
Monocle provides users with the ability to add custom attributes to various spans, such as inference and retrieval spans, by utilizing the output processor within its metamodel. This feature allows for dynamic attribute assignment through lambda functions, which operate on an arguments dictionary.
|
|
46
|
+
The arguments dictionary contains key-value pairs that can be used to compute custom attributes. The dictionary includes the following components:
|
|
47
|
+
```python
|
|
48
|
+
arguments = {"instance":instance, "args":args, "kwargs":kwargs, "output":return_value}
|
|
49
|
+
```
|
|
50
|
+
By leveraging this dictionary, users can define custom attributes for spans, enabling the integration of additional context and information into the tracing process. The lambda functions used in the attributes field can access and process these values to enrich the span with relevant custom data.
|
|
51
|
+
|
|
43
52
|
#### Example - Enable Monocle tracing in your application
|
|
44
53
|
```python
|
|
45
54
|
from monocle_apptrace.instrumentor import setup_monocle_telemetry
|
|
@@ -22,5 +22,35 @@ After setup of credentials, follow the commands below to publish the package to
|
|
|
22
22
|
|
|
23
23
|
```
|
|
24
24
|
> python3 -m pip install pipenv
|
|
25
|
-
> pipenv install monocle-
|
|
25
|
+
> pipenv install monocle-apptrace
|
|
26
26
|
```
|
|
27
|
+
### Running the testcases
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
##### Activate the virtual environment
|
|
31
|
+
```
|
|
32
|
+
cd monocle
|
|
33
|
+
monocle% python -m pip install pipenv
|
|
34
|
+
monocle% pipenv --python 3.11.9
|
|
35
|
+
monocle% source $(pipenv --venv)/bin/activate
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
##### Install the dependencies
|
|
39
|
+
```
|
|
40
|
+
monocle% pip install -e '.[dev]'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
##### Run the unit tests
|
|
44
|
+
```
|
|
45
|
+
monocle% pytest tests/unit/*_test.py
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
##### Run the integration tests
|
|
49
|
+
```
|
|
50
|
+
monocle% pytest -m integration
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
###### Run the integration test individually
|
|
54
|
+
```
|
|
55
|
+
monocle% pytest tests/integration/test_langchain_rag_l_to_m.py
|
|
56
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: monocle_apptrace
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: package with monocle genAI tracing
|
|
5
5
|
Project-URL: Homepage, https://github.com/monocle2ai/monocle
|
|
6
6
|
Project-URL: Issues, https://github.com/monocle2ai/monocle/issues
|
|
@@ -20,19 +20,36 @@ Requires-Dist: boto3==1.35.19; extra == 'aws'
|
|
|
20
20
|
Provides-Extra: azure
|
|
21
21
|
Requires-Dist: azure-storage-blob==12.22.0; extra == 'azure'
|
|
22
22
|
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: azure-storage-blob==12.22.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: boto3==1.34.131; extra == 'dev'
|
|
25
|
+
Requires-Dist: chromadb==0.4.22; extra == 'dev'
|
|
23
26
|
Requires-Dist: datasets==2.20.0; extra == 'dev'
|
|
24
27
|
Requires-Dist: faiss-cpu==1.8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: flask; extra == 'dev'
|
|
29
|
+
Requires-Dist: haystack-ai==2.3.0; extra == 'dev'
|
|
25
30
|
Requires-Dist: instructorembedding==1.0.1; extra == 'dev'
|
|
31
|
+
Requires-Dist: langchain-aws==0.1.10; extra == 'dev'
|
|
26
32
|
Requires-Dist: langchain-chroma==0.1.1; extra == 'dev'
|
|
27
33
|
Requires-Dist: langchain-community==0.2.5; extra == 'dev'
|
|
34
|
+
Requires-Dist: langchain-mistralai==0.1.13; extra == 'dev'
|
|
28
35
|
Requires-Dist: langchain-openai==0.1.8; extra == 'dev'
|
|
29
36
|
Requires-Dist: langchain==0.2.5; extra == 'dev'
|
|
37
|
+
Requires-Dist: langchainhub==0.1.21; extra == 'dev'
|
|
38
|
+
Requires-Dist: langgraph==0.2.68; extra == 'dev'
|
|
30
39
|
Requires-Dist: llama-index-embeddings-huggingface==0.2.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: llama-index-llms-azure-openai==0.1.9; extra == 'dev'
|
|
41
|
+
Requires-Dist: llama-index-llms-mistralai==0.1.20; extra == 'dev'
|
|
31
42
|
Requires-Dist: llama-index-vector-stores-chroma==0.1.9; extra == 'dev'
|
|
43
|
+
Requires-Dist: llama-index-vector-stores-opensearch==0.1.10; extra == 'dev'
|
|
32
44
|
Requires-Dist: llama-index==0.10.30; extra == 'dev'
|
|
45
|
+
Requires-Dist: mistral-haystack==0.0.2; extra == 'dev'
|
|
33
46
|
Requires-Dist: numpy==1.26.4; extra == 'dev'
|
|
47
|
+
Requires-Dist: opendal==0.45.14; extra == 'dev'
|
|
48
|
+
Requires-Dist: opensearch-haystack==1.2.0; extra == 'dev'
|
|
49
|
+
Requires-Dist: opentelemetry-instrumentation-flask; extra == 'dev'
|
|
34
50
|
Requires-Dist: parameterized==0.9.0; extra == 'dev'
|
|
35
51
|
Requires-Dist: pytest==8.0.0; extra == 'dev'
|
|
52
|
+
Requires-Dist: requests-aws4auth==1.2.3; extra == 'dev'
|
|
36
53
|
Requires-Dist: sentence-transformers==2.6.1; extra == 'dev'
|
|
37
54
|
Requires-Dist: types-requests==2.31.0.20240106; extra == 'dev'
|
|
38
55
|
Description-Content-Type: text/markdown
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "monocle_apptrace"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
authors = []
|
|
9
9
|
description = "package with monocle genAI tracing"
|
|
10
10
|
readme = "README.md"
|
|
@@ -23,8 +23,20 @@ dependencies = [
|
|
|
23
23
|
]
|
|
24
24
|
|
|
25
25
|
[tool.pytest.ini_options]
|
|
26
|
+
#log_cli = true
|
|
27
|
+
#log_cli_level = "INFO"
|
|
28
|
+
# Enable file logging
|
|
29
|
+
log_file = "traces.log"
|
|
30
|
+
log_file_level = "INFO"
|
|
31
|
+
log_format = "%(asctime)s %(levelname)s %(pathname)s:%(lineno)d %(message)s"
|
|
32
|
+
log_date_format = "%Y-%m-%d %H:%M:%S"
|
|
33
|
+
|
|
26
34
|
pythonpath = [
|
|
27
|
-
"src"
|
|
35
|
+
"src",
|
|
36
|
+
"tests"
|
|
37
|
+
]
|
|
38
|
+
markers = [
|
|
39
|
+
"integration"
|
|
28
40
|
]
|
|
29
41
|
|
|
30
42
|
[project.optional-dependencies]
|
|
@@ -43,7 +55,24 @@ dev = [
|
|
|
43
55
|
'llama-index==0.10.30',
|
|
44
56
|
'llama-index-embeddings-huggingface==0.2.0',
|
|
45
57
|
'llama-index-vector-stores-chroma==0.1.9',
|
|
46
|
-
'parameterized==0.9.0'
|
|
58
|
+
'parameterized==0.9.0',
|
|
59
|
+
'llama-index-llms-mistralai==0.1.20',
|
|
60
|
+
'langchain-mistralai==0.1.13',
|
|
61
|
+
'mistral-haystack==0.0.2',
|
|
62
|
+
'langchain-aws==0.1.10',
|
|
63
|
+
'azure-storage-blob==12.22.0', # this is for blob exporter
|
|
64
|
+
'boto3==1.34.131', # this is for aws exporter
|
|
65
|
+
'llama-index-vector-stores-opensearch==0.1.10',
|
|
66
|
+
'haystack-ai==2.3.0',
|
|
67
|
+
'llama-index-llms-azure-openai==0.1.9',
|
|
68
|
+
'requests-aws4auth==1.2.3',
|
|
69
|
+
'opensearch-haystack==1.2.0',
|
|
70
|
+
'langchainhub==0.1.21',
|
|
71
|
+
'chromadb==0.4.22',
|
|
72
|
+
'flask',
|
|
73
|
+
'opentelemetry-instrumentation-flask',
|
|
74
|
+
'langgraph==0.2.68',
|
|
75
|
+
'opendal==0.45.14'
|
|
47
76
|
]
|
|
48
77
|
|
|
49
78
|
azure = [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .instrumentation import *
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import sys, os
|
|
2
|
+
import runpy
|
|
3
|
+
from monocle_apptrace.instrumentation.common.instrumentor import setup_monocle_telemetry
|
|
4
|
+
|
|
5
|
+
def main():
|
|
6
|
+
if len(sys.argv) < 2 or not sys.argv[1].endswith(".py"):
|
|
7
|
+
print("Usage: python -m monocle_apptrace <your-main-module-file> <args>")
|
|
8
|
+
sys.exit(1)
|
|
9
|
+
file_name = os.path.basename(sys.argv[1])
|
|
10
|
+
workflow_name = file_name[:-3]
|
|
11
|
+
setup_monocle_telemetry(workflow_name=workflow_name)
|
|
12
|
+
sys.argv.pop(0)
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
runpy.run_path(path_name=sys.argv[0], run_name="__main__")
|
|
16
|
+
except Exception as e:
|
|
17
|
+
print(e)
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
main()
|
{monocle_apptrace-0.2.0 → monocle_apptrace-0.3.0}/src/monocle_apptrace/exporters/aws/s3_exporter.py
RENAMED
|
@@ -6,43 +6,59 @@ import logging
|
|
|
6
6
|
import asyncio
|
|
7
7
|
import boto3
|
|
8
8
|
from botocore.exceptions import ClientError
|
|
9
|
+
from botocore.exceptions import (
|
|
10
|
+
BotoCoreError,
|
|
11
|
+
ConnectionClosedError,
|
|
12
|
+
ConnectTimeoutError,
|
|
13
|
+
EndpointConnectionError,
|
|
14
|
+
ReadTimeoutError,
|
|
15
|
+
)
|
|
9
16
|
from opentelemetry.sdk.trace import ReadableSpan
|
|
10
17
|
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
|
|
11
18
|
from monocle_apptrace.exporters.base_exporter import SpanExporterBase
|
|
12
|
-
from
|
|
19
|
+
from monocle_apptrace.exporters.exporter_processor import ExportTaskProcessor
|
|
20
|
+
from typing import Sequence, Optional
|
|
13
21
|
import json
|
|
14
22
|
logger = logging.getLogger(__name__)
|
|
15
23
|
|
|
16
24
|
class S3SpanExporter(SpanExporterBase):
|
|
17
|
-
def __init__(self, bucket_name=None, region_name=
|
|
25
|
+
def __init__(self, bucket_name=None, region_name=None, task_processor: Optional[ExportTaskProcessor] = None):
|
|
18
26
|
super().__init__()
|
|
19
27
|
# Use environment variables if credentials are not provided
|
|
20
|
-
DEFAULT_FILE_PREFIX = "
|
|
28
|
+
DEFAULT_FILE_PREFIX = "monocle_trace_"
|
|
21
29
|
DEFAULT_TIME_FORMAT = "%Y-%m-%d__%H.%M.%S"
|
|
22
30
|
self.max_batch_size = 500
|
|
23
31
|
self.export_interval = 1
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
if(os.getenv('MONOCLE_AWS_ACCESS_KEY_ID') and os.getenv('MONOCLE_AWS_SECRET_ACCESS_KEY')):
|
|
33
|
+
self.s3_client = boto3.client(
|
|
34
|
+
's3',
|
|
35
|
+
aws_access_key_id=os.getenv('MONOCLE_AWS_ACCESS_KEY_ID'),
|
|
36
|
+
aws_secret_access_key=os.getenv('MONOCLE_AWS_SECRET_ACCESS_KEY'),
|
|
37
|
+
region_name=region_name,
|
|
38
|
+
)
|
|
39
|
+
else:
|
|
40
|
+
self.s3_client = boto3.client(
|
|
41
|
+
's3',
|
|
42
|
+
aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
|
|
43
|
+
aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),
|
|
44
|
+
region_name=region_name,
|
|
45
|
+
)
|
|
30
46
|
self.bucket_name = bucket_name or os.getenv('MONOCLE_S3_BUCKET_NAME','default-bucket')
|
|
31
|
-
self.file_prefix = DEFAULT_FILE_PREFIX
|
|
47
|
+
self.file_prefix = os.getenv('MONOCLE_S3_KEY_PREFIX', DEFAULT_FILE_PREFIX)
|
|
32
48
|
self.time_format = DEFAULT_TIME_FORMAT
|
|
33
49
|
self.export_queue = []
|
|
34
50
|
self.last_export_time = time.time()
|
|
51
|
+
self.task_processor = task_processor
|
|
52
|
+
if self.task_processor is not None:
|
|
53
|
+
self.task_processor.start()
|
|
35
54
|
|
|
36
55
|
# Check if bucket exists or create it
|
|
37
56
|
if not self.__bucket_exists(self.bucket_name):
|
|
38
57
|
try:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Bucket=self.bucket_name,
|
|
44
|
-
CreateBucketConfiguration={'LocationConstraint': region_name}
|
|
45
|
-
)
|
|
58
|
+
self.s3_client.create_bucket(
|
|
59
|
+
Bucket=self.bucket_name,
|
|
60
|
+
CreateBucketConfiguration={'LocationConstraint': region_name}
|
|
61
|
+
)
|
|
46
62
|
logger.info(f"Bucket {self.bucket_name} created successfully.")
|
|
47
63
|
except ClientError as e:
|
|
48
64
|
logger.error(f"Error creating bucket {self.bucket_name}: {e}")
|
|
@@ -80,6 +96,7 @@ class S3SpanExporter(SpanExporterBase):
|
|
|
80
96
|
"""Synchronous export method that internally handles async logic."""
|
|
81
97
|
try:
|
|
82
98
|
# Run the asynchronous export logic in an event loop
|
|
99
|
+
logger.info(f"Exporting {len(spans)} spans to S3.")
|
|
83
100
|
asyncio.run(self.__export_async(spans))
|
|
84
101
|
return SpanExportResult.SUCCESS
|
|
85
102
|
except Exception as e:
|
|
@@ -88,6 +105,7 @@ class S3SpanExporter(SpanExporterBase):
|
|
|
88
105
|
|
|
89
106
|
async def __export_async(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
|
90
107
|
try:
|
|
108
|
+
logger.info(f"__export_async {len(spans)} spans to S3.")
|
|
91
109
|
# Add spans to the export queue
|
|
92
110
|
for span in spans:
|
|
93
111
|
self.export_queue.append(span)
|
|
@@ -130,19 +148,22 @@ class S3SpanExporter(SpanExporterBase):
|
|
|
130
148
|
batch_to_export = self.export_queue[:self.max_batch_size]
|
|
131
149
|
serialized_data = self.__serialize_spans(batch_to_export)
|
|
132
150
|
self.export_queue = self.export_queue[self.max_batch_size:]
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
151
|
+
# to calculate is_root_span loop over each span in batch_to_export and check if parent id is none or null
|
|
152
|
+
is_root_span = any(not span.parent for span in batch_to_export)
|
|
153
|
+
logger.info(f"Exporting {len(batch_to_export)} spans to S3 is_root_span : {is_root_span}.")
|
|
154
|
+
if self.task_processor is not None and callable(getattr(self.task_processor, 'queue_task', None)):
|
|
155
|
+
self.task_processor.queue_task(self.__upload_to_s3, serialized_data, is_root_span)
|
|
156
|
+
else:
|
|
157
|
+
try:
|
|
158
|
+
self.__upload_to_s3(serialized_data)
|
|
159
|
+
except Exception as e:
|
|
160
|
+
logger.error(f"Failed to upload span batch: {e}")
|
|
142
161
|
|
|
162
|
+
@SpanExporterBase.retry_with_backoff(exceptions=(EndpointConnectionError, ConnectionClosedError, ReadTimeoutError, ConnectTimeoutError))
|
|
143
163
|
def __upload_to_s3(self, span_data_batch: str):
|
|
144
164
|
current_time = datetime.datetime.now().strftime(self.time_format)
|
|
145
|
-
|
|
165
|
+
prefix = self.file_prefix + os.environ.get('MONOCLE_S3_KEY_PREFIX_CURRENT', '')
|
|
166
|
+
file_name = f"{prefix}{current_time}.ndjson"
|
|
146
167
|
self.s3_client.put_object(
|
|
147
168
|
Bucket=self.bucket_name,
|
|
148
169
|
Key=file_name,
|
|
@@ -155,4 +176,6 @@ class S3SpanExporter(SpanExporterBase):
|
|
|
155
176
|
return True
|
|
156
177
|
|
|
157
178
|
def shutdown(self) -> None:
|
|
179
|
+
if hasattr(self, 'task_processor') and self.task_processor is not None:
|
|
180
|
+
self.task_processor.stop()
|
|
158
181
|
logger.info("S3SpanExporter has been shut down.")
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
import datetime
|
|
4
|
+
import logging
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Sequence, Optional
|
|
7
|
+
from opentelemetry.sdk.trace import ReadableSpan
|
|
8
|
+
from opentelemetry.sdk.trace.export import SpanExportResult
|
|
9
|
+
from monocle_apptrace.exporters.base_exporter import SpanExporterBase
|
|
10
|
+
from monocle_apptrace.exporters.exporter_processor import ExportTaskProcessor
|
|
11
|
+
from opendal import Operator
|
|
12
|
+
from opendal.exceptions import PermissionDenied, ConfigInvalid, Unexpected
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
class OpenDALS3Exporter(SpanExporterBase):
|
|
18
|
+
def __init__(self, bucket_name=None, region_name=None, task_processor: Optional[ExportTaskProcessor] = None):
|
|
19
|
+
super().__init__()
|
|
20
|
+
DEFAULT_FILE_PREFIX = "monocle_trace_"
|
|
21
|
+
DEFAULT_TIME_FORMAT = "%Y-%m-%d__%H.%M.%S"
|
|
22
|
+
self.max_batch_size = 500
|
|
23
|
+
self.export_interval = 1
|
|
24
|
+
self.file_prefix = DEFAULT_FILE_PREFIX
|
|
25
|
+
self.time_format = DEFAULT_TIME_FORMAT
|
|
26
|
+
self.export_queue = []
|
|
27
|
+
self.last_export_time = time.time()
|
|
28
|
+
self.bucket_name = bucket_name or os.getenv("MONOCLE_S3_BUCKET_NAME", "default-bucket")
|
|
29
|
+
|
|
30
|
+
# Initialize OpenDAL S3 operator
|
|
31
|
+
self.op = Operator(
|
|
32
|
+
"s3",
|
|
33
|
+
root = "/",
|
|
34
|
+
region=os.getenv("AWS_REGION", region_name),
|
|
35
|
+
bucket=self.bucket_name,
|
|
36
|
+
access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
|
|
37
|
+
secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
self.task_processor = task_processor
|
|
41
|
+
if self.task_processor is not None:
|
|
42
|
+
self.task_processor.start()
|
|
43
|
+
|
|
44
|
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
|
45
|
+
"""Synchronous export method that internally handles async logic."""
|
|
46
|
+
try:
|
|
47
|
+
# Run the asynchronous export logic in an event loop
|
|
48
|
+
asyncio.run(self.__export_async(spans))
|
|
49
|
+
return SpanExportResult.SUCCESS
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logger.error(f"Error exporting spans: {e}")
|
|
52
|
+
return SpanExportResult.FAILURE
|
|
53
|
+
|
|
54
|
+
async def __export_async(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
|
55
|
+
try:
|
|
56
|
+
# Add spans to the export queue
|
|
57
|
+
for span in spans:
|
|
58
|
+
self.export_queue.append(span)
|
|
59
|
+
if len(self.export_queue) >= self.max_batch_size:
|
|
60
|
+
await self.__export_spans()
|
|
61
|
+
|
|
62
|
+
# Check if it's time to force a flush
|
|
63
|
+
current_time = time.time()
|
|
64
|
+
if current_time - self.last_export_time >= self.export_interval:
|
|
65
|
+
await self.__export_spans()
|
|
66
|
+
self.last_export_time = current_time
|
|
67
|
+
|
|
68
|
+
return SpanExportResult.SUCCESS
|
|
69
|
+
except Exception as e:
|
|
70
|
+
logger.error(f"Error exporting spans: {e}")
|
|
71
|
+
return SpanExportResult.FAILURE
|
|
72
|
+
|
|
73
|
+
def __serialize_spans(self, spans: Sequence[ReadableSpan]) -> str:
|
|
74
|
+
try:
|
|
75
|
+
# Serialize spans to JSON or any other format you prefer
|
|
76
|
+
valid_json_list = []
|
|
77
|
+
for span in spans:
|
|
78
|
+
try:
|
|
79
|
+
valid_json_list.append(span.to_json(indent=0).replace("\n", ""))
|
|
80
|
+
except json.JSONDecodeError as e:
|
|
81
|
+
logger.warning(f"Invalid JSON format in span data: {span.context.span_id}. Error: {e}")
|
|
82
|
+
continue
|
|
83
|
+
return "\n".join(valid_json_list) + "\n"
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logger.warning(f"Error serializing spans: {e}")
|
|
86
|
+
|
|
87
|
+
async def __export_spans(self):
|
|
88
|
+
if not self.export_queue:
|
|
89
|
+
return
|
|
90
|
+
# Take a batch of spans from the queue
|
|
91
|
+
batch_to_export = self.export_queue[:self.max_batch_size]
|
|
92
|
+
serialized_data = self.__serialize_spans(batch_to_export)
|
|
93
|
+
self.export_queue = self.export_queue[self.max_batch_size:]
|
|
94
|
+
|
|
95
|
+
# Calculate is_root_span by checking if any span has no parent
|
|
96
|
+
is_root_span = any(not span.parent for span in batch_to_export)
|
|
97
|
+
|
|
98
|
+
if self.task_processor is not None and callable(getattr(self.task_processor, 'queue_task', None)):
|
|
99
|
+
self.task_processor.queue_task(self.__upload_to_s3, serialized_data, is_root_span)
|
|
100
|
+
else:
|
|
101
|
+
try:
|
|
102
|
+
self.__upload_to_s3(serialized_data, is_root_span)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.error(f"Failed to upload span batch: {e}")
|
|
105
|
+
|
|
106
|
+
@SpanExporterBase.retry_with_backoff(exceptions=(Unexpected))
|
|
107
|
+
def __upload_to_s3(self, span_data_batch: str, is_root_span: bool = False):
|
|
108
|
+
current_time = datetime.datetime.now().strftime(self.time_format)
|
|
109
|
+
file_name = f"{self.file_prefix}{current_time}.ndjson"
|
|
110
|
+
try:
|
|
111
|
+
# Attempt to write the span data batch to S3
|
|
112
|
+
self.op.write(file_name, span_data_batch.encode("utf-8"))
|
|
113
|
+
logger.info(f"Span batch uploaded to S3 as {file_name}. Is root span: {is_root_span}")
|
|
114
|
+
|
|
115
|
+
except PermissionDenied as e:
|
|
116
|
+
# S3 bucket is forbidden.
|
|
117
|
+
logger.error(f"Access to bucket {self.bucket_name} is forbidden (403).")
|
|
118
|
+
raise PermissionError(f"Access to bucket {self.bucket_name} is forbidden.")
|
|
119
|
+
|
|
120
|
+
except ConfigInvalid as e:
|
|
121
|
+
# Bucket does not exist.
|
|
122
|
+
if "404" in str(e):
|
|
123
|
+
logger.error("Bucket does not exist. Please check the bucket name and region.")
|
|
124
|
+
raise Exception(f"Bucket does not exist. Error: {e}")
|
|
125
|
+
else:
|
|
126
|
+
logger.error(f"Unexpected error when accessing bucket {self.bucket_name}: {e}")
|
|
127
|
+
raise e
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
async def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
131
|
+
await self.__export_spans()
|
|
132
|
+
return True
|
|
133
|
+
|
|
134
|
+
def shutdown(self) -> None:
|
|
135
|
+
if hasattr(self, 'task_processor') and self.task_processor is not None:
|
|
136
|
+
self.task_processor.stop()
|
|
137
|
+
logger.info("S3SpanExporter has been shut down.")
|
|
@@ -8,13 +8,15 @@ from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient
|
|
|
8
8
|
from azure.core.exceptions import ResourceNotFoundError, ClientAuthenticationError, ServiceRequestError
|
|
9
9
|
from opentelemetry.sdk.trace import ReadableSpan
|
|
10
10
|
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
|
|
11
|
-
from typing import Sequence
|
|
11
|
+
from typing import Sequence, Optional
|
|
12
12
|
from monocle_apptrace.exporters.base_exporter import SpanExporterBase
|
|
13
|
+
from monocle_apptrace.exporters.exporter_processor import ExportTaskProcessor
|
|
13
14
|
import json
|
|
15
|
+
from monocle_apptrace.instrumentation.common.constants import MONOCLE_SDK_VERSION
|
|
14
16
|
logger = logging.getLogger(__name__)
|
|
15
17
|
|
|
16
18
|
class AzureBlobSpanExporter(SpanExporterBase):
|
|
17
|
-
def __init__(self, connection_string=None, container_name=None):
|
|
19
|
+
def __init__(self, connection_string=None, container_name=None, task_processor: Optional[ExportTaskProcessor] = None):
|
|
18
20
|
super().__init__()
|
|
19
21
|
DEFAULT_FILE_PREFIX = "monocle_trace_"
|
|
20
22
|
DEFAULT_TIME_FORMAT = "%Y-%m-%d_%H.%M.%S"
|
|
@@ -43,6 +45,10 @@ class AzureBlobSpanExporter(SpanExporterBase):
|
|
|
43
45
|
logger.error(f"Error creating container {container_name}: {e}")
|
|
44
46
|
raise e
|
|
45
47
|
|
|
48
|
+
self.task_processor = task_processor
|
|
49
|
+
if self.task_processor is not None:
|
|
50
|
+
self.task_processor.start()
|
|
51
|
+
|
|
46
52
|
def __container_exists(self, container_name):
|
|
47
53
|
try:
|
|
48
54
|
container_client = self.blob_service_client.get_container_client(container_name)
|
|
@@ -72,6 +78,12 @@ class AzureBlobSpanExporter(SpanExporterBase):
|
|
|
72
78
|
"""The actual async export logic is run here."""
|
|
73
79
|
# Add spans to the export queue
|
|
74
80
|
for span in spans:
|
|
81
|
+
# Azure blob library has a check to generate it's own span if OpenTelemetry is loaded and Azure trace package is installed (just pip install azure-trace-opentelemetry)
|
|
82
|
+
# With Monocle,OpenTelemetry is always loaded. If the Azure trace package is installed, then it triggers the blob trace generation on every blob operation.
|
|
83
|
+
# Thus, the Monocle span write ends up generating a blob span which again comes back to the exporter .. and would result in an infinite loop.
|
|
84
|
+
# To avoid this, we check if the span has the Monocle SDK version attribute and skip it if it doesn't. That way the blob span genearted by Azure library are not exported.
|
|
85
|
+
if not span.attributes.get(MONOCLE_SDK_VERSION):
|
|
86
|
+
continue # TODO: All exporters to use same base class and check it there
|
|
75
87
|
self.export_queue.append(span)
|
|
76
88
|
if len(self.export_queue) >= self.max_batch_size:
|
|
77
89
|
await self.__export_spans()
|
|
@@ -104,25 +116,31 @@ class AzureBlobSpanExporter(SpanExporterBase):
|
|
|
104
116
|
batch_to_export = self.export_queue[:self.max_batch_size]
|
|
105
117
|
serialized_data = self.__serialize_spans(batch_to_export)
|
|
106
118
|
self.export_queue = self.export_queue[self.max_batch_size:]
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
|
|
120
|
+
# Calculate is_root_span by checking if any span has no parent
|
|
121
|
+
is_root_span = any(not span.parent for span in batch_to_export)
|
|
122
|
+
|
|
123
|
+
if self.task_processor is not None and callable(getattr(self.task_processor, 'queue_task', None)):
|
|
124
|
+
self.task_processor.queue_task(self.__upload_to_blob, serialized_data, is_root_span)
|
|
125
|
+
else:
|
|
126
|
+
try:
|
|
127
|
+
self.__upload_to_blob(serialized_data, is_root_span)
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.error(f"Failed to upload span batch: {e}")
|
|
115
130
|
|
|
116
|
-
|
|
131
|
+
@SpanExporterBase.retry_with_backoff(exceptions=(ResourceNotFoundError, ClientAuthenticationError, ServiceRequestError))
|
|
132
|
+
def __upload_to_blob(self, span_data_batch: str, is_root_span: bool = False):
|
|
117
133
|
current_time = datetime.datetime.now().strftime(self.time_format)
|
|
118
134
|
file_name = f"{self.file_prefix}{current_time}.ndjson"
|
|
119
135
|
blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name)
|
|
120
136
|
blob_client.upload_blob(span_data_batch, overwrite=True)
|
|
121
|
-
logger.info(f"Span batch uploaded to Azure Blob Storage as {file_name}.")
|
|
137
|
+
logger.info(f"Span batch uploaded to Azure Blob Storage as {file_name}. Is root span: {is_root_span}")
|
|
122
138
|
|
|
123
139
|
async def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
124
140
|
await self.__export_spans()
|
|
125
141
|
return True
|
|
126
142
|
|
|
127
143
|
def shutdown(self) -> None:
|
|
144
|
+
if hasattr(self, 'task_processor') and self.task_processor is not None:
|
|
145
|
+
self.task_processor.stop()
|
|
128
146
|
logger.info("AzureBlobSpanExporter has been shut down.")
|