monocle-apptrace 0.6.0__tar.gz → 0.7.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.
Files changed (175) hide show
  1. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/PKG-INFO +16 -4
  2. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/pyproject.toml +48 -11
  3. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/exporters/aws/s3_exporter.py +99 -41
  4. monocle_apptrace-0.7.0/src/monocle_apptrace/exporters/azure/blob_exporter.py +208 -0
  5. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/exporters/base_exporter.py +10 -1
  6. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/exporters/file_exporter.py +3 -3
  7. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/exporters/okahu/okahu_exporter.py +1 -15
  8. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/__init__.py +2 -1
  9. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/constants.py +58 -9
  10. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/instrumentor.py +6 -1
  11. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/method_wrappers.py +4 -4
  12. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/scope_wrapper.py +17 -2
  13. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/span_handler.py +94 -29
  14. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/utils.py +157 -8
  15. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/wrapper.py +111 -42
  16. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/wrapper_method.py +17 -2
  17. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/a2a/methods.py +1 -1
  18. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/adk/_helper.py +12 -1
  19. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/adk/adk_handler.py +27 -0
  20. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/adk/entities/agent.py +40 -3
  21. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/adk/entities/tool.py +1 -1
  22. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/adk/methods.py +4 -2
  23. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/agents/_helper.py +9 -8
  24. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/agents/agents_processor.py +15 -33
  25. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +17 -5
  26. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/agents/methods.py +0 -7
  27. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +1 -1
  28. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/anthropic/_helper.py +55 -2
  29. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +15 -4
  30. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +1 -1
  31. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/azureaiinference/_helper.py +56 -0
  32. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/azureaiinference/entities/inference.py +14 -0
  33. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +69 -5
  34. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +18 -0
  35. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/crew_ai/_helper.py +260 -0
  36. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/crew_ai/crew_ai_processor.py +72 -0
  37. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/crew_ai/entities/inference.py +179 -0
  38. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/crew_ai/methods.py +70 -0
  39. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/fastapi/_helper.py +5 -5
  40. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +4 -4
  41. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/fastmcp/_helper.py +261 -0
  42. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/fastmcp/entities/prompts.py +96 -0
  43. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/fastmcp/entities/resources.py +97 -0
  44. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/fastmcp/entities/tools.py +97 -0
  45. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/fastmcp/methods.py +56 -0
  46. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/finish_types.py +35 -18
  47. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/flask/_helper.py +3 -3
  48. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/gemini/_helper.py +62 -3
  49. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/gemini/entities/inference.py +16 -1
  50. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +63 -0
  51. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +14 -0
  52. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/hugging_face/_helper.py +1 -1
  53. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/hugging_face/entities/inference.py +1 -4
  54. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/lambdafunc/_helper.py +1 -1
  55. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +54 -3
  56. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +15 -4
  57. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/langchain/methods.py +2 -0
  58. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +72 -8
  59. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +16 -5
  60. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +122 -0
  61. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +22 -6
  62. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/litellm/_helper.py +88 -1
  63. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/litellm/entities/inference.py +26 -0
  64. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +103 -10
  65. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +4 -3
  66. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +15 -0
  67. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/llamaindex_processor.py +25 -11
  68. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +2 -8
  69. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +1 -1
  70. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/mistral/_helper.py +91 -21
  71. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/mistral/entities/inference.py +15 -4
  72. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/mistral/methods.py +0 -8
  73. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/openai/_helper.py +104 -30
  74. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +34 -4
  75. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/openai/methods.py +1 -1
  76. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/openai/openai_processor.py +81 -0
  77. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/strands/_helper.py +71 -0
  78. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/strands/entities/agent.py +179 -0
  79. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/strands/entities/tool.py +62 -0
  80. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/strands/methods.py +21 -0
  81. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/strands/strands_processor.py +22 -0
  82. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/teamsai/__init__.py +0 -0
  83. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/teamsai/entities/__init__.py +0 -0
  84. monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/__init__.py +0 -0
  85. monocle_apptrace-0.6.0/src/monocle_apptrace/exporters/azure/blob_exporter.py +0 -146
  86. monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +0 -50
  87. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/.gitignore +0 -0
  88. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/LICENSE +0 -0
  89. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/README.md +0 -0
  90. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/README.md +0 -0
  91. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/__init__.py +0 -0
  92. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/__main__.py +0 -0
  93. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/exporters/aws/s3_exporter_opendal.py +0 -0
  94. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/exporters/azure/blob_exporter_opendal.py +0 -0
  95. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/exporters/exporter_processor.py +0 -0
  96. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/exporters/monocle_exporters.py +0 -0
  97. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/__init__.py +0 -0
  98. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/common/tracing.md +0 -0
  99. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/__init__.py +0 -0
  100. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/a2a/__init__.py +0 -0
  101. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/a2a/_helper.py +0 -0
  102. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/a2a/entities/__init__.py +0 -0
  103. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/a2a/entities/inference.py +0 -0
  104. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/adk/__init__.py +0 -0
  105. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/agents/__init__.py +0 -0
  106. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/agents/entities/__init__.py +0 -0
  107. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/aiohttp/__init__.py +0 -0
  108. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/aiohttp/entities/http.py +0 -0
  109. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/aiohttp/methods.py +0 -0
  110. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/anthropic/__init__.py +0 -0
  111. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/anthropic/entities/__init__.py +0 -0
  112. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/anthropic/methods.py +0 -0
  113. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/azfunc/entities/http.py +0 -0
  114. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/azfunc/methods.py +0 -0
  115. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/azfunc/wrapper.py +0 -0
  116. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/azureaiinference/__init__.py +0 -0
  117. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/azureaiinference/methods.py +0 -0
  118. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/botocore/__init__.py +0 -0
  119. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/botocore/entities/__init__.py +0 -0
  120. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +0 -0
  121. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/botocore/methods.py +0 -0
  122. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/fastapi → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/crew_ai}/__init__.py +0 -0
  123. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/fastapi → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/crew_ai}/entities/__init__.py +0 -0
  124. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/flask → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/fastapi}/__init__.py +0 -0
  125. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/gemini → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/fastapi/entities}/__init__.py +0 -0
  126. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/fastapi/entities/http.py +0 -0
  127. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/gemini/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/fastmcp}/__init__.py +0 -0
  128. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/haystack → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/flask}/__init__.py +0 -0
  129. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/flask/entities/http.py +0 -0
  130. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/flask/methods.py +0 -0
  131. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/haystack/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/gemini}/__init__.py +0 -0
  132. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/hugging_face → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/gemini/entities}/__init__.py +0 -0
  133. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/gemini/entities/retrieval.py +0 -0
  134. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/gemini/methods.py +0 -0
  135. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/hugging_face/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/haystack}/__init__.py +0 -0
  136. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/langchain → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/haystack/entities}/__init__.py +0 -0
  137. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/retrieval.py +0 -0
  138. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/haystack/methods.py +0 -0
  139. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/langchain/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/hugging_face}/__init__.py +0 -0
  140. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/langgraph → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/hugging_face/entities}/__init__.py +0 -0
  141. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/hugging_face/methods.py +0 -0
  142. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/lambdafunc/entities/http.py +0 -0
  143. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/lambdafunc/methods.py +0 -0
  144. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/lambdafunc/wrapper.py +0 -0
  145. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/langgraph/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/langchain}/__init__.py +0 -0
  146. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/litellm → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/langchain/entities}/__init__.py +0 -0
  147. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/retrieval.py +0 -0
  148. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/litellm/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/langgraph}/__init__.py +0 -0
  149. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/langgraph/entities}/__init__.py +0 -0
  150. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/litellm}/__init__.py +0 -0
  151. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/mcp → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/litellm/entities}/__init__.py +0 -0
  152. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/litellm/methods.py +0 -0
  153. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/mcp/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex}/__init__.py +0 -0
  154. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/mistral → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities}/__init__.py +0 -0
  155. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/retrieval.py +0 -0
  156. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/mistral/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/mcp}/__init__.py +0 -0
  157. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/openai → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/mcp/entities}/__init__.py +0 -0
  158. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/mcp/entities/inference.py +0 -0
  159. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/mcp/mcp_processor.py +0 -0
  160. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/mcp/methods.py +0 -0
  161. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/openai/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/mistral}/__init__.py +0 -0
  162. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/teamsai → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/mistral/entities}/__init__.py +0 -0
  163. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/mistral/entities/retrieval.py +0 -0
  164. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/teamsai/entities → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/openai}/__init__.py +0 -0
  165. {monocle_apptrace-0.6.0/src/monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference → monocle_apptrace-0.7.0/src/monocle_apptrace/instrumentation/metamodel/openai/entities}/__init__.py +0 -0
  166. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +0 -0
  167. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/requests/__init__.py +0 -0
  168. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/requests/_helper.py +0 -0
  169. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/requests/entities/http.py +0 -0
  170. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/requests/methods.py +0 -0
  171. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +0 -0
  172. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +0 -0
  173. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +0 -0
  174. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/teamsai/methods.py +0 -0
  175. {monocle_apptrace-0.6.0 → monocle_apptrace-0.7.0}/src/monocle_apptrace/instrumentation/metamodel/teamsai/sample.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: monocle_apptrace
