monocle-apptrace 0.3.0b2__tar.gz → 0.3.0b4__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.

Files changed (93) hide show
  1. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/CHANGELOG.md +1 -1
  2. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/Monocle_committer_guide.md +31 -1
  3. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/PKG-INFO +14 -1
  4. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/pyproject.toml +28 -3
  5. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/src/monocle_apptrace/exporters/aws/s3_exporter.py +18 -9
  6. monocle_apptrace-0.3.0b4/src/monocle_apptrace/exporters/aws/s3_exporter_opendal.py +126 -0
  7. monocle_apptrace-0.3.0b4/src/monocle_apptrace/exporters/azure/blob_exporter_opendal.py +147 -0
  8. monocle_apptrace-0.3.0b4/src/monocle_apptrace/exporters/monocle_exporters.py +45 -0
  9. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/__init__.py +0 -0
  10. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/common/__init__.py +0 -0
  11. {monocle_apptrace-0.3.0b2/src/monocle_apptrace → monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/common}/constants.py +13 -0
  12. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/common/instrumentor.py +208 -0
  13. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/common/span_handler.py +154 -0
  14. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/common/utils.py +171 -0
  15. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/common/wrapper.py +69 -0
  16. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/common/wrapper_method.py +45 -0
  17. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/__init__.py +0 -0
  18. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/botocore/__init__.py +0 -0
  19. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +126 -0
  20. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/botocore/entities/__init__.py +0 -0
  21. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +65 -0
  22. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/botocore/methods.py +16 -0
  23. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/haystack/__init__.py +0 -0
  24. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +127 -0
  25. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/__init__.py +0 -0
  26. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +76 -0
  27. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/retrieval.py +61 -0
  28. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/haystack/methods.py +42 -0
  29. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/langchain/__init__.py +0 -0
  30. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +123 -0
  31. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/__init__.py +0 -0
  32. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +71 -0
  33. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/retrieval.py +58 -0
  34. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/langchain/methods.py +105 -0
  35. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/llamaindex/__init__.py +0 -0
  36. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +154 -0
  37. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/__init__.py +0 -0
  38. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +71 -0
  39. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/retrieval.py +57 -0
  40. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/llamaindex_methods.json → monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +28 -31
  41. monocle_apptrace-0.3.0b2/src/monocle_apptrace/botocore/__init__.py +0 -9
  42. monocle_apptrace-0.3.0b2/src/monocle_apptrace/exporters/monocle_exporters.py +0 -27
  43. monocle_apptrace-0.3.0b2/src/monocle_apptrace/haystack/__init__.py +0 -9
  44. monocle_apptrace-0.3.0b2/src/monocle_apptrace/haystack/wrap_pipeline.py +0 -63
  45. monocle_apptrace-0.3.0b2/src/monocle_apptrace/instrumentor.py +0 -121
  46. monocle_apptrace-0.3.0b2/src/monocle_apptrace/langchain/__init__.py +0 -9
  47. monocle_apptrace-0.3.0b2/src/monocle_apptrace/llamaindex/__init__.py +0 -16
  48. monocle_apptrace-0.3.0b2/src/monocle_apptrace/message_processing.py +0 -80
  49. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/README.md +0 -47
  50. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/entities/README.md +0 -77
  51. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/entities/app_hosting_types.json +0 -29
  52. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/entities/entities.json +0 -49
  53. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/entities/inference_types.json +0 -33
  54. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/entities/model_types.json +0 -41
  55. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/entities/vector_store_types.json +0 -25
  56. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/entities/workflow_types.json +0 -22
  57. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/attributes/inference/botocore_entities.json +0 -27
  58. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/attributes/inference/haystack_entities.json +0 -57
  59. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/attributes/inference/langchain_entities.json +0 -57
  60. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/attributes/inference/llamaindex_entities.json +0 -57
  61. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/attributes/retrieval/haystack_entities.json +0 -31
  62. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/attributes/retrieval/langchain_entities.json +0 -31
  63. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/attributes/retrieval/llamaindex_entities.json +0 -31
  64. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/botocore_methods.json +0 -13
  65. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/haystack_methods.json +0 -45
  66. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/maps/langchain_methods.json +0 -129
  67. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/spans/README.md +0 -121
  68. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/spans/span_example.json +0 -140
  69. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/spans/span_format.json +0 -55
  70. monocle_apptrace-0.3.0b2/src/monocle_apptrace/metamodel/spans/span_types.json +0 -16
  71. monocle_apptrace-0.3.0b2/src/monocle_apptrace/utils.py +0 -252
  72. monocle_apptrace-0.3.0b2/src/monocle_apptrace/wrap_common.py +0 -511
  73. monocle_apptrace-0.3.0b2/src/monocle_apptrace/wrapper.py +0 -27
  74. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/.gitignore +0 -0
  75. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/CODEOWNERS.md +0 -0
  76. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/CODE_OF_CONDUCT.md +0 -0
  77. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/CONTRIBUTING.md +0 -0
  78. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/COPYRIGHT.template +0 -0
  79. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/LICENSE +0 -0
  80. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/MAINTAINER.md +0 -0
  81. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/Monocle_User_Guide.md +0 -0
  82. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/Monocle_contributor_guide.md +0 -0
  83. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/NOTICE +0 -0
  84. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/README.md +0 -0
  85. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/SECURITY.md +0 -0
  86. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/src/monocle_apptrace/README.md +0 -0
  87. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/src/monocle_apptrace/__init__.py +0 -0
  88. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/src/monocle_apptrace/exporters/azure/blob_exporter.py +0 -0
  89. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/src/monocle_apptrace/exporters/base_exporter.py +0 -0
  90. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/src/monocle_apptrace/exporters/exporter_processor.py +0 -0
  91. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/src/monocle_apptrace/exporters/file_exporter.py +0 -0
  92. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/src/monocle_apptrace/exporters/okahu/okahu_exporter.py +0 -0
  93. {monocle_apptrace-0.3.0b2 → monocle_apptrace-0.3.0b4}/tox.ini +0 -0
@@ -1,4 +1,4 @@
1
- ## Version 0.3.0b2 (2024-12-10)
1
+ ## Version 0.3.0b4 (2024-12-10)
2
2
 
3
3
  - Add dev dependency for Mistral AI integration ([#81](https://github.com/monocle2ai/monocle/pull/81))
4
4
  - Add VectorStore deployment URL capture support ([#80](https://github.com/monocle2ai/monocle/pull/80))
@@ -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-observability
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
1
  Metadata-Version: 2.4
2
2
  Name: monocle_apptrace
3
- Version: 0.3.0b2
3
+ Version: 0.3.0b4
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,22 +20,35 @@ 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'
28
34
  Requires-Dist: langchain-mistralai==0.1.13; extra == 'dev'
29
35
  Requires-Dist: langchain-openai==0.1.8; extra == 'dev'
30
36
  Requires-Dist: langchain==0.2.5; extra == 'dev'
37
+ Requires-Dist: langchainhub==0.1.21; extra == 'dev'
31
38
  Requires-Dist: llama-index-embeddings-huggingface==0.2.0; extra == 'dev'
39
+ Requires-Dist: llama-index-llms-azure-openai==0.1.9; extra == 'dev'
32
40
  Requires-Dist: llama-index-llms-mistralai==0.1.20; extra == 'dev'
33
41
  Requires-Dist: llama-index-vector-stores-chroma==0.1.9; extra == 'dev'
42
+ Requires-Dist: llama-index-vector-stores-opensearch==0.1.10; extra == 'dev'
34
43
  Requires-Dist: llama-index==0.10.30; extra == 'dev'
35
44
  Requires-Dist: mistral-haystack==0.0.2; extra == 'dev'
36
45
  Requires-Dist: numpy==1.26.4; extra == 'dev'
46
+ Requires-Dist: opendal==0.45.14; extra == 'dev'
47
+ Requires-Dist: opensearch-haystack==1.2.0; extra == 'dev'
48
+ Requires-Dist: opentelemetry-instrumentation-flask; extra == 'dev'
37
49
  Requires-Dist: parameterized==0.9.0; extra == 'dev'
38
50
  Requires-Dist: pytest==8.0.0; extra == 'dev'
51
+ Requires-Dist: requests-aws4auth==1.2.3; extra == 'dev'
39
52
  Requires-Dist: sentence-transformers==2.6.1; extra == 'dev'
40
53
  Requires-Dist: types-requests==2.31.0.20240106; extra == 'dev'
41
54
  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.3.0b2"
7
+ version = "0.3.0b4"
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]
@@ -46,7 +58,20 @@ dev = [
46
58
  'parameterized==0.9.0',
47
59
  'llama-index-llms-mistralai==0.1.20',
48
60
  'langchain-mistralai==0.1.13',
49
- 'mistral-haystack==0.0.2'
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
+ 'opendal==0.45.14'
50
75
  ]
51
76
 
52
77
  azure = [
@@ -25,17 +25,25 @@ class S3SpanExporter(SpanExporterBase):
25
25
  super().__init__()
26
26
  # Use environment variables if credentials are not provided
27
27
  DEFAULT_FILE_PREFIX = "monocle_trace_"
28
- DEFAULT_TIME_FORMAT = "%Y-%m-%d_%H.%M.%S"
28
+ DEFAULT_TIME_FORMAT = "%Y-%m-%d__%H.%M.%S"
29
29
  self.max_batch_size = 500
30
30
  self.export_interval = 1
31
- self.s3_client = boto3.client(
32
- 's3',
33
- aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
34
- aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),
35
- region_name=region_name,
36
- )
31
+ if(os.getenv('MONOCLE_AWS_ACCESS_KEY_ID') and os.getenv('MONOCLE_AWS_SECRET_ACCESS_KEY')):
32
+ self.s3_client = boto3.client(
33
+ 's3',
34
+ aws_access_key_id=os.getenv('MONOCLE_AWS_ACCESS_KEY_ID'),
35
+ aws_secret_access_key=os.getenv('MONOCLE_AWS_SECRET_ACCESS_KEY'),
36
+ region_name=region_name,
37
+ )
38
+ else:
39
+ self.s3_client = boto3.client(
40
+ 's3',
41
+ aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
42
+ aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),
43
+ region_name=region_name,
44
+ )
37
45
  self.bucket_name = bucket_name or os.getenv('MONOCLE_S3_BUCKET_NAME','default-bucket')
38
- self.file_prefix = DEFAULT_FILE_PREFIX
46
+ self.file_prefix = os.getenv('MONOCLE_S3_KEY_PREFIX', DEFAULT_FILE_PREFIX)
39
47
  self.time_format = DEFAULT_TIME_FORMAT
40
48
  self.export_queue = []
41
49
  self.last_export_time = time.time()
@@ -142,7 +150,8 @@ class S3SpanExporter(SpanExporterBase):
142
150
  @SpanExporterBase.retry_with_backoff(exceptions=(EndpointConnectionError, ConnectionClosedError, ReadTimeoutError, ConnectTimeoutError))
143
151
  def __upload_to_s3(self, span_data_batch: str):
144
152
  current_time = datetime.datetime.now().strftime(self.time_format)
145
- file_name = f"{self.file_prefix}{current_time}.ndjson"
153
+ prefix = self.file_prefix + os.environ.get('MONOCLE_S3_KEY_PREFIX_CURRENT', '')
154
+ file_name = f"{prefix}{current_time}.ndjson"
146
155
  self.s3_client.put_object(
147
156
  Bucket=self.bucket_name,
148
157
  Key=file_name,
@@ -0,0 +1,126 @@
1
+ import os
2
+ import time
3
+ import datetime
4
+ import logging
5
+ import asyncio
6
+ from typing import Sequence
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 opendal import Operator
11
+ from opendal.exceptions import PermissionDenied, ConfigInvalid, Unexpected
12
+
13
+
14
+ import json
15
+
16
+ logger = logging.getLogger(__name__)
17
+ class OpenDALS3Exporter(SpanExporterBase):
18
+ def __init__(self, bucket_name=None, region_name=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
+
41
+ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
42
+ """Synchronous export method that internally handles async logic."""
43
+ try:
44
+ # Run the asynchronous export logic in an event loop
45
+ asyncio.run(self.__export_async(spans))
46
+ return SpanExportResult.SUCCESS
47
+ except Exception as e:
48
+ logger.error(f"Error exporting spans: {e}")
49
+ return SpanExportResult.FAILURE
50
+
51
+ async def __export_async(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
52
+ try:
53
+ # Add spans to the export queue
54
+ for span in spans:
55
+ self.export_queue.append(span)
56
+ if len(self.export_queue) >= self.max_batch_size:
57
+ await self.__export_spans()
58
+
59
+ # Check if it's time to force a flush
60
+ current_time = time.time()
61
+ if current_time - self.last_export_time >= self.export_interval:
62
+ await self.__export_spans()
63
+ self.last_export_time = current_time
64
+
65
+ return SpanExportResult.SUCCESS
66
+ except Exception as e:
67
+ logger.error(f"Error exporting spans: {e}")
68
+ return SpanExportResult.FAILURE
69
+
70
+ def __serialize_spans(self, spans: Sequence[ReadableSpan]) -> str:
71
+ try:
72
+ # Serialize spans to JSON or any other format you prefer
73
+ valid_json_list = []
74
+ for span in spans:
75
+ try:
76
+ valid_json_list.append(span.to_json(indent=0).replace("\n", ""))
77
+ except json.JSONDecodeError as e:
78
+ logger.warning(f"Invalid JSON format in span data: {span.context.span_id}. Error: {e}")
79
+ continue
80
+ return "\n".join(valid_json_list) + "\n"
81
+ except Exception as e:
82
+ logger.warning(f"Error serializing spans: {e}")
83
+
84
+ async def __export_spans(self):
85
+ if not self.export_queue:
86
+ return
87
+ # Take a batch of spans from the queue
88
+ batch_to_export = self.export_queue[:self.max_batch_size]
89
+ serialized_data = self.__serialize_spans(batch_to_export)
90
+ self.export_queue = self.export_queue[self.max_batch_size:]
91
+ try:
92
+ self.__upload_to_s3(serialized_data)
93
+ except Exception as e:
94
+ logger.error(f"Failed to upload span batch: {e}")
95
+
96
+ @SpanExporterBase.retry_with_backoff(exceptions=(Unexpected))
97
+ def __upload_to_s3(self, span_data_batch: str):
98
+
99
+ current_time = datetime.datetime.now().strftime(self.time_format)
100
+ file_name = f"{self.file_prefix}{current_time}.ndjson"
101
+ try:
102
+ # Attempt to write the span data batch to S3
103
+ self.op.write(file_name, span_data_batch.encode("utf-8"))
104
+ logger.info(f"Span batch uploaded to S3 as {file_name}.")
105
+
106
+ except PermissionDenied as e:
107
+ # S3 bucket is forbidden.
108
+ logger.error(f"Access to bucket {self.bucket_name} is forbidden (403).")
109
+ raise PermissionError(f"Access to bucket {self.bucket_name} is forbidden.")
110
+
111
+ except ConfigInvalid as e:
112
+ # Bucket does not exist.
113
+ if "404" in str(e):
114
+ logger.error("Bucket does not exist. Please check the bucket name and region.")
115
+ raise Exception(f"Bucket does not exist. Error: {e}")
116
+ else:
117
+ logger.error(f"Unexpected error when accessing bucket {self.bucket_name}: {e}")
118
+ raise e
119
+
120
+
121
+ async def force_flush(self, timeout_millis: int = 30000) -> bool:
122
+ await self.__export_spans()
123
+ return True
124
+
125
+ def shutdown(self) -> None:
126
+ logger.info("S3SpanExporter has been shut down.")
@@ -0,0 +1,147 @@
1
+ import os
2
+ import time
3
+ import datetime
4
+ import logging
5
+ import asyncio
6
+ from opentelemetry.sdk.trace import ReadableSpan
7
+ from opentelemetry.sdk.trace.export import SpanExportResult
8
+ from typing import Sequence
9
+ from opendal import Operator
10
+ from monocle_apptrace.exporters.base_exporter import SpanExporterBase
11
+ from opendal.exceptions import Unexpected, PermissionDenied, NotFound
12
+ import json
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class OpenDALAzureExporter(SpanExporterBase):
17
+ def __init__(self, connection_string=None, container_name=None):
18
+ super().__init__()
19
+ DEFAULT_FILE_PREFIX = "monocle_trace_"
20
+ DEFAULT_TIME_FORMAT = "%Y-%m-%d_%H.%M.%S"
21
+ self.max_batch_size = 500
22
+ self.export_interval = 1
23
+ self.container_name = container_name
24
+
25
+ # Default values
26
+ self.file_prefix = DEFAULT_FILE_PREFIX
27
+ self.time_format = DEFAULT_TIME_FORMAT
28
+
29
+ # Validate input
30
+ if not connection_string:
31
+ connection_string = os.getenv('MONOCLE_BLOB_CONNECTION_STRING')
32
+ if not connection_string:
33
+ raise ValueError("Azure Storage connection string is not provided or set in environment variables.")
34
+
35
+ if not container_name:
36
+ container_name = os.getenv('MONOCLE_BLOB_CONTAINER_NAME', 'default-container')
37
+ endpoint, account_name , account_key = self.parse_connection_string(connection_string)
38
+
39
+ if not account_name or not account_key:
40
+ raise ValueError("AccountName or AccountKey missing in the connection string.")
41
+
42
+ try:
43
+ # Initialize OpenDAL operator with explicit credentials
44
+ self.operator = Operator(
45
+ "azblob",
46
+ endpoint=endpoint,
47
+ account_name=account_name,
48
+ account_key=account_key,
49
+ container=container_name
50
+ )
51
+ except Exception as e:
52
+ raise RuntimeError(f"Failed to initialize OpenDAL operator: {e}")
53
+
54
+
55
+ def parse_connection_string(self,connection_string):
56
+ connection_params = dict(item.split('=', 1) for item in connection_string.split(';') if '=' in item)
57
+
58
+ account_name = connection_params.get('AccountName')
59
+ account_key = connection_params.get('AccountKey')
60
+ endpoint_suffix = connection_params.get('EndpointSuffix')
61
+
62
+ if not all([account_name, account_key, endpoint_suffix]):
63
+ raise ValueError("Invalid connection string. Ensure it contains AccountName, AccountKey, and EndpointSuffix.")
64
+
65
+ endpoint = f"https://{account_name}.blob.{endpoint_suffix}"
66
+ return endpoint, account_name, account_key
67
+
68
+
69
+ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
70
+ """Synchronous export method that internally handles async logic."""
71
+ try:
72
+ # Run the asynchronous export logic in an event loop
73
+ asyncio.run(self._export_async(spans))
74
+ return SpanExportResult.SUCCESS
75
+ except Exception as e:
76
+ logger.error(f"Error exporting spans: {e}")
77
+ return SpanExportResult.FAILURE
78
+
79
+ async def _export_async(self, spans: Sequence[ReadableSpan]):
80
+ """The actual async export logic is run here."""
81
+ # Add spans to the export queue
82
+ for span in spans:
83
+ self.export_queue.append(span)
84
+ if len(self.export_queue) >= self.max_batch_size:
85
+ await self.__export_spans()
86
+
87
+ # Force a flush if the interval has passed
88
+ current_time = time.time()
89
+ if current_time - self.last_export_time >= self.export_interval:
90
+ await self.__export_spans()
91
+ self.last_export_time = current_time
92
+
93
+ def __serialize_spans(self, spans: Sequence[ReadableSpan]) -> str:
94
+ try:
95
+ valid_json_list = []
96
+ for span in spans:
97
+ try:
98
+ valid_json_list.append(span.to_json(indent=0).replace("\n", ""))
99
+ except json.JSONDecodeError as e:
100
+ logger.warning(f"Invalid JSON format in span data: {span.context.span_id}. Error: {e}")
101
+ continue
102
+
103
+ ndjson_data = "\n".join(valid_json_list) + "\n"
104
+ return ndjson_data
105
+ except Exception as e:
106
+ logger.warning(f"Error serializing spans: {e}")
107
+
108
+ async def __export_spans(self):
109
+ if len(self.export_queue) == 0:
110
+ return
111
+
112
+ batch_to_export = self.export_queue[:self.max_batch_size]
113
+ serialized_data = self.__serialize_spans(batch_to_export)
114
+ self.export_queue = self.export_queue[self.max_batch_size:]
115
+ try:
116
+ self.__upload_to_opendal(serialized_data)
117
+ except Exception as e:
118
+ logger.error(f"Failed to upload span batch: {e}")
119
+
120
+ @SpanExporterBase.retry_with_backoff(exceptions=(Unexpected,))
121
+ def __upload_to_opendal(self, span_data_batch: str):
122
+ current_time = datetime.datetime.now().strftime(self.time_format)
123
+ file_name = f"{self.file_prefix}{current_time}.ndjson"
124
+
125
+ try:
126
+ self.operator.write(file_name, span_data_batch.encode('utf-8'))
127
+ logger.info(f"Span batch uploaded to Azure Blob Storage as {file_name}.")
128
+ except PermissionDenied as e:
129
+ # Azure Container is forbidden.
130
+ logger.error(f"Access to container {self.container_name} is forbidden (403).")
131
+ raise PermissionError(f"Access to container {self.container_name} is forbidden.")
132
+
133
+ except NotFound as e:
134
+ # Container does not exist.
135
+ if "404" in str(e):
136
+ logger.error("Container does not exist. Please check the container name.")
137
+ raise Exception(f"Container does not exist. Error: {e}")
138
+ else:
139
+ logger.error(f"Unexpected NotFound error when accessing container {self.container_name}: {e}")
140
+ raise e
141
+
142
+ async def force_flush(self, timeout_millis: int = 30000) -> bool:
143
+ await self.__export_spans()
144
+ return True
145
+
146
+ def shutdown(self) -> None:
147
+ logger.info("OpenDALAzureExporter has been shut down.")
@@ -0,0 +1,45 @@
1
+ from typing import Dict, Any, List
2
+ import os
3
+ import warnings
4
+ from importlib import import_module
5
+ from opentelemetry.sdk.trace.export import SpanExporter, ConsoleSpanExporter
6
+ from monocle_apptrace.exporters.file_exporter import FileSpanExporter
7
+
8
+ monocle_exporters: Dict[str, Any] = {
9
+ "s3": {"module": "monocle_apptrace.exporters.aws.s3_exporter", "class": "S3SpanExporter"},
10
+ "blob": {"module": "monocle_apptrace.exporters.azure.blob_exporter", "class": "AzureBlobSpanExporter"},
11
+ "okahu": {"module": "monocle_apptrace.exporters.okahu.okahu_exporter", "class": "OkahuSpanExporter"},
12
+ "file": {"module": "monocle_apptrace.exporters.file_exporter", "class": "FileSpanExporter"},
13
+ "memory": {"module": "opentelemetry.sdk.trace.export.in_memory_span_exporter", "class": "InMemorySpanExporter"},
14
+ "console": {"module": "opentelemetry.sdk.trace.export", "class": "ConsoleSpanExporter"}
15
+ }
16
+
17
+
18
+ def get_monocle_exporter() -> List[SpanExporter]:
19
+ # Retrieve the MONOCLE_EXPORTER environment variable and split it into a list
20
+ exporter_names = os.environ.get("MONOCLE_EXPORTER", "file").split(",")
21
+ exporters = []
22
+
23
+ for exporter_name in exporter_names:
24
+ exporter_name = exporter_name.strip()
25
+ try:
26
+ exporter_class_path = monocle_exporters[exporter_name]
27
+ except KeyError:
28
+ warnings.warn(f"Unsupported Monocle span exporter '{exporter_name}', skipping.")
29
+ continue
30
+ try:
31
+ exporter_module = import_module(exporter_class_path["module"])
32
+ exporter_class = getattr(exporter_module, exporter_class_path["class"])
33
+ exporters.append(exporter_class())
34
+ except Exception as ex:
35
+ warnings.warn(
36
+ f"Unable to initialize Monocle span exporter '{exporter_name}', error: {ex}. Using ConsoleSpanExporter as a fallback.")
37
+ exporters.append(ConsoleSpanExporter())
38
+ continue
39
+
40
+ # If no exporters were created, default to FileSpanExporter
41
+ if not exporters:
42
+ warnings.warn("No valid Monocle span exporters configured. Defaulting to FileSpanExporter.")
43
+ exporters.append(FileSpanExporter())
44
+
45
+ return exporters
@@ -9,6 +9,8 @@ AWS_LAMBDA_FUNCTION_IDENTIFIER_ENV_NAME = "AWS_LAMBDA_FUNCTION_NAME"
9
9
  AZURE_FUNCTION_IDENTIFIER_ENV_NAME = "WEBSITE_SITE_NAME"
10
10
  AZURE_APP_SERVICE_IDENTIFIER_ENV_NAME = "WEBSITE_DEPLOYMENT_ID"
11
11
  GITHUB_CODESPACE_IDENTIFIER_ENV_NAME = "GITHUB_REPOSITORY"
12
+
13
+
12
14
  # Azure naming reference can be found here
13
15
  # https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations
14
16
  AZURE_FUNCTION_NAME = "azure.func"
@@ -34,3 +36,14 @@ service_name_map = {
34
36
  AWS_LAMBDA_SERVICE_NAME: AWS_LAMBDA_FUNCTION_IDENTIFIER_ENV_NAME,
35
37
  GITHUB_CODESPACE_SERVICE_NAME: GITHUB_CODESPACE_IDENTIFIER_ENV_NAME
36
38
  }
39
+
40
+ WORKFLOW_TYPE_KEY = "workflow_type"
41
+ DATA_INPUT_KEY = "data.input"
42
+ DATA_OUTPUT_KEY = "data.output"
43
+ PROMPT_INPUT_KEY = "data.input"
44
+ PROMPT_OUTPUT_KEY = "data.output"
45
+ QUERY = "input"
46
+ RESPONSE = "response"
47
+ SESSION_PROPERTIES_KEY = "session"
48
+ INFRA_SERVICE_KEY = "infra_service_name"
49
+ META_DATA = 'metadata'