3
- Version: 0.6.0
3
+ Version: 0.7.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
@@ -14,6 +14,7 @@ Requires-Dist: opentelemetry-api>=1.21.0
14
14
  Requires-Dist: opentelemetry-instrumentation
15
15
  Requires-Dist: opentelemetry-sdk>=1.21.0
16
16
  Requires-Dist: requests
17
+ Requires-Dist: rfc3986>=2.0.0
17
18
  Requires-Dist: wrapt>=1.14.0
18
19
  Provides-Extra: ai-test
19
20
  Requires-Dist: bert-score; extra == 'ai-test'
@@ -21,20 +22,23 @@ Requires-Dist: transformers; extra == 'ai-test'
21
22
  Provides-Extra: aws
22
23
  Requires-Dist: boto3==1.37.24; extra == 'aws'
23
24
  Provides-Extra: azure
25
+ Requires-Dist: azure-ai-inference; extra == 'azure'
24
26
  Requires-Dist: azure-storage-blob==12.22.0; extra == 'azure'
25
27
  Provides-Extra: dev
26
28
  Requires-Dist: a2a-sdk==0.3.6; extra == 'dev'
27
29
  Requires-Dist: anthropic-haystack; extra == 'dev'
28
30
  Requires-Dist: anthropic==0.57.1; extra == 'dev'
29
31
  Requires-Dist: azure-storage-blob==12.22.0; extra == 'dev'
30
- Requires-Dist: boto3==1.37.24; extra == 'dev'
32
+ Requires-Dist: boto3==1.39.0; extra == 'dev'
31
33
  Requires-Dist: chromadb==1.0.15; extra == 'dev'
32
34
  Requires-Dist: click==8.2.1; extra == 'dev'
33
- Requires-Dist: datasets==2.20.0; extra == 'dev'
35
+ Requires-Dist: crewai==0.95.0; extra == 'dev'
36
+ Requires-Dist: datasets==4.3.0; extra == 'dev'
34
37
  Requires-Dist: faiss-cpu==1.8.0; extra == 'dev'
35
38
  Requires-Dist: fastapi>=0.115.0; extra == 'dev'
36
39
  Requires-Dist: flask; extra == 'dev'
37
40
  Requires-Dist: google-adk==1.10.0; extra == 'dev'
41
+ Requires-Dist: google-ai-haystack; extra == 'dev'
38
42
  Requires-Dist: google-generativeai==0.8.5; extra == 'dev'
39
43
  Requires-Dist: haystack-ai==2.3.0; extra == 'dev'
40
44
  Requires-Dist: httpx==0.28.1; extra == 'dev'
@@ -52,6 +56,7 @@ Requires-Dist: langchain==0.3.25; extra == 'dev'
52
56
  Requires-Dist: langchainhub==0.1.21; extra == 'dev'
53
57
  Requires-Dist: langgraph-supervisor==0.0.28; extra == 'dev'
54
58
  Requires-Dist: langgraph==0.5.4; extra == 'dev'
59
+ Requires-Dist: litellm==1.77.5; extra == 'dev'
55
60
  Requires-Dist: llama-index-embeddings-huggingface==0.6.0; extra == 'dev'
56
61
  Requires-Dist: llama-index-llms-anthropic==0.8.1; extra == 'dev'
57
62
  Requires-Dist: llama-index-llms-azure-openai==0.4.0; extra == 'dev'
@@ -72,12 +77,19 @@ Requires-Dist: opentelemetry-instrumentation-flask; extra == 'dev'
72
77
  Requires-Dist: parameterized==0.9.0; extra == 'dev'
73
78
  Requires-Dist: pydantic==2.11.7; extra == 'dev'
74
79
  Requires-Dist: pytest-asyncio==0.26.0; extra == 'dev'
80
+ Requires-Dist: pytest-venv; extra == 'dev'
75
81
  Requires-Dist: pytest==8.3.5; extra == 'dev'
76
82
  Requires-Dist: python-dotenv>=1.1.0; extra == 'dev'
77
83
  Requires-Dist: requests-aws4auth==1.2.3; extra == 'dev'
78
- Requires-Dist: sentence-transformers==2.6.1; extra == 'dev'
79
84
  Requires-Dist: types-requests==2.31.0.20240106; extra == 'dev'
80
85
  Requires-Dist: uvicorn==0.35.0; extra == 'dev'
86
+ Provides-Extra: dev-gemini
87
+ Requires-Dist: llama-index-llms-gemini==0.6.0; extra == 'dev-gemini'
88
+ Provides-Extra: dev-strands
89
+ Requires-Dist: strands-agents-tools==0.2.10; extra == 'dev-strands'
90
+ Requires-Dist: strands-agents==1.11.0; extra == 'dev-strands'
91
+ Provides-Extra: dev-tranformers
92
+ Requires-Dist: sentence-transformers==3.3.0; extra == 'dev-tranformers'
81
93
  Description-Content-Type: text/markdown
82
94
 
83
95
  # Monocle Apptrace
@@ -10,7 +10,7 @@ commands = [
10
10
 
11
11
  [project]
12
12
  name = "monocle_apptrace"
13
- version = "0.6.0"
13
+ version = "0.7.0"
14
14
  authors = [
15
15
  { name="Okahu Inc.", email="okahu-pypi@okahu.ai" },
16
16
  ]
@@ -26,17 +26,18 @@ classifiers = [
26
26
  dependencies = [
27
27
  'requests',
28
28
  'wrapt>=1.14.0',
29
+ 'rfc3986>=2.0.0',
29
30
  'opentelemetry-api>=1.21.0',
30
31
  'opentelemetry-sdk>=1.21.0',
31
32
  'opentelemetry-instrumentation',
32
33
  ]
33
34
 
34
35
  [tool.pytest.ini_options]
35
- #log_cli = true
36
- #log_cli_level = "INFO"
36
+ log_cli = true
37
+ log_cli_level = "INFO"
37
38
  # Enable file logging
38
- log_file = "traces.log"
39
- log_file_level = "INFO"
39
+ #log_file = "traces.log"
40
+ #log_file_level = "INFO"
40
41
  log_format = "%(asctime)s %(levelname)s %(pathname)s:%(lineno)d %(message)s"
41
42
  log_date_format = "%Y-%m-%d %H:%M:%S"
42
43
 
@@ -44,8 +45,27 @@ pythonpath = [
44
45
  "src",
45
46
  "tests"
46
47
  ]
47
- markers = [
48
- "integration"
48
+
49
+ # Simple ignore patterns - uncomment lines to ignore specific tests
50
+ # Just uncomment the patterns you want to ignore:
51
+
52
+ #ignore = [
53
+ # "tests/integration/", # Ignore ALL integration tests
54
+ # "tests/unit/", # Ignore ALL unit tests
55
+ #]
56
+
57
+ # Or ignore specific test files/patterns:
58
+ #ignore = [
59
+ # "tests/integration/test_langchain_bedrock_sample.py",
60
+ # "tests/integration/test_anthropic_*.py",
61
+ # "tests/integration/test_azure_*.py",
62
+ # "tests/integration/test_openai_*.py",
63
+ # "tests/unit/*_processor_test.py",
64
+ #]
65
+
66
+ # Test discovery patterns
67
+ testpaths = [
68
+ "tests"
49
69
  ]
50
70
 
51
71
  [project.optional-dependencies]
@@ -59,13 +79,13 @@ dev = [
59
79
  'langchain-chroma==0.2.4',
60
80
  'langchain-community==0.3.24',
61
81
  'langchain==0.3.25',
62
- 'datasets==2.20.0',
82
+ 'datasets==4.3.0',
63
83
  'numpy==1.26.4',
64
84
  'types-requests==2.31.0.20240106',
65
85
  'InstructorEmbedding==1.0.1',
66
- 'sentence-transformers==2.6.1',
67
86
  'faiss-cpu==1.8.0',
68
87
  'pytest==8.3.5',
88
+ 'pytest-venv',
69
89
  'llama-index==0.13.0',
70
90
  'llama-index-llms-openai==0.5.0',
71
91
  'llama-index-tools-mcp==0.3.0',
@@ -77,7 +97,7 @@ dev = [
77
97
  'mistral-haystack==0.0.2',
78
98
  'langchain-aws==0.2.23',
79
99
  'azure-storage-blob==12.22.0', # this is for blob exporter
80
- 'boto3==1.37.24', # this is for aws exporter
100
+ 'boto3==1.39.0', # this is for aws exporter
81
101
  'llama-index-vector-stores-opensearch==0.6.0',
82
102
  'haystack-ai==2.3.0',
83
103
  'llama-index-llms-azure-openai==0.4.0',
@@ -109,11 +129,28 @@ dev = [
109
129
  'google-generativeai==0.8.5',
110
130
  'openai-agents==0.2.6',
111
131
  'mistralai==1.9.9',
112
- 'huggingface_hub==0.35.3'
132
+ 'huggingface_hub==0.35.3',
133
+ 'litellm==1.77.5',
134
+ 'google-ai-haystack',
135
+ 'crewai==0.95.0'
136
+ ]
137
+
138
+ dev_tranformers = [
139
+ 'sentence-transformers==3.3.0'
140
+ ]
141
+
142
+ dev_gemini = [
143
+ 'llama-index-llms-gemini==0.6.0'
144
+ ]
145
+
146
+ dev_strands = [
147
+ 'strands-agents==1.11.0',
148
+ 'strands-agents-tools==0.2.10'
113
149
  ]
114
150
 
115
151
  azure = [
116
152
  'azure-storage-blob==12.22.0',
153
+ 'azure-ai-inference'
117
154
  ]
118
155
 
119
156
  aws = [
@@ -1,6 +1,4 @@
1
1
  import os
2
- import time
3
- import random
4
2
  import datetime
5
3
  import logging
6
4
  import asyncio
@@ -15,12 +13,14 @@ from botocore.exceptions import (
15
13
  )
16
14
  from opentelemetry.sdk.trace import ReadableSpan
17
15
  from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
18
- from monocle_apptrace.exporters.base_exporter import SpanExporterBase
16
+ from monocle_apptrace.exporters.base_exporter import SpanExporterBase, format_trace_id_without_0x
19
17
  from monocle_apptrace.exporters.exporter_processor import ExportTaskProcessor
20
- from typing import Sequence, Optional
18
+ from typing import Sequence, Optional, Dict, List, Tuple
21
19
  import json
22
20
  logger = logging.getLogger(__name__)
23
21
 
22
+ HANDLE_TIMEOUT_SECONDS = 60
23
+
24
24
  class S3SpanExporter(SpanExporterBase):
25
25
  def __init__(self, bucket_name=None, region_name=None, task_processor: Optional[ExportTaskProcessor] = None):
26
26
  super().__init__()
@@ -29,6 +29,8 @@ class S3SpanExporter(SpanExporterBase):
29
29
  DEFAULT_TIME_FORMAT = "%Y-%m-%d__%H.%M.%S"
30
30
  self.max_batch_size = 500
31
31
  self.export_interval = 1
32
+ # Dictionary to store spans by trace_id: {trace_id: (spans_list, creation_time, has_root_span)}
33
+ self.trace_spans: Dict[int, Tuple[List[ReadableSpan], datetime.datetime, bool]] = {}
32
34
  if(os.getenv('MONOCLE_AWS_ACCESS_KEY_ID') and os.getenv('MONOCLE_AWS_SECRET_ACCESS_KEY')):
33
35
  self.s3_client = boto3.client(
34
36
  's3',
@@ -46,8 +48,6 @@ class S3SpanExporter(SpanExporterBase):
46
48
  self.bucket_name = bucket_name or os.getenv('MONOCLE_S3_BUCKET_NAME','default-bucket')
47
49
  self.file_prefix = os.getenv('MONOCLE_S3_KEY_PREFIX', DEFAULT_FILE_PREFIX)
48
50
  self.time_format = DEFAULT_TIME_FORMAT
49
- self.export_queue = []
50
- self.last_export_time = time.time()
51
51
  self.task_processor = task_processor
52
52
  if self.task_processor is not None:
53
53
  self.task_processor.start()
@@ -92,6 +92,47 @@ class S3SpanExporter(SpanExporterBase):
92
92
  logger.error(f"Type error while checking bucket existence: {e}")
93
93
  raise e
94
94
 
95
+ def _cleanup_expired_traces(self) -> None:
96
+ """Upload and remove traces that have exceeded the timeout."""
97
+ current_time = datetime.datetime.now()
98
+ expired_trace_ids = []
99
+
100
+ for trace_id, (spans, creation_time, _) in self.trace_spans.items():
101
+ if (current_time - creation_time).total_seconds() > HANDLE_TIMEOUT_SECONDS:
102
+ expired_trace_ids.append(trace_id)
103
+
104
+ for trace_id in expired_trace_ids:
105
+ self._upload_trace(trace_id)
106
+
107
+ def _add_spans_to_trace(self, trace_id: int, spans: List[ReadableSpan], has_root: bool = False) -> None:
108
+ """Add spans to a trace buffer, creating it if needed."""
109
+ if trace_id in self.trace_spans:
110
+ existing_spans, creation_time, existing_root = self.trace_spans[trace_id]
111
+ existing_spans.extend(spans)
112
+ has_root = has_root or existing_root
113
+ self.trace_spans[trace_id] = (existing_spans, creation_time, has_root)
114
+ else:
115
+ self.trace_spans[trace_id] = (spans.copy(), datetime.datetime.now(), has_root)
116
+
117
+ def _upload_trace(self, trace_id: int) -> None:
118
+ """Upload a specific trace to S3 and remove it from the buffer."""
119
+ if trace_id not in self.trace_spans:
120
+ return
121
+
122
+ spans, _, _ = self.trace_spans[trace_id]
123
+ if len(spans) == 0:
124
+ del self.trace_spans[trace_id]
125
+ return
126
+
127
+ serialized_data = self.__serialize_spans(spans)
128
+ if serialized_data:
129
+ try:
130
+ self.__upload_to_s3_with_trace_id(serialized_data, trace_id)
131
+ except Exception as e:
132
+ logger.error(f"Failed to upload trace {format_trace_id_without_0x(trace_id)}: {e}")
133
+
134
+ del self.trace_spans[trace_id]
135
+
95
136
  def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
96
137
  """Synchronous export method that internally handles async logic."""
97
138
  try:
@@ -106,18 +147,44 @@ class S3SpanExporter(SpanExporterBase):
106
147
  async def __export_async(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
107
148
  try:
108
149
  logger.info(f"__export_async {len(spans)} spans to S3.")
109
- # Add spans to the export queue
150
+
151
+ # Cleanup expired traces first
152
+ self._cleanup_expired_traces()
153
+
154
+ # Group spans by trace_id
155
+ spans_by_trace = {}
156
+ root_span_traces = set()
157
+
110
158
  for span in spans:
111
- self.export_queue.append(span)
112
- # If the queue reaches MAX_BATCH_SIZE, export the spans
113
- if len(self.export_queue) >= self.max_batch_size:
114
- await self.__export_spans()
115
-
116
- # Check if it's time to force a flush
117
- current_time = time.time()
118
- if current_time - self.last_export_time >= self.export_interval:
119
- await self.__export_spans() # Export spans if time interval has passed
120
- self.last_export_time = current_time # Reset the last export time
159
+ if self.skip_export(span):
160
+ continue
161
+
162
+ trace_id = span.context.trace_id
163
+ if trace_id not in spans_by_trace:
164
+ spans_by_trace[trace_id] = []
165
+ spans_by_trace[trace_id].append(span)
166
+
167
+ # Check if this span is a root span (no parent)
168
+ if not span.parent:
169
+ root_span_traces.add(trace_id)
170
+
171
+ # Add spans to their respective trace buffers
172
+ for trace_id, trace_spans in spans_by_trace.items():
173
+ has_root = trace_id in root_span_traces
174
+ self._add_spans_to_trace(trace_id, trace_spans, has_root)
175
+
176
+ # Upload complete traces (those with root spans)
177
+ for trace_id in root_span_traces:
178
+ if self.task_processor is not None and callable(getattr(self.task_processor, 'queue_task', None)):
179
+ # Queue the upload task
180
+ if trace_id in self.trace_spans:
181
+ spans_to_upload, _, _ = self.trace_spans[trace_id]
182
+ serialized_data = self.__serialize_spans(spans_to_upload)
183
+ if serialized_data:
184
+ self.task_processor.queue_task(self.__upload_to_s3_with_trace_id, serialized_data, trace_id, True)
185
+ del self.trace_spans[trace_id]
186
+ else:
187
+ self._upload_trace(trace_id)
121
188
 
122
189
  return SpanExportResult.SUCCESS
123
190
  except Exception as e:
@@ -140,44 +207,35 @@ class S3SpanExporter(SpanExporterBase):
140
207
  return ndjson_data
141
208
  except Exception as e:
142
209
  logger.warning(f"Error serializing spans: {e}")
143
-
144
-
145
- async def __export_spans(self):
146
- if len(self.export_queue) == 0:
147
- return
148
-
149
- # Take a batch of spans from the queue
150
- batch_to_export = self.export_queue[:self.max_batch_size]
151
- serialized_data = self.__serialize_spans(batch_to_export)
152
- self.export_queue = self.export_queue[self.max_batch_size:]
153
- # to calculate is_root_span loop over each span in batch_to_export and check if parent id is none or null
154
- is_root_span = any(not span.parent for span in batch_to_export)
155
- logger.info(f"Exporting {len(batch_to_export)} spans to S3 is_root_span : {is_root_span}.")
156
- if self.task_processor is not None and callable(getattr(self.task_processor, 'queue_task', None)):
157
- self.task_processor.queue_task(self.__upload_to_s3, serialized_data, is_root_span)
158
- else:
159
- try:
160
- self.__upload_to_s3(serialized_data)
161
- except Exception as e:
162
- logger.error(f"Failed to upload span batch: {e}")
210
+ return ""
163
211
 
164
212
  @SpanExporterBase.retry_with_backoff(exceptions=(EndpointConnectionError, ConnectionClosedError, ReadTimeoutError, ConnectTimeoutError))
165
- def __upload_to_s3(self, span_data_batch: str):
213
+ def __upload_to_s3_with_trace_id(self, span_data_batch: str, trace_id: int):
214
+ """Upload spans for a specific trace to S3 with trace ID in filename."""
166
215
  current_time = datetime.datetime.now().strftime(self.time_format)
167
216
  prefix = self.file_prefix + os.environ.get('MONOCLE_S3_KEY_PREFIX_CURRENT', '')
168
- file_name = f"{prefix}{current_time}.ndjson"
217
+ file_name = f"{prefix}{current_time}_{format_trace_id_without_0x(trace_id)}.ndjson"
169
218
  self.s3_client.put_object(
170
219
  Bucket=self.bucket_name,
171
220
  Key=file_name,
172
221
  Body=span_data_batch
173
222
  )
174
- logger.debug(f"Span batch uploaded to AWS S3 as {file_name}.")
223
+ logger.debug(f"Trace {format_trace_id_without_0x(trace_id)} uploaded to AWS S3 as {file_name}.")
175
224
 
176
225
  async def force_flush(self, timeout_millis: int = 30000) -> bool:
177
- await self.__export_spans() # Export any remaining spans in the queue
226
+ """Flush all pending traces to S3."""
227
+ trace_ids_to_upload = list(self.trace_spans.keys())
228
+ for trace_id in trace_ids_to_upload:
229
+ self._upload_trace(trace_id)
178
230
  return True
179
231
 
180
232
  def shutdown(self) -> None:
233
+ """Upload all pending traces and shutdown."""
234
+ # Upload all remaining traces
235
+ trace_ids_to_upload = list(self.trace_spans.keys())
236
+ for trace_id in trace_ids_to_upload:
237
+ self._upload_trace(trace_id)
238
+
181
239
  if hasattr(self, 'task_processor') and self.task_processor is not None:
182
240
  self.task_processor.stop()
183
241
  logger.info("S3SpanExporter has been shut down.")
@@ -0,0 +1,208 @@
1
+ import os
2
+ import datetime
3
+ import logging
4
+ import asyncio
5
+ from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient
6
+ from azure.core.exceptions import ResourceNotFoundError, ClientAuthenticationError, ServiceRequestError
7
+ from opentelemetry.sdk.trace import ReadableSpan
8
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
9
+ from typing import Sequence, Optional, Dict, List, Tuple
10
+ from monocle_apptrace.exporters.base_exporter import SpanExporterBase, format_trace_id_without_0x
11
+ from monocle_apptrace.exporters.exporter_processor import ExportTaskProcessor
12
+ import json
13
+ from monocle_apptrace.instrumentation.common.constants import MONOCLE_SDK_VERSION
14
+ logger = logging.getLogger(__name__)
15
+
16
+ HANDLE_TIMEOUT_SECONDS = 60 # 1 minute timeout for orphaned traces
17
+
18
+ class AzureBlobSpanExporter(SpanExporterBase):
19
+ def __init__(self, connection_string=None, container_name=None, task_processor: Optional[ExportTaskProcessor] = None):
20
+ super().__init__()
21
+ DEFAULT_FILE_PREFIX = "monocle_trace_"
22
+ DEFAULT_TIME_FORMAT = "%Y-%m-%d_%H.%M.%S"
23
+ self.max_batch_size = 500
24
+ self.export_interval = 1
25
+ # Dictionary to store spans by trace_id: {trace_id: (spans_list, creation_time, has_root_span)}
26
+ self.trace_spans: Dict[int, Tuple[List[ReadableSpan], datetime.datetime, bool]] = {}
27
+ # Use default values if none are provided
28
+ if not connection_string:
29
+ connection_string = os.getenv('MONOCLE_BLOB_CONNECTION_STRING')
30
+ if not connection_string:
31
+ raise ValueError("Azure Storage connection string is not provided or set in environment variables.")
32
+
33
+ if not container_name:
34
+ container_name = os.getenv('MONOCLE_BLOB_CONTAINER_NAME', 'default-container')
35
+
36
+ self.blob_service_client = BlobServiceClient.from_connection_string(connection_string)
37
+ self.container_name = container_name
38
+ self.file_prefix = DEFAULT_FILE_PREFIX
39
+ self.time_format = DEFAULT_TIME_FORMAT
40
+
41
+ # Check if container exists or create it
42
+ if not self.__container_exists(container_name):
43
+ try:
44
+ self.blob_service_client.create_container(container_name)
45
+ logger.info(f"Container {container_name} created successfully.")
46
+ except Exception as e:
47
+ logger.error(f"Error creating container {container_name}: {e}")
48
+ raise e
49
+
50
+ self.task_processor = task_processor
51
+ if self.task_processor is not None:
52
+ self.task_processor.start()
53
+
54
+ def __container_exists(self, container_name):
55
+ try:
56
+ container_client = self.blob_service_client.get_container_client(container_name)
57
+ container_client.get_container_properties()
58
+ return True
59
+ except ResourceNotFoundError:
60
+ logger.error(f"Container {container_name} not found (404).")
61
+ return False
62
+ except ClientAuthenticationError:
63
+ logger.error(f"Access to container {container_name} is forbidden (403).")
64
+ raise PermissionError(f"Access to container {container_name} is forbidden.")
65
+ except Exception as e:
66
+ logger.error(f"Unexpected error when checking if container {container_name} exists: {e}")
67
+ raise e
68
+
69
+ def _cleanup_expired_traces(self) -> None:
70
+ """Upload and remove traces that have exceeded the timeout."""
71
+ current_time = datetime.datetime.now()
72
+ expired_trace_ids = []
73
+
74
+ for trace_id, (spans, creation_time, _) in self.trace_spans.items():
75
+ if (current_time - creation_time).total_seconds() > HANDLE_TIMEOUT_SECONDS:
76
+ expired_trace_ids.append(trace_id)
77
+
78
+ for trace_id in expired_trace_ids:
79
+ self._upload_trace(trace_id)
80
+
81
+ def _add_spans_to_trace(self, trace_id: int, spans: List[ReadableSpan], has_root: bool = False) -> None:
82
+ """Add spans to a trace buffer, creating it if needed."""
83
+ if trace_id in self.trace_spans:
84
+ existing_spans, creation_time, existing_root = self.trace_spans[trace_id]
85
+ existing_spans.extend(spans)
86
+ has_root = has_root or existing_root
87
+ self.trace_spans[trace_id] = (existing_spans, creation_time, has_root)
88
+ else:
89
+ self.trace_spans[trace_id] = (spans.copy(), datetime.datetime.now(), has_root)
90
+
91
+ def _upload_trace(self, trace_id: int) -> None:
92
+ """Upload a specific trace to Azure Blob and remove it from the buffer."""
93
+ if trace_id not in self.trace_spans:
94
+ return
95
+
96
+ spans, _, _ = self.trace_spans[trace_id]
97
+ if len(spans) == 0:
98
+ del self.trace_spans[trace_id]
99
+ return
100
+
101
+ serialized_data = self.__serialize_spans(spans)
102
+ if serialized_data:
103
+ try:
104
+ self.__upload_to_blob_with_trace_id(serialized_data, trace_id)
105
+ except Exception as e:
106
+ logger.error(f"Failed to upload trace {format_trace_id_without_0x(trace_id)}: {e}")
107
+
108
+ del self.trace_spans[trace_id]
109
+
110
+ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
111
+ """Synchronous export method that internally handles async logic."""
112
+ try:
113
+ # Run the asynchronous export logic in an event loop
114
+ asyncio.run(self._export_async(spans))
115
+ return SpanExportResult.SUCCESS
116
+ except Exception as e:
117
+ logger.error(f"Error exporting spans: {e}")
118
+ return SpanExportResult.FAILURE
119
+
120
+ async def _export_async(self, spans: Sequence[ReadableSpan]):
121
+ """The actual async export logic is run here."""
122
+ try:
123
+ # Cleanup expired traces first
124
+ self._cleanup_expired_traces()
125
+
126
+ # Group spans by trace_id
127
+ spans_by_trace = {}
128
+ root_span_traces = set()
129
+
130
+ for span in spans:
131
+ # 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)
132
+ # With Monocle,OpenTelemetry is always loaded. If the Azure trace package is installed, then it triggers the blob trace generation on every blob operation.
133
+ # 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.
134
+ # 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.
135
+ if self.skip_export(span):
136
+ continue
137
+
138
+ trace_id = span.context.trace_id
139
+ if trace_id not in spans_by_trace:
140
+ spans_by_trace[trace_id] = []
141
+ spans_by_trace[trace_id].append(span)
142
+
143
+ # Check if this span is a root span (no parent)
144
+ if not span.parent:
145
+ root_span_traces.add(trace_id)
146
+
147
+ # Add spans to their respective trace buffers
148
+ for trace_id, trace_spans in spans_by_trace.items():
149
+ has_root = trace_id in root_span_traces
150
+ self._add_spans_to_trace(trace_id, trace_spans, has_root)
151
+
152
+ # Upload complete traces (those with root spans)
153
+ for trace_id in root_span_traces:
154
+ if self.task_processor is not None and callable(getattr(self.task_processor, 'queue_task', None)):
155
+ # Queue the upload task
156
+ if trace_id in self.trace_spans:
157
+ spans_to_upload, _, _ = self.trace_spans[trace_id]
158
+ serialized_data = self.__serialize_spans(spans_to_upload)
159
+ if serialized_data:
160
+ self.task_processor.queue_task(self.__upload_to_blob_with_trace_id, serialized_data, trace_id)
161
+ del self.trace_spans[trace_id]
162
+ else:
163
+ self._upload_trace(trace_id)
164
+ except Exception as e:
165
+ logger.error(f"Error in _export_async: {e}")
166
+
167
+ def __serialize_spans(self, spans: Sequence[ReadableSpan]) -> str:
168
+ try:
169
+ valid_json_list = []
170
+ for span in spans:
171
+ try:
172
+ valid_json_list.append(span.to_json(indent=0).replace("\n", ""))
173
+ except json.JSONDecodeError as e:
174
+ logger.warning(f"Invalid JSON format in span data: {span.context.span_id}. Error: {e}")
175
+ continue
176
+
177
+ ndjson_data = "\n".join(valid_json_list) + "\n"
178
+ return ndjson_data
179
+ except Exception as e:
180
+ logger.warning(f"Error serializing spans: {e}")
181
+ return ""
182
+
183
+ @SpanExporterBase.retry_with_backoff(exceptions=(ResourceNotFoundError, ClientAuthenticationError, ServiceRequestError))
184
+ def __upload_to_blob_with_trace_id(self, span_data_batch: str, trace_id: int):
185
+ """Upload spans for a specific trace to Azure Blob with trace ID in filename."""
186
+ current_time = datetime.datetime.now().strftime(self.time_format)
187
+ file_name = f"{self.file_prefix}{current_time}_{format_trace_id_without_0x(trace_id)}.ndjson"
188
+ blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name)
189
+ blob_client.upload_blob(span_data_batch, overwrite=True)
190
+ logger.debug(f"Trace {format_trace_id_without_0x(trace_id)} uploaded to Azure Blob Storage as {file_name}.")
191
+
192
+ async def force_flush(self, timeout_millis: int = 30000) -> bool:
193
+ """Flush all pending traces to Azure Blob."""
194
+ trace_ids_to_upload = list(self.trace_spans.keys())
195
+ for trace_id in trace_ids_to_upload:
196
+ self._upload_trace(trace_id)
197
+ return True
198
+
199
+ def shutdown(self) -> None:
200
+ """Upload all pending traces and shutdown."""
201
+ # Upload all remaining traces
202
+ trace_ids_to_upload = list(self.trace_spans.keys())
203
+ for trace_id in trace_ids_to_upload:
204
+ self._upload_trace(trace_id)
205
+
206
+ if hasattr(self, 'task_processor') and self.task_processor is not None:
207
+ self.task_processor.stop()
208
+ logger.info("AzureBlobSpanExporter has been shut down.")
@@ -51,4 +51,13 @@ class SpanExporterBase(ABC):
51
51
 
52
52
  return wrapper
53
53
 
54
- return decorator
54
+ return decorator
55
+
56
+
57
+ def format_trace_id_without_0x(trace_id: int) -> str:
58
+ """Format trace_id as 32-character lowercase hex string without 0x prefix."""
59
+ return f"{trace_id:032x}"
60
+
61
+ def format_span_id_without_0x(span_id: int) -> str:
62
+ """Format span_id as 16-character lowercase hex string without 0x prefix."""
63
+ return f"{span_id:016x}"
@@ -8,7 +8,7 @@ from typing import Optional, Callable, Sequence, Dict, Tuple
8
8
  from opentelemetry.sdk.trace import ReadableSpan
9
9
  from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
10
10
  from opentelemetry.sdk.resources import SERVICE_NAME
11
- from monocle_apptrace.exporters.base_exporter import SpanExporterBase
11
+ from monocle_apptrace.exporters.base_exporter import SpanExporterBase, format_trace_id_without_0x
12
12
  from monocle_apptrace.exporters.exporter_processor import ExportTaskProcessor
13
13
 
14
14
  DEFAULT_FILE_PREFIX:str = "monocle_trace_"
@@ -25,7 +25,7 @@ class FileSpanExporter(SpanExporterBase):
25
25
  time_format = DEFAULT_TIME_FORMAT,
26
26
  formatter: Callable[
27
27
  [ReadableSpan], str
28
- ] = lambda span: span.to_json()
28
+ ] = lambda span: span.to_json(indent = 4)
29
29
  + linesep,
30
30
  task_processor: Optional[ExportTaskProcessor] = None
31
31
  ):
@@ -80,7 +80,7 @@ class FileSpanExporter(SpanExporterBase):
80
80
 
81
81
  # Create new handle
82
82
  file_path = path.join(self.output_path,
83
- self.file_prefix + service_name + "_" + hex(trace_id) + "_"
83
+ self.file_prefix + service_name + "_" + format_trace_id_without_0x(trace_id) + "_"
84
84
  + datetime.now().strftime(self.time_format) + ".json")
85
85
 
86
86
  try:
@@ -63,13 +63,6 @@ class OkahuSpanExporter(SpanExporterBase):
63
63
  continue
64
64
  # create a object from serialized span
65
65
  obj = json.loads(span.to_json())
66
- if obj["parent_id"] is None:
67
- obj["parent_id"] = "None"
68
- else:
69
- obj["parent_id"] = remove_0x_from_start(obj["parent_id"])
70
- if obj["context"] is not None:
71
- obj["context"]["trace_id"] = remove_0x_from_start(obj["context"]["trace_id"])
72
- obj["context"]["span_id"] = remove_0x_from_start(obj["context"]["span_id"])
73
66
  span_list["batch"].append(obj)
74
67
 
75
68
  # Calculate is_root_span by checking if any span has no parent
@@ -110,11 +103,4 @@ class OkahuSpanExporter(SpanExporterBase):
110
103
  self._closed = True
111
104
 
112
105
  def force_flush(self, timeout_millis: int = 30000) -> bool:
113
- return True
114
-
115
-
116
- # only removes the first occurrence of 0x from the string
117
- def remove_0x_from_start(my_str: str):
118
- if my_str.startswith("0x"):
119
- return my_str.replace("0x", "", 1)
120
- return my_str
106
+ return